Composer Version Change
This commit is contained in:
parent
20678a6a0c
commit
6f751ead83
@ -24,7 +24,7 @@ class IndexController
|
||||
public function domain(Request $request)
|
||||
{
|
||||
View::assign('userinfo', $request->session()->get('userinfo'));
|
||||
return view('index');
|
||||
return view('domain');
|
||||
}
|
||||
public function json(Request $request)
|
||||
{
|
||||
|
112
app/view/domain.html
Normal file
112
app/view/domain.html
Normal file
@ -0,0 +1,112 @@
|
||||
<?php
|
||||
$page_title='域名列表';
|
||||
include('common_head.html');?>
|
||||
<link href="vendor/datatables/dataTables.bootstrap4.min.css" rel="stylesheet">
|
||||
<!-- Begin Page Content -->
|
||||
<div class="container-fluid">
|
||||
|
||||
<!-- Page Heading -->
|
||||
<h1 class="h3 mb-4 text-gray-800">域名列表</h1>
|
||||
<a href="#" class="btn btn-primary btn-icon-split ">
|
||||
<span class="icon text-white-50">
|
||||
<i class="fas fa-plus"></i>
|
||||
</span>
|
||||
<span class="text">添加新域名</span>
|
||||
</a>
|
||||
<div class="my-2"></div>
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 font-weight-bold text-primary">域名列表</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
如遇需更改账号,请先以新账号添加用户,再删除旧账号
|
||||
<div><code id="notice"></code></div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>头像</th>
|
||||
<th>姓名</th>
|
||||
<th>邮箱</th>
|
||||
<th>账号</th>
|
||||
<th>密码</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th>头像</th>
|
||||
<th>姓名</th>
|
||||
<th>邮箱</th>
|
||||
<th>账号</th>
|
||||
<th>密码</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>新用户</td>
|
||||
<td>管理员</td>
|
||||
<td>admin@admin.com</td>
|
||||
<td>admin</td>
|
||||
<td>Start date</td>
|
||||
<td>Salary</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.container-fluid -->
|
||||
|
||||
</div>
|
||||
<!-- End of Main Content -->
|
||||
|
||||
|
||||
<?php include('common_foot.html');?>
|
||||
|
||||
<!-- Page level plugins -->
|
||||
<script src="vendor/datatables/jquery.dataTables.min.js"></script>
|
||||
<script src="vendor/datatables/dataTables.bootstrap4.min.js"></script>
|
||||
|
||||
<!-- Page level custom scripts -->
|
||||
<script src="js/demo/datatables-demo.js"></script>
|
||||
<script>
|
||||
function change($account){
|
||||
var name = $("#"+$account+'name').val();
|
||||
var password = $("#"+$account+"password").val();
|
||||
var email = $("#"+$account+"email").val();
|
||||
if(name=="" || email==""){
|
||||
$('#notice').html('数据不完整');
|
||||
return false;
|
||||
}
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: '/api/user/change',
|
||||
data: {'name':name,'password':password,'email':email,'account':$account},
|
||||
async: false,
|
||||
dataType: 'json',
|
||||
cache: false,
|
||||
beforeSubmit: function () {
|
||||
$('#notice').html('正在操作');
|
||||
},
|
||||
success: function (data) {
|
||||
if (data.code == 200) {
|
||||
$('#notice').html('操作成功,即将刷新页面');
|
||||
$(location).attr('href','user');
|
||||
} else {
|
||||
$('#notice').html(data.msg);
|
||||
}
|
||||
},
|
||||
clearForm: false,
|
||||
resetForm: false
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
@ -32,11 +32,15 @@
|
||||
"geoip2/geoip2": "~2.0",
|
||||
"illuminate/redis": "^9.45",
|
||||
"symfony/cache": "^6.0",
|
||||
"illuminate/events": "^9.45",
|
||||
"illuminate/events": "^9.52",
|
||||
"yzh52521/webman-throttle": "^1.0",
|
||||
"workerman/validation": "^3.0",
|
||||
"yzh52521/easyhttp": "^1.0",
|
||||
"laysense/dns": "^0.1.0"
|
||||
"laysense/dns": "^0.1.0",
|
||||
"illuminate/database": "^9.52",
|
||||
"illuminate/pagination": "^9.52",
|
||||
"symfony/var-dumper": "^6.0",
|
||||
"robmorgan/phinx": "^0.14.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-event": "For better performance. "
|
||||
@ -63,8 +67,5 @@
|
||||
"pre-package-uninstall": [
|
||||
"support\\Plugin::uninstall"
|
||||
]
|
||||
},
|
||||
"require-dev": {
|
||||
"webman-tech/debugbar": "^2.1"
|
||||
}
|
||||
}
|
||||
|
1366
composer.lock
generated
1366
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
$enable = config('app.debug', false) && class_exists('WebmanTech\Debugbar\WebmanDebugBar');
|
||||
|
||||
return [
|
||||
'enable' => $enable,
|
||||
/**
|
||||
* @see \WebmanTech\Debugbar\WebmanDebugBar::$config
|
||||
*/
|
||||
'debugbar' => [
|
||||
'enable' => $enable,
|
||||
],
|
||||
];
|
@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
use WebmanTech\Debugbar\Bootstrap\LaravelQuery;
|
||||
use WebmanTech\Debugbar\Bootstrap\LaravelRedisExec;
|
||||
|
||||
return [
|
||||
LaravelQuery::class,
|
||||
LaravelRedisExec::class,
|
||||
];
|
@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
use WebmanTech\Debugbar\Middleware\DebugBarMiddleware;
|
||||
|
||||
return [
|
||||
'' => [
|
||||
DebugBarMiddleware::class,
|
||||
],
|
||||
];
|
@ -1,5 +0,0 @@
|
||||
<?php
|
||||
|
||||
use WebmanTech\Debugbar\DebugBar;
|
||||
|
||||
DebugBar::instance()->registerRoute();
|
24
database/migrations/20240204114015_my_new_migration.php
Normal file
24
database/migrations/20240204114015_my_new_migration.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
final class MyNewMigration extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* Change Method.
|
||||
*
|
||||
* Write your reversible migrations using this method.
|
||||
*
|
||||
* More information on writing migrations is available here:
|
||||
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
|
||||
*
|
||||
* Remember to call "create()" or "update()" and NOT "save()" when working
|
||||
* with the Table class.
|
||||
*/
|
||||
public function change(): void
|
||||
{
|
||||
|
||||
}
|
||||
}
|
21
phinx.php
Normal file
21
phinx.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
return [
|
||||
"paths" => [
|
||||
"migrations" => "database/migrations",
|
||||
"seeds" => "database/seeds"
|
||||
],
|
||||
"environments" => [
|
||||
"default_migration_table" => "phinxlog",
|
||||
"default_database" => "dev",
|
||||
"default_environment" => "dev",
|
||||
"dev" => [
|
||||
"adapter" => "DB_CONNECTION",
|
||||
"host" => "DB_HOST",
|
||||
"name" => "DB_DATABASE",
|
||||
"user" => "DB_USERNAME",
|
||||
"pass" => "DB_PASSWORD",
|
||||
"port" => "DB_PORT",
|
||||
"charset" => "utf8"
|
||||
]
|
||||
]
|
||||
];
|
120
vendor/bin/phinx
vendored
Executable file
120
vendor/bin/phinx
vendored
Executable 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
5
vendor/bin/phinx.bat
vendored
Executable file
@ -0,0 +1,5 @@
|
||||
@ECHO OFF
|
||||
setlocal DISABLEDELAYEDEXPANSION
|
||||
SET BIN_TARGET=%~dp0/phinx
|
||||
SET COMPOSER_RUNTIME_BIN_DIR=%~dp0
|
||||
php "%BIN_TARGET%" %*
|
445
vendor/brick/math/CHANGELOG.md
vendored
Normal file
445
vendor/brick/math/CHANGELOG.md
vendored
Normal file
@ -0,0 +1,445 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [0.11.0](https://github.com/brick/math/releases/tag/0.11.0) - 2023-01-16
|
||||
|
||||
💥 **Breaking changes**
|
||||
|
||||
- Minimum PHP version is now 8.0
|
||||
- Methods accepting a union of types are now strongly typed<sup>*</sup>
|
||||
- `MathException` now extends `Exception` instead of `RuntimeException`
|
||||
|
||||
<sup>* You may now run into type errors if you were passing `Stringable` objects to `of()` or any of the methods
|
||||
internally calling `of()`, with `strict_types` enabled. You can fix this by casting `Stringable` objects to `string`
|
||||
first.</sup>
|
||||
|
||||
## [0.10.2](https://github.com/brick/math/releases/tag/0.10.2) - 2022-08-11
|
||||
|
||||
👌 **Improvements**
|
||||
|
||||
- `BigRational::toFloat()` now simplifies the fraction before performing division (#73) thanks to @olsavmic
|
||||
|
||||
## [0.10.1](https://github.com/brick/math/releases/tag/0.10.1) - 2022-08-02
|
||||
|
||||
✨ **New features**
|
||||
|
||||
- `BigInteger::gcdMultiple()` returns the GCD of multiple `BigInteger` numbers
|
||||
|
||||
## [0.10.0](https://github.com/brick/math/releases/tag/0.10.0) - 2022-06-18
|
||||
|
||||
💥 **Breaking changes**
|
||||
|
||||
- Minimum PHP version is now 7.4
|
||||
|
||||
## [0.9.3](https://github.com/brick/math/releases/tag/0.9.3) - 2021-08-15
|
||||
|
||||
🚀 **Compatibility with PHP 8.1**
|
||||
|
||||
- Support for custom object serialization; this removes a warning on PHP 8.1 due to the `Serializable` interface being deprecated (#60) thanks @TRowbotham
|
||||
|
||||
## [0.9.2](https://github.com/brick/math/releases/tag/0.9.2) - 2021-01-20
|
||||
|
||||
🐛 **Bug fix**
|
||||
|
||||
- Incorrect results could be returned when using the BCMath calculator, with a default scale set with `bcscale()`, on PHP >= 7.2 (#55).
|
||||
|
||||
## [0.9.1](https://github.com/brick/math/releases/tag/0.9.1) - 2020-08-19
|
||||
|
||||
✨ **New features**
|
||||
|
||||
- `BigInteger::not()` returns the bitwise `NOT` value
|
||||
|
||||
🐛 **Bug fixes**
|
||||
|
||||
- `BigInteger::toBytes()` could return an incorrect binary representation for some numbers
|
||||
- The bitwise operations `and()`, `or()`, `xor()` on `BigInteger` could return an incorrect result when the GMP extension is not available
|
||||
|
||||
## [0.9.0](https://github.com/brick/math/releases/tag/0.9.0) - 2020-08-18
|
||||
|
||||
👌 **Improvements**
|
||||
|
||||
- `BigNumber::of()` now accepts `.123` and `123.` formats, both of which return a `BigDecimal`
|
||||
|
||||
💥 **Breaking changes**
|
||||
|
||||
- Deprecated method `BigInteger::powerMod()` has been removed - use `modPow()` instead
|
||||
- Deprecated method `BigInteger::parse()` has been removed - use `fromBase()` instead
|
||||
|
||||
## [0.8.17](https://github.com/brick/math/releases/tag/0.8.17) - 2020-08-19
|
||||
|
||||
🐛 **Bug fix**
|
||||
|
||||
- `BigInteger::toBytes()` could return an incorrect binary representation for some numbers
|
||||
- The bitwise operations `and()`, `or()`, `xor()` on `BigInteger` could return an incorrect result when the GMP extension is not available
|
||||
|
||||
## [0.8.16](https://github.com/brick/math/releases/tag/0.8.16) - 2020-08-18
|
||||
|
||||
🚑 **Critical fix**
|
||||
|
||||
- This version reintroduces the deprecated `BigInteger::parse()` method, that has been removed by mistake in version `0.8.9` and should have lasted for the whole `0.8` release cycle.
|
||||
|
||||
✨ **New features**
|
||||
|
||||
- `BigInteger::modInverse()` calculates a modular multiplicative inverse
|
||||
- `BigInteger::fromBytes()` creates a `BigInteger` from a byte string
|
||||
- `BigInteger::toBytes()` converts a `BigInteger` to a byte string
|
||||
- `BigInteger::randomBits()` creates a pseudo-random `BigInteger` of a given bit length
|
||||
- `BigInteger::randomRange()` creates a pseudo-random `BigInteger` between two bounds
|
||||
|
||||
💩 **Deprecations**
|
||||
|
||||
- `BigInteger::powerMod()` is now deprecated in favour of `modPow()`
|
||||
|
||||
## [0.8.15](https://github.com/brick/math/releases/tag/0.8.15) - 2020-04-15
|
||||
|
||||
🐛 **Fixes**
|
||||
|
||||
- added missing `ext-json` requirement, due to `BigNumber` implementing `JsonSerializable`
|
||||
|
||||
⚡️ **Optimizations**
|
||||
|
||||
- additional optimization in `BigInteger::remainder()`
|
||||
|
||||
## [0.8.14](https://github.com/brick/math/releases/tag/0.8.14) - 2020-02-18
|
||||
|
||||
✨ **New features**
|
||||
|
||||
- `BigInteger::getLowestSetBit()` returns the index of the rightmost one bit
|
||||
|
||||
## [0.8.13](https://github.com/brick/math/releases/tag/0.8.13) - 2020-02-16
|
||||
|
||||
✨ **New features**
|
||||
|
||||
- `BigInteger::isEven()` tests whether the number is even
|
||||
- `BigInteger::isOdd()` tests whether the number is odd
|
||||
- `BigInteger::testBit()` tests if a bit is set
|
||||
- `BigInteger::getBitLength()` returns the number of bits in the minimal representation of the number
|
||||
|
||||
## [0.8.12](https://github.com/brick/math/releases/tag/0.8.12) - 2020-02-03
|
||||
|
||||
🛠️ **Maintenance release**
|
||||
|
||||
Classes are now annotated for better static analysis with [psalm](https://psalm.dev/).
|
||||
|
||||
This is a maintenance release: no bug fixes, no new features, no breaking changes.
|
||||
|
||||
## [0.8.11](https://github.com/brick/math/releases/tag/0.8.11) - 2020-01-23
|
||||
|
||||
✨ **New feature**
|
||||
|
||||
`BigInteger::powerMod()` performs a power-with-modulo operation. Useful for crypto.
|
||||
|
||||
## [0.8.10](https://github.com/brick/math/releases/tag/0.8.10) - 2020-01-21
|
||||
|
||||
✨ **New feature**
|
||||
|
||||
`BigInteger::mod()` returns the **modulo** of two numbers. The *modulo* differs from the *remainder* when the signs of the operands are different.
|
||||
|
||||
## [0.8.9](https://github.com/brick/math/releases/tag/0.8.9) - 2020-01-08
|
||||
|
||||
⚡️ **Performance improvements**
|
||||
|
||||
A few additional optimizations in `BigInteger` and `BigDecimal` when one of the operands can be returned as is. Thanks to @tomtomsen in #24.
|
||||
|
||||
## [0.8.8](https://github.com/brick/math/releases/tag/0.8.8) - 2019-04-25
|
||||
|
||||
🐛 **Bug fixes**
|
||||
|
||||
- `BigInteger::toBase()` could return an empty string for zero values (BCMath & Native calculators only, GMP calculator unaffected)
|
||||
|
||||
✨ **New features**
|
||||
|
||||
- `BigInteger::toArbitraryBase()` converts a number to an arbitrary base, using a custom alphabet
|
||||
- `BigInteger::fromArbitraryBase()` converts a string in an arbitrary base, using a custom alphabet, back to a number
|
||||
|
||||
These methods can be used as the foundation to convert strings between different bases/alphabets, using BigInteger as an intermediate representation.
|
||||
|
||||
💩 **Deprecations**
|
||||
|
||||
- `BigInteger::parse()` is now deprecated in favour of `fromBase()`
|
||||
|
||||
`BigInteger::fromBase()` works the same way as `parse()`, with 2 minor differences:
|
||||
|
||||
- the `$base` parameter is required, it does not default to `10`
|
||||
- it throws a `NumberFormatException` instead of an `InvalidArgumentException` when the number is malformed
|
||||
|
||||
## [0.8.7](https://github.com/brick/math/releases/tag/0.8.7) - 2019-04-20
|
||||
|
||||
**Improvements**
|
||||
|
||||
- Safer conversion from `float` when using custom locales
|
||||
- **Much faster** `NativeCalculator` implementation 🚀
|
||||
|
||||
You can expect **at least a 3x performance improvement** for common arithmetic operations when using the library on systems without GMP or BCMath; it gets exponentially faster on multiplications with a high number of digits. This is due to calculations now being performed on whole blocks of digits (the block size depending on the platform, 32-bit or 64-bit) instead of digit-by-digit as before.
|
||||
|
||||
## [0.8.6](https://github.com/brick/math/releases/tag/0.8.6) - 2019-04-11
|
||||
|
||||
**New method**
|
||||
|
||||
`BigNumber::sum()` returns the sum of one or more numbers.
|
||||
|
||||
## [0.8.5](https://github.com/brick/math/releases/tag/0.8.5) - 2019-02-12
|
||||
|
||||
**Bug fix**: `of()` factory methods could fail when passing a `float` in environments using a `LC_NUMERIC` locale with a decimal separator other than `'.'` (#20).
|
||||
|
||||
Thanks @manowark 👍
|
||||
|
||||
## [0.8.4](https://github.com/brick/math/releases/tag/0.8.4) - 2018-12-07
|
||||
|
||||
**New method**
|
||||
|
||||
`BigDecimal::sqrt()` calculates the square root of a decimal number, to a given scale.
|
||||
|
||||
## [0.8.3](https://github.com/brick/math/releases/tag/0.8.3) - 2018-12-06
|
||||
|
||||
**New method**
|
||||
|
||||
`BigInteger::sqrt()` calculates the square root of a number (thanks @peter279k).
|
||||
|
||||
**New exception**
|
||||
|
||||
`NegativeNumberException` is thrown when calling `sqrt()` on a negative number.
|
||||
|
||||
## [0.8.2](https://github.com/brick/math/releases/tag/0.8.2) - 2018-11-08
|
||||
|
||||
**Performance update**
|
||||
|
||||
- Further improvement of `toInt()` performance
|
||||
- `NativeCalculator` can now perform some multiplications more efficiently
|
||||
|
||||
## [0.8.1](https://github.com/brick/math/releases/tag/0.8.1) - 2018-11-07
|
||||
|
||||
Performance optimization of `toInt()` methods.
|
||||
|
||||
## [0.8.0](https://github.com/brick/math/releases/tag/0.8.0) - 2018-10-13
|
||||
|
||||
**Breaking changes**
|
||||
|
||||
The following deprecated methods have been removed. Use the new method name instead:
|
||||
|
||||
| Method removed | Replacement method |
|
||||
| --- | --- |
|
||||
| `BigDecimal::getIntegral()` | `BigDecimal::getIntegralPart()` |
|
||||
| `BigDecimal::getFraction()` | `BigDecimal::getFractionalPart()` |
|
||||
|
||||
---
|
||||
|
||||
**New features**
|
||||
|
||||
`BigInteger` has been augmented with 5 new methods for bitwise operations:
|
||||
|
||||
| New method | Description |
|
||||
| --- | --- |
|
||||
| `and()` | performs a bitwise `AND` operation on two numbers |
|
||||
| `or()` | performs a bitwise `OR` operation on two numbers |
|
||||
| `xor()` | performs a bitwise `XOR` operation on two numbers |
|
||||
| `shiftedLeft()` | returns the number shifted left by a number of bits |
|
||||
| `shiftedRight()` | returns the number shifted right by a number of bits |
|
||||
|
||||
Thanks to @DASPRiD 👍
|
||||
|
||||
## [0.7.3](https://github.com/brick/math/releases/tag/0.7.3) - 2018-08-20
|
||||
|
||||
**New method:** `BigDecimal::hasNonZeroFractionalPart()`
|
||||
|
||||
**Renamed/deprecated methods:**
|
||||
|
||||
- `BigDecimal::getIntegral()` has been renamed to `getIntegralPart()` and is now deprecated
|
||||
- `BigDecimal::getFraction()` has been renamed to `getFractionalPart()` and is now deprecated
|
||||
|
||||
## [0.7.2](https://github.com/brick/math/releases/tag/0.7.2) - 2018-07-21
|
||||
|
||||
**Performance update**
|
||||
|
||||
`BigInteger::parse()` and `toBase()` now use GMP's built-in base conversion features when available.
|
||||
|
||||
## [0.7.1](https://github.com/brick/math/releases/tag/0.7.1) - 2018-03-01
|
||||
|
||||
This is a maintenance release, no code has been changed.
|
||||
|
||||
- When installed with `--no-dev`, the autoloader does not autoload tests anymore
|
||||
- Tests and other files unnecessary for production are excluded from the dist package
|
||||
|
||||
This will help make installations more compact.
|
||||
|
||||
## [0.7.0](https://github.com/brick/math/releases/tag/0.7.0) - 2017-10-02
|
||||
|
||||
Methods renamed:
|
||||
|
||||
- `BigNumber:sign()` has been renamed to `getSign()`
|
||||
- `BigDecimal::unscaledValue()` has been renamed to `getUnscaledValue()`
|
||||
- `BigDecimal::scale()` has been renamed to `getScale()`
|
||||
- `BigDecimal::integral()` has been renamed to `getIntegral()`
|
||||
- `BigDecimal::fraction()` has been renamed to `getFraction()`
|
||||
- `BigRational::numerator()` has been renamed to `getNumerator()`
|
||||
- `BigRational::denominator()` has been renamed to `getDenominator()`
|
||||
|
||||
Classes renamed:
|
||||
|
||||
- `ArithmeticException` has been renamed to `MathException`
|
||||
|
||||
## [0.6.2](https://github.com/brick/math/releases/tag/0.6.2) - 2017-10-02
|
||||
|
||||
The base class for all exceptions is now `MathException`.
|
||||
`ArithmeticException` has been deprecated, and will be removed in 0.7.0.
|
||||
|
||||
## [0.6.1](https://github.com/brick/math/releases/tag/0.6.1) - 2017-10-02
|
||||
|
||||
A number of methods have been renamed:
|
||||
|
||||
- `BigNumber:sign()` is deprecated; use `getSign()` instead
|
||||
- `BigDecimal::unscaledValue()` is deprecated; use `getUnscaledValue()` instead
|
||||
- `BigDecimal::scale()` is deprecated; use `getScale()` instead
|
||||
- `BigDecimal::integral()` is deprecated; use `getIntegral()` instead
|
||||
- `BigDecimal::fraction()` is deprecated; use `getFraction()` instead
|
||||
- `BigRational::numerator()` is deprecated; use `getNumerator()` instead
|
||||
- `BigRational::denominator()` is deprecated; use `getDenominator()` instead
|
||||
|
||||
The old methods will be removed in version 0.7.0.
|
||||
|
||||
## [0.6.0](https://github.com/brick/math/releases/tag/0.6.0) - 2017-08-25
|
||||
|
||||
- Minimum PHP version is now [7.1](https://gophp71.org/); for PHP 5.6 and PHP 7.0 support, use version `0.5`
|
||||
- Deprecated method `BigDecimal::withScale()` has been removed; use `toScale()` instead
|
||||
- Method `BigNumber::toInteger()` has been renamed to `toInt()`
|
||||
|
||||
## [0.5.4](https://github.com/brick/math/releases/tag/0.5.4) - 2016-10-17
|
||||
|
||||
`BigNumber` classes now implement [JsonSerializable](http://php.net/manual/en/class.jsonserializable.php).
|
||||
The JSON output is always a string.
|
||||
|
||||
## [0.5.3](https://github.com/brick/math/releases/tag/0.5.3) - 2016-03-31
|
||||
|
||||
This is a bugfix release. Dividing by a negative power of 1 with the same scale as the dividend could trigger an incorrect optimization which resulted in a wrong result. See #6.
|
||||
|
||||
## [0.5.2](https://github.com/brick/math/releases/tag/0.5.2) - 2015-08-06
|
||||
|
||||
The `$scale` parameter of `BigDecimal::dividedBy()` is now optional again.
|
||||
|
||||
## [0.5.1](https://github.com/brick/math/releases/tag/0.5.1) - 2015-07-05
|
||||
|
||||
**New method: `BigNumber::toScale()`**
|
||||
|
||||
This allows to convert any `BigNumber` to a `BigDecimal` with a given scale, using rounding if necessary.
|
||||
|
||||
## [0.5.0](https://github.com/brick/math/releases/tag/0.5.0) - 2015-07-04
|
||||
|
||||
**New features**
|
||||
- Common `BigNumber` interface for all classes, with the following methods:
|
||||
- `sign()` and derived methods (`isZero()`, `isPositive()`, ...)
|
||||
- `compareTo()` and derived methods (`isEqualTo()`, `isGreaterThan()`, ...) that work across different `BigNumber` types
|
||||
- `toBigInteger()`, `toBigDecimal()`, `toBigRational`() conversion methods
|
||||
- `toInteger()` and `toFloat()` conversion methods to native types
|
||||
- Unified `of()` behaviour: every class now accepts any type of number, provided that it can be safely converted to the current type
|
||||
- New method: `BigDecimal::exactlyDividedBy()`; this method automatically computes the scale of the result, provided that the division yields a finite number of digits
|
||||
- New methods: `BigRational::quotient()` and `remainder()`
|
||||
- Fine-grained exceptions: `DivisionByZeroException`, `RoundingNecessaryException`, `NumberFormatException`
|
||||
- Factory methods `zero()`, `one()` and `ten()` available in all classes
|
||||
- Rounding mode reintroduced in `BigInteger::dividedBy()`
|
||||
|
||||
This release also comes with many performance improvements.
|
||||
|
||||
---
|
||||
|
||||
**Breaking changes**
|
||||
- `BigInteger`:
|
||||
- `getSign()` is renamed to `sign()`
|
||||
- `toString()` is renamed to `toBase()`
|
||||
- `BigInteger::dividedBy()` now throws an exception by default if the remainder is not zero; use `quotient()` to get the previous behaviour
|
||||
- `BigDecimal`:
|
||||
- `getSign()` is renamed to `sign()`
|
||||
- `getUnscaledValue()` is renamed to `unscaledValue()`
|
||||
- `getScale()` is renamed to `scale()`
|
||||
- `getIntegral()` is renamed to `integral()`
|
||||
- `getFraction()` is renamed to `fraction()`
|
||||
- `divideAndRemainder()` is renamed to `quotientAndRemainder()`
|
||||
- `dividedBy()` now takes a **mandatory** `$scale` parameter **before** the rounding mode
|
||||
- `toBigInteger()` does not accept a `$roundingMode` parameter anymore
|
||||
- `toBigRational()` does not simplify the fraction anymore; explicitly add `->simplified()` to get the previous behaviour
|
||||
- `BigRational`:
|
||||
- `getSign()` is renamed to `sign()`
|
||||
- `getNumerator()` is renamed to `numerator()`
|
||||
- `getDenominator()` is renamed to `denominator()`
|
||||
- `of()` is renamed to `nd()`, while `parse()` is renamed to `of()`
|
||||
- Miscellaneous:
|
||||
- `ArithmeticException` is moved to an `Exception\` sub-namespace
|
||||
- `of()` factory methods now throw `NumberFormatException` instead of `InvalidArgumentException`
|
||||
|
||||
## [0.4.3](https://github.com/brick/math/releases/tag/0.4.3) - 2016-03-31
|
||||
|
||||
Backport of two bug fixes from the 0.5 branch:
|
||||
- `BigInteger::parse()` did not always throw `InvalidArgumentException` as expected
|
||||
- Dividing by a negative power of 1 with the same scale as the dividend could trigger an incorrect optimization which resulted in a wrong result. See #6.
|
||||
|
||||
## [0.4.2](https://github.com/brick/math/releases/tag/0.4.2) - 2015-06-16
|
||||
|
||||
New method: `BigDecimal::stripTrailingZeros()`
|
||||
|
||||
## [0.4.1](https://github.com/brick/math/releases/tag/0.4.1) - 2015-06-12
|
||||
|
||||
Introducing a `BigRational` class, to perform calculations on fractions of any size.
|
||||
|
||||
## [0.4.0](https://github.com/brick/math/releases/tag/0.4.0) - 2015-06-12
|
||||
|
||||
Rounding modes have been removed from `BigInteger`, and are now a concept specific to `BigDecimal`.
|
||||
|
||||
`BigInteger::dividedBy()` now always returns the quotient of the division.
|
||||
|
||||
## [0.3.5](https://github.com/brick/math/releases/tag/0.3.5) - 2016-03-31
|
||||
|
||||
Backport of two bug fixes from the 0.5 branch:
|
||||
|
||||
- `BigInteger::parse()` did not always throw `InvalidArgumentException` as expected
|
||||
- Dividing by a negative power of 1 with the same scale as the dividend could trigger an incorrect optimization which resulted in a wrong result. See #6.
|
||||
|
||||
## [0.3.4](https://github.com/brick/math/releases/tag/0.3.4) - 2015-06-11
|
||||
|
||||
New methods:
|
||||
- `BigInteger::remainder()` returns the remainder of a division only
|
||||
- `BigInteger::gcd()` returns the greatest common divisor of two numbers
|
||||
|
||||
## [0.3.3](https://github.com/brick/math/releases/tag/0.3.3) - 2015-06-07
|
||||
|
||||
Fix `toString()` not handling negative numbers.
|
||||
|
||||
## [0.3.2](https://github.com/brick/math/releases/tag/0.3.2) - 2015-06-07
|
||||
|
||||
`BigInteger` and `BigDecimal` now have a `getSign()` method that returns:
|
||||
- `-1` if the number is negative
|
||||
- `0` if the number is zero
|
||||
- `1` if the number is positive
|
||||
|
||||
## [0.3.1](https://github.com/brick/math/releases/tag/0.3.1) - 2015-06-05
|
||||
|
||||
Minor performance improvements
|
||||
|
||||
## [0.3.0](https://github.com/brick/math/releases/tag/0.3.0) - 2015-06-04
|
||||
|
||||
The `$roundingMode` and `$scale` parameters have been swapped in `BigDecimal::dividedBy()`.
|
||||
|
||||
## [0.2.2](https://github.com/brick/math/releases/tag/0.2.2) - 2015-06-04
|
||||
|
||||
Stronger immutability guarantee for `BigInteger` and `BigDecimal`.
|
||||
|
||||
So far, it would have been possible to break immutability of these classes by calling the `unserialize()` internal function. This release fixes that.
|
||||
|
||||
## [0.2.1](https://github.com/brick/math/releases/tag/0.2.1) - 2015-06-02
|
||||
|
||||
Added `BigDecimal::divideAndRemainder()`
|
||||
|
||||
## [0.2.0](https://github.com/brick/math/releases/tag/0.2.0) - 2015-05-22
|
||||
|
||||
- `min()` and `max()` do not accept an `array` anymore, but a variable number of parameters
|
||||
- **minimum PHP version is now 5.6**
|
||||
- continuous integration with PHP 7
|
||||
|
||||
## [0.1.1](https://github.com/brick/math/releases/tag/0.1.1) - 2014-09-01
|
||||
|
||||
- Added `BigInteger::power()`
|
||||
- Added HHVM support
|
||||
|
||||
## [0.1.0](https://github.com/brick/math/releases/tag/0.1.0) - 2014-08-31
|
||||
|
||||
First beta release.
|
||||
|
20
vendor/brick/math/LICENSE
vendored
Normal file
20
vendor/brick/math/LICENSE
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013-present Benjamin Morel
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
34
vendor/brick/math/composer.json
vendored
Normal file
34
vendor/brick/math/composer.json
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "brick/math",
|
||||
"description": "Arbitrary-precision arithmetic library",
|
||||
"type": "library",
|
||||
"keywords": [
|
||||
"Brick",
|
||||
"Math",
|
||||
"Arbitrary-precision",
|
||||
"Arithmetic",
|
||||
"BigInteger",
|
||||
"BigDecimal",
|
||||
"BigRational",
|
||||
"Bignum"
|
||||
],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.0",
|
||||
"php-coveralls/php-coveralls": "^2.2",
|
||||
"vimeo/psalm": "5.0.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Brick\\Math\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Brick\\Math\\Tests\\": "tests/"
|
||||
}
|
||||
}
|
||||
}
|
786
vendor/brick/math/src/BigDecimal.php
vendored
Normal file
786
vendor/brick/math/src/BigDecimal.php
vendored
Normal file
@ -0,0 +1,786 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math;
|
||||
|
||||
use Brick\Math\Exception\DivisionByZeroException;
|
||||
use Brick\Math\Exception\MathException;
|
||||
use Brick\Math\Exception\NegativeNumberException;
|
||||
use Brick\Math\Internal\Calculator;
|
||||
|
||||
/**
|
||||
* Immutable, arbitrary-precision signed decimal numbers.
|
||||
*
|
||||
* @psalm-immutable
|
||||
*/
|
||||
final class BigDecimal extends BigNumber
|
||||
{
|
||||
/**
|
||||
* The unscaled value of this decimal number.
|
||||
*
|
||||
* This is a string of digits with an optional leading minus sign.
|
||||
* No leading zero must be present.
|
||||
* No leading minus sign must be present if the value is 0.
|
||||
*/
|
||||
private string $value;
|
||||
|
||||
/**
|
||||
* The scale (number of digits after the decimal point) of this decimal number.
|
||||
*
|
||||
* This must be zero or more.
|
||||
*/
|
||||
private int $scale;
|
||||
|
||||
/**
|
||||
* Protected constructor. Use a factory method to obtain an instance.
|
||||
*
|
||||
* @param string $value The unscaled value, validated.
|
||||
* @param int $scale The scale, validated.
|
||||
*/
|
||||
protected function __construct(string $value, int $scale = 0)
|
||||
{
|
||||
$this->value = $value;
|
||||
$this->scale = $scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BigDecimal of the given value.
|
||||
*
|
||||
* @throws MathException If the value cannot be converted to a BigDecimal.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function of(BigNumber|int|float|string $value) : BigDecimal
|
||||
{
|
||||
return parent::of($value)->toBigDecimal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BigDecimal from an unscaled value and a scale.
|
||||
*
|
||||
* Example: `(12345, 3)` will result in the BigDecimal `12.345`.
|
||||
*
|
||||
* @param BigNumber|int|float|string $value The unscaled value. Must be convertible to a BigInteger.
|
||||
* @param int $scale The scale of the number, positive or zero.
|
||||
*
|
||||
* @throws \InvalidArgumentException If the scale is negative.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function ofUnscaledValue(BigNumber|int|float|string $value, int $scale = 0) : BigDecimal
|
||||
{
|
||||
if ($scale < 0) {
|
||||
throw new \InvalidArgumentException('The scale cannot be negative.');
|
||||
}
|
||||
|
||||
return new BigDecimal((string) BigInteger::of($value), $scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a BigDecimal representing zero, with a scale of zero.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function zero() : BigDecimal
|
||||
{
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticVariable
|
||||
* @var BigDecimal|null $zero
|
||||
*/
|
||||
static $zero;
|
||||
|
||||
if ($zero === null) {
|
||||
$zero = new BigDecimal('0');
|
||||
}
|
||||
|
||||
return $zero;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a BigDecimal representing one, with a scale of zero.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function one() : BigDecimal
|
||||
{
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticVariable
|
||||
* @var BigDecimal|null $one
|
||||
*/
|
||||
static $one;
|
||||
|
||||
if ($one === null) {
|
||||
$one = new BigDecimal('1');
|
||||
}
|
||||
|
||||
return $one;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a BigDecimal representing ten, with a scale of zero.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function ten() : BigDecimal
|
||||
{
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticVariable
|
||||
* @var BigDecimal|null $ten
|
||||
*/
|
||||
static $ten;
|
||||
|
||||
if ($ten === null) {
|
||||
$ten = new BigDecimal('10');
|
||||
}
|
||||
|
||||
return $ten;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sum of this number and the given one.
|
||||
*
|
||||
* The result has a scale of `max($this->scale, $that->scale)`.
|
||||
*
|
||||
* @param BigNumber|int|float|string $that The number to add. Must be convertible to a BigDecimal.
|
||||
*
|
||||
* @throws MathException If the number is not valid, or is not convertible to a BigDecimal.
|
||||
*/
|
||||
public function plus(BigNumber|int|float|string $that) : BigDecimal
|
||||
{
|
||||
$that = BigDecimal::of($that);
|
||||
|
||||
if ($that->value === '0' && $that->scale <= $this->scale) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($this->value === '0' && $this->scale <= $that->scale) {
|
||||
return $that;
|
||||
}
|
||||
|
||||
[$a, $b] = $this->scaleValues($this, $that);
|
||||
|
||||
$value = Calculator::get()->add($a, $b);
|
||||
$scale = $this->scale > $that->scale ? $this->scale : $that->scale;
|
||||
|
||||
return new BigDecimal($value, $scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the difference of this number and the given one.
|
||||
*
|
||||
* The result has a scale of `max($this->scale, $that->scale)`.
|
||||
*
|
||||
* @param BigNumber|int|float|string $that The number to subtract. Must be convertible to a BigDecimal.
|
||||
*
|
||||
* @throws MathException If the number is not valid, or is not convertible to a BigDecimal.
|
||||
*/
|
||||
public function minus(BigNumber|int|float|string $that) : BigDecimal
|
||||
{
|
||||
$that = BigDecimal::of($that);
|
||||
|
||||
if ($that->value === '0' && $that->scale <= $this->scale) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
[$a, $b] = $this->scaleValues($this, $that);
|
||||
|
||||
$value = Calculator::get()->sub($a, $b);
|
||||
$scale = $this->scale > $that->scale ? $this->scale : $that->scale;
|
||||
|
||||
return new BigDecimal($value, $scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the product of this number and the given one.
|
||||
*
|
||||
* The result has a scale of `$this->scale + $that->scale`.
|
||||
*
|
||||
* @param BigNumber|int|float|string $that The multiplier. Must be convertible to a BigDecimal.
|
||||
*
|
||||
* @throws MathException If the multiplier is not a valid number, or is not convertible to a BigDecimal.
|
||||
*/
|
||||
public function multipliedBy(BigNumber|int|float|string $that) : BigDecimal
|
||||
{
|
||||
$that = BigDecimal::of($that);
|
||||
|
||||
if ($that->value === '1' && $that->scale === 0) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($this->value === '1' && $this->scale === 0) {
|
||||
return $that;
|
||||
}
|
||||
|
||||
$value = Calculator::get()->mul($this->value, $that->value);
|
||||
$scale = $this->scale + $that->scale;
|
||||
|
||||
return new BigDecimal($value, $scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result of the division of this number by the given one, at the given scale.
|
||||
*
|
||||
* @param BigNumber|int|float|string $that The divisor.
|
||||
* @param int|null $scale The desired scale, or null to use the scale of this number.
|
||||
* @param int $roundingMode An optional rounding mode.
|
||||
*
|
||||
* @throws \InvalidArgumentException If the scale or rounding mode is invalid.
|
||||
* @throws MathException If the number is invalid, is zero, or rounding was necessary.
|
||||
*/
|
||||
public function dividedBy(BigNumber|int|float|string $that, ?int $scale = null, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
|
||||
{
|
||||
$that = BigDecimal::of($that);
|
||||
|
||||
if ($that->isZero()) {
|
||||
throw DivisionByZeroException::divisionByZero();
|
||||
}
|
||||
|
||||
if ($scale === null) {
|
||||
$scale = $this->scale;
|
||||
} elseif ($scale < 0) {
|
||||
throw new \InvalidArgumentException('Scale cannot be negative.');
|
||||
}
|
||||
|
||||
if ($that->value === '1' && $that->scale === 0 && $scale === $this->scale) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$p = $this->valueWithMinScale($that->scale + $scale);
|
||||
$q = $that->valueWithMinScale($this->scale - $scale);
|
||||
|
||||
$result = Calculator::get()->divRound($p, $q, $roundingMode);
|
||||
|
||||
return new BigDecimal($result, $scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the exact result of the division of this number by the given one.
|
||||
*
|
||||
* The scale of the result is automatically calculated to fit all the fraction digits.
|
||||
*
|
||||
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
|
||||
*
|
||||
* @throws MathException If the divisor is not a valid number, is not convertible to a BigDecimal, is zero,
|
||||
* or the result yields an infinite number of digits.
|
||||
*/
|
||||
public function exactlyDividedBy(BigNumber|int|float|string $that) : BigDecimal
|
||||
{
|
||||
$that = BigDecimal::of($that);
|
||||
|
||||
if ($that->value === '0') {
|
||||
throw DivisionByZeroException::divisionByZero();
|
||||
}
|
||||
|
||||
[, $b] = $this->scaleValues($this, $that);
|
||||
|
||||
$d = \rtrim($b, '0');
|
||||
$scale = \strlen($b) - \strlen($d);
|
||||
|
||||
$calculator = Calculator::get();
|
||||
|
||||
foreach ([5, 2] as $prime) {
|
||||
for (;;) {
|
||||
$lastDigit = (int) $d[-1];
|
||||
|
||||
if ($lastDigit % $prime !== 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
$d = $calculator->divQ($d, (string) $prime);
|
||||
$scale++;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->dividedBy($that, $scale)->stripTrailingZeros();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this number exponentiated to the given value.
|
||||
*
|
||||
* The result has a scale of `$this->scale * $exponent`.
|
||||
*
|
||||
* @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000.
|
||||
*/
|
||||
public function power(int $exponent) : BigDecimal
|
||||
{
|
||||
if ($exponent === 0) {
|
||||
return BigDecimal::one();
|
||||
}
|
||||
|
||||
if ($exponent === 1) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($exponent < 0 || $exponent > Calculator::MAX_POWER) {
|
||||
throw new \InvalidArgumentException(\sprintf(
|
||||
'The exponent %d is not in the range 0 to %d.',
|
||||
$exponent,
|
||||
Calculator::MAX_POWER
|
||||
));
|
||||
}
|
||||
|
||||
return new BigDecimal(Calculator::get()->pow($this->value, $exponent), $this->scale * $exponent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the quotient of the division of this number by this given one.
|
||||
*
|
||||
* The quotient has a scale of `0`.
|
||||
*
|
||||
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
|
||||
*
|
||||
* @throws MathException If the divisor is not a valid decimal number, or is zero.
|
||||
*/
|
||||
public function quotient(BigNumber|int|float|string $that) : BigDecimal
|
||||
{
|
||||
$that = BigDecimal::of($that);
|
||||
|
||||
if ($that->isZero()) {
|
||||
throw DivisionByZeroException::divisionByZero();
|
||||
}
|
||||
|
||||
$p = $this->valueWithMinScale($that->scale);
|
||||
$q = $that->valueWithMinScale($this->scale);
|
||||
|
||||
$quotient = Calculator::get()->divQ($p, $q);
|
||||
|
||||
return new BigDecimal($quotient, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the remainder of the division of this number by this given one.
|
||||
*
|
||||
* The remainder has a scale of `max($this->scale, $that->scale)`.
|
||||
*
|
||||
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
|
||||
*
|
||||
* @throws MathException If the divisor is not a valid decimal number, or is zero.
|
||||
*/
|
||||
public function remainder(BigNumber|int|float|string $that) : BigDecimal
|
||||
{
|
||||
$that = BigDecimal::of($that);
|
||||
|
||||
if ($that->isZero()) {
|
||||
throw DivisionByZeroException::divisionByZero();
|
||||
}
|
||||
|
||||
$p = $this->valueWithMinScale($that->scale);
|
||||
$q = $that->valueWithMinScale($this->scale);
|
||||
|
||||
$remainder = Calculator::get()->divR($p, $q);
|
||||
|
||||
$scale = $this->scale > $that->scale ? $this->scale : $that->scale;
|
||||
|
||||
return new BigDecimal($remainder, $scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the quotient and remainder of the division of this number by the given one.
|
||||
*
|
||||
* The quotient has a scale of `0`, and the remainder has a scale of `max($this->scale, $that->scale)`.
|
||||
*
|
||||
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
|
||||
*
|
||||
* @return BigDecimal[] An array containing the quotient and the remainder.
|
||||
*
|
||||
* @throws MathException If the divisor is not a valid decimal number, or is zero.
|
||||
*/
|
||||
public function quotientAndRemainder(BigNumber|int|float|string $that) : array
|
||||
{
|
||||
$that = BigDecimal::of($that);
|
||||
|
||||
if ($that->isZero()) {
|
||||
throw DivisionByZeroException::divisionByZero();
|
||||
}
|
||||
|
||||
$p = $this->valueWithMinScale($that->scale);
|
||||
$q = $that->valueWithMinScale($this->scale);
|
||||
|
||||
[$quotient, $remainder] = Calculator::get()->divQR($p, $q);
|
||||
|
||||
$scale = $this->scale > $that->scale ? $this->scale : $that->scale;
|
||||
|
||||
$quotient = new BigDecimal($quotient, 0);
|
||||
$remainder = new BigDecimal($remainder, $scale);
|
||||
|
||||
return [$quotient, $remainder];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the square root of this number, rounded down to the given number of decimals.
|
||||
*
|
||||
* @throws \InvalidArgumentException If the scale is negative.
|
||||
* @throws NegativeNumberException If this number is negative.
|
||||
*/
|
||||
public function sqrt(int $scale) : BigDecimal
|
||||
{
|
||||
if ($scale < 0) {
|
||||
throw new \InvalidArgumentException('Scale cannot be negative.');
|
||||
}
|
||||
|
||||
if ($this->value === '0') {
|
||||
return new BigDecimal('0', $scale);
|
||||
}
|
||||
|
||||
if ($this->value[0] === '-') {
|
||||
throw new NegativeNumberException('Cannot calculate the square root of a negative number.');
|
||||
}
|
||||
|
||||
$value = $this->value;
|
||||
$addDigits = 2 * $scale - $this->scale;
|
||||
|
||||
if ($addDigits > 0) {
|
||||
// add zeros
|
||||
$value .= \str_repeat('0', $addDigits);
|
||||
} elseif ($addDigits < 0) {
|
||||
// trim digits
|
||||
if (-$addDigits >= \strlen($this->value)) {
|
||||
// requesting a scale too low, will always yield a zero result
|
||||
return new BigDecimal('0', $scale);
|
||||
}
|
||||
|
||||
$value = \substr($value, 0, $addDigits);
|
||||
}
|
||||
|
||||
$value = Calculator::get()->sqrt($value);
|
||||
|
||||
return new BigDecimal($value, $scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of this BigDecimal with the decimal point moved $n places to the left.
|
||||
*/
|
||||
public function withPointMovedLeft(int $n) : BigDecimal
|
||||
{
|
||||
if ($n === 0) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($n < 0) {
|
||||
return $this->withPointMovedRight(-$n);
|
||||
}
|
||||
|
||||
return new BigDecimal($this->value, $this->scale + $n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of this BigDecimal with the decimal point moved $n places to the right.
|
||||
*/
|
||||
public function withPointMovedRight(int $n) : BigDecimal
|
||||
{
|
||||
if ($n === 0) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($n < 0) {
|
||||
return $this->withPointMovedLeft(-$n);
|
||||
}
|
||||
|
||||
$value = $this->value;
|
||||
$scale = $this->scale - $n;
|
||||
|
||||
if ($scale < 0) {
|
||||
if ($value !== '0') {
|
||||
$value .= \str_repeat('0', -$scale);
|
||||
}
|
||||
$scale = 0;
|
||||
}
|
||||
|
||||
return new BigDecimal($value, $scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of this BigDecimal with any trailing zeros removed from the fractional part.
|
||||
*/
|
||||
public function stripTrailingZeros() : BigDecimal
|
||||
{
|
||||
if ($this->scale === 0) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$trimmedValue = \rtrim($this->value, '0');
|
||||
|
||||
if ($trimmedValue === '') {
|
||||
return BigDecimal::zero();
|
||||
}
|
||||
|
||||
$trimmableZeros = \strlen($this->value) - \strlen($trimmedValue);
|
||||
|
||||
if ($trimmableZeros === 0) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($trimmableZeros > $this->scale) {
|
||||
$trimmableZeros = $this->scale;
|
||||
}
|
||||
|
||||
$value = \substr($this->value, 0, -$trimmableZeros);
|
||||
$scale = $this->scale - $trimmableZeros;
|
||||
|
||||
return new BigDecimal($value, $scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute value of this number.
|
||||
*/
|
||||
public function abs() : BigDecimal
|
||||
{
|
||||
return $this->isNegative() ? $this->negated() : $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the negated value of this number.
|
||||
*/
|
||||
public function negated() : BigDecimal
|
||||
{
|
||||
return new BigDecimal(Calculator::get()->neg($this->value), $this->scale);
|
||||
}
|
||||
|
||||
public function compareTo(BigNumber|int|float|string $that) : int
|
||||
{
|
||||
$that = BigNumber::of($that);
|
||||
|
||||
if ($that instanceof BigInteger) {
|
||||
$that = $that->toBigDecimal();
|
||||
}
|
||||
|
||||
if ($that instanceof BigDecimal) {
|
||||
[$a, $b] = $this->scaleValues($this, $that);
|
||||
|
||||
return Calculator::get()->cmp($a, $b);
|
||||
}
|
||||
|
||||
return - $that->compareTo($this);
|
||||
}
|
||||
|
||||
public function getSign() : int
|
||||
{
|
||||
return ($this->value === '0') ? 0 : (($this->value[0] === '-') ? -1 : 1);
|
||||
}
|
||||
|
||||
public function getUnscaledValue() : BigInteger
|
||||
{
|
||||
return self::newBigInteger($this->value);
|
||||
}
|
||||
|
||||
public function getScale() : int
|
||||
{
|
||||
return $this->scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representing the integral part of this decimal number.
|
||||
*
|
||||
* Example: `-123.456` => `-123`.
|
||||
*/
|
||||
public function getIntegralPart() : string
|
||||
{
|
||||
if ($this->scale === 0) {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
$value = $this->getUnscaledValueWithLeadingZeros();
|
||||
|
||||
return \substr($value, 0, -$this->scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representing the fractional part of this decimal number.
|
||||
*
|
||||
* If the scale is zero, an empty string is returned.
|
||||
*
|
||||
* Examples: `-123.456` => '456', `123` => ''.
|
||||
*/
|
||||
public function getFractionalPart() : string
|
||||
{
|
||||
if ($this->scale === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$value = $this->getUnscaledValueWithLeadingZeros();
|
||||
|
||||
return \substr($value, -$this->scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this decimal number has a non-zero fractional part.
|
||||
*/
|
||||
public function hasNonZeroFractionalPart() : bool
|
||||
{
|
||||
return $this->getFractionalPart() !== \str_repeat('0', $this->scale);
|
||||
}
|
||||
|
||||
public function toBigInteger() : BigInteger
|
||||
{
|
||||
$zeroScaleDecimal = $this->scale === 0 ? $this : $this->dividedBy(1, 0);
|
||||
|
||||
return self::newBigInteger($zeroScaleDecimal->value);
|
||||
}
|
||||
|
||||
public function toBigDecimal() : BigDecimal
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function toBigRational() : BigRational
|
||||
{
|
||||
$numerator = self::newBigInteger($this->value);
|
||||
$denominator = self::newBigInteger('1' . \str_repeat('0', $this->scale));
|
||||
|
||||
return self::newBigRational($numerator, $denominator, false);
|
||||
}
|
||||
|
||||
public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
|
||||
{
|
||||
if ($scale === $this->scale) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return $this->dividedBy(BigDecimal::one(), $scale, $roundingMode);
|
||||
}
|
||||
|
||||
public function toInt() : int
|
||||
{
|
||||
return $this->toBigInteger()->toInt();
|
||||
}
|
||||
|
||||
public function toFloat() : float
|
||||
{
|
||||
return (float) (string) $this;
|
||||
}
|
||||
|
||||
public function __toString() : string
|
||||
{
|
||||
if ($this->scale === 0) {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
$value = $this->getUnscaledValueWithLeadingZeros();
|
||||
|
||||
return \substr($value, 0, -$this->scale) . '.' . \substr($value, -$this->scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is required for serializing the object and SHOULD NOT be accessed directly.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @return array{value: string, scale: int}
|
||||
*/
|
||||
public function __serialize(): array
|
||||
{
|
||||
return ['value' => $this->value, 'scale' => $this->scale];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is only here to allow unserializing the object and cannot be accessed directly.
|
||||
*
|
||||
* @internal
|
||||
* @psalm-suppress RedundantPropertyInitializationCheck
|
||||
*
|
||||
* @param array{value: string, scale: int} $data
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function __unserialize(array $data): void
|
||||
{
|
||||
if (isset($this->value)) {
|
||||
throw new \LogicException('__unserialize() is an internal function, it must not be called directly.');
|
||||
}
|
||||
|
||||
$this->value = $data['value'];
|
||||
$this->scale = $data['scale'];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is required by interface Serializable and SHOULD NOT be accessed directly.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function serialize() : string
|
||||
{
|
||||
return $this->value . ':' . $this->scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is only here to implement interface Serializable and cannot be accessed directly.
|
||||
*
|
||||
* @internal
|
||||
* @psalm-suppress RedundantPropertyInitializationCheck
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function unserialize($value) : void
|
||||
{
|
||||
if (isset($this->value)) {
|
||||
throw new \LogicException('unserialize() is an internal function, it must not be called directly.');
|
||||
}
|
||||
|
||||
[$value, $scale] = \explode(':', $value);
|
||||
|
||||
$this->value = $value;
|
||||
$this->scale = (int) $scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the internal values of the given decimal numbers on the same scale.
|
||||
*
|
||||
* @return array{string, string} The scaled integer values of $x and $y.
|
||||
*/
|
||||
private function scaleValues(BigDecimal $x, BigDecimal $y) : array
|
||||
{
|
||||
$a = $x->value;
|
||||
$b = $y->value;
|
||||
|
||||
if ($b !== '0' && $x->scale > $y->scale) {
|
||||
$b .= \str_repeat('0', $x->scale - $y->scale);
|
||||
} elseif ($a !== '0' && $x->scale < $y->scale) {
|
||||
$a .= \str_repeat('0', $y->scale - $x->scale);
|
||||
}
|
||||
|
||||
return [$a, $b];
|
||||
}
|
||||
|
||||
private function valueWithMinScale(int $scale) : string
|
||||
{
|
||||
$value = $this->value;
|
||||
|
||||
if ($this->value !== '0' && $scale > $this->scale) {
|
||||
$value .= \str_repeat('0', $scale - $this->scale);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds leading zeros if necessary to the unscaled value to represent the full decimal number.
|
||||
*/
|
||||
private function getUnscaledValueWithLeadingZeros() : string
|
||||
{
|
||||
$value = $this->value;
|
||||
$targetLength = $this->scale + 1;
|
||||
$negative = ($value[0] === '-');
|
||||
$length = \strlen($value);
|
||||
|
||||
if ($negative) {
|
||||
$length--;
|
||||
}
|
||||
|
||||
if ($length >= $targetLength) {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
if ($negative) {
|
||||
$value = \substr($value, 1);
|
||||
}
|
||||
|
||||
$value = \str_pad($value, $targetLength, '0', STR_PAD_LEFT);
|
||||
|
||||
if ($negative) {
|
||||
$value = '-' . $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
1079
vendor/brick/math/src/BigInteger.php
vendored
Normal file
1079
vendor/brick/math/src/BigInteger.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
512
vendor/brick/math/src/BigNumber.php
vendored
Normal file
512
vendor/brick/math/src/BigNumber.php
vendored
Normal file
@ -0,0 +1,512 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math;
|
||||
|
||||
use Brick\Math\Exception\DivisionByZeroException;
|
||||
use Brick\Math\Exception\MathException;
|
||||
use Brick\Math\Exception\NumberFormatException;
|
||||
use Brick\Math\Exception\RoundingNecessaryException;
|
||||
|
||||
/**
|
||||
* Common interface for arbitrary-precision rational numbers.
|
||||
*
|
||||
* @psalm-immutable
|
||||
*/
|
||||
abstract class BigNumber implements \Serializable, \JsonSerializable
|
||||
{
|
||||
/**
|
||||
* The regular expression used to parse integer, decimal and rational numbers.
|
||||
*/
|
||||
private const PARSE_REGEXP =
|
||||
'/^' .
|
||||
'(?<sign>[\-\+])?' .
|
||||
'(?:' .
|
||||
'(?:' .
|
||||
'(?<integral>[0-9]+)?' .
|
||||
'(?<point>\.)?' .
|
||||
'(?<fractional>[0-9]+)?' .
|
||||
'(?:[eE](?<exponent>[\-\+]?[0-9]+))?' .
|
||||
')|(?:' .
|
||||
'(?<numerator>[0-9]+)' .
|
||||
'\/?' .
|
||||
'(?<denominator>[0-9]+)' .
|
||||
')' .
|
||||
')' .
|
||||
'$/';
|
||||
|
||||
/**
|
||||
* Creates a BigNumber of the given value.
|
||||
*
|
||||
* The concrete return type is dependent on the given value, with the following rules:
|
||||
*
|
||||
* - BigNumber instances are returned as is
|
||||
* - integer numbers are returned as BigInteger
|
||||
* - floating point numbers are converted to a string then parsed as such
|
||||
* - strings containing a `/` character are returned as BigRational
|
||||
* - strings containing a `.` character or using an exponential notation are returned as BigDecimal
|
||||
* - strings containing only digits with an optional leading `+` or `-` sign are returned as BigInteger
|
||||
*
|
||||
* @throws NumberFormatException If the format of the number is not valid.
|
||||
* @throws DivisionByZeroException If the value represents a rational number with a denominator of zero.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function of(BigNumber|int|float|string $value) : BigNumber
|
||||
{
|
||||
if ($value instanceof BigNumber) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (\is_int($value)) {
|
||||
return new BigInteger((string) $value);
|
||||
}
|
||||
|
||||
$value = \is_float($value) ? self::floatToString($value) : $value;
|
||||
|
||||
$throw = static function() use ($value) : void {
|
||||
throw new NumberFormatException(\sprintf(
|
||||
'The given value "%s" does not represent a valid number.',
|
||||
$value
|
||||
));
|
||||
};
|
||||
|
||||
if (\preg_match(self::PARSE_REGEXP, $value, $matches) !== 1) {
|
||||
$throw();
|
||||
}
|
||||
|
||||
$getMatch = static fn(string $value): ?string => (($matches[$value] ?? '') !== '') ? $matches[$value] : null;
|
||||
|
||||
$sign = $getMatch('sign');
|
||||
$numerator = $getMatch('numerator');
|
||||
$denominator = $getMatch('denominator');
|
||||
|
||||
if ($numerator !== null) {
|
||||
assert($denominator !== null);
|
||||
|
||||
if ($sign !== null) {
|
||||
$numerator = $sign . $numerator;
|
||||
}
|
||||
|
||||
$numerator = self::cleanUp($numerator);
|
||||
$denominator = self::cleanUp($denominator);
|
||||
|
||||
if ($denominator === '0') {
|
||||
throw DivisionByZeroException::denominatorMustNotBeZero();
|
||||
}
|
||||
|
||||
return new BigRational(
|
||||
new BigInteger($numerator),
|
||||
new BigInteger($denominator),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
$point = $getMatch('point');
|
||||
$integral = $getMatch('integral');
|
||||
$fractional = $getMatch('fractional');
|
||||
$exponent = $getMatch('exponent');
|
||||
|
||||
if ($integral === null && $fractional === null) {
|
||||
$throw();
|
||||
}
|
||||
|
||||
if ($integral === null) {
|
||||
$integral = '0';
|
||||
}
|
||||
|
||||
if ($point !== null || $exponent !== null) {
|
||||
$fractional = ($fractional ?? '');
|
||||
$exponent = ($exponent !== null) ? (int) $exponent : 0;
|
||||
|
||||
if ($exponent === PHP_INT_MIN || $exponent === PHP_INT_MAX) {
|
||||
throw new NumberFormatException('Exponent too large.');
|
||||
}
|
||||
|
||||
$unscaledValue = self::cleanUp(($sign ?? ''). $integral . $fractional);
|
||||
|
||||
$scale = \strlen($fractional) - $exponent;
|
||||
|
||||
if ($scale < 0) {
|
||||
if ($unscaledValue !== '0') {
|
||||
$unscaledValue .= \str_repeat('0', - $scale);
|
||||
}
|
||||
$scale = 0;
|
||||
}
|
||||
|
||||
return new BigDecimal($unscaledValue, $scale);
|
||||
}
|
||||
|
||||
$integral = self::cleanUp(($sign ?? '') . $integral);
|
||||
|
||||
return new BigInteger($integral);
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely converts float to string, avoiding locale-dependent issues.
|
||||
*
|
||||
* @see https://github.com/brick/math/pull/20
|
||||
*
|
||||
* @psalm-pure
|
||||
* @psalm-suppress ImpureFunctionCall
|
||||
*/
|
||||
private static function floatToString(float $float) : string
|
||||
{
|
||||
$currentLocale = \setlocale(LC_NUMERIC, '0');
|
||||
\setlocale(LC_NUMERIC, 'C');
|
||||
|
||||
$result = (string) $float;
|
||||
|
||||
\setlocale(LC_NUMERIC, $currentLocale);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy method to access BigInteger's protected constructor from sibling classes.
|
||||
*
|
||||
* @internal
|
||||
* @psalm-pure
|
||||
*/
|
||||
protected function newBigInteger(string $value) : BigInteger
|
||||
{
|
||||
return new BigInteger($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy method to access BigDecimal's protected constructor from sibling classes.
|
||||
*
|
||||
* @internal
|
||||
* @psalm-pure
|
||||
*/
|
||||
protected function newBigDecimal(string $value, int $scale = 0) : BigDecimal
|
||||
{
|
||||
return new BigDecimal($value, $scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy method to access BigRational's protected constructor from sibling classes.
|
||||
*
|
||||
* @internal
|
||||
* @psalm-pure
|
||||
*/
|
||||
protected function newBigRational(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator) : BigRational
|
||||
{
|
||||
return new BigRational($numerator, $denominator, $checkDenominator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum of the given values.
|
||||
*
|
||||
* @param BigNumber|int|float|string ...$values The numbers to compare. All the numbers need to be convertible
|
||||
* to an instance of the class this method is called on.
|
||||
*
|
||||
* @throws \InvalidArgumentException If no values are given.
|
||||
* @throws MathException If an argument is not valid.
|
||||
*
|
||||
* @psalm-suppress LessSpecificReturnStatement
|
||||
* @psalm-suppress MoreSpecificReturnType
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function min(BigNumber|int|float|string ...$values) : static
|
||||
{
|
||||
$min = null;
|
||||
|
||||
foreach ($values as $value) {
|
||||
$value = static::of($value);
|
||||
|
||||
if ($min === null || $value->isLessThan($min)) {
|
||||
$min = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if ($min === null) {
|
||||
throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
|
||||
}
|
||||
|
||||
return $min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum of the given values.
|
||||
*
|
||||
* @param BigNumber|int|float|string ...$values The numbers to compare. All the numbers need to be convertible
|
||||
* to an instance of the class this method is called on.
|
||||
*
|
||||
* @throws \InvalidArgumentException If no values are given.
|
||||
* @throws MathException If an argument is not valid.
|
||||
*
|
||||
* @psalm-suppress LessSpecificReturnStatement
|
||||
* @psalm-suppress MoreSpecificReturnType
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function max(BigNumber|int|float|string ...$values) : static
|
||||
{
|
||||
$max = null;
|
||||
|
||||
foreach ($values as $value) {
|
||||
$value = static::of($value);
|
||||
|
||||
if ($max === null || $value->isGreaterThan($max)) {
|
||||
$max = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if ($max === null) {
|
||||
throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
|
||||
}
|
||||
|
||||
return $max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sum of the given values.
|
||||
*
|
||||
* @param BigNumber|int|float|string ...$values The numbers to add. All the numbers need to be convertible
|
||||
* to an instance of the class this method is called on.
|
||||
*
|
||||
* @throws \InvalidArgumentException If no values are given.
|
||||
* @throws MathException If an argument is not valid.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function sum(BigNumber|int|float|string ...$values) : static
|
||||
{
|
||||
/** @var static|null $sum */
|
||||
$sum = null;
|
||||
|
||||
foreach ($values as $value) {
|
||||
$value = static::of($value);
|
||||
|
||||
$sum = $sum === null ? $value : self::add($sum, $value);
|
||||
}
|
||||
|
||||
if ($sum === null) {
|
||||
throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
|
||||
}
|
||||
|
||||
return $sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds two BigNumber instances in the correct order to avoid a RoundingNecessaryException.
|
||||
*
|
||||
* @todo This could be better resolved by creating an abstract protected method in BigNumber, and leaving to
|
||||
* concrete classes the responsibility to perform the addition themselves or delegate it to the given number,
|
||||
* depending on their ability to perform the operation. This will also require a version bump because we're
|
||||
* potentially breaking custom BigNumber implementations (if any...)
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
private static function add(BigNumber $a, BigNumber $b) : BigNumber
|
||||
{
|
||||
if ($a instanceof BigRational) {
|
||||
return $a->plus($b);
|
||||
}
|
||||
|
||||
if ($b instanceof BigRational) {
|
||||
return $b->plus($a);
|
||||
}
|
||||
|
||||
if ($a instanceof BigDecimal) {
|
||||
return $a->plus($b);
|
||||
}
|
||||
|
||||
if ($b instanceof BigDecimal) {
|
||||
return $b->plus($a);
|
||||
}
|
||||
|
||||
/** @var BigInteger $a */
|
||||
|
||||
return $a->plus($b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes optional leading zeros and + sign from the given number.
|
||||
*
|
||||
* @param string $number The number, validated as a non-empty string of digits with optional leading sign.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
private static function cleanUp(string $number) : string
|
||||
{
|
||||
$firstChar = $number[0];
|
||||
|
||||
if ($firstChar === '+' || $firstChar === '-') {
|
||||
$number = \substr($number, 1);
|
||||
}
|
||||
|
||||
$number = \ltrim($number, '0');
|
||||
|
||||
if ($number === '') {
|
||||
return '0';
|
||||
}
|
||||
|
||||
if ($firstChar === '-') {
|
||||
return '-' . $number;
|
||||
}
|
||||
|
||||
return $number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this number is equal to the given one.
|
||||
*/
|
||||
public function isEqualTo(BigNumber|int|float|string $that) : bool
|
||||
{
|
||||
return $this->compareTo($that) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this number is strictly lower than the given one.
|
||||
*/
|
||||
public function isLessThan(BigNumber|int|float|string $that) : bool
|
||||
{
|
||||
return $this->compareTo($that) < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this number is lower than or equal to the given one.
|
||||
*/
|
||||
public function isLessThanOrEqualTo(BigNumber|int|float|string $that) : bool
|
||||
{
|
||||
return $this->compareTo($that) <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this number is strictly greater than the given one.
|
||||
*/
|
||||
public function isGreaterThan(BigNumber|int|float|string $that) : bool
|
||||
{
|
||||
return $this->compareTo($that) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this number is greater than or equal to the given one.
|
||||
*/
|
||||
public function isGreaterThanOrEqualTo(BigNumber|int|float|string $that) : bool
|
||||
{
|
||||
return $this->compareTo($that) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this number equals zero.
|
||||
*/
|
||||
public function isZero() : bool
|
||||
{
|
||||
return $this->getSign() === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this number is strictly negative.
|
||||
*/
|
||||
public function isNegative() : bool
|
||||
{
|
||||
return $this->getSign() < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this number is negative or zero.
|
||||
*/
|
||||
public function isNegativeOrZero() : bool
|
||||
{
|
||||
return $this->getSign() <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this number is strictly positive.
|
||||
*/
|
||||
public function isPositive() : bool
|
||||
{
|
||||
return $this->getSign() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this number is positive or zero.
|
||||
*/
|
||||
public function isPositiveOrZero() : bool
|
||||
{
|
||||
return $this->getSign() >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sign of this number.
|
||||
*
|
||||
* @return int -1 if the number is negative, 0 if zero, 1 if positive.
|
||||
*/
|
||||
abstract public function getSign() : int;
|
||||
|
||||
/**
|
||||
* Compares this number to the given one.
|
||||
*
|
||||
* @return int [-1,0,1] If `$this` is lower than, equal to, or greater than `$that`.
|
||||
*
|
||||
* @throws MathException If the number is not valid.
|
||||
*/
|
||||
abstract public function compareTo(BigNumber|int|float|string $that) : int;
|
||||
|
||||
/**
|
||||
* Converts this number to a BigInteger.
|
||||
*
|
||||
* @throws RoundingNecessaryException If this number cannot be converted to a BigInteger without rounding.
|
||||
*/
|
||||
abstract public function toBigInteger() : BigInteger;
|
||||
|
||||
/**
|
||||
* Converts this number to a BigDecimal.
|
||||
*
|
||||
* @throws RoundingNecessaryException If this number cannot be converted to a BigDecimal without rounding.
|
||||
*/
|
||||
abstract public function toBigDecimal() : BigDecimal;
|
||||
|
||||
/**
|
||||
* Converts this number to a BigRational.
|
||||
*/
|
||||
abstract public function toBigRational() : BigRational;
|
||||
|
||||
/**
|
||||
* Converts this number to a BigDecimal with the given scale, using rounding if necessary.
|
||||
*
|
||||
* @param int $scale The scale of the resulting `BigDecimal`.
|
||||
* @param int $roundingMode A `RoundingMode` constant.
|
||||
*
|
||||
* @throws RoundingNecessaryException If this number cannot be converted to the given scale without rounding.
|
||||
* This only applies when RoundingMode::UNNECESSARY is used.
|
||||
*/
|
||||
abstract public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal;
|
||||
|
||||
/**
|
||||
* Returns the exact value of this number as a native integer.
|
||||
*
|
||||
* If this number cannot be converted to a native integer without losing precision, an exception is thrown.
|
||||
* Note that the acceptable range for an integer depends on the platform and differs for 32-bit and 64-bit.
|
||||
*
|
||||
* @throws MathException If this number cannot be exactly converted to a native integer.
|
||||
*/
|
||||
abstract public function toInt() : int;
|
||||
|
||||
/**
|
||||
* Returns an approximation of this number as a floating-point value.
|
||||
*
|
||||
* Note that this method can discard information as the precision of a floating-point value
|
||||
* is inherently limited.
|
||||
*
|
||||
* If the number is greater than the largest representable floating point number, positive infinity is returned.
|
||||
* If the number is less than the smallest representable floating point number, negative infinity is returned.
|
||||
*/
|
||||
abstract public function toFloat() : float;
|
||||
|
||||
/**
|
||||
* Returns a string representation of this number.
|
||||
*
|
||||
* The output of this method can be parsed by the `of()` factory method;
|
||||
* this will yield an object equal to this one, without any information loss.
|
||||
*/
|
||||
abstract public function __toString() : string;
|
||||
|
||||
public function jsonSerialize() : string
|
||||
{
|
||||
return $this->__toString();
|
||||
}
|
||||
}
|
445
vendor/brick/math/src/BigRational.php
vendored
Normal file
445
vendor/brick/math/src/BigRational.php
vendored
Normal file
@ -0,0 +1,445 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math;
|
||||
|
||||
use Brick\Math\Exception\DivisionByZeroException;
|
||||
use Brick\Math\Exception\MathException;
|
||||
use Brick\Math\Exception\NumberFormatException;
|
||||
use Brick\Math\Exception\RoundingNecessaryException;
|
||||
|
||||
/**
|
||||
* An arbitrarily large rational number.
|
||||
*
|
||||
* This class is immutable.
|
||||
*
|
||||
* @psalm-immutable
|
||||
*/
|
||||
final class BigRational extends BigNumber
|
||||
{
|
||||
/**
|
||||
* The numerator.
|
||||
*/
|
||||
private BigInteger $numerator;
|
||||
|
||||
/**
|
||||
* The denominator. Always strictly positive.
|
||||
*/
|
||||
private BigInteger $denominator;
|
||||
|
||||
/**
|
||||
* Protected constructor. Use a factory method to obtain an instance.
|
||||
*
|
||||
* @param BigInteger $numerator The numerator.
|
||||
* @param BigInteger $denominator The denominator.
|
||||
* @param bool $checkDenominator Whether to check the denominator for negative and zero.
|
||||
*
|
||||
* @throws DivisionByZeroException If the denominator is zero.
|
||||
*/
|
||||
protected function __construct(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator)
|
||||
{
|
||||
if ($checkDenominator) {
|
||||
if ($denominator->isZero()) {
|
||||
throw DivisionByZeroException::denominatorMustNotBeZero();
|
||||
}
|
||||
|
||||
if ($denominator->isNegative()) {
|
||||
$numerator = $numerator->negated();
|
||||
$denominator = $denominator->negated();
|
||||
}
|
||||
}
|
||||
|
||||
$this->numerator = $numerator;
|
||||
$this->denominator = $denominator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BigRational of the given value.
|
||||
*
|
||||
* @throws MathException If the value cannot be converted to a BigRational.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function of(BigNumber|int|float|string $value) : BigRational
|
||||
{
|
||||
return parent::of($value)->toBigRational();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BigRational out of a numerator and a denominator.
|
||||
*
|
||||
* If the denominator is negative, the signs of both the numerator and the denominator
|
||||
* will be inverted to ensure that the denominator is always positive.
|
||||
*
|
||||
* @param BigNumber|int|float|string $numerator The numerator. Must be convertible to a BigInteger.
|
||||
* @param BigNumber|int|float|string $denominator The denominator. Must be convertible to a BigInteger.
|
||||
*
|
||||
* @throws NumberFormatException If an argument does not represent a valid number.
|
||||
* @throws RoundingNecessaryException If an argument represents a non-integer number.
|
||||
* @throws DivisionByZeroException If the denominator is zero.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function nd(
|
||||
BigNumber|int|float|string $numerator,
|
||||
BigNumber|int|float|string $denominator,
|
||||
) : BigRational {
|
||||
$numerator = BigInteger::of($numerator);
|
||||
$denominator = BigInteger::of($denominator);
|
||||
|
||||
return new BigRational($numerator, $denominator, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a BigRational representing zero.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function zero() : BigRational
|
||||
{
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticVariable
|
||||
* @var BigRational|null $zero
|
||||
*/
|
||||
static $zero;
|
||||
|
||||
if ($zero === null) {
|
||||
$zero = new BigRational(BigInteger::zero(), BigInteger::one(), false);
|
||||
}
|
||||
|
||||
return $zero;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a BigRational representing one.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function one() : BigRational
|
||||
{
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticVariable
|
||||
* @var BigRational|null $one
|
||||
*/
|
||||
static $one;
|
||||
|
||||
if ($one === null) {
|
||||
$one = new BigRational(BigInteger::one(), BigInteger::one(), false);
|
||||
}
|
||||
|
||||
return $one;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a BigRational representing ten.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function ten() : BigRational
|
||||
{
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticVariable
|
||||
* @var BigRational|null $ten
|
||||
*/
|
||||
static $ten;
|
||||
|
||||
if ($ten === null) {
|
||||
$ten = new BigRational(BigInteger::ten(), BigInteger::one(), false);
|
||||
}
|
||||
|
||||
return $ten;
|
||||
}
|
||||
|
||||
public function getNumerator() : BigInteger
|
||||
{
|
||||
return $this->numerator;
|
||||
}
|
||||
|
||||
public function getDenominator() : BigInteger
|
||||
{
|
||||
return $this->denominator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the quotient of the division of the numerator by the denominator.
|
||||
*/
|
||||
public function quotient() : BigInteger
|
||||
{
|
||||
return $this->numerator->quotient($this->denominator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the remainder of the division of the numerator by the denominator.
|
||||
*/
|
||||
public function remainder() : BigInteger
|
||||
{
|
||||
return $this->numerator->remainder($this->denominator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the quotient and remainder of the division of the numerator by the denominator.
|
||||
*
|
||||
* @return BigInteger[]
|
||||
*/
|
||||
public function quotientAndRemainder() : array
|
||||
{
|
||||
return $this->numerator->quotientAndRemainder($this->denominator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sum of this number and the given one.
|
||||
*
|
||||
* @param BigNumber|int|float|string $that The number to add.
|
||||
*
|
||||
* @throws MathException If the number is not valid.
|
||||
*/
|
||||
public function plus(BigNumber|int|float|string $that) : BigRational
|
||||
{
|
||||
$that = BigRational::of($that);
|
||||
|
||||
$numerator = $this->numerator->multipliedBy($that->denominator);
|
||||
$numerator = $numerator->plus($that->numerator->multipliedBy($this->denominator));
|
||||
$denominator = $this->denominator->multipliedBy($that->denominator);
|
||||
|
||||
return new BigRational($numerator, $denominator, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the difference of this number and the given one.
|
||||
*
|
||||
* @param BigNumber|int|float|string $that The number to subtract.
|
||||
*
|
||||
* @throws MathException If the number is not valid.
|
||||
*/
|
||||
public function minus(BigNumber|int|float|string $that) : BigRational
|
||||
{
|
||||
$that = BigRational::of($that);
|
||||
|
||||
$numerator = $this->numerator->multipliedBy($that->denominator);
|
||||
$numerator = $numerator->minus($that->numerator->multipliedBy($this->denominator));
|
||||
$denominator = $this->denominator->multipliedBy($that->denominator);
|
||||
|
||||
return new BigRational($numerator, $denominator, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the product of this number and the given one.
|
||||
*
|
||||
* @param BigNumber|int|float|string $that The multiplier.
|
||||
*
|
||||
* @throws MathException If the multiplier is not a valid number.
|
||||
*/
|
||||
public function multipliedBy(BigNumber|int|float|string $that) : BigRational
|
||||
{
|
||||
$that = BigRational::of($that);
|
||||
|
||||
$numerator = $this->numerator->multipliedBy($that->numerator);
|
||||
$denominator = $this->denominator->multipliedBy($that->denominator);
|
||||
|
||||
return new BigRational($numerator, $denominator, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result of the division of this number by the given one.
|
||||
*
|
||||
* @param BigNumber|int|float|string $that The divisor.
|
||||
*
|
||||
* @throws MathException If the divisor is not a valid number, or is zero.
|
||||
*/
|
||||
public function dividedBy(BigNumber|int|float|string $that) : BigRational
|
||||
{
|
||||
$that = BigRational::of($that);
|
||||
|
||||
$numerator = $this->numerator->multipliedBy($that->denominator);
|
||||
$denominator = $this->denominator->multipliedBy($that->numerator);
|
||||
|
||||
return new BigRational($numerator, $denominator, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this number exponentiated to the given value.
|
||||
*
|
||||
* @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000.
|
||||
*/
|
||||
public function power(int $exponent) : BigRational
|
||||
{
|
||||
if ($exponent === 0) {
|
||||
$one = BigInteger::one();
|
||||
|
||||
return new BigRational($one, $one, false);
|
||||
}
|
||||
|
||||
if ($exponent === 1) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return new BigRational(
|
||||
$this->numerator->power($exponent),
|
||||
$this->denominator->power($exponent),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the reciprocal of this BigRational.
|
||||
*
|
||||
* The reciprocal has the numerator and denominator swapped.
|
||||
*
|
||||
* @throws DivisionByZeroException If the numerator is zero.
|
||||
*/
|
||||
public function reciprocal() : BigRational
|
||||
{
|
||||
return new BigRational($this->denominator, $this->numerator, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute value of this BigRational.
|
||||
*/
|
||||
public function abs() : BigRational
|
||||
{
|
||||
return new BigRational($this->numerator->abs(), $this->denominator, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the negated value of this BigRational.
|
||||
*/
|
||||
public function negated() : BigRational
|
||||
{
|
||||
return new BigRational($this->numerator->negated(), $this->denominator, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the simplified value of this BigRational.
|
||||
*/
|
||||
public function simplified() : BigRational
|
||||
{
|
||||
$gcd = $this->numerator->gcd($this->denominator);
|
||||
|
||||
$numerator = $this->numerator->quotient($gcd);
|
||||
$denominator = $this->denominator->quotient($gcd);
|
||||
|
||||
return new BigRational($numerator, $denominator, false);
|
||||
}
|
||||
|
||||
public function compareTo(BigNumber|int|float|string $that) : int
|
||||
{
|
||||
return $this->minus($that)->getSign();
|
||||
}
|
||||
|
||||
public function getSign() : int
|
||||
{
|
||||
return $this->numerator->getSign();
|
||||
}
|
||||
|
||||
public function toBigInteger() : BigInteger
|
||||
{
|
||||
$simplified = $this->simplified();
|
||||
|
||||
if (! $simplified->denominator->isEqualTo(1)) {
|
||||
throw new RoundingNecessaryException('This rational number cannot be represented as an integer value without rounding.');
|
||||
}
|
||||
|
||||
return $simplified->numerator;
|
||||
}
|
||||
|
||||
public function toBigDecimal() : BigDecimal
|
||||
{
|
||||
return $this->numerator->toBigDecimal()->exactlyDividedBy($this->denominator);
|
||||
}
|
||||
|
||||
public function toBigRational() : BigRational
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
|
||||
{
|
||||
return $this->numerator->toBigDecimal()->dividedBy($this->denominator, $scale, $roundingMode);
|
||||
}
|
||||
|
||||
public function toInt() : int
|
||||
{
|
||||
return $this->toBigInteger()->toInt();
|
||||
}
|
||||
|
||||
public function toFloat() : float
|
||||
{
|
||||
$simplified = $this->simplified();
|
||||
return $simplified->numerator->toFloat() / $simplified->denominator->toFloat();
|
||||
}
|
||||
|
||||
public function __toString() : string
|
||||
{
|
||||
$numerator = (string) $this->numerator;
|
||||
$denominator = (string) $this->denominator;
|
||||
|
||||
if ($denominator === '1') {
|
||||
return $numerator;
|
||||
}
|
||||
|
||||
return $this->numerator . '/' . $this->denominator;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is required for serializing the object and SHOULD NOT be accessed directly.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @return array{numerator: BigInteger, denominator: BigInteger}
|
||||
*/
|
||||
public function __serialize(): array
|
||||
{
|
||||
return ['numerator' => $this->numerator, 'denominator' => $this->denominator];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is only here to allow unserializing the object and cannot be accessed directly.
|
||||
*
|
||||
* @internal
|
||||
* @psalm-suppress RedundantPropertyInitializationCheck
|
||||
*
|
||||
* @param array{numerator: BigInteger, denominator: BigInteger} $data
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function __unserialize(array $data): void
|
||||
{
|
||||
if (isset($this->numerator)) {
|
||||
throw new \LogicException('__unserialize() is an internal function, it must not be called directly.');
|
||||
}
|
||||
|
||||
$this->numerator = $data['numerator'];
|
||||
$this->denominator = $data['denominator'];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is required by interface Serializable and SHOULD NOT be accessed directly.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function serialize() : string
|
||||
{
|
||||
return $this->numerator . '/' . $this->denominator;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is only here to implement interface Serializable and cannot be accessed directly.
|
||||
*
|
||||
* @internal
|
||||
* @psalm-suppress RedundantPropertyInitializationCheck
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function unserialize($value) : void
|
||||
{
|
||||
if (isset($this->numerator)) {
|
||||
throw new \LogicException('unserialize() is an internal function, it must not be called directly.');
|
||||
}
|
||||
|
||||
[$numerator, $denominator] = \explode('/', $value);
|
||||
|
||||
$this->numerator = BigInteger::of($numerator);
|
||||
$this->denominator = BigInteger::of($denominator);
|
||||
}
|
||||
}
|
35
vendor/brick/math/src/Exception/DivisionByZeroException.php
vendored
Normal file
35
vendor/brick/math/src/Exception/DivisionByZeroException.php
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math\Exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when a division by zero occurs.
|
||||
*/
|
||||
class DivisionByZeroException extends MathException
|
||||
{
|
||||
/**
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function divisionByZero() : DivisionByZeroException
|
||||
{
|
||||
return new self('Division by zero.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function modulusMustNotBeZero() : DivisionByZeroException
|
||||
{
|
||||
return new self('The modulus must not be zero.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function denominatorMustNotBeZero() : DivisionByZeroException
|
||||
{
|
||||
return new self('The denominator of a rational number cannot be zero.');
|
||||
}
|
||||
}
|
23
vendor/brick/math/src/Exception/IntegerOverflowException.php
vendored
Normal file
23
vendor/brick/math/src/Exception/IntegerOverflowException.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math\Exception;
|
||||
|
||||
use Brick\Math\BigInteger;
|
||||
|
||||
/**
|
||||
* Exception thrown when an integer overflow occurs.
|
||||
*/
|
||||
class IntegerOverflowException extends MathException
|
||||
{
|
||||
/**
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function toIntOverflow(BigInteger $value) : IntegerOverflowException
|
||||
{
|
||||
$message = '%s is out of range %d to %d and cannot be represented as an integer.';
|
||||
|
||||
return new self(\sprintf($message, (string) $value, PHP_INT_MIN, PHP_INT_MAX));
|
||||
}
|
||||
}
|
12
vendor/brick/math/src/Exception/MathException.php
vendored
Normal file
12
vendor/brick/math/src/Exception/MathException.php
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math\Exception;
|
||||
|
||||
/**
|
||||
* Base class for all math exceptions.
|
||||
*/
|
||||
class MathException extends \Exception
|
||||
{
|
||||
}
|
12
vendor/brick/math/src/Exception/NegativeNumberException.php
vendored
Normal file
12
vendor/brick/math/src/Exception/NegativeNumberException.php
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math\Exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when attempting to perform an unsupported operation, such as a square root, on a negative number.
|
||||
*/
|
||||
class NegativeNumberException extends MathException
|
||||
{
|
||||
}
|
33
vendor/brick/math/src/Exception/NumberFormatException.php
vendored
Normal file
33
vendor/brick/math/src/Exception/NumberFormatException.php
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math\Exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when attempting to create a number from a string with an invalid format.
|
||||
*/
|
||||
class NumberFormatException extends MathException
|
||||
{
|
||||
/**
|
||||
* @param string $char The failing character.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function charNotInAlphabet(string $char) : self
|
||||
{
|
||||
$ord = \ord($char);
|
||||
|
||||
if ($ord < 32 || $ord > 126) {
|
||||
$char = \strtoupper(\dechex($ord));
|
||||
|
||||
if ($ord < 10) {
|
||||
$char = '0' . $char;
|
||||
}
|
||||
} else {
|
||||
$char = '"' . $char . '"';
|
||||
}
|
||||
|
||||
return new self(sprintf('Char %s is not a valid character in the given alphabet.', $char));
|
||||
}
|
||||
}
|
19
vendor/brick/math/src/Exception/RoundingNecessaryException.php
vendored
Normal file
19
vendor/brick/math/src/Exception/RoundingNecessaryException.php
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math\Exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when a number cannot be represented at the requested scale without rounding.
|
||||
*/
|
||||
class RoundingNecessaryException extends MathException
|
||||
{
|
||||
/**
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function roundingNecessary() : RoundingNecessaryException
|
||||
{
|
||||
return new self('Rounding is necessary to represent the result of the operation at this scale.');
|
||||
}
|
||||
}
|
676
vendor/brick/math/src/Internal/Calculator.php
vendored
Normal file
676
vendor/brick/math/src/Internal/Calculator.php
vendored
Normal file
@ -0,0 +1,676 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math\Internal;
|
||||
|
||||
use Brick\Math\Exception\RoundingNecessaryException;
|
||||
use Brick\Math\RoundingMode;
|
||||
|
||||
/**
|
||||
* Performs basic operations on arbitrary size integers.
|
||||
*
|
||||
* Unless otherwise specified, all parameters must be validated as non-empty strings of digits,
|
||||
* without leading zero, and with an optional leading minus sign if the number is not zero.
|
||||
*
|
||||
* Any other parameter format will lead to undefined behaviour.
|
||||
* All methods must return strings respecting this format, unless specified otherwise.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @psalm-immutable
|
||||
*/
|
||||
abstract class Calculator
|
||||
{
|
||||
/**
|
||||
* The maximum exponent value allowed for the pow() method.
|
||||
*/
|
||||
public const MAX_POWER = 1000000;
|
||||
|
||||
/**
|
||||
* The alphabet for converting from and to base 2 to 36, lowercase.
|
||||
*/
|
||||
public const ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz';
|
||||
|
||||
/**
|
||||
* The Calculator instance in use.
|
||||
*/
|
||||
private static ?Calculator $instance = null;
|
||||
|
||||
/**
|
||||
* Sets the Calculator instance to use.
|
||||
*
|
||||
* An instance is typically set only in unit tests: the autodetect is usually the best option.
|
||||
*
|
||||
* @param Calculator|null $calculator The calculator instance, or NULL to revert to autodetect.
|
||||
*/
|
||||
final public static function set(?Calculator $calculator) : void
|
||||
{
|
||||
self::$instance = $calculator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Calculator instance to use.
|
||||
*
|
||||
* If none has been explicitly set, the fastest available implementation will be returned.
|
||||
*
|
||||
* @psalm-pure
|
||||
* @psalm-suppress ImpureStaticProperty
|
||||
*/
|
||||
final public static function get() : Calculator
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
/** @psalm-suppress ImpureMethodCall */
|
||||
self::$instance = self::detect();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fastest available Calculator implementation.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private static function detect() : Calculator
|
||||
{
|
||||
if (\extension_loaded('gmp')) {
|
||||
return new Calculator\GmpCalculator();
|
||||
}
|
||||
|
||||
if (\extension_loaded('bcmath')) {
|
||||
return new Calculator\BcMathCalculator();
|
||||
}
|
||||
|
||||
return new Calculator\NativeCalculator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the sign & digits of the operands.
|
||||
*
|
||||
* @return array{bool, bool, string, string} Whether $a and $b are negative, followed by their digits.
|
||||
*/
|
||||
final protected function init(string $a, string $b) : array
|
||||
{
|
||||
return [
|
||||
$aNeg = ($a[0] === '-'),
|
||||
$bNeg = ($b[0] === '-'),
|
||||
|
||||
$aNeg ? \substr($a, 1) : $a,
|
||||
$bNeg ? \substr($b, 1) : $b,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute value of a number.
|
||||
*/
|
||||
final public function abs(string $n) : string
|
||||
{
|
||||
return ($n[0] === '-') ? \substr($n, 1) : $n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Negates a number.
|
||||
*/
|
||||
final public function neg(string $n) : string
|
||||
{
|
||||
if ($n === '0') {
|
||||
return '0';
|
||||
}
|
||||
|
||||
if ($n[0] === '-') {
|
||||
return \substr($n, 1);
|
||||
}
|
||||
|
||||
return '-' . $n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two numbers.
|
||||
*
|
||||
* @return int [-1, 0, 1] If the first number is less than, equal to, or greater than the second number.
|
||||
*/
|
||||
final public function cmp(string $a, string $b) : int
|
||||
{
|
||||
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
|
||||
|
||||
if ($aNeg && ! $bNeg) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ($bNeg && ! $aNeg) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
$aLen = \strlen($aDig);
|
||||
$bLen = \strlen($bDig);
|
||||
|
||||
if ($aLen < $bLen) {
|
||||
$result = -1;
|
||||
} elseif ($aLen > $bLen) {
|
||||
$result = 1;
|
||||
} else {
|
||||
$result = $aDig <=> $bDig;
|
||||
}
|
||||
|
||||
return $aNeg ? -$result : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds two numbers.
|
||||
*/
|
||||
abstract public function add(string $a, string $b) : string;
|
||||
|
||||
/**
|
||||
* Subtracts two numbers.
|
||||
*/
|
||||
abstract public function sub(string $a, string $b) : string;
|
||||
|
||||
/**
|
||||
* Multiplies two numbers.
|
||||
*/
|
||||
abstract public function mul(string $a, string $b) : string;
|
||||
|
||||
/**
|
||||
* Returns the quotient of the division of two numbers.
|
||||
*
|
||||
* @param string $a The dividend.
|
||||
* @param string $b The divisor, must not be zero.
|
||||
*
|
||||
* @return string The quotient.
|
||||
*/
|
||||
abstract public function divQ(string $a, string $b) : string;
|
||||
|
||||
/**
|
||||
* Returns the remainder of the division of two numbers.
|
||||
*
|
||||
* @param string $a The dividend.
|
||||
* @param string $b The divisor, must not be zero.
|
||||
*
|
||||
* @return string The remainder.
|
||||
*/
|
||||
abstract public function divR(string $a, string $b) : string;
|
||||
|
||||
/**
|
||||
* Returns the quotient and remainder of the division of two numbers.
|
||||
*
|
||||
* @param string $a The dividend.
|
||||
* @param string $b The divisor, must not be zero.
|
||||
*
|
||||
* @return array{string, string} An array containing the quotient and remainder.
|
||||
*/
|
||||
abstract public function divQR(string $a, string $b) : array;
|
||||
|
||||
/**
|
||||
* Exponentiates a number.
|
||||
*
|
||||
* @param string $a The base number.
|
||||
* @param int $e The exponent, validated as an integer between 0 and MAX_POWER.
|
||||
*
|
||||
* @return string The power.
|
||||
*/
|
||||
abstract public function pow(string $a, int $e) : string;
|
||||
|
||||
/**
|
||||
* @param string $b The modulus; must not be zero.
|
||||
*/
|
||||
public function mod(string $a, string $b) : string
|
||||
{
|
||||
return $this->divR($this->add($this->divR($a, $b), $b), $b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the modular multiplicative inverse of $x modulo $m.
|
||||
*
|
||||
* If $x has no multiplicative inverse mod m, this method must return null.
|
||||
*
|
||||
* This method can be overridden by the concrete implementation if the underlying library has built-in support.
|
||||
*
|
||||
* @param string $m The modulus; must not be negative or zero.
|
||||
*/
|
||||
public function modInverse(string $x, string $m) : ?string
|
||||
{
|
||||
if ($m === '1') {
|
||||
return '0';
|
||||
}
|
||||
|
||||
$modVal = $x;
|
||||
|
||||
if ($x[0] === '-' || ($this->cmp($this->abs($x), $m) >= 0)) {
|
||||
$modVal = $this->mod($x, $m);
|
||||
}
|
||||
|
||||
[$g, $x] = $this->gcdExtended($modVal, $m);
|
||||
|
||||
if ($g !== '1') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->mod($this->add($this->mod($x, $m), $m), $m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Raises a number into power with modulo.
|
||||
*
|
||||
* @param string $base The base number; must be positive or zero.
|
||||
* @param string $exp The exponent; must be positive or zero.
|
||||
* @param string $mod The modulus; must be strictly positive.
|
||||
*/
|
||||
abstract public function modPow(string $base, string $exp, string $mod) : string;
|
||||
|
||||
/**
|
||||
* Returns the greatest common divisor of the two numbers.
|
||||
*
|
||||
* This method can be overridden by the concrete implementation if the underlying library
|
||||
* has built-in support for GCD calculations.
|
||||
*
|
||||
* @return string The GCD, always positive, or zero if both arguments are zero.
|
||||
*/
|
||||
public function gcd(string $a, string $b) : string
|
||||
{
|
||||
if ($a === '0') {
|
||||
return $this->abs($b);
|
||||
}
|
||||
|
||||
if ($b === '0') {
|
||||
return $this->abs($a);
|
||||
}
|
||||
|
||||
return $this->gcd($b, $this->divR($a, $b));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{string, string, string} GCD, X, Y
|
||||
*/
|
||||
private function gcdExtended(string $a, string $b) : array
|
||||
{
|
||||
if ($a === '0') {
|
||||
return [$b, '0', '1'];
|
||||
}
|
||||
|
||||
[$gcd, $x1, $y1] = $this->gcdExtended($this->mod($b, $a), $a);
|
||||
|
||||
$x = $this->sub($y1, $this->mul($this->divQ($b, $a), $x1));
|
||||
$y = $x1;
|
||||
|
||||
return [$gcd, $x, $y];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the square root of the given number, rounded down.
|
||||
*
|
||||
* The result is the largest x such that x² ≤ n.
|
||||
* The input MUST NOT be negative.
|
||||
*/
|
||||
abstract public function sqrt(string $n) : string;
|
||||
|
||||
/**
|
||||
* Converts a number from an arbitrary base.
|
||||
*
|
||||
* This method can be overridden by the concrete implementation if the underlying library
|
||||
* has built-in support for base conversion.
|
||||
*
|
||||
* @param string $number The number, positive or zero, non-empty, case-insensitively validated for the given base.
|
||||
* @param int $base The base of the number, validated from 2 to 36.
|
||||
*
|
||||
* @return string The converted number, following the Calculator conventions.
|
||||
*/
|
||||
public function fromBase(string $number, int $base) : string
|
||||
{
|
||||
return $this->fromArbitraryBase(\strtolower($number), self::ALPHABET, $base);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a number to an arbitrary base.
|
||||
*
|
||||
* This method can be overridden by the concrete implementation if the underlying library
|
||||
* has built-in support for base conversion.
|
||||
*
|
||||
* @param string $number The number to convert, following the Calculator conventions.
|
||||
* @param int $base The base to convert to, validated from 2 to 36.
|
||||
*
|
||||
* @return string The converted number, lowercase.
|
||||
*/
|
||||
public function toBase(string $number, int $base) : string
|
||||
{
|
||||
$negative = ($number[0] === '-');
|
||||
|
||||
if ($negative) {
|
||||
$number = \substr($number, 1);
|
||||
}
|
||||
|
||||
$number = $this->toArbitraryBase($number, self::ALPHABET, $base);
|
||||
|
||||
if ($negative) {
|
||||
return '-' . $number;
|
||||
}
|
||||
|
||||
return $number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a non-negative number in an arbitrary base using a custom alphabet, to base 10.
|
||||
*
|
||||
* @param string $number The number to convert, validated as a non-empty string,
|
||||
* containing only chars in the given alphabet/base.
|
||||
* @param string $alphabet The alphabet that contains every digit, validated as 2 chars minimum.
|
||||
* @param int $base The base of the number, validated from 2 to alphabet length.
|
||||
*
|
||||
* @return string The number in base 10, following the Calculator conventions.
|
||||
*/
|
||||
final public function fromArbitraryBase(string $number, string $alphabet, int $base) : string
|
||||
{
|
||||
// remove leading "zeros"
|
||||
$number = \ltrim($number, $alphabet[0]);
|
||||
|
||||
if ($number === '') {
|
||||
return '0';
|
||||
}
|
||||
|
||||
// optimize for "one"
|
||||
if ($number === $alphabet[1]) {
|
||||
return '1';
|
||||
}
|
||||
|
||||
$result = '0';
|
||||
$power = '1';
|
||||
|
||||
$base = (string) $base;
|
||||
|
||||
for ($i = \strlen($number) - 1; $i >= 0; $i--) {
|
||||
$index = \strpos($alphabet, $number[$i]);
|
||||
|
||||
if ($index !== 0) {
|
||||
$result = $this->add($result, ($index === 1)
|
||||
? $power
|
||||
: $this->mul($power, (string) $index)
|
||||
);
|
||||
}
|
||||
|
||||
if ($i !== 0) {
|
||||
$power = $this->mul($power, $base);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a non-negative number to an arbitrary base using a custom alphabet.
|
||||
*
|
||||
* @param string $number The number to convert, positive or zero, following the Calculator conventions.
|
||||
* @param string $alphabet The alphabet that contains every digit, validated as 2 chars minimum.
|
||||
* @param int $base The base to convert to, validated from 2 to alphabet length.
|
||||
*
|
||||
* @return string The converted number in the given alphabet.
|
||||
*/
|
||||
final public function toArbitraryBase(string $number, string $alphabet, int $base) : string
|
||||
{
|
||||
if ($number === '0') {
|
||||
return $alphabet[0];
|
||||
}
|
||||
|
||||
$base = (string) $base;
|
||||
$result = '';
|
||||
|
||||
while ($number !== '0') {
|
||||
[$number, $remainder] = $this->divQR($number, $base);
|
||||
$remainder = (int) $remainder;
|
||||
|
||||
$result .= $alphabet[$remainder];
|
||||
}
|
||||
|
||||
return \strrev($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a rounded division.
|
||||
*
|
||||
* Rounding is performed when the remainder of the division is not zero.
|
||||
*
|
||||
* @param string $a The dividend.
|
||||
* @param string $b The divisor, must not be zero.
|
||||
* @param int $roundingMode The rounding mode.
|
||||
*
|
||||
* @throws \InvalidArgumentException If the rounding mode is invalid.
|
||||
* @throws RoundingNecessaryException If RoundingMode::UNNECESSARY is provided but rounding is necessary.
|
||||
*
|
||||
* @psalm-suppress ImpureFunctionCall
|
||||
*/
|
||||
final public function divRound(string $a, string $b, int $roundingMode) : string
|
||||
{
|
||||
[$quotient, $remainder] = $this->divQR($a, $b);
|
||||
|
||||
$hasDiscardedFraction = ($remainder !== '0');
|
||||
$isPositiveOrZero = ($a[0] === '-') === ($b[0] === '-');
|
||||
|
||||
$discardedFractionSign = function() use ($remainder, $b) : int {
|
||||
$r = $this->abs($this->mul($remainder, '2'));
|
||||
$b = $this->abs($b);
|
||||
|
||||
return $this->cmp($r, $b);
|
||||
};
|
||||
|
||||
$increment = false;
|
||||
|
||||
switch ($roundingMode) {
|
||||
case RoundingMode::UNNECESSARY:
|
||||
if ($hasDiscardedFraction) {
|
||||
throw RoundingNecessaryException::roundingNecessary();
|
||||
}
|
||||
break;
|
||||
|
||||
case RoundingMode::UP:
|
||||
$increment = $hasDiscardedFraction;
|
||||
break;
|
||||
|
||||
case RoundingMode::DOWN:
|
||||
break;
|
||||
|
||||
case RoundingMode::CEILING:
|
||||
$increment = $hasDiscardedFraction && $isPositiveOrZero;
|
||||
break;
|
||||
|
||||
case RoundingMode::FLOOR:
|
||||
$increment = $hasDiscardedFraction && ! $isPositiveOrZero;
|
||||
break;
|
||||
|
||||
case RoundingMode::HALF_UP:
|
||||
$increment = $discardedFractionSign() >= 0;
|
||||
break;
|
||||
|
||||
case RoundingMode::HALF_DOWN:
|
||||
$increment = $discardedFractionSign() > 0;
|
||||
break;
|
||||
|
||||
case RoundingMode::HALF_CEILING:
|
||||
$increment = $isPositiveOrZero ? $discardedFractionSign() >= 0 : $discardedFractionSign() > 0;
|
||||
break;
|
||||
|
||||
case RoundingMode::HALF_FLOOR:
|
||||
$increment = $isPositiveOrZero ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0;
|
||||
break;
|
||||
|
||||
case RoundingMode::HALF_EVEN:
|
||||
$lastDigit = (int) $quotient[-1];
|
||||
$lastDigitIsEven = ($lastDigit % 2 === 0);
|
||||
$increment = $lastDigitIsEven ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new \InvalidArgumentException('Invalid rounding mode.');
|
||||
}
|
||||
|
||||
if ($increment) {
|
||||
return $this->add($quotient, $isPositiveOrZero ? '1' : '-1');
|
||||
}
|
||||
|
||||
return $quotient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates bitwise AND of two numbers.
|
||||
*
|
||||
* This method can be overridden by the concrete implementation if the underlying library
|
||||
* has built-in support for bitwise operations.
|
||||
*/
|
||||
public function and(string $a, string $b) : string
|
||||
{
|
||||
return $this->bitwise('and', $a, $b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates bitwise OR of two numbers.
|
||||
*
|
||||
* This method can be overridden by the concrete implementation if the underlying library
|
||||
* has built-in support for bitwise operations.
|
||||
*/
|
||||
public function or(string $a, string $b) : string
|
||||
{
|
||||
return $this->bitwise('or', $a, $b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates bitwise XOR of two numbers.
|
||||
*
|
||||
* This method can be overridden by the concrete implementation if the underlying library
|
||||
* has built-in support for bitwise operations.
|
||||
*/
|
||||
public function xor(string $a, string $b) : string
|
||||
{
|
||||
return $this->bitwise('xor', $a, $b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a bitwise operation on a decimal number.
|
||||
*
|
||||
* @param 'and'|'or'|'xor' $operator The operator to use.
|
||||
* @param string $a The left operand.
|
||||
* @param string $b The right operand.
|
||||
*/
|
||||
private function bitwise(string $operator, string $a, string $b) : string
|
||||
{
|
||||
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
|
||||
|
||||
$aBin = $this->toBinary($aDig);
|
||||
$bBin = $this->toBinary($bDig);
|
||||
|
||||
$aLen = \strlen($aBin);
|
||||
$bLen = \strlen($bBin);
|
||||
|
||||
if ($aLen > $bLen) {
|
||||
$bBin = \str_repeat("\x00", $aLen - $bLen) . $bBin;
|
||||
} elseif ($bLen > $aLen) {
|
||||
$aBin = \str_repeat("\x00", $bLen - $aLen) . $aBin;
|
||||
}
|
||||
|
||||
if ($aNeg) {
|
||||
$aBin = $this->twosComplement($aBin);
|
||||
}
|
||||
if ($bNeg) {
|
||||
$bBin = $this->twosComplement($bBin);
|
||||
}
|
||||
|
||||
switch ($operator) {
|
||||
case 'and':
|
||||
$value = $aBin & $bBin;
|
||||
$negative = ($aNeg and $bNeg);
|
||||
break;
|
||||
|
||||
case 'or':
|
||||
$value = $aBin | $bBin;
|
||||
$negative = ($aNeg or $bNeg);
|
||||
break;
|
||||
|
||||
case 'xor':
|
||||
$value = $aBin ^ $bBin;
|
||||
$negative = ($aNeg xor $bNeg);
|
||||
break;
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
default:
|
||||
throw new \InvalidArgumentException('Invalid bitwise operator.');
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
if ($negative) {
|
||||
$value = $this->twosComplement($value);
|
||||
}
|
||||
|
||||
$result = $this->toDecimal($value);
|
||||
|
||||
return $negative ? $this->neg($result) : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $number A positive, binary number.
|
||||
*/
|
||||
private function twosComplement(string $number) : string
|
||||
{
|
||||
$xor = \str_repeat("\xff", \strlen($number));
|
||||
|
||||
$number ^= $xor;
|
||||
|
||||
for ($i = \strlen($number) - 1; $i >= 0; $i--) {
|
||||
$byte = \ord($number[$i]);
|
||||
|
||||
if (++$byte !== 256) {
|
||||
$number[$i] = \chr($byte);
|
||||
break;
|
||||
}
|
||||
|
||||
$number[$i] = "\x00";
|
||||
|
||||
if ($i === 0) {
|
||||
$number = "\x01" . $number;
|
||||
}
|
||||
}
|
||||
|
||||
return $number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a decimal number to a binary string.
|
||||
*
|
||||
* @param string $number The number to convert, positive or zero, only digits.
|
||||
*/
|
||||
private function toBinary(string $number) : string
|
||||
{
|
||||
$result = '';
|
||||
|
||||
while ($number !== '0') {
|
||||
[$number, $remainder] = $this->divQR($number, '256');
|
||||
$result .= \chr((int) $remainder);
|
||||
}
|
||||
|
||||
return \strrev($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the positive decimal representation of a binary number.
|
||||
*
|
||||
* @param string $bytes The bytes representing the number.
|
||||
*/
|
||||
private function toDecimal(string $bytes) : string
|
||||
{
|
||||
$result = '0';
|
||||
$power = '1';
|
||||
|
||||
for ($i = \strlen($bytes) - 1; $i >= 0; $i--) {
|
||||
$index = \ord($bytes[$i]);
|
||||
|
||||
if ($index !== 0) {
|
||||
$result = $this->add($result, ($index === 1)
|
||||
? $power
|
||||
: $this->mul($power, (string) $index)
|
||||
);
|
||||
}
|
||||
|
||||
if ($i !== 0) {
|
||||
$power = $this->mul($power, '256');
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
75
vendor/brick/math/src/Internal/Calculator/BcMathCalculator.php
vendored
Normal file
75
vendor/brick/math/src/Internal/Calculator/BcMathCalculator.php
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math\Internal\Calculator;
|
||||
|
||||
use Brick\Math\Internal\Calculator;
|
||||
|
||||
/**
|
||||
* Calculator implementation built around the bcmath library.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @psalm-immutable
|
||||
*/
|
||||
class BcMathCalculator extends Calculator
|
||||
{
|
||||
public function add(string $a, string $b) : string
|
||||
{
|
||||
return \bcadd($a, $b, 0);
|
||||
}
|
||||
|
||||
public function sub(string $a, string $b) : string
|
||||
{
|
||||
return \bcsub($a, $b, 0);
|
||||
}
|
||||
|
||||
public function mul(string $a, string $b) : string
|
||||
{
|
||||
return \bcmul($a, $b, 0);
|
||||
}
|
||||
|
||||
public function divQ(string $a, string $b) : string
|
||||
{
|
||||
return \bcdiv($a, $b, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-suppress InvalidNullableReturnType
|
||||
* @psalm-suppress NullableReturnStatement
|
||||
*/
|
||||
public function divR(string $a, string $b) : string
|
||||
{
|
||||
return \bcmod($a, $b, 0);
|
||||
}
|
||||
|
||||
public function divQR(string $a, string $b) : array
|
||||
{
|
||||
$q = \bcdiv($a, $b, 0);
|
||||
$r = \bcmod($a, $b, 0);
|
||||
|
||||
assert($r !== null);
|
||||
|
||||
return [$q, $r];
|
||||
}
|
||||
|
||||
public function pow(string $a, int $e) : string
|
||||
{
|
||||
return \bcpow($a, (string) $e, 0);
|
||||
}
|
||||
|
||||
public function modPow(string $base, string $exp, string $mod) : string
|
||||
{
|
||||
return \bcpowmod($base, $exp, $mod, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-suppress InvalidNullableReturnType
|
||||
* @psalm-suppress NullableReturnStatement
|
||||
*/
|
||||
public function sqrt(string $n) : string
|
||||
{
|
||||
return \bcsqrt($n, 0);
|
||||
}
|
||||
}
|
108
vendor/brick/math/src/Internal/Calculator/GmpCalculator.php
vendored
Normal file
108
vendor/brick/math/src/Internal/Calculator/GmpCalculator.php
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math\Internal\Calculator;
|
||||
|
||||
use Brick\Math\Internal\Calculator;
|
||||
|
||||
/**
|
||||
* Calculator implementation built around the GMP library.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @psalm-immutable
|
||||
*/
|
||||
class GmpCalculator extends Calculator
|
||||
{
|
||||
public function add(string $a, string $b) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_add($a, $b));
|
||||
}
|
||||
|
||||
public function sub(string $a, string $b) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_sub($a, $b));
|
||||
}
|
||||
|
||||
public function mul(string $a, string $b) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_mul($a, $b));
|
||||
}
|
||||
|
||||
public function divQ(string $a, string $b) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_div_q($a, $b));
|
||||
}
|
||||
|
||||
public function divR(string $a, string $b) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_div_r($a, $b));
|
||||
}
|
||||
|
||||
public function divQR(string $a, string $b) : array
|
||||
{
|
||||
[$q, $r] = \gmp_div_qr($a, $b);
|
||||
|
||||
return [
|
||||
\gmp_strval($q),
|
||||
\gmp_strval($r)
|
||||
];
|
||||
}
|
||||
|
||||
public function pow(string $a, int $e) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_pow($a, $e));
|
||||
}
|
||||
|
||||
public function modInverse(string $x, string $m) : ?string
|
||||
{
|
||||
$result = \gmp_invert($x, $m);
|
||||
|
||||
if ($result === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return \gmp_strval($result);
|
||||
}
|
||||
|
||||
public function modPow(string $base, string $exp, string $mod) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_powm($base, $exp, $mod));
|
||||
}
|
||||
|
||||
public function gcd(string $a, string $b) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_gcd($a, $b));
|
||||
}
|
||||
|
||||
public function fromBase(string $number, int $base) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_init($number, $base));
|
||||
}
|
||||
|
||||
public function toBase(string $number, int $base) : string
|
||||
{
|
||||
return \gmp_strval($number, $base);
|
||||
}
|
||||
|
||||
public function and(string $a, string $b) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_and($a, $b));
|
||||
}
|
||||
|
||||
public function or(string $a, string $b) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_or($a, $b));
|
||||
}
|
||||
|
||||
public function xor(string $a, string $b) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_xor($a, $b));
|
||||
}
|
||||
|
||||
public function sqrt(string $n) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_sqrt($n));
|
||||
}
|
||||
}
|
581
vendor/brick/math/src/Internal/Calculator/NativeCalculator.php
vendored
Normal file
581
vendor/brick/math/src/Internal/Calculator/NativeCalculator.php
vendored
Normal file
@ -0,0 +1,581 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math\Internal\Calculator;
|
||||
|
||||
use Brick\Math\Internal\Calculator;
|
||||
|
||||
/**
|
||||
* Calculator implementation using only native PHP code.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @psalm-immutable
|
||||
*/
|
||||
class NativeCalculator extends Calculator
|
||||
{
|
||||
/**
|
||||
* The max number of digits the platform can natively add, subtract, multiply or divide without overflow.
|
||||
* For multiplication, this represents the max sum of the lengths of both operands.
|
||||
*
|
||||
* In addition, it is assumed that an extra digit can hold a carry (1) without overflowing.
|
||||
* Example: 32-bit: max number 1,999,999,999 (9 digits + carry)
|
||||
* 64-bit: max number 1,999,999,999,999,999,999 (18 digits + carry)
|
||||
*/
|
||||
private int $maxDigits;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
switch (PHP_INT_SIZE) {
|
||||
case 4:
|
||||
$this->maxDigits = 9;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
$this->maxDigits = 18;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new \RuntimeException('The platform is not 32-bit or 64-bit as expected.');
|
||||
}
|
||||
}
|
||||
|
||||
public function add(string $a, string $b) : string
|
||||
{
|
||||
/**
|
||||
* @psalm-var numeric-string $a
|
||||
* @psalm-var numeric-string $b
|
||||
*/
|
||||
$result = $a + $b;
|
||||
|
||||
if (is_int($result)) {
|
||||
return (string) $result;
|
||||
}
|
||||
|
||||
if ($a === '0') {
|
||||
return $b;
|
||||
}
|
||||
|
||||
if ($b === '0') {
|
||||
return $a;
|
||||
}
|
||||
|
||||
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
|
||||
|
||||
$result = $aNeg === $bNeg ? $this->doAdd($aDig, $bDig) : $this->doSub($aDig, $bDig);
|
||||
|
||||
if ($aNeg) {
|
||||
$result = $this->neg($result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function sub(string $a, string $b) : string
|
||||
{
|
||||
return $this->add($a, $this->neg($b));
|
||||
}
|
||||
|
||||
public function mul(string $a, string $b) : string
|
||||
{
|
||||
/**
|
||||
* @psalm-var numeric-string $a
|
||||
* @psalm-var numeric-string $b
|
||||
*/
|
||||
$result = $a * $b;
|
||||
|
||||
if (is_int($result)) {
|
||||
return (string) $result;
|
||||
}
|
||||
|
||||
if ($a === '0' || $b === '0') {
|
||||
return '0';
|
||||
}
|
||||
|
||||
if ($a === '1') {
|
||||
return $b;
|
||||
}
|
||||
|
||||
if ($b === '1') {
|
||||
return $a;
|
||||
}
|
||||
|
||||
if ($a === '-1') {
|
||||
return $this->neg($b);
|
||||
}
|
||||
|
||||
if ($b === '-1') {
|
||||
return $this->neg($a);
|
||||
}
|
||||
|
||||
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
|
||||
|
||||
$result = $this->doMul($aDig, $bDig);
|
||||
|
||||
if ($aNeg !== $bNeg) {
|
||||
$result = $this->neg($result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function divQ(string $a, string $b) : string
|
||||
{
|
||||
return $this->divQR($a, $b)[0];
|
||||
}
|
||||
|
||||
public function divR(string $a, string $b): string
|
||||
{
|
||||
return $this->divQR($a, $b)[1];
|
||||
}
|
||||
|
||||
public function divQR(string $a, string $b) : array
|
||||
{
|
||||
if ($a === '0') {
|
||||
return ['0', '0'];
|
||||
}
|
||||
|
||||
if ($a === $b) {
|
||||
return ['1', '0'];
|
||||
}
|
||||
|
||||
if ($b === '1') {
|
||||
return [$a, '0'];
|
||||
}
|
||||
|
||||
if ($b === '-1') {
|
||||
return [$this->neg($a), '0'];
|
||||
}
|
||||
|
||||
/** @psalm-var numeric-string $a */
|
||||
$na = $a * 1; // cast to number
|
||||
|
||||
if (is_int($na)) {
|
||||
/** @psalm-var numeric-string $b */
|
||||
$nb = $b * 1;
|
||||
|
||||
if (is_int($nb)) {
|
||||
// the only division that may overflow is PHP_INT_MIN / -1,
|
||||
// which cannot happen here as we've already handled a divisor of -1 above.
|
||||
$r = $na % $nb;
|
||||
$q = ($na - $r) / $nb;
|
||||
|
||||
assert(is_int($q));
|
||||
|
||||
return [
|
||||
(string) $q,
|
||||
(string) $r
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
|
||||
|
||||
[$q, $r] = $this->doDiv($aDig, $bDig);
|
||||
|
||||
if ($aNeg !== $bNeg) {
|
||||
$q = $this->neg($q);
|
||||
}
|
||||
|
||||
if ($aNeg) {
|
||||
$r = $this->neg($r);
|
||||
}
|
||||
|
||||
return [$q, $r];
|
||||
}
|
||||
|
||||
public function pow(string $a, int $e) : string
|
||||
{
|
||||
if ($e === 0) {
|
||||
return '1';
|
||||
}
|
||||
|
||||
if ($e === 1) {
|
||||
return $a;
|
||||
}
|
||||
|
||||
$odd = $e % 2;
|
||||
$e -= $odd;
|
||||
|
||||
$aa = $this->mul($a, $a);
|
||||
|
||||
/** @psalm-suppress PossiblyInvalidArgument We're sure that $e / 2 is an int now */
|
||||
$result = $this->pow($aa, $e / 2);
|
||||
|
||||
if ($odd === 1) {
|
||||
$result = $this->mul($result, $a);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Algorithm from: https://www.geeksforgeeks.org/modular-exponentiation-power-in-modular-arithmetic/
|
||||
*/
|
||||
public function modPow(string $base, string $exp, string $mod) : string
|
||||
{
|
||||
// special case: the algorithm below fails with 0 power 0 mod 1 (returns 1 instead of 0)
|
||||
if ($base === '0' && $exp === '0' && $mod === '1') {
|
||||
return '0';
|
||||
}
|
||||
|
||||
// special case: the algorithm below fails with power 0 mod 1 (returns 1 instead of 0)
|
||||
if ($exp === '0' && $mod === '1') {
|
||||
return '0';
|
||||
}
|
||||
|
||||
$x = $base;
|
||||
|
||||
$res = '1';
|
||||
|
||||
// numbers are positive, so we can use remainder instead of modulo
|
||||
$x = $this->divR($x, $mod);
|
||||
|
||||
while ($exp !== '0') {
|
||||
if (in_array($exp[-1], ['1', '3', '5', '7', '9'])) { // odd
|
||||
$res = $this->divR($this->mul($res, $x), $mod);
|
||||
}
|
||||
|
||||
$exp = $this->divQ($exp, '2');
|
||||
$x = $this->divR($this->mul($x, $x), $mod);
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapted from https://cp-algorithms.com/num_methods/roots_newton.html
|
||||
*/
|
||||
public function sqrt(string $n) : string
|
||||
{
|
||||
if ($n === '0') {
|
||||
return '0';
|
||||
}
|
||||
|
||||
// initial approximation
|
||||
$x = \str_repeat('9', \intdiv(\strlen($n), 2) ?: 1);
|
||||
|
||||
$decreased = false;
|
||||
|
||||
for (;;) {
|
||||
$nx = $this->divQ($this->add($x, $this->divQ($n, $x)), '2');
|
||||
|
||||
if ($x === $nx || $this->cmp($nx, $x) > 0 && $decreased) {
|
||||
break;
|
||||
}
|
||||
|
||||
$decreased = $this->cmp($nx, $x) < 0;
|
||||
$x = $nx;
|
||||
}
|
||||
|
||||
return $x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the addition of two non-signed large integers.
|
||||
*/
|
||||
private function doAdd(string $a, string $b) : string
|
||||
{
|
||||
[$a, $b, $length] = $this->pad($a, $b);
|
||||
|
||||
$carry = 0;
|
||||
$result = '';
|
||||
|
||||
for ($i = $length - $this->maxDigits;; $i -= $this->maxDigits) {
|
||||
$blockLength = $this->maxDigits;
|
||||
|
||||
if ($i < 0) {
|
||||
$blockLength += $i;
|
||||
/** @psalm-suppress LoopInvalidation */
|
||||
$i = 0;
|
||||
}
|
||||
|
||||
/** @psalm-var numeric-string $blockA */
|
||||
$blockA = \substr($a, $i, $blockLength);
|
||||
|
||||
/** @psalm-var numeric-string $blockB */
|
||||
$blockB = \substr($b, $i, $blockLength);
|
||||
|
||||
$sum = (string) ($blockA + $blockB + $carry);
|
||||
$sumLength = \strlen($sum);
|
||||
|
||||
if ($sumLength > $blockLength) {
|
||||
$sum = \substr($sum, 1);
|
||||
$carry = 1;
|
||||
} else {
|
||||
if ($sumLength < $blockLength) {
|
||||
$sum = \str_repeat('0', $blockLength - $sumLength) . $sum;
|
||||
}
|
||||
$carry = 0;
|
||||
}
|
||||
|
||||
$result = $sum . $result;
|
||||
|
||||
if ($i === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($carry === 1) {
|
||||
$result = '1' . $result;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the subtraction of two non-signed large integers.
|
||||
*/
|
||||
private function doSub(string $a, string $b) : string
|
||||
{
|
||||
if ($a === $b) {
|
||||
return '0';
|
||||
}
|
||||
|
||||
// Ensure that we always subtract to a positive result: biggest minus smallest.
|
||||
$cmp = $this->doCmp($a, $b);
|
||||
|
||||
$invert = ($cmp === -1);
|
||||
|
||||
if ($invert) {
|
||||
$c = $a;
|
||||
$a = $b;
|
||||
$b = $c;
|
||||
}
|
||||
|
||||
[$a, $b, $length] = $this->pad($a, $b);
|
||||
|
||||
$carry = 0;
|
||||
$result = '';
|
||||
|
||||
$complement = 10 ** $this->maxDigits;
|
||||
|
||||
for ($i = $length - $this->maxDigits;; $i -= $this->maxDigits) {
|
||||
$blockLength = $this->maxDigits;
|
||||
|
||||
if ($i < 0) {
|
||||
$blockLength += $i;
|
||||
/** @psalm-suppress LoopInvalidation */
|
||||
$i = 0;
|
||||
}
|
||||
|
||||
/** @psalm-var numeric-string $blockA */
|
||||
$blockA = \substr($a, $i, $blockLength);
|
||||
|
||||
/** @psalm-var numeric-string $blockB */
|
||||
$blockB = \substr($b, $i, $blockLength);
|
||||
|
||||
$sum = $blockA - $blockB - $carry;
|
||||
|
||||
if ($sum < 0) {
|
||||
$sum += $complement;
|
||||
$carry = 1;
|
||||
} else {
|
||||
$carry = 0;
|
||||
}
|
||||
|
||||
$sum = (string) $sum;
|
||||
$sumLength = \strlen($sum);
|
||||
|
||||
if ($sumLength < $blockLength) {
|
||||
$sum = \str_repeat('0', $blockLength - $sumLength) . $sum;
|
||||
}
|
||||
|
||||
$result = $sum . $result;
|
||||
|
||||
if ($i === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Carry cannot be 1 when the loop ends, as a > b
|
||||
assert($carry === 0);
|
||||
|
||||
$result = \ltrim($result, '0');
|
||||
|
||||
if ($invert) {
|
||||
$result = $this->neg($result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the multiplication of two non-signed large integers.
|
||||
*/
|
||||
private function doMul(string $a, string $b) : string
|
||||
{
|
||||
$x = \strlen($a);
|
||||
$y = \strlen($b);
|
||||
|
||||
$maxDigits = \intdiv($this->maxDigits, 2);
|
||||
$complement = 10 ** $maxDigits;
|
||||
|
||||
$result = '0';
|
||||
|
||||
for ($i = $x - $maxDigits;; $i -= $maxDigits) {
|
||||
$blockALength = $maxDigits;
|
||||
|
||||
if ($i < 0) {
|
||||
$blockALength += $i;
|
||||
/** @psalm-suppress LoopInvalidation */
|
||||
$i = 0;
|
||||
}
|
||||
|
||||
$blockA = (int) \substr($a, $i, $blockALength);
|
||||
|
||||
$line = '';
|
||||
$carry = 0;
|
||||
|
||||
for ($j = $y - $maxDigits;; $j -= $maxDigits) {
|
||||
$blockBLength = $maxDigits;
|
||||
|
||||
if ($j < 0) {
|
||||
$blockBLength += $j;
|
||||
/** @psalm-suppress LoopInvalidation */
|
||||
$j = 0;
|
||||
}
|
||||
|
||||
$blockB = (int) \substr($b, $j, $blockBLength);
|
||||
|
||||
$mul = $blockA * $blockB + $carry;
|
||||
$value = $mul % $complement;
|
||||
$carry = ($mul - $value) / $complement;
|
||||
|
||||
$value = (string) $value;
|
||||
$value = \str_pad($value, $maxDigits, '0', STR_PAD_LEFT);
|
||||
|
||||
$line = $value . $line;
|
||||
|
||||
if ($j === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($carry !== 0) {
|
||||
$line = $carry . $line;
|
||||
}
|
||||
|
||||
$line = \ltrim($line, '0');
|
||||
|
||||
if ($line !== '') {
|
||||
$line .= \str_repeat('0', $x - $blockALength - $i);
|
||||
$result = $this->add($result, $line);
|
||||
}
|
||||
|
||||
if ($i === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the division of two non-signed large integers.
|
||||
*
|
||||
* @return string[] The quotient and remainder.
|
||||
*/
|
||||
private function doDiv(string $a, string $b) : array
|
||||
{
|
||||
$cmp = $this->doCmp($a, $b);
|
||||
|
||||
if ($cmp === -1) {
|
||||
return ['0', $a];
|
||||
}
|
||||
|
||||
$x = \strlen($a);
|
||||
$y = \strlen($b);
|
||||
|
||||
// we now know that a >= b && x >= y
|
||||
|
||||
$q = '0'; // quotient
|
||||
$r = $a; // remainder
|
||||
$z = $y; // focus length, always $y or $y+1
|
||||
|
||||
for (;;) {
|
||||
$focus = \substr($a, 0, $z);
|
||||
|
||||
$cmp = $this->doCmp($focus, $b);
|
||||
|
||||
if ($cmp === -1) {
|
||||
if ($z === $x) { // remainder < dividend
|
||||
break;
|
||||
}
|
||||
|
||||
$z++;
|
||||
}
|
||||
|
||||
$zeros = \str_repeat('0', $x - $z);
|
||||
|
||||
$q = $this->add($q, '1' . $zeros);
|
||||
$a = $this->sub($a, $b . $zeros);
|
||||
|
||||
$r = $a;
|
||||
|
||||
if ($r === '0') { // remainder == 0
|
||||
break;
|
||||
}
|
||||
|
||||
$x = \strlen($a);
|
||||
|
||||
if ($x < $y) { // remainder < dividend
|
||||
break;
|
||||
}
|
||||
|
||||
$z = $y;
|
||||
}
|
||||
|
||||
return [$q, $r];
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two non-signed large numbers.
|
||||
*
|
||||
* @return int [-1, 0, 1]
|
||||
*/
|
||||
private function doCmp(string $a, string $b) : int
|
||||
{
|
||||
$x = \strlen($a);
|
||||
$y = \strlen($b);
|
||||
|
||||
$cmp = $x <=> $y;
|
||||
|
||||
if ($cmp !== 0) {
|
||||
return $cmp;
|
||||
}
|
||||
|
||||
return \strcmp($a, $b) <=> 0; // enforce [-1, 0, 1]
|
||||
}
|
||||
|
||||
/**
|
||||
* Pads the left of one of the given numbers with zeros if necessary to make both numbers the same length.
|
||||
*
|
||||
* The numbers must only consist of digits, without leading minus sign.
|
||||
*
|
||||
* @return array{string, string, int}
|
||||
*/
|
||||
private function pad(string $a, string $b) : array
|
||||
{
|
||||
$x = \strlen($a);
|
||||
$y = \strlen($b);
|
||||
|
||||
if ($x > $y) {
|
||||
$b = \str_repeat('0', $x - $y) . $b;
|
||||
|
||||
return [$a, $b, $x];
|
||||
}
|
||||
|
||||
if ($x < $y) {
|
||||
$a = \str_repeat('0', $y - $x) . $a;
|
||||
|
||||
return [$a, $b, $y];
|
||||
}
|
||||
|
||||
return [$a, $b, $x];
|
||||
}
|
||||
}
|
107
vendor/brick/math/src/RoundingMode.php
vendored
Normal file
107
vendor/brick/math/src/RoundingMode.php
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math;
|
||||
|
||||
/**
|
||||
* Specifies a rounding behavior for numerical operations capable of discarding precision.
|
||||
*
|
||||
* Each rounding mode indicates how the least significant returned digit of a rounded result
|
||||
* is to be calculated. If fewer digits are returned than the digits needed to represent the
|
||||
* exact numerical result, the discarded digits will be referred to as the discarded fraction
|
||||
* regardless the digits' contribution to the value of the number. In other words, considered
|
||||
* as a numerical value, the discarded fraction could have an absolute value greater than one.
|
||||
*/
|
||||
final class RoundingMode
|
||||
{
|
||||
/**
|
||||
* Private constructor. This class is not instantiable.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the requested operation has an exact result, hence no rounding is necessary.
|
||||
*
|
||||
* If this rounding mode is specified on an operation that yields a result that
|
||||
* cannot be represented at the requested scale, a RoundingNecessaryException is thrown.
|
||||
*/
|
||||
public const UNNECESSARY = 0;
|
||||
|
||||
/**
|
||||
* Rounds away from zero.
|
||||
*
|
||||
* Always increments the digit prior to a nonzero discarded fraction.
|
||||
* Note that this rounding mode never decreases the magnitude of the calculated value.
|
||||
*/
|
||||
public const UP = 1;
|
||||
|
||||
/**
|
||||
* Rounds towards zero.
|
||||
*
|
||||
* Never increments the digit prior to a discarded fraction (i.e., truncates).
|
||||
* Note that this rounding mode never increases the magnitude of the calculated value.
|
||||
*/
|
||||
public const DOWN = 2;
|
||||
|
||||
/**
|
||||
* Rounds towards positive infinity.
|
||||
*
|
||||
* If the result is positive, behaves as for UP; if negative, behaves as for DOWN.
|
||||
* Note that this rounding mode never decreases the calculated value.
|
||||
*/
|
||||
public const CEILING = 3;
|
||||
|
||||
/**
|
||||
* Rounds towards negative infinity.
|
||||
*
|
||||
* If the result is positive, behave as for DOWN; if negative, behave as for UP.
|
||||
* Note that this rounding mode never increases the calculated value.
|
||||
*/
|
||||
public const FLOOR = 4;
|
||||
|
||||
/**
|
||||
* Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round up.
|
||||
*
|
||||
* Behaves as for UP if the discarded fraction is >= 0.5; otherwise, behaves as for DOWN.
|
||||
* Note that this is the rounding mode commonly taught at school.
|
||||
*/
|
||||
public const HALF_UP = 5;
|
||||
|
||||
/**
|
||||
* Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round down.
|
||||
*
|
||||
* Behaves as for UP if the discarded fraction is > 0.5; otherwise, behaves as for DOWN.
|
||||
*/
|
||||
public const HALF_DOWN = 6;
|
||||
|
||||
/**
|
||||
* Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round towards positive infinity.
|
||||
*
|
||||
* If the result is positive, behaves as for HALF_UP; if negative, behaves as for HALF_DOWN.
|
||||
*/
|
||||
public const HALF_CEILING = 7;
|
||||
|
||||
/**
|
||||
* Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round towards negative infinity.
|
||||
*
|
||||
* If the result is positive, behaves as for HALF_DOWN; if negative, behaves as for HALF_UP.
|
||||
*/
|
||||
public const HALF_FLOOR = 8;
|
||||
|
||||
/**
|
||||
* Rounds towards the "nearest neighbor" unless both neighbors are equidistant, in which case rounds towards the even neighbor.
|
||||
*
|
||||
* Behaves as for HALF_UP if the digit to the left of the discarded fraction is odd;
|
||||
* behaves as for HALF_DOWN if it's even.
|
||||
*
|
||||
* Note that this is the rounding mode that statistically minimizes
|
||||
* cumulative error when applied repeatedly over a sequence of calculations.
|
||||
* It is sometimes known as "Banker's rounding", and is chiefly used in the USA.
|
||||
*/
|
||||
public const HALF_EVEN = 9;
|
||||
}
|
269
vendor/cakephp/core/App.php
vendored
Normal file
269
vendor/cakephp/core/App.php
vendored
Normal file
@ -0,0 +1,269 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 1.2.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
/**
|
||||
* App is responsible for resource location, and path management.
|
||||
*
|
||||
* ### Adding paths
|
||||
*
|
||||
* Additional paths for Templates and Plugins are configured with Configure now. See config/app.php for an
|
||||
* example. The `App.paths.plugins` and `App.paths.templates` variables are used to configure paths for plugins
|
||||
* and templates respectively. All class based resources should be mapped using your application's autoloader.
|
||||
*
|
||||
* ### Inspecting loaded paths
|
||||
*
|
||||
* You can inspect the currently loaded paths using `App::classPath('Controller')` for example to see loaded
|
||||
* controller paths.
|
||||
*
|
||||
* It is also possible to inspect paths for plugin classes, for instance, to get
|
||||
* the path to a plugin's helpers you would call `App::classPath('View/Helper', 'MyPlugin')`
|
||||
*
|
||||
* ### Locating plugins
|
||||
*
|
||||
* Plugins can be located with App as well. Using Plugin::path('DebugKit') for example, will
|
||||
* give you the full path to the DebugKit plugin.
|
||||
*
|
||||
* @link https://book.cakephp.org/4/en/core-libraries/app.html
|
||||
*/
|
||||
class App
|
||||
{
|
||||
/**
|
||||
* Return the class name namespaced. This method checks if the class is defined on the
|
||||
* application/plugin, otherwise try to load from the CakePHP core
|
||||
*
|
||||
* @param string $class Class name
|
||||
* @param string $type Type of class
|
||||
* @param string $suffix Class name suffix
|
||||
* @return string|null Namespaced class name, null if the class is not found.
|
||||
* @psalm-return class-string|null
|
||||
*/
|
||||
public static function className(string $class, string $type = '', string $suffix = ''): ?string
|
||||
{
|
||||
if (strpos($class, '\\') !== false) {
|
||||
return class_exists($class) ? $class : null;
|
||||
}
|
||||
|
||||
[$plugin, $name] = pluginSplit($class);
|
||||
$fullname = '\\' . str_replace('/', '\\', $type . '\\' . $name) . $suffix;
|
||||
|
||||
$base = $plugin ?: Configure::read('App.namespace');
|
||||
if ($base !== null) {
|
||||
$base = str_replace('/', '\\', rtrim($base, '\\'));
|
||||
|
||||
if (static::_classExistsInBase($fullname, $base)) {
|
||||
/** @var class-string */
|
||||
return $base . $fullname;
|
||||
}
|
||||
}
|
||||
|
||||
if ($plugin || !static::_classExistsInBase($fullname, 'Cake')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var class-string */
|
||||
return 'Cake' . $fullname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the plugin split name of a class
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* ```
|
||||
* App::shortName(
|
||||
* 'SomeVendor\SomePlugin\Controller\Component\TestComponent',
|
||||
* 'Controller/Component',
|
||||
* 'Component'
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* Returns: SomeVendor/SomePlugin.Test
|
||||
*
|
||||
* ```
|
||||
* App::shortName(
|
||||
* 'SomeVendor\SomePlugin\Controller\Component\Subfolder\TestComponent',
|
||||
* 'Controller/Component',
|
||||
* 'Component'
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* Returns: SomeVendor/SomePlugin.Subfolder/Test
|
||||
*
|
||||
* ```
|
||||
* App::shortName(
|
||||
* 'Cake\Controller\Component\AuthComponent',
|
||||
* 'Controller/Component',
|
||||
* 'Component'
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* Returns: Auth
|
||||
*
|
||||
* @param string $class Class name
|
||||
* @param string $type Type of class
|
||||
* @param string $suffix Class name suffix
|
||||
* @return string Plugin split name of class
|
||||
*/
|
||||
public static function shortName(string $class, string $type, string $suffix = ''): string
|
||||
{
|
||||
$class = str_replace('\\', '/', $class);
|
||||
$type = '/' . $type . '/';
|
||||
|
||||
$pos = strrpos($class, $type);
|
||||
if ($pos === false) {
|
||||
return $class;
|
||||
}
|
||||
|
||||
$pluginName = (string)substr($class, 0, $pos);
|
||||
$name = (string)substr($class, $pos + strlen($type));
|
||||
|
||||
if ($suffix) {
|
||||
$name = (string)substr($name, 0, -strlen($suffix));
|
||||
}
|
||||
|
||||
$nonPluginNamespaces = [
|
||||
'Cake',
|
||||
str_replace('\\', '/', (string)Configure::read('App.namespace')),
|
||||
];
|
||||
if (in_array($pluginName, $nonPluginNamespaces, true)) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
return $pluginName . '.' . $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* _classExistsInBase
|
||||
*
|
||||
* Test isolation wrapper
|
||||
*
|
||||
* @param string $name Class name.
|
||||
* @param string $namespace Namespace.
|
||||
* @return bool
|
||||
*/
|
||||
protected static function _classExistsInBase(string $name, string $namespace): bool
|
||||
{
|
||||
return class_exists($namespace . $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to read information stored path.
|
||||
*
|
||||
* The 1st character of $type argument should be lower cased and will return the
|
||||
* value of `App.paths.$type` config.
|
||||
*
|
||||
* Default types:
|
||||
* - plugins
|
||||
* - templates
|
||||
* - locales
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* App::path('plugins');
|
||||
* ```
|
||||
*
|
||||
* Will return the value of `App.paths.plugins` config.
|
||||
*
|
||||
* Deprecated: 4.0 App::path() is deprecated for class path (inside src/ directory).
|
||||
* Use \Cake\Core\App::classPath() instead or directly the method on \Cake\Core\Plugin class.
|
||||
*
|
||||
* @param string $type Type of path
|
||||
* @param string|null $plugin Plugin name
|
||||
* @return array<string>
|
||||
* @link https://book.cakephp.org/4/en/core-libraries/app.html#finding-paths-to-namespaces
|
||||
*/
|
||||
public static function path(string $type, ?string $plugin = null): array
|
||||
{
|
||||
if ($plugin === null && $type[0] === strtolower($type[0])) {
|
||||
return (array)Configure::read('App.paths.' . $type);
|
||||
}
|
||||
|
||||
if ($type === 'templates') {
|
||||
/** @psalm-suppress PossiblyNullArgument */
|
||||
return [Plugin::templatePath($plugin)];
|
||||
}
|
||||
|
||||
if ($type === 'locales') {
|
||||
/** @psalm-suppress PossiblyNullArgument */
|
||||
return [Plugin::path($plugin) . 'resources' . DIRECTORY_SEPARATOR . 'locales' . DIRECTORY_SEPARATOR];
|
||||
}
|
||||
|
||||
deprecationWarning(
|
||||
'App::path() is deprecated for class path.'
|
||||
. ' Use \Cake\Core\App::classPath() or \Cake\Core\Plugin::classPath() instead.'
|
||||
);
|
||||
|
||||
return static::classPath($type, $plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path to a class type in the application or a plugin.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* App::classPath('Model/Table');
|
||||
* ```
|
||||
*
|
||||
* Will return the path for tables - e.g. `src/Model/Table/`.
|
||||
*
|
||||
* ```
|
||||
* App::classPath('Model/Table', 'My/Plugin');
|
||||
* ```
|
||||
*
|
||||
* Will return the plugin based path for those.
|
||||
*
|
||||
* @param string $type Package type.
|
||||
* @param string|null $plugin Plugin name.
|
||||
* @return array<string>
|
||||
*/
|
||||
public static function classPath(string $type, ?string $plugin = null): array
|
||||
{
|
||||
if ($plugin !== null) {
|
||||
return [
|
||||
Plugin::classPath($plugin) . $type . DIRECTORY_SEPARATOR,
|
||||
];
|
||||
}
|
||||
|
||||
return [APP . $type . DIRECTORY_SEPARATOR];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full path to a package inside the CakePHP core
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* ```
|
||||
* App::core('Cache/Engine');
|
||||
* ```
|
||||
*
|
||||
* Will return the full path to the cache engines package.
|
||||
*
|
||||
* @param string $type Package type.
|
||||
* @return array<string> Full path to package
|
||||
*/
|
||||
public static function core(string $type): array
|
||||
{
|
||||
if ($type === 'templates') {
|
||||
return [CORE_PATH . 'templates' . DIRECTORY_SEPARATOR];
|
||||
}
|
||||
|
||||
return [CAKE . str_replace('/', DIRECTORY_SEPARATOR, $type) . DIRECTORY_SEPARATOR];
|
||||
}
|
||||
}
|
305
vendor/cakephp/core/BasePlugin.php
vendored
Normal file
305
vendor/cakephp/core/BasePlugin.php
vendored
Normal file
@ -0,0 +1,305 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright 2005-2011, Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.6.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use Cake\Console\CommandCollection;
|
||||
use Cake\Http\MiddlewareQueue;
|
||||
use Cake\Routing\RouteBuilder;
|
||||
use Closure;
|
||||
use InvalidArgumentException;
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
* Base Plugin Class
|
||||
*
|
||||
* Every plugin should extend from this class or implement the interfaces and
|
||||
* include a plugin class in its src root folder.
|
||||
*/
|
||||
class BasePlugin implements PluginInterface
|
||||
{
|
||||
/**
|
||||
* Do bootstrapping or not
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $bootstrapEnabled = true;
|
||||
|
||||
/**
|
||||
* Console middleware
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $consoleEnabled = true;
|
||||
|
||||
/**
|
||||
* Enable middleware
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $middlewareEnabled = true;
|
||||
|
||||
/**
|
||||
* Register container services
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $servicesEnabled = true;
|
||||
|
||||
/**
|
||||
* Load routes or not
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $routesEnabled = true;
|
||||
|
||||
/**
|
||||
* The path to this plugin.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* The class path for this plugin.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $classPath;
|
||||
|
||||
/**
|
||||
* The config path for this plugin.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $configPath;
|
||||
|
||||
/**
|
||||
* The templates path for this plugin.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $templatePath;
|
||||
|
||||
/**
|
||||
* The name of this plugin
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array<string, mixed> $options Options
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
foreach (static::VALID_HOOKS as $key) {
|
||||
if (isset($options[$key])) {
|
||||
$this->{"{$key}Enabled"} = (bool)$options[$key];
|
||||
}
|
||||
}
|
||||
foreach (['name', 'path', 'classPath', 'configPath', 'templatePath'] as $path) {
|
||||
if (isset($options[$path])) {
|
||||
$this->{$path} = $options[$path];
|
||||
}
|
||||
}
|
||||
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization hook called from constructor.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initialize(): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
if ($this->name) {
|
||||
return $this->name;
|
||||
}
|
||||
$parts = explode('\\', static::class);
|
||||
array_pop($parts);
|
||||
$this->name = implode('/', $parts);
|
||||
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getPath(): string
|
||||
{
|
||||
if ($this->path) {
|
||||
return $this->path;
|
||||
}
|
||||
$reflection = new ReflectionClass($this);
|
||||
$path = dirname($reflection->getFileName());
|
||||
|
||||
// Trim off src
|
||||
if (substr($path, -3) === 'src') {
|
||||
$path = substr($path, 0, -3);
|
||||
}
|
||||
$this->path = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
|
||||
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getConfigPath(): string
|
||||
{
|
||||
if ($this->configPath) {
|
||||
return $this->configPath;
|
||||
}
|
||||
$path = $this->getPath();
|
||||
|
||||
return $path . 'config' . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getClassPath(): string
|
||||
{
|
||||
if ($this->classPath) {
|
||||
return $this->classPath;
|
||||
}
|
||||
$path = $this->getPath();
|
||||
|
||||
return $path . 'src' . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getTemplatePath(): string
|
||||
{
|
||||
if ($this->templatePath) {
|
||||
return $this->templatePath;
|
||||
}
|
||||
$path = $this->getPath();
|
||||
|
||||
return $this->templatePath = $path . 'templates' . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function enable(string $hook)
|
||||
{
|
||||
$this->checkHook($hook);
|
||||
$this->{"{$hook}Enabled"} = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function disable(string $hook)
|
||||
{
|
||||
$this->checkHook($hook);
|
||||
$this->{"{$hook}Enabled"} = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function isEnabled(string $hook): bool
|
||||
{
|
||||
$this->checkHook($hook);
|
||||
|
||||
return $this->{"{$hook}Enabled"} === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a hook name is valid
|
||||
*
|
||||
* @param string $hook The hook name to check
|
||||
* @throws \InvalidArgumentException on invalid hooks
|
||||
* @return void
|
||||
*/
|
||||
protected function checkHook(string $hook): void
|
||||
{
|
||||
if (!in_array($hook, static::VALID_HOOKS, true)) {
|
||||
throw new InvalidArgumentException(
|
||||
"`$hook` is not a valid hook name. Must be one of " . implode(', ', static::VALID_HOOKS)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function routes(RouteBuilder $routes): void
|
||||
{
|
||||
$path = $this->getConfigPath() . 'routes.php';
|
||||
if (is_file($path)) {
|
||||
$return = require $path;
|
||||
if ($return instanceof Closure) {
|
||||
$return($routes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function bootstrap(PluginApplicationInterface $app): void
|
||||
{
|
||||
$bootstrap = $this->getConfigPath() . 'bootstrap.php';
|
||||
if (is_file($bootstrap)) {
|
||||
require $bootstrap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function console(CommandCollection $commands): CommandCollection
|
||||
{
|
||||
return $commands->addMany($commands->discoverPlugin($this->getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
|
||||
{
|
||||
return $middlewareQueue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register container services for this plugin.
|
||||
*
|
||||
* @param \Cake\Core\ContainerInterface $container The container to add services to.
|
||||
* @return void
|
||||
*/
|
||||
public function services(ContainerInterface $container): void
|
||||
{
|
||||
}
|
||||
}
|
139
vendor/cakephp/core/ClassLoader.php
vendored
Normal file
139
vendor/cakephp/core/ClassLoader.php
vendored
Normal file
@ -0,0 +1,139 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright 2005-2011, Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
/**
|
||||
* ClassLoader
|
||||
*
|
||||
* @deprecated 4.0.3 Use composer to generate autoload files instead.
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
/**
|
||||
* An associative array where the key is a namespace prefix and the value
|
||||
* is an array of base directories for classes in that namespace.
|
||||
*
|
||||
* @var array<string, array>
|
||||
*/
|
||||
protected $_prefixes = [];
|
||||
|
||||
/**
|
||||
* Register loader with SPL autoloader stack.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
/** @var callable $callable */
|
||||
$callable = [$this, 'loadClass'];
|
||||
spl_autoload_register($callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a base directory for a namespace prefix.
|
||||
*
|
||||
* @param string $prefix The namespace prefix.
|
||||
* @param string $baseDir A base directory for class files in the
|
||||
* namespace.
|
||||
* @param bool $prepend If true, prepend the base directory to the stack
|
||||
* instead of appending it; this causes it to be searched first rather
|
||||
* than last.
|
||||
* @return void
|
||||
*/
|
||||
public function addNamespace(string $prefix, string $baseDir, bool $prepend = false): void
|
||||
{
|
||||
$prefix = trim($prefix, '\\') . '\\';
|
||||
|
||||
$baseDir = rtrim($baseDir, '/') . DIRECTORY_SEPARATOR;
|
||||
$baseDir = rtrim($baseDir, DIRECTORY_SEPARATOR) . '/';
|
||||
|
||||
$this->_prefixes[$prefix] = $this->_prefixes[$prefix] ?? [];
|
||||
|
||||
if ($prepend) {
|
||||
array_unshift($this->_prefixes[$prefix], $baseDir);
|
||||
} else {
|
||||
$this->_prefixes[$prefix][] = $baseDir;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the class file for a given class name.
|
||||
*
|
||||
* @param string $class The fully-qualified class name.
|
||||
* @return string|false The mapped file name on success, or boolean false on
|
||||
* failure.
|
||||
*/
|
||||
public function loadClass(string $class)
|
||||
{
|
||||
$prefix = $class;
|
||||
|
||||
while (($pos = strrpos($prefix, '\\')) !== false) {
|
||||
$prefix = substr($class, 0, $pos + 1);
|
||||
$relativeClass = substr($class, $pos + 1);
|
||||
|
||||
$mappedFile = $this->_loadMappedFile($prefix, $relativeClass);
|
||||
if ($mappedFile) {
|
||||
return $mappedFile;
|
||||
}
|
||||
|
||||
$prefix = rtrim($prefix, '\\');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the mapped file for a namespace prefix and relative class.
|
||||
*
|
||||
* @param string $prefix The namespace prefix.
|
||||
* @param string $relativeClass The relative class name.
|
||||
* @return string|false Boolean false if no mapped file can be loaded, or the
|
||||
* name of the mapped file that was loaded.
|
||||
*/
|
||||
protected function _loadMappedFile(string $prefix, string $relativeClass)
|
||||
{
|
||||
if (!isset($this->_prefixes[$prefix])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->_prefixes[$prefix] as $baseDir) {
|
||||
$file = $baseDir . str_replace('\\', DIRECTORY_SEPARATOR, $relativeClass) . '.php';
|
||||
|
||||
if ($this->_requireFile($file)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If a file exists, require it from the file system.
|
||||
*
|
||||
* @param string $file The file to require.
|
||||
* @return bool True if the file exists, false if not.
|
||||
*/
|
||||
protected function _requireFile(string $file): bool
|
||||
{
|
||||
if (file_exists($file)) {
|
||||
require $file;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
498
vendor/cakephp/core/Configure.php
vendored
Normal file
498
vendor/cakephp/core/Configure.php
vendored
Normal file
@ -0,0 +1,498 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 1.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use Cake\Cache\Cache;
|
||||
use Cake\Core\Configure\ConfigEngineInterface;
|
||||
use Cake\Core\Configure\Engine\PhpConfig;
|
||||
use Cake\Core\Exception\CakeException;
|
||||
use Cake\Utility\Hash;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Configuration class. Used for managing runtime configuration information.
|
||||
*
|
||||
* Provides features for reading and writing to the runtime configuration, as well
|
||||
* as methods for loading additional configuration files or storing runtime configuration
|
||||
* for future use.
|
||||
*
|
||||
* @link https://book.cakephp.org/4/en/development/configuration.html
|
||||
*/
|
||||
class Configure
|
||||
{
|
||||
/**
|
||||
* Array of values currently stored in Configure.
|
||||
*
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
protected static $_values = [
|
||||
'debug' => false,
|
||||
];
|
||||
|
||||
/**
|
||||
* Configured engine classes, used to load config files from resources
|
||||
*
|
||||
* @see \Cake\Core\Configure::load()
|
||||
* @var array<\Cake\Core\Configure\ConfigEngineInterface>
|
||||
*/
|
||||
protected static $_engines = [];
|
||||
|
||||
/**
|
||||
* Flag to track whether ini_set exists.
|
||||
*
|
||||
* @var bool|null
|
||||
*/
|
||||
protected static $_hasIniSet;
|
||||
|
||||
/**
|
||||
* Used to store a dynamic variable in Configure.
|
||||
*
|
||||
* Usage:
|
||||
* ```
|
||||
* Configure::write('One.key1', 'value of the Configure::One[key1]');
|
||||
* Configure::write(['One.key1' => 'value of the Configure::One[key1]']);
|
||||
* Configure::write('One', [
|
||||
* 'key1' => 'value of the Configure::One[key1]',
|
||||
* 'key2' => 'value of the Configure::One[key2]'
|
||||
* ]);
|
||||
*
|
||||
* Configure::write([
|
||||
* 'One.key1' => 'value of the Configure::One[key1]',
|
||||
* 'One.key2' => 'value of the Configure::One[key2]'
|
||||
* ]);
|
||||
* ```
|
||||
*
|
||||
* @param array<string, mixed>|string $config The key to write, can be a dot notation value.
|
||||
* Alternatively can be an array containing key(s) and value(s).
|
||||
* @param mixed $value Value to set for the given key.
|
||||
* @return void
|
||||
* @link https://book.cakephp.org/4/en/development/configuration.html#writing-configuration-data
|
||||
*/
|
||||
public static function write($config, $value = null): void
|
||||
{
|
||||
if (!is_array($config)) {
|
||||
$config = [$config => $value];
|
||||
}
|
||||
|
||||
foreach ($config as $name => $valueToInsert) {
|
||||
static::$_values = Hash::insert(static::$_values, $name, $valueToInsert);
|
||||
}
|
||||
|
||||
if (isset($config['debug'])) {
|
||||
if (static::$_hasIniSet === null) {
|
||||
static::$_hasIniSet = function_exists('ini_set');
|
||||
}
|
||||
if (static::$_hasIniSet) {
|
||||
ini_set('display_errors', $config['debug'] ? '1' : '0');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to read information stored in Configure. It's not
|
||||
* possible to store `null` values in Configure.
|
||||
*
|
||||
* Usage:
|
||||
* ```
|
||||
* Configure::read('Name'); will return all values for Name
|
||||
* Configure::read('Name.key'); will return only the value of Configure::Name[key]
|
||||
* ```
|
||||
*
|
||||
* @param string|null $var Variable to obtain. Use '.' to access array elements.
|
||||
* @param mixed $default The return value when the configure does not exist
|
||||
* @return mixed Value stored in configure, or null.
|
||||
* @link https://book.cakephp.org/4/en/development/configuration.html#reading-configuration-data
|
||||
*/
|
||||
public static function read(?string $var = null, $default = null)
|
||||
{
|
||||
if ($var === null) {
|
||||
return static::$_values;
|
||||
}
|
||||
|
||||
return Hash::get(static::$_values, $var, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if given variable is set in Configure.
|
||||
*
|
||||
* @param string $var Variable name to check for
|
||||
* @return bool True if variable is there
|
||||
*/
|
||||
public static function check(string $var): bool
|
||||
{
|
||||
if (empty($var)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return static::read($var) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to get information stored in Configure. It's not
|
||||
* possible to store `null` values in Configure.
|
||||
*
|
||||
* Acts as a wrapper around Configure::read() and Configure::check().
|
||||
* The configure key/value pair fetched via this method is expected to exist.
|
||||
* In case it does not an exception will be thrown.
|
||||
*
|
||||
* Usage:
|
||||
* ```
|
||||
* Configure::readOrFail('Name'); will return all values for Name
|
||||
* Configure::readOrFail('Name.key'); will return only the value of Configure::Name[key]
|
||||
* ```
|
||||
*
|
||||
* @param string $var Variable to obtain. Use '.' to access array elements.
|
||||
* @return mixed Value stored in configure.
|
||||
* @throws \RuntimeException if the requested configuration is not set.
|
||||
* @link https://book.cakephp.org/4/en/development/configuration.html#reading-configuration-data
|
||||
*/
|
||||
public static function readOrFail(string $var)
|
||||
{
|
||||
if (!static::check($var)) {
|
||||
throw new RuntimeException(sprintf('Expected configuration key "%s" not found.', $var));
|
||||
}
|
||||
|
||||
return static::read($var);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to delete a variable from Configure.
|
||||
*
|
||||
* Usage:
|
||||
* ```
|
||||
* Configure::delete('Name'); will delete the entire Configure::Name
|
||||
* Configure::delete('Name.key'); will delete only the Configure::Name[key]
|
||||
* ```
|
||||
*
|
||||
* @param string $var the var to be deleted
|
||||
* @return void
|
||||
* @link https://book.cakephp.org/4/en/development/configuration.html#deleting-configuration-data
|
||||
*/
|
||||
public static function delete(string $var): void
|
||||
{
|
||||
static::$_values = Hash::remove(static::$_values, $var);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to consume information stored in Configure. It's not
|
||||
* possible to store `null` values in Configure.
|
||||
*
|
||||
* Acts as a wrapper around Configure::consume() and Configure::check().
|
||||
* The configure key/value pair consumed via this method is expected to exist.
|
||||
* In case it does not an exception will be thrown.
|
||||
*
|
||||
* @param string $var Variable to consume. Use '.' to access array elements.
|
||||
* @return mixed Value stored in configure.
|
||||
* @throws \RuntimeException if the requested configuration is not set.
|
||||
* @since 3.6.0
|
||||
*/
|
||||
public static function consumeOrFail(string $var)
|
||||
{
|
||||
if (!static::check($var)) {
|
||||
throw new RuntimeException(sprintf('Expected configuration key "%s" not found.', $var));
|
||||
}
|
||||
|
||||
return static::consume($var);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to read and delete a variable from Configure.
|
||||
*
|
||||
* This is primarily used during bootstrapping to move configuration data
|
||||
* out of configure into the various other classes in CakePHP.
|
||||
*
|
||||
* @param string $var The key to read and remove.
|
||||
* @return array|string|null
|
||||
*/
|
||||
public static function consume(string $var)
|
||||
{
|
||||
if (strpos($var, '.') === false) {
|
||||
if (!isset(static::$_values[$var])) {
|
||||
return null;
|
||||
}
|
||||
$value = static::$_values[$var];
|
||||
unset(static::$_values[$var]);
|
||||
|
||||
return $value;
|
||||
}
|
||||
$value = Hash::get(static::$_values, $var);
|
||||
static::delete($var);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new engine to Configure. Engines allow you to read configuration
|
||||
* files in various formats/storage locations. CakePHP comes with two built-in engines
|
||||
* PhpConfig and IniConfig. You can also implement your own engine classes in your application.
|
||||
*
|
||||
* To add a new engine to Configure:
|
||||
*
|
||||
* ```
|
||||
* Configure::config('ini', new IniConfig());
|
||||
* ```
|
||||
*
|
||||
* @param string $name The name of the engine being configured. This alias is used later to
|
||||
* read values from a specific engine.
|
||||
* @param \Cake\Core\Configure\ConfigEngineInterface $engine The engine to append.
|
||||
* @return void
|
||||
*/
|
||||
public static function config(string $name, ConfigEngineInterface $engine): void
|
||||
{
|
||||
static::$_engines[$name] = $engine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the Engine objects is configured.
|
||||
*
|
||||
* @param string $name Engine name.
|
||||
* @return bool
|
||||
*/
|
||||
public static function isConfigured(string $name): bool
|
||||
{
|
||||
return isset(static::$_engines[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the names of the configured Engine objects.
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public static function configured(): array
|
||||
{
|
||||
$engines = array_keys(static::$_engines);
|
||||
|
||||
return array_map(function ($key) {
|
||||
return (string)$key;
|
||||
}, $engines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a configured engine. This will unset the engine
|
||||
* and make any future attempts to use it cause an Exception.
|
||||
*
|
||||
* @param string $name Name of the engine to drop.
|
||||
* @return bool Success
|
||||
*/
|
||||
public static function drop(string $name): bool
|
||||
{
|
||||
if (!isset(static::$_engines[$name])) {
|
||||
return false;
|
||||
}
|
||||
unset(static::$_engines[$name]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads stored configuration information from a resource. You can add
|
||||
* config file resource engines with `Configure::config()`.
|
||||
*
|
||||
* Loaded configuration information will be merged with the current
|
||||
* runtime configuration. You can load configuration files from plugins
|
||||
* by preceding the filename with the plugin name.
|
||||
*
|
||||
* `Configure::load('Users.user', 'default')`
|
||||
*
|
||||
* Would load the 'user' config file using the default config engine. You can load
|
||||
* app config files by giving the name of the resource you want loaded.
|
||||
*
|
||||
* ```
|
||||
* Configure::load('setup', 'default');
|
||||
* ```
|
||||
*
|
||||
* If using `default` config and no engine has been configured for it yet,
|
||||
* one will be automatically created using PhpConfig
|
||||
*
|
||||
* @param string $key name of configuration resource to load.
|
||||
* @param string $config Name of the configured engine to use to read the resource identified by $key.
|
||||
* @param bool $merge if config files should be merged instead of simply overridden
|
||||
* @return bool True if load successful.
|
||||
* @throws \Cake\Core\Exception\CakeException if the $config engine is not found
|
||||
* @link https://book.cakephp.org/4/en/development/configuration.html#reading-and-writing-configuration-files
|
||||
*/
|
||||
public static function load(string $key, string $config = 'default', bool $merge = true): bool
|
||||
{
|
||||
$engine = static::_getEngine($config);
|
||||
if (!$engine) {
|
||||
throw new CakeException(
|
||||
sprintf(
|
||||
'Config %s engine not found when attempting to load %s.',
|
||||
$config,
|
||||
$key
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$values = $engine->read($key);
|
||||
|
||||
if ($merge) {
|
||||
$values = Hash::merge(static::$_values, $values);
|
||||
}
|
||||
|
||||
static::write($values);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump data currently in Configure into $key. The serialization format
|
||||
* is decided by the config engine attached as $config. For example, if the
|
||||
* 'default' adapter is a PhpConfig, the generated file will be a PHP
|
||||
* configuration file loadable by the PhpConfig.
|
||||
*
|
||||
* ### Usage
|
||||
*
|
||||
* Given that the 'default' engine is an instance of PhpConfig.
|
||||
* Save all data in Configure to the file `my_config.php`:
|
||||
*
|
||||
* ```
|
||||
* Configure::dump('my_config', 'default');
|
||||
* ```
|
||||
*
|
||||
* Save only the error handling configuration:
|
||||
*
|
||||
* ```
|
||||
* Configure::dump('error', 'default', ['Error', 'Exception'];
|
||||
* ```
|
||||
*
|
||||
* @param string $key The identifier to create in the config adapter.
|
||||
* This could be a filename or a cache key depending on the adapter being used.
|
||||
* @param string $config The name of the configured adapter to dump data with.
|
||||
* @param array<string> $keys The name of the top-level keys you want to dump.
|
||||
* This allows you save only some data stored in Configure.
|
||||
* @return bool Success
|
||||
* @throws \Cake\Core\Exception\CakeException if the adapter does not implement a `dump` method.
|
||||
*/
|
||||
public static function dump(string $key, string $config = 'default', array $keys = []): bool
|
||||
{
|
||||
$engine = static::_getEngine($config);
|
||||
if (!$engine) {
|
||||
throw new CakeException(sprintf('There is no "%s" config engine.', $config));
|
||||
}
|
||||
$values = static::$_values;
|
||||
if (!empty($keys)) {
|
||||
$values = array_intersect_key($values, array_flip($keys));
|
||||
}
|
||||
|
||||
return $engine->dump($key, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configured engine. Internally used by `Configure::load()` and `Configure::dump()`
|
||||
* Will create new PhpConfig for default if not configured yet.
|
||||
*
|
||||
* @param string $config The name of the configured adapter
|
||||
* @return \Cake\Core\Configure\ConfigEngineInterface|null Engine instance or null
|
||||
*/
|
||||
protected static function _getEngine(string $config): ?ConfigEngineInterface
|
||||
{
|
||||
if (!isset(static::$_engines[$config])) {
|
||||
if ($config !== 'default') {
|
||||
return null;
|
||||
}
|
||||
static::config($config, new PhpConfig());
|
||||
}
|
||||
|
||||
return static::$_engines[$config];
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to determine the current version of CakePHP.
|
||||
*
|
||||
* Usage
|
||||
* ```
|
||||
* Configure::version();
|
||||
* ```
|
||||
*
|
||||
* @return string Current version of CakePHP
|
||||
*/
|
||||
public static function version(): string
|
||||
{
|
||||
$version = static::read('Cake.version');
|
||||
if ($version !== null) {
|
||||
return $version;
|
||||
}
|
||||
|
||||
$path = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'config/config.php';
|
||||
if (is_file($path)) {
|
||||
$config = require $path;
|
||||
static::write($config);
|
||||
|
||||
return static::read('Cake.version');
|
||||
}
|
||||
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to write runtime configuration into Cache. Stored runtime configuration can be
|
||||
* restored using `Configure::restore()`. These methods can be used to enable configuration managers
|
||||
* frontends, or other GUI type interfaces for configuration.
|
||||
*
|
||||
* @param string $name The storage name for the saved configuration.
|
||||
* @param string $cacheConfig The cache configuration to save into. Defaults to 'default'
|
||||
* @param array|null $data Either an array of data to store, or leave empty to store all values.
|
||||
* @return bool Success
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public static function store(string $name, string $cacheConfig = 'default', ?array $data = null): bool
|
||||
{
|
||||
if ($data === null) {
|
||||
$data = static::$_values;
|
||||
}
|
||||
if (!class_exists(Cache::class)) {
|
||||
throw new RuntimeException('You must install cakephp/cache to use Configure::store()');
|
||||
}
|
||||
|
||||
return Cache::write($name, $data, $cacheConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores configuration data stored in the Cache into configure. Restored
|
||||
* values will overwrite existing ones.
|
||||
*
|
||||
* @param string $name Name of the stored config file to load.
|
||||
* @param string $cacheConfig Name of the Cache configuration to read from.
|
||||
* @return bool Success.
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public static function restore(string $name, string $cacheConfig = 'default'): bool
|
||||
{
|
||||
if (!class_exists(Cache::class)) {
|
||||
throw new RuntimeException('You must install cakephp/cache to use Configure::restore()');
|
||||
}
|
||||
$values = Cache::read($name, $cacheConfig);
|
||||
if ($values) {
|
||||
static::write($values);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all values stored in Configure.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function clear(): void
|
||||
{
|
||||
static::$_values = [];
|
||||
}
|
||||
}
|
44
vendor/cakephp/core/Configure/ConfigEngineInterface.php
vendored
Normal file
44
vendor/cakephp/core/Configure/ConfigEngineInterface.php
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 1.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core\Configure;
|
||||
|
||||
/**
|
||||
* An interface for creating objects compatible with Configure::load()
|
||||
*/
|
||||
interface ConfigEngineInterface
|
||||
{
|
||||
/**
|
||||
* Read a configuration file/storage key
|
||||
*
|
||||
* This method is used for reading configuration information from sources.
|
||||
* These sources can either be static resources like files, or dynamic ones like
|
||||
* a database, or other datasource.
|
||||
*
|
||||
* @param string $key Key to read.
|
||||
* @return array An array of data to merge into the runtime configuration
|
||||
*/
|
||||
public function read(string $key): array;
|
||||
|
||||
/**
|
||||
* Dumps the configure data into the storage key/file of the given `$key`.
|
||||
*
|
||||
* @param string $key The identifier to write to.
|
||||
* @param array $data The data to dump.
|
||||
* @return bool True on success or false on failure.
|
||||
*/
|
||||
public function dump(string $key, array $data): bool;
|
||||
}
|
203
vendor/cakephp/core/Configure/Engine/IniConfig.php
vendored
Normal file
203
vendor/cakephp/core/Configure/Engine/IniConfig.php
vendored
Normal file
@ -0,0 +1,203 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 2.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core\Configure\Engine;
|
||||
|
||||
use Cake\Core\Configure\ConfigEngineInterface;
|
||||
use Cake\Core\Configure\FileConfigTrait;
|
||||
use Cake\Utility\Hash;
|
||||
|
||||
/**
|
||||
* Ini file configuration engine.
|
||||
*
|
||||
* Since IniConfig uses parse_ini_file underneath, you should be aware that this
|
||||
* class shares the same behavior, especially with regards to boolean and null values.
|
||||
*
|
||||
* In addition to the native `parse_ini_file` features, IniConfig also allows you
|
||||
* to create nested array structures through usage of `.` delimited names. This allows
|
||||
* you to create nested arrays structures in an ini config file. For example:
|
||||
*
|
||||
* `db.password = secret` would turn into `['db' => ['password' => 'secret']]`
|
||||
*
|
||||
* You can nest properties as deeply as needed using `.`'s. In addition to using `.` you
|
||||
* can use standard ini section notation to create nested structures:
|
||||
*
|
||||
* ```
|
||||
* [section]
|
||||
* key = value
|
||||
* ```
|
||||
*
|
||||
* Once loaded into Configure, the above would be accessed using:
|
||||
*
|
||||
* `Configure::read('section.key');
|
||||
*
|
||||
* You can also use `.` separated values in section names to create more deeply
|
||||
* nested structures.
|
||||
*
|
||||
* IniConfig also manipulates how the special ini values of
|
||||
* 'yes', 'no', 'on', 'off', 'null' are handled. These values will be
|
||||
* converted to their boolean equivalents.
|
||||
*
|
||||
* @see https://secure.php.net/parse_ini_file
|
||||
*/
|
||||
class IniConfig implements ConfigEngineInterface
|
||||
{
|
||||
use FileConfigTrait;
|
||||
|
||||
/**
|
||||
* File extension.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_extension = '.ini';
|
||||
|
||||
/**
|
||||
* The section to read, if null all sections will be read.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $_section;
|
||||
|
||||
/**
|
||||
* Build and construct a new ini file parser. The parser can be used to read
|
||||
* ini files that are on the filesystem.
|
||||
*
|
||||
* @param string|null $path Path to load ini config files from. Defaults to CONFIG.
|
||||
* @param string|null $section Only get one section, leave null to parse and fetch
|
||||
* all sections in the ini file.
|
||||
*/
|
||||
public function __construct(?string $path = null, ?string $section = null)
|
||||
{
|
||||
if ($path === null) {
|
||||
$path = CONFIG;
|
||||
}
|
||||
$this->_path = $path;
|
||||
$this->_section = $section;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an ini file and return the results as an array.
|
||||
*
|
||||
* @param string $key The identifier to read from. If the key has a . it will be treated
|
||||
* as a plugin prefix. The chosen file must be on the engine's path.
|
||||
* @return array Parsed configuration values.
|
||||
* @throws \Cake\Core\Exception\CakeException when files don't exist.
|
||||
* Or when files contain '..' as this could lead to abusive reads.
|
||||
*/
|
||||
public function read(string $key): array
|
||||
{
|
||||
$file = $this->_getFilePath($key, true);
|
||||
|
||||
$contents = parse_ini_file($file, true);
|
||||
if ($this->_section && isset($contents[$this->_section])) {
|
||||
$values = $this->_parseNestedValues($contents[$this->_section]);
|
||||
} else {
|
||||
$values = [];
|
||||
foreach ($contents as $section => $attribs) {
|
||||
if (is_array($attribs)) {
|
||||
$values[$section] = $this->_parseNestedValues($attribs);
|
||||
} else {
|
||||
$parse = $this->_parseNestedValues([$attribs]);
|
||||
$values[$section] = array_shift($parse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* parses nested values out of keys.
|
||||
*
|
||||
* @param array $values Values to be exploded.
|
||||
* @return array Array of values exploded
|
||||
*/
|
||||
protected function _parseNestedValues(array $values): array
|
||||
{
|
||||
foreach ($values as $key => $value) {
|
||||
if ($value === '1') {
|
||||
$value = true;
|
||||
}
|
||||
if ($value === '') {
|
||||
$value = false;
|
||||
}
|
||||
unset($values[$key]);
|
||||
if (strpos((string)$key, '.') !== false) {
|
||||
$values = Hash::insert($values, $key, $value);
|
||||
} else {
|
||||
$values[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps the state of Configure data into an ini formatted string.
|
||||
*
|
||||
* @param string $key The identifier to write to. If the key has a . it will be treated
|
||||
* as a plugin prefix.
|
||||
* @param array $data The data to convert to ini file.
|
||||
* @return bool Success.
|
||||
*/
|
||||
public function dump(string $key, array $data): bool
|
||||
{
|
||||
$result = [];
|
||||
foreach ($data as $k => $value) {
|
||||
$isSection = false;
|
||||
/** @psalm-suppress InvalidArrayAccess */
|
||||
if ($k[0] !== '[') {
|
||||
$result[] = "[$k]";
|
||||
$isSection = true;
|
||||
}
|
||||
if (is_array($value)) {
|
||||
$kValues = Hash::flatten($value, '.');
|
||||
foreach ($kValues as $k2 => $v) {
|
||||
$result[] = "$k2 = " . $this->_value($v);
|
||||
}
|
||||
}
|
||||
if ($isSection) {
|
||||
$result[] = '';
|
||||
}
|
||||
}
|
||||
$contents = trim(implode("\n", $result));
|
||||
|
||||
$filename = $this->_getFilePath($key);
|
||||
|
||||
return file_put_contents($filename, $contents) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a value into the ini equivalent
|
||||
*
|
||||
* @param mixed $value Value to export.
|
||||
* @return string String value for ini file.
|
||||
*/
|
||||
protected function _value($value): string
|
||||
{
|
||||
if ($value === null) {
|
||||
return 'null';
|
||||
}
|
||||
if ($value === true) {
|
||||
return 'true';
|
||||
}
|
||||
if ($value === false) {
|
||||
return 'false';
|
||||
}
|
||||
|
||||
return (string)$value;
|
||||
}
|
||||
}
|
115
vendor/cakephp/core/Configure/Engine/JsonConfig.php
vendored
Normal file
115
vendor/cakephp/core/Configure/Engine/JsonConfig.php
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core\Configure\Engine;
|
||||
|
||||
use Cake\Core\Configure\ConfigEngineInterface;
|
||||
use Cake\Core\Configure\FileConfigTrait;
|
||||
use Cake\Core\Exception\CakeException;
|
||||
|
||||
/**
|
||||
* JSON engine allows Configure to load configuration values from
|
||||
* files containing JSON strings.
|
||||
*
|
||||
* An example JSON file would look like::
|
||||
*
|
||||
* ```
|
||||
* {
|
||||
* "debug": false,
|
||||
* "App": {
|
||||
* "namespace": "MyApp"
|
||||
* },
|
||||
* "Security": {
|
||||
* "salt": "its-secret"
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class JsonConfig implements ConfigEngineInterface
|
||||
{
|
||||
use FileConfigTrait;
|
||||
|
||||
/**
|
||||
* File extension.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_extension = '.json';
|
||||
|
||||
/**
|
||||
* Constructor for JSON Config file reading.
|
||||
*
|
||||
* @param string|null $path The path to read config files from. Defaults to CONFIG.
|
||||
*/
|
||||
public function __construct(?string $path = null)
|
||||
{
|
||||
if ($path === null) {
|
||||
$path = CONFIG;
|
||||
}
|
||||
$this->_path = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a config file and return its contents.
|
||||
*
|
||||
* Files with `.` in the name will be treated as values in plugins. Instead of
|
||||
* reading from the initialized path, plugin keys will be located using Plugin::path().
|
||||
*
|
||||
* @param string $key The identifier to read from. If the key has a . it will be treated
|
||||
* as a plugin prefix.
|
||||
* @return array Parsed configuration values.
|
||||
* @throws \Cake\Core\Exception\CakeException When files don't exist or when
|
||||
* files contain '..' (as this could lead to abusive reads) or when there
|
||||
* is an error parsing the JSON string.
|
||||
*/
|
||||
public function read(string $key): array
|
||||
{
|
||||
$file = $this->_getFilePath($key, true);
|
||||
|
||||
$values = json_decode(file_get_contents($file), true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
throw new CakeException(sprintf(
|
||||
'Error parsing JSON string fetched from config file "%s.json": %s',
|
||||
$key,
|
||||
json_last_error_msg()
|
||||
));
|
||||
}
|
||||
if (!is_array($values)) {
|
||||
throw new CakeException(sprintf(
|
||||
'Decoding JSON config file "%s.json" did not return an array',
|
||||
$key
|
||||
));
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the provided $data into a JSON string that can be used saved
|
||||
* into a file and loaded later.
|
||||
*
|
||||
* @param string $key The identifier to write to. If the key has a . it will
|
||||
* be treated as a plugin prefix.
|
||||
* @param array $data Data to dump.
|
||||
* @return bool Success
|
||||
*/
|
||||
public function dump(string $key, array $data): bool
|
||||
{
|
||||
$filename = $this->_getFilePath($key);
|
||||
|
||||
return file_put_contents($filename, json_encode($data, JSON_PRETTY_PRINT)) > 0;
|
||||
}
|
||||
}
|
114
vendor/cakephp/core/Configure/Engine/PhpConfig.php
vendored
Normal file
114
vendor/cakephp/core/Configure/Engine/PhpConfig.php
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 2.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core\Configure\Engine;
|
||||
|
||||
use Cake\Core\Configure\ConfigEngineInterface;
|
||||
use Cake\Core\Configure\FileConfigTrait;
|
||||
use Cake\Core\Exception\CakeException;
|
||||
|
||||
/**
|
||||
* PHP engine allows Configure to load configuration values from
|
||||
* files containing simple PHP arrays.
|
||||
*
|
||||
* Files compatible with PhpConfig should return an array that
|
||||
* contains all the configuration data contained in the file.
|
||||
*
|
||||
* An example configuration file would look like::
|
||||
*
|
||||
* ```
|
||||
* <?php
|
||||
* return [
|
||||
* 'debug' => false,
|
||||
* 'Security' => [
|
||||
* 'salt' => 'its-secret'
|
||||
* ],
|
||||
* 'App' => [
|
||||
* 'namespace' => 'App'
|
||||
* ]
|
||||
* ];
|
||||
* ```
|
||||
*
|
||||
* @see \Cake\Core\Configure::load() for how to load custom configuration files.
|
||||
*/
|
||||
class PhpConfig implements ConfigEngineInterface
|
||||
{
|
||||
use FileConfigTrait;
|
||||
|
||||
/**
|
||||
* File extension.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_extension = '.php';
|
||||
|
||||
/**
|
||||
* Constructor for PHP Config file reading.
|
||||
*
|
||||
* @param string|null $path The path to read config files from. Defaults to CONFIG.
|
||||
*/
|
||||
public function __construct(?string $path = null)
|
||||
{
|
||||
if ($path === null) {
|
||||
$path = CONFIG;
|
||||
}
|
||||
$this->_path = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a config file and return its contents.
|
||||
*
|
||||
* Files with `.` in the name will be treated as values in plugins. Instead of
|
||||
* reading from the initialized path, plugin keys will be located using Plugin::path().
|
||||
*
|
||||
* @param string $key The identifier to read from. If the key has a . it will be treated
|
||||
* as a plugin prefix.
|
||||
* @return array Parsed configuration values.
|
||||
* @throws \Cake\Core\Exception\CakeException when files don't exist or they don't contain `$config`.
|
||||
* Or when files contain '..' as this could lead to abusive reads.
|
||||
*/
|
||||
public function read(string $key): array
|
||||
{
|
||||
$file = $this->_getFilePath($key, true);
|
||||
|
||||
$config = null;
|
||||
|
||||
$return = include $file;
|
||||
if (is_array($return)) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
throw new CakeException(sprintf('Config file "%s" did not return an array', $key . '.php'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the provided $data into a string of PHP code that can
|
||||
* be used saved into a file and loaded later.
|
||||
*
|
||||
* @param string $key The identifier to write to. If the key has a . it will be treated
|
||||
* as a plugin prefix.
|
||||
* @param array $data Data to dump.
|
||||
* @return bool Success
|
||||
*/
|
||||
public function dump(string $key, array $data): bool
|
||||
{
|
||||
$contents = '<?php' . "\n" . 'return ' . var_export($data, true) . ';';
|
||||
|
||||
$filename = $this->_getFilePath($key);
|
||||
|
||||
return file_put_contents($filename, $contents) > 0;
|
||||
}
|
||||
}
|
72
vendor/cakephp/core/Configure/FileConfigTrait.php
vendored
Normal file
72
vendor/cakephp/core/Configure/FileConfigTrait.php
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core\Configure;
|
||||
|
||||
use Cake\Core\Exception\CakeException;
|
||||
use Cake\Core\Plugin;
|
||||
use function Cake\Core\pluginSplit;
|
||||
|
||||
/**
|
||||
* Trait providing utility methods for file based config engines.
|
||||
*/
|
||||
trait FileConfigTrait
|
||||
{
|
||||
/**
|
||||
* The path this engine finds files on.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_path = '';
|
||||
|
||||
/**
|
||||
* Get file path
|
||||
*
|
||||
* @param string $key The identifier to write to. If the key has a . it will be treated
|
||||
* as a plugin prefix.
|
||||
* @param bool $checkExists Whether to check if file exists. Defaults to false.
|
||||
* @return string Full file path
|
||||
* @throws \Cake\Core\Exception\CakeException When files don't exist or when
|
||||
* files contain '..' as this could lead to abusive reads.
|
||||
*/
|
||||
protected function _getFilePath(string $key, bool $checkExists = false): string
|
||||
{
|
||||
if (strpos($key, '..') !== false) {
|
||||
throw new CakeException('Cannot load/dump configuration files with ../ in them.');
|
||||
}
|
||||
|
||||
[$plugin, $key] = pluginSplit($key);
|
||||
|
||||
if ($plugin) {
|
||||
$file = Plugin::configPath($plugin) . $key;
|
||||
} else {
|
||||
$file = $this->_path . $key;
|
||||
}
|
||||
|
||||
$file .= $this->_extension;
|
||||
|
||||
if (!$checkExists || is_file($file)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
$realPath = realpath($file);
|
||||
if ($realPath !== false && is_file($realPath)) {
|
||||
return $realPath;
|
||||
}
|
||||
|
||||
throw new CakeException(sprintf('Could not load configuration file: %s', $file));
|
||||
}
|
||||
}
|
42
vendor/cakephp/core/ConsoleApplicationInterface.php
vendored
Normal file
42
vendor/cakephp/core/ConsoleApplicationInterface.php
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright 2005-2011, Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.5.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use Cake\Console\CommandCollection;
|
||||
|
||||
/**
|
||||
* An interface defining the methods that the
|
||||
* console runner depend on.
|
||||
*/
|
||||
interface ConsoleApplicationInterface
|
||||
{
|
||||
/**
|
||||
* Load all the application configuration and bootstrap logic.
|
||||
*
|
||||
* Override this method to add additional bootstrap logic for your application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function bootstrap(): void;
|
||||
|
||||
/**
|
||||
* Define the console commands for an application.
|
||||
*
|
||||
* @param \Cake\Console\CommandCollection $commands The CommandCollection to add commands into.
|
||||
* @return \Cake\Console\CommandCollection The updated collection.
|
||||
*/
|
||||
public function console(CommandCollection $commands): CommandCollection;
|
||||
}
|
28
vendor/cakephp/core/Container.php
vendored
Normal file
28
vendor/cakephp/core/Container.php
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 4.2.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use League\Container\Container as LeagueContainer;
|
||||
|
||||
/**
|
||||
* Dependency Injection container
|
||||
*
|
||||
* Based on the container out of League\Container
|
||||
*/
|
||||
class Container extends LeagueContainer implements ContainerInterface
|
||||
{
|
||||
}
|
45
vendor/cakephp/core/ContainerApplicationInterface.php
vendored
Normal file
45
vendor/cakephp/core/ContainerApplicationInterface.php
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 4.2.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
/**
|
||||
* Interface for applications that configure and use a dependency injection container.
|
||||
*/
|
||||
interface ContainerApplicationInterface
|
||||
{
|
||||
/**
|
||||
* Register services to the container
|
||||
*
|
||||
* Registered services can have instances fetched out of the container
|
||||
* using `get()`. Dependencies and parameters will be resolved based
|
||||
* on service definitions.
|
||||
*
|
||||
* @param \Cake\Core\ContainerInterface $container The container to add services to
|
||||
* @return void
|
||||
*/
|
||||
public function services(ContainerInterface $container): void;
|
||||
|
||||
/**
|
||||
* Create a new container and register services.
|
||||
*
|
||||
* This will `register()` services provided by both the application
|
||||
* and any plugins if the application has plugin support.
|
||||
*
|
||||
* @return \Cake\Core\ContainerInterface A populated container
|
||||
*/
|
||||
public function getContainer(): ContainerInterface;
|
||||
}
|
32
vendor/cakephp/core/ContainerInterface.php
vendored
Normal file
32
vendor/cakephp/core/ContainerInterface.php
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 4.2.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use League\Container\DefinitionContainerInterface;
|
||||
|
||||
/**
|
||||
* Interface for the Dependency Injection Container in CakePHP applications
|
||||
*
|
||||
* This interface extends the PSR-11 container interface and adds
|
||||
* methods to add services and service providers to the container.
|
||||
*
|
||||
* The methods defined in this interface use the conventions provided
|
||||
* by league/container as that is the library that CakePHP uses.
|
||||
*/
|
||||
interface ContainerInterface extends DefinitionContainerInterface
|
||||
{
|
||||
}
|
156
vendor/cakephp/core/ConventionsTrait.php
vendored
Normal file
156
vendor/cakephp/core/ConventionsTrait.php
vendored
Normal file
@ -0,0 +1,156 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use Cake\Utility\Inflector;
|
||||
|
||||
/**
|
||||
* Provides methods that allow other classes access to conventions based inflections.
|
||||
*/
|
||||
trait ConventionsTrait
|
||||
{
|
||||
/**
|
||||
* Creates a fixture name
|
||||
*
|
||||
* @param string $name Model class name
|
||||
* @return string Singular model key
|
||||
*/
|
||||
protected function _fixtureName(string $name): string
|
||||
{
|
||||
return Inflector::camelize($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the proper entity name (singular) for the specified name
|
||||
*
|
||||
* @param string $name Name
|
||||
* @return string Camelized and plural model name
|
||||
*/
|
||||
protected function _entityName(string $name): string
|
||||
{
|
||||
return Inflector::singularize(Inflector::camelize($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the proper underscored model key for associations
|
||||
*
|
||||
* If the input contains a dot, assume that the right side is the real table name.
|
||||
*
|
||||
* @param string $name Model class name
|
||||
* @return string Singular model key
|
||||
*/
|
||||
protected function _modelKey(string $name): string
|
||||
{
|
||||
[, $name] = pluginSplit($name);
|
||||
|
||||
return Inflector::underscore(Inflector::singularize($name)) . '_id';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the proper model name from a foreign key
|
||||
*
|
||||
* @param string $key Foreign key
|
||||
* @return string Model name
|
||||
*/
|
||||
protected function _modelNameFromKey(string $key): string
|
||||
{
|
||||
$key = str_replace('_id', '', $key);
|
||||
|
||||
return Inflector::camelize(Inflector::pluralize($key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the singular name for use in views.
|
||||
*
|
||||
* @param string $name Name to use
|
||||
* @return string Variable name
|
||||
*/
|
||||
protected function _singularName(string $name): string
|
||||
{
|
||||
return Inflector::variable(Inflector::singularize($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the plural variable name for views
|
||||
*
|
||||
* @param string $name Name to use
|
||||
* @return string Plural name for views
|
||||
*/
|
||||
protected function _variableName(string $name): string
|
||||
{
|
||||
return Inflector::variable($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the singular human name used in views
|
||||
*
|
||||
* @param string $name Controller name
|
||||
* @return string Singular human name
|
||||
*/
|
||||
protected function _singularHumanName(string $name): string
|
||||
{
|
||||
return Inflector::humanize(Inflector::underscore(Inflector::singularize($name)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a camelized version of $name
|
||||
*
|
||||
* @param string $name name
|
||||
* @return string Camelized name
|
||||
*/
|
||||
protected function _camelize(string $name): string
|
||||
{
|
||||
return Inflector::camelize($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the plural human name used in views
|
||||
*
|
||||
* @param string $name Controller name
|
||||
* @return string Plural human name
|
||||
*/
|
||||
protected function _pluralHumanName(string $name): string
|
||||
{
|
||||
return Inflector::humanize(Inflector::underscore($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the correct path for a plugin. Scans $pluginPaths for the plugin you want.
|
||||
*
|
||||
* @param string $pluginName Name of the plugin you want ie. DebugKit
|
||||
* @return string path path to the correct plugin.
|
||||
*/
|
||||
protected function _pluginPath(string $pluginName): string
|
||||
{
|
||||
if (Plugin::isLoaded($pluginName)) {
|
||||
return Plugin::path($pluginName);
|
||||
}
|
||||
|
||||
return current(App::path('plugins')) . $pluginName . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return plugin's namespace
|
||||
*
|
||||
* @param string $pluginName Plugin name
|
||||
* @return string Plugin's namespace
|
||||
*/
|
||||
protected function _pluginNamespace(string $pluginName): string
|
||||
{
|
||||
return str_replace('/', '\\', $pluginName);
|
||||
}
|
||||
}
|
123
vendor/cakephp/core/Exception/CakeException.php
vendored
Normal file
123
vendor/cakephp/core/Exception/CakeException.php
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @since 3.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
use function Cake\Core\deprecationWarning;
|
||||
|
||||
/**
|
||||
* Base class that all CakePHP Exceptions extend.
|
||||
*
|
||||
* @method int getCode() Gets the Exception code.
|
||||
*/
|
||||
class CakeException extends RuntimeException
|
||||
{
|
||||
/**
|
||||
* Array of attributes that are passed in from the constructor, and
|
||||
* made available in the view when a development error is displayed.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_attributes = [];
|
||||
|
||||
/**
|
||||
* Template string that has attributes sprintf()'ed into it.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_messageTemplate = '';
|
||||
|
||||
/**
|
||||
* Array of headers to be passed to {@link \Cake\Http\Response::withHeader()}
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
protected $_responseHeaders;
|
||||
|
||||
/**
|
||||
* Default exception code
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $_defaultCode = 0;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Allows you to create exceptions that are treated as framework errors and disabled
|
||||
* when debug mode is off.
|
||||
*
|
||||
* @param array|string $message Either the string of the error message, or an array of attributes
|
||||
* that are made available in the view, and sprintf()'d into Exception::$_messageTemplate
|
||||
* @param int|null $code The error code
|
||||
* @param \Throwable|null $previous the previous exception.
|
||||
*/
|
||||
public function __construct($message = '', ?int $code = null, ?Throwable $previous = null)
|
||||
{
|
||||
if (is_array($message)) {
|
||||
$this->_attributes = $message;
|
||||
$message = vsprintf($this->_messageTemplate, $message);
|
||||
}
|
||||
parent::__construct($message, $code ?? $this->_defaultCode, $previous);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the passed in attributes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAttributes(): array
|
||||
{
|
||||
return $this->_attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/set the response header to be used
|
||||
*
|
||||
* See also {@link \Cake\Http\Response::withHeader()}
|
||||
*
|
||||
* @param array|string|null $header A single header string or an associative
|
||||
* array of "header name" => "header value"
|
||||
* @param string|null $value The header value.
|
||||
* @return array|null
|
||||
* @deprecated 4.2.0 Use `HttpException::setHeaders()` instead. Response headers
|
||||
* should be set for HttpException only.
|
||||
*/
|
||||
public function responseHeader($header = null, $value = null): ?array
|
||||
{
|
||||
if ($header === null) {
|
||||
return $this->_responseHeaders;
|
||||
}
|
||||
|
||||
deprecationWarning(
|
||||
'Setting HTTP response headers from Exception directly is deprecated. ' .
|
||||
'If your exceptions extend Exception, they must now extend HttpException. ' .
|
||||
'You should only set HTTP headers on HttpException instances via the `setHeaders()` method.'
|
||||
);
|
||||
if (is_array($header)) {
|
||||
return $this->_responseHeaders = $header;
|
||||
}
|
||||
|
||||
return $this->_responseHeaders = [$header => $value];
|
||||
}
|
||||
}
|
||||
|
||||
// phpcs:disable
|
||||
class_alias(
|
||||
'Cake\Core\Exception\CakeException',
|
||||
'Cake\Core\Exception\Exception'
|
||||
);
|
||||
// phpcs:enable
|
21
vendor/cakephp/core/Exception/Exception.php
vendored
Normal file
21
vendor/cakephp/core/Exception/Exception.php
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use function Cake\Core\deprecationWarning;
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @since 4.2.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
deprecationWarning(
|
||||
'Since 4.2.0: Cake\Core\Exception\Exception is deprecated.' .
|
||||
'Use Cake\Core\Exception\CakeException instead.'
|
||||
);
|
||||
class_exists('Cake\Core\Exception\CakeException');
|
26
vendor/cakephp/core/Exception/MissingPluginException.php
vendored
Normal file
26
vendor/cakephp/core/Exception/MissingPluginException.php
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @since 3.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core\Exception;
|
||||
|
||||
/**
|
||||
* Exception raised when a plugin could not be found
|
||||
*/
|
||||
class MissingPluginException extends CakeException
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $_messageTemplate = 'Plugin %s could not be found.';
|
||||
}
|
43
vendor/cakephp/core/HttpApplicationInterface.php
vendored
Normal file
43
vendor/cakephp/core/HttpApplicationInterface.php
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright 2005-2011, Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.5.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use Cake\Http\MiddlewareQueue;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
/**
|
||||
* An interface defining the methods that the
|
||||
* http server depend on.
|
||||
*/
|
||||
interface HttpApplicationInterface extends RequestHandlerInterface
|
||||
{
|
||||
/**
|
||||
* Load all the application configuration and bootstrap logic.
|
||||
*
|
||||
* Override this method to add additional bootstrap logic for your application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function bootstrap(): void;
|
||||
|
||||
/**
|
||||
* Define the HTTP middleware layers for an application.
|
||||
*
|
||||
* @param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to set in your App Class
|
||||
* @return \Cake\Http\MiddlewareQueue
|
||||
*/
|
||||
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue;
|
||||
}
|
312
vendor/cakephp/core/InstanceConfigTrait.php
vendored
Normal file
312
vendor/cakephp/core/InstanceConfigTrait.php
vendored
Normal file
@ -0,0 +1,312 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use Cake\Core\Exception\CakeException;
|
||||
use Cake\Utility\Hash;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* A trait for reading and writing instance config
|
||||
*
|
||||
* Implementing objects are expected to declare a `$_defaultConfig` property.
|
||||
*/
|
||||
trait InstanceConfigTrait
|
||||
{
|
||||
/**
|
||||
* Runtime config
|
||||
*
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
protected $_config = [];
|
||||
|
||||
/**
|
||||
* Whether the config property has already been configured with defaults
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $_configInitialized = false;
|
||||
|
||||
/**
|
||||
* Sets the config.
|
||||
*
|
||||
* ### Usage
|
||||
*
|
||||
* Setting a specific value:
|
||||
*
|
||||
* ```
|
||||
* $this->setConfig('key', $value);
|
||||
* ```
|
||||
*
|
||||
* Setting a nested value:
|
||||
*
|
||||
* ```
|
||||
* $this->setConfig('some.nested.key', $value);
|
||||
* ```
|
||||
*
|
||||
* Updating multiple config settings at the same time:
|
||||
*
|
||||
* ```
|
||||
* $this->setConfig(['one' => 'value', 'another' => 'value']);
|
||||
* ```
|
||||
*
|
||||
* @param array<string, mixed>|string $key The key to set, or a complete array of configs.
|
||||
* @param mixed|null $value The value to set.
|
||||
* @param bool $merge Whether to recursively merge or overwrite existing config, defaults to true.
|
||||
* @return $this
|
||||
* @throws \Cake\Core\Exception\CakeException When trying to set a key that is invalid.
|
||||
*/
|
||||
public function setConfig($key, $value = null, $merge = true)
|
||||
{
|
||||
if (!$this->_configInitialized) {
|
||||
$this->_config = $this->_defaultConfig;
|
||||
$this->_configInitialized = true;
|
||||
}
|
||||
|
||||
$this->_configWrite($key, $value, $merge);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the config.
|
||||
*
|
||||
* ### Usage
|
||||
*
|
||||
* Reading the whole config:
|
||||
*
|
||||
* ```
|
||||
* $this->getConfig();
|
||||
* ```
|
||||
*
|
||||
* Reading a specific value:
|
||||
*
|
||||
* ```
|
||||
* $this->getConfig('key');
|
||||
* ```
|
||||
*
|
||||
* Reading a nested value:
|
||||
*
|
||||
* ```
|
||||
* $this->getConfig('some.nested.key');
|
||||
* ```
|
||||
*
|
||||
* Reading with default value:
|
||||
*
|
||||
* ```
|
||||
* $this->getConfig('some-key', 'default-value');
|
||||
* ```
|
||||
*
|
||||
* @param string|null $key The key to get or null for the whole config.
|
||||
* @param mixed $default The return value when the key does not exist.
|
||||
* @return mixed Configuration data at the named key or null if the key does not exist.
|
||||
*/
|
||||
public function getConfig(?string $key = null, $default = null)
|
||||
{
|
||||
if (!$this->_configInitialized) {
|
||||
$this->_config = $this->_defaultConfig;
|
||||
$this->_configInitialized = true;
|
||||
}
|
||||
|
||||
$return = $this->_configRead($key);
|
||||
|
||||
return $return ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the config for this specific key.
|
||||
*
|
||||
* The config value for this key must exist, it can never be null.
|
||||
*
|
||||
* @param string $key The key to get.
|
||||
* @return mixed Configuration data at the named key
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function getConfigOrFail(string $key)
|
||||
{
|
||||
$config = $this->getConfig($key);
|
||||
if ($config === null) {
|
||||
throw new InvalidArgumentException(sprintf('Expected configuration `%s` not found.', $key));
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge provided config with existing config. Unlike `config()` which does
|
||||
* a recursive merge for nested keys, this method does a simple merge.
|
||||
*
|
||||
* Setting a specific value:
|
||||
*
|
||||
* ```
|
||||
* $this->configShallow('key', $value);
|
||||
* ```
|
||||
*
|
||||
* Setting a nested value:
|
||||
*
|
||||
* ```
|
||||
* $this->configShallow('some.nested.key', $value);
|
||||
* ```
|
||||
*
|
||||
* Updating multiple config settings at the same time:
|
||||
*
|
||||
* ```
|
||||
* $this->configShallow(['one' => 'value', 'another' => 'value']);
|
||||
* ```
|
||||
*
|
||||
* @param array<string, mixed>|string $key The key to set, or a complete array of configs.
|
||||
* @param mixed|null $value The value to set.
|
||||
* @return $this
|
||||
*/
|
||||
public function configShallow($key, $value = null)
|
||||
{
|
||||
if (!$this->_configInitialized) {
|
||||
$this->_config = $this->_defaultConfig;
|
||||
$this->_configInitialized = true;
|
||||
}
|
||||
|
||||
$this->_configWrite($key, $value, 'shallow');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a config key.
|
||||
*
|
||||
* @param string|null $key Key to read.
|
||||
* @return mixed
|
||||
*/
|
||||
protected function _configRead(?string $key)
|
||||
{
|
||||
if ($key === null) {
|
||||
return $this->_config;
|
||||
}
|
||||
|
||||
if (strpos($key, '.') === false) {
|
||||
return $this->_config[$key] ?? null;
|
||||
}
|
||||
|
||||
$return = $this->_config;
|
||||
|
||||
foreach (explode('.', $key) as $k) {
|
||||
if (!is_array($return) || !isset($return[$k])) {
|
||||
$return = null;
|
||||
break;
|
||||
}
|
||||
|
||||
$return = $return[$k];
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a config key.
|
||||
*
|
||||
* @param array<string, mixed>|string $key Key to write to.
|
||||
* @param mixed $value Value to write.
|
||||
* @param string|bool $merge True to merge recursively, 'shallow' for simple merge,
|
||||
* false to overwrite, defaults to false.
|
||||
* @return void
|
||||
* @throws \Cake\Core\Exception\CakeException if attempting to clobber existing config
|
||||
*/
|
||||
protected function _configWrite($key, $value, $merge = false): void
|
||||
{
|
||||
if (is_string($key) && $value === null) {
|
||||
$this->_configDelete($key);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($merge) {
|
||||
$update = is_array($key) ? $key : [$key => $value];
|
||||
if ($merge === 'shallow') {
|
||||
$this->_config = array_merge($this->_config, Hash::expand($update));
|
||||
} else {
|
||||
$this->_config = Hash::merge($this->_config, Hash::expand($update));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_array($key)) {
|
||||
foreach ($key as $k => $val) {
|
||||
$this->_configWrite($k, $val);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (strpos($key, '.') === false) {
|
||||
$this->_config[$key] = $value;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$update = &$this->_config;
|
||||
$stack = explode('.', $key);
|
||||
|
||||
foreach ($stack as $k) {
|
||||
if (!is_array($update)) {
|
||||
throw new CakeException(sprintf('Cannot set %s value', $key));
|
||||
}
|
||||
|
||||
$update[$k] = $update[$k] ?? [];
|
||||
|
||||
$update = &$update[$k];
|
||||
}
|
||||
|
||||
$update = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a single config key.
|
||||
*
|
||||
* @param string $key Key to delete.
|
||||
* @return void
|
||||
* @throws \Cake\Core\Exception\CakeException if attempting to clobber existing config
|
||||
*/
|
||||
protected function _configDelete(string $key): void
|
||||
{
|
||||
if (strpos($key, '.') === false) {
|
||||
unset($this->_config[$key]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$update = &$this->_config;
|
||||
$stack = explode('.', $key);
|
||||
$length = count($stack);
|
||||
|
||||
foreach ($stack as $i => $k) {
|
||||
if (!is_array($update)) {
|
||||
throw new CakeException(sprintf('Cannot unset %s value', $key));
|
||||
}
|
||||
|
||||
if (!isset($update[$k])) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($i === $length - 1) {
|
||||
unset($update[$k]);
|
||||
break;
|
||||
}
|
||||
|
||||
$update = &$update[$k];
|
||||
}
|
||||
}
|
||||
}
|
22
vendor/cakephp/core/LICENSE.txt
vendored
Normal file
22
vendor/cakephp/core/LICENSE.txt
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
CakePHP(tm) : The Rapid Development PHP Framework (https://cakephp.org)
|
||||
Copyright (c) 2005-2020, Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
416
vendor/cakephp/core/ObjectRegistry.php
vendored
Normal file
416
vendor/cakephp/core/ObjectRegistry.php
vendored
Normal file
@ -0,0 +1,416 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use ArrayIterator;
|
||||
use Cake\Event\EventDispatcherInterface;
|
||||
use Cake\Event\EventListenerInterface;
|
||||
use Countable;
|
||||
use IteratorAggregate;
|
||||
use RuntimeException;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* Acts as a registry/factory for objects.
|
||||
*
|
||||
* Provides registry & factory functionality for object types. Used
|
||||
* as a super class for various composition based re-use features in CakePHP.
|
||||
*
|
||||
* Each subclass needs to implement the various abstract methods to complete
|
||||
* the template method load().
|
||||
*
|
||||
* The ObjectRegistry is EventManager aware, but each extending class will need to use
|
||||
* \Cake\Event\EventDispatcherTrait to attach and detach on set and bind
|
||||
*
|
||||
* @see \Cake\Controller\ComponentRegistry
|
||||
* @see \Cake\View\HelperRegistry
|
||||
* @see \Cake\Console\TaskRegistry
|
||||
* @template TObject of object
|
||||
* @template-implements \IteratorAggregate<string, TObject>
|
||||
*/
|
||||
abstract class ObjectRegistry implements Countable, IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* Map of loaded objects.
|
||||
*
|
||||
* @var array<object>
|
||||
* @psalm-var array<array-key, TObject>
|
||||
*/
|
||||
protected $_loaded = [];
|
||||
|
||||
/**
|
||||
* Loads/constructs an object instance.
|
||||
*
|
||||
* Will return the instance in the registry if it already exists.
|
||||
* If a subclass provides event support, you can use `$config['enabled'] = false`
|
||||
* to exclude constructed objects from being registered for events.
|
||||
*
|
||||
* Using {@link \Cake\Controller\Component::$components} as an example. You can alias
|
||||
* an object by setting the 'className' key, i.e.,
|
||||
*
|
||||
* ```
|
||||
* protected $components = [
|
||||
* 'Email' => [
|
||||
* 'className' => 'App\Controller\Component\AliasedEmailComponent'
|
||||
* ];
|
||||
* ];
|
||||
* ```
|
||||
*
|
||||
* All calls to the `Email` component would use `AliasedEmail` instead.
|
||||
*
|
||||
* @param string $name The name/class of the object to load.
|
||||
* @param array<string, mixed> $config Additional settings to use when loading the object.
|
||||
* @return mixed
|
||||
* @psalm-return TObject
|
||||
* @throws \Exception If the class cannot be found.
|
||||
*/
|
||||
public function load(string $name, array $config = [])
|
||||
{
|
||||
if (isset($config['className'])) {
|
||||
$objName = $name;
|
||||
$name = $config['className'];
|
||||
} else {
|
||||
[, $objName] = pluginSplit($name);
|
||||
}
|
||||
|
||||
$loaded = isset($this->_loaded[$objName]);
|
||||
if ($loaded && !empty($config)) {
|
||||
$this->_checkDuplicate($objName, $config);
|
||||
}
|
||||
if ($loaded) {
|
||||
return $this->_loaded[$objName];
|
||||
}
|
||||
|
||||
$className = $name;
|
||||
if (is_string($name)) {
|
||||
$className = $this->_resolveClassName($name);
|
||||
if ($className === null) {
|
||||
[$plugin, $name] = pluginSplit($name);
|
||||
$this->_throwMissingClassError($name, $plugin);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-var TObject $instance
|
||||
* @psalm-suppress PossiblyNullArgument
|
||||
**/
|
||||
$instance = $this->_create($className, $objName, $config);
|
||||
$this->_loaded[$objName] = $instance;
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for duplicate object loading.
|
||||
*
|
||||
* If a duplicate is being loaded and has different configuration, that is
|
||||
* bad and an exception will be raised.
|
||||
*
|
||||
* An exception is raised, as replacing the object will not update any
|
||||
* references other objects may have. Additionally, simply updating the runtime
|
||||
* configuration is not a good option as we may be missing important constructor
|
||||
* logic dependent on the configuration.
|
||||
*
|
||||
* @param string $name The name of the alias in the registry.
|
||||
* @param array<string, mixed> $config The config data for the new instance.
|
||||
* @return void
|
||||
* @throws \RuntimeException When a duplicate is found.
|
||||
*/
|
||||
protected function _checkDuplicate(string $name, array $config): void
|
||||
{
|
||||
$existing = $this->_loaded[$name];
|
||||
$msg = sprintf('The "%s" alias has already been loaded.', $name);
|
||||
$hasConfig = method_exists($existing, 'getConfig');
|
||||
if (!$hasConfig) {
|
||||
throw new RuntimeException($msg);
|
||||
}
|
||||
if (empty($config)) {
|
||||
return;
|
||||
}
|
||||
$existingConfig = $existing->getConfig();
|
||||
unset($config['enabled'], $existingConfig['enabled']);
|
||||
|
||||
$failure = null;
|
||||
foreach ($config as $key => $value) {
|
||||
if (!array_key_exists($key, $existingConfig)) {
|
||||
$failure = " The `{$key}` was not defined in the previous configuration data.";
|
||||
break;
|
||||
}
|
||||
if (isset($existingConfig[$key]) && $existingConfig[$key] !== $value) {
|
||||
$failure = sprintf(
|
||||
' The `%s` key has a value of `%s` but previously had a value of `%s`',
|
||||
$key,
|
||||
json_encode($value),
|
||||
json_encode($existingConfig[$key])
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($failure) {
|
||||
throw new RuntimeException($msg . $failure);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Should resolve the classname for a given object type.
|
||||
*
|
||||
* @param string $class The class to resolve.
|
||||
* @return string|null The resolved name or null for failure.
|
||||
* @psalm-return class-string|null
|
||||
*/
|
||||
abstract protected function _resolveClassName(string $class): ?string;
|
||||
|
||||
/**
|
||||
* Throw an exception when the requested object name is missing.
|
||||
*
|
||||
* @param string $class The class that is missing.
|
||||
* @param string|null $plugin The plugin $class is missing from.
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
abstract protected function _throwMissingClassError(string $class, ?string $plugin): void;
|
||||
|
||||
/**
|
||||
* Create an instance of a given classname.
|
||||
*
|
||||
* This method should construct and do any other initialization logic
|
||||
* required.
|
||||
*
|
||||
* @param object|string $class The class to build.
|
||||
* @param string $alias The alias of the object.
|
||||
* @param array<string, mixed> $config The Configuration settings for construction
|
||||
* @return object
|
||||
* @psalm-param TObject|string $class
|
||||
* @psalm-return TObject
|
||||
*/
|
||||
abstract protected function _create($class, string $alias, array $config);
|
||||
|
||||
/**
|
||||
* Get the list of loaded objects.
|
||||
*
|
||||
* @return array<string> List of object names.
|
||||
*/
|
||||
public function loaded(): array
|
||||
{
|
||||
return array_keys($this->_loaded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given object is loaded.
|
||||
*
|
||||
* @param string $name The object name to check for.
|
||||
* @return bool True is object is loaded else false.
|
||||
*/
|
||||
public function has(string $name): bool
|
||||
{
|
||||
return isset($this->_loaded[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get loaded object instance.
|
||||
*
|
||||
* @param string $name Name of object.
|
||||
* @return object Object instance.
|
||||
* @throws \RuntimeException If not loaded or found.
|
||||
* @psalm-return TObject
|
||||
*/
|
||||
public function get(string $name)
|
||||
{
|
||||
if (!isset($this->_loaded[$name])) {
|
||||
throw new RuntimeException(sprintf('Unknown object "%s"', $name));
|
||||
}
|
||||
|
||||
return $this->_loaded[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide public read access to the loaded objects
|
||||
*
|
||||
* @param string $name Name of property to read
|
||||
* @return object|null
|
||||
* @psalm-return TObject|null
|
||||
*/
|
||||
public function __get(string $name)
|
||||
{
|
||||
return $this->_loaded[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide isset access to _loaded
|
||||
*
|
||||
* @param string $name Name of object being checked.
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset(string $name): bool
|
||||
{
|
||||
return $this->has($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an object.
|
||||
*
|
||||
* @param string $name Name of a property to set.
|
||||
* @param object $object Object to set.
|
||||
* @psalm-param TObject $object
|
||||
* @return void
|
||||
*/
|
||||
public function __set(string $name, $object): void
|
||||
{
|
||||
$this->set($name, $object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets an object.
|
||||
*
|
||||
* @param string $name Name of a property to unset.
|
||||
* @return void
|
||||
*/
|
||||
public function __unset(string $name): void
|
||||
{
|
||||
$this->unload($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes an object array, creates an array that makes lazy loading
|
||||
* easier
|
||||
*
|
||||
* @param array $objects Array of child objects to normalize.
|
||||
* @return array<string, array> Array of normalized objects.
|
||||
*/
|
||||
public function normalizeArray(array $objects): array
|
||||
{
|
||||
$normal = [];
|
||||
foreach ($objects as $i => $objectName) {
|
||||
$config = [];
|
||||
if (!is_int($i)) {
|
||||
$config = (array)$objectName;
|
||||
$objectName = $i;
|
||||
}
|
||||
[, $name] = pluginSplit($objectName);
|
||||
if (isset($config['class'])) {
|
||||
$normal[$name] = $config + ['config' => []];
|
||||
} else {
|
||||
$normal[$name] = ['class' => $objectName, 'config' => $config];
|
||||
}
|
||||
}
|
||||
|
||||
return $normal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear loaded instances in the registry.
|
||||
*
|
||||
* If the registry subclass has an event manager, the objects will be detached from events as well.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
foreach (array_keys($this->_loaded) as $name) {
|
||||
$this->unload((string)$name);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an object directly into the registry by name.
|
||||
*
|
||||
* If this collection implements events, the passed object will
|
||||
* be attached into the event manager
|
||||
*
|
||||
* @param string $name The name of the object to set in the registry.
|
||||
* @param object $object instance to store in the registry
|
||||
* @return $this
|
||||
* @psalm-param TObject $object
|
||||
*/
|
||||
public function set(string $name, object $object)
|
||||
{
|
||||
[, $objName] = pluginSplit($name);
|
||||
|
||||
// Just call unload if the object was loaded before
|
||||
if (array_key_exists($name, $this->_loaded)) {
|
||||
$this->unload($name);
|
||||
}
|
||||
if ($this instanceof EventDispatcherInterface && $object instanceof EventListenerInterface) {
|
||||
$this->getEventManager()->on($object);
|
||||
}
|
||||
$this->_loaded[$objName] = $object;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an object from the registry.
|
||||
*
|
||||
* If this registry has an event manager, the object will be detached from any events as well.
|
||||
*
|
||||
* @param string $name The name of the object to remove from the registry.
|
||||
* @return $this
|
||||
*/
|
||||
public function unload(string $name)
|
||||
{
|
||||
if (empty($this->_loaded[$name])) {
|
||||
[$plugin, $name] = pluginSplit($name);
|
||||
$this->_throwMissingClassError($name, $plugin);
|
||||
}
|
||||
|
||||
$object = $this->_loaded[$name];
|
||||
if ($this instanceof EventDispatcherInterface && $object instanceof EventListenerInterface) {
|
||||
$this->getEventManager()->off($object);
|
||||
}
|
||||
unset($this->_loaded[$name]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array iterator.
|
||||
*
|
||||
* @return \Traversable
|
||||
* @psalm-return \Traversable<string, TObject>
|
||||
*/
|
||||
public function getIterator(): Traversable
|
||||
{
|
||||
return new ArrayIterator($this->_loaded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of loaded objects.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->_loaded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug friendly object properties.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
$properties = get_object_vars($this);
|
||||
if (isset($properties['_loaded'])) {
|
||||
$properties['_loaded'] = array_keys($properties['_loaded']);
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
}
|
136
vendor/cakephp/core/Plugin.php
vendored
Normal file
136
vendor/cakephp/core/Plugin.php
vendored
Normal file
@ -0,0 +1,136 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 2.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
/**
|
||||
* Plugin is used to load and locate plugins.
|
||||
*
|
||||
* It also can retrieve plugin paths and load their bootstrap and routes files.
|
||||
*
|
||||
* @link https://book.cakephp.org/4/en/plugins.html
|
||||
*/
|
||||
class Plugin
|
||||
{
|
||||
/**
|
||||
* Holds a list of all loaded plugins and their configuration
|
||||
*
|
||||
* @var \Cake\Core\PluginCollection|null
|
||||
*/
|
||||
protected static $plugins;
|
||||
|
||||
/**
|
||||
* Returns the filesystem path for a plugin
|
||||
*
|
||||
* @param string $name name of the plugin in CamelCase format
|
||||
* @return string path to the plugin folder
|
||||
* @throws \Cake\Core\Exception\MissingPluginException If the folder for plugin was not found
|
||||
* or plugin has not been loaded.
|
||||
*/
|
||||
public static function path(string $name): string
|
||||
{
|
||||
$plugin = static::getCollection()->get($name);
|
||||
|
||||
return $plugin->getPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the filesystem path for plugin's folder containing class files.
|
||||
*
|
||||
* @param string $name name of the plugin in CamelCase format.
|
||||
* @return string Path to the plugin folder containing class files.
|
||||
* @throws \Cake\Core\Exception\MissingPluginException If plugin has not been loaded.
|
||||
*/
|
||||
public static function classPath(string $name): string
|
||||
{
|
||||
$plugin = static::getCollection()->get($name);
|
||||
|
||||
return $plugin->getClassPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the filesystem path for plugin's folder containing config files.
|
||||
*
|
||||
* @param string $name name of the plugin in CamelCase format.
|
||||
* @return string Path to the plugin folder containing config files.
|
||||
* @throws \Cake\Core\Exception\MissingPluginException If plugin has not been loaded.
|
||||
*/
|
||||
public static function configPath(string $name): string
|
||||
{
|
||||
$plugin = static::getCollection()->get($name);
|
||||
|
||||
return $plugin->getConfigPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the filesystem path for plugin's folder containing template files.
|
||||
*
|
||||
* @param string $name name of the plugin in CamelCase format.
|
||||
* @return string Path to the plugin folder containing template files.
|
||||
* @throws \Cake\Core\Exception\MissingPluginException If plugin has not been loaded.
|
||||
*/
|
||||
public static function templatePath(string $name): string
|
||||
{
|
||||
$plugin = static::getCollection()->get($name);
|
||||
|
||||
return $plugin->getTemplatePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the plugin $plugin is already loaded.
|
||||
*
|
||||
* @param string $plugin Plugin name.
|
||||
* @return bool
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public static function isLoaded(string $plugin): bool
|
||||
{
|
||||
return static::getCollection()->has($plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of loaded plugins.
|
||||
*
|
||||
* @return array<string> A list of plugins that have been loaded
|
||||
*/
|
||||
public static function loaded(): array
|
||||
{
|
||||
$names = [];
|
||||
foreach (static::getCollection() as $plugin) {
|
||||
$names[] = $plugin->getName();
|
||||
}
|
||||
sort($names);
|
||||
|
||||
return $names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the shared plugin collection.
|
||||
*
|
||||
* This method should generally not be used during application
|
||||
* runtime as plugins should be set during Application startup.
|
||||
*
|
||||
* @return \Cake\Core\PluginCollection
|
||||
*/
|
||||
public static function getCollection(): PluginCollection
|
||||
{
|
||||
if (!isset(static::$plugins)) {
|
||||
static::$plugins = new PluginCollection();
|
||||
}
|
||||
|
||||
return static::$plugins;
|
||||
}
|
||||
}
|
75
vendor/cakephp/core/PluginApplicationInterface.php
vendored
Normal file
75
vendor/cakephp/core/PluginApplicationInterface.php
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.6.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use Cake\Console\CommandCollection;
|
||||
use Cake\Event\EventDispatcherInterface;
|
||||
use Cake\Http\MiddlewareQueue;
|
||||
use Cake\Routing\RouteBuilder;
|
||||
|
||||
/**
|
||||
* Interface for Applications that leverage plugins & events.
|
||||
*
|
||||
* Events can be bound to the application event manager during
|
||||
* the application's bootstrap and plugin bootstrap.
|
||||
*/
|
||||
interface PluginApplicationInterface extends EventDispatcherInterface
|
||||
{
|
||||
/**
|
||||
* Add a plugin to the loaded plugin set.
|
||||
*
|
||||
* If the named plugin does not exist, or does not define a Plugin class, an
|
||||
* instance of `Cake\Core\BasePlugin` will be used. This generated class will have
|
||||
* all plugin hooks enabled.
|
||||
*
|
||||
* @param \Cake\Core\PluginInterface|string $name The plugin name or plugin object.
|
||||
* @param array<string, mixed> $config The configuration data for the plugin if using a string for $name
|
||||
* @return $this
|
||||
*/
|
||||
public function addPlugin($name, array $config = []);
|
||||
|
||||
/**
|
||||
* Run bootstrap logic for loaded plugins.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function pluginBootstrap(): void;
|
||||
|
||||
/**
|
||||
* Run routes hooks for loaded plugins
|
||||
*
|
||||
* @param \Cake\Routing\RouteBuilder $routes The route builder to use.
|
||||
* @return \Cake\Routing\RouteBuilder
|
||||
*/
|
||||
public function pluginRoutes(RouteBuilder $routes): RouteBuilder;
|
||||
|
||||
/**
|
||||
* Run middleware hooks for plugins
|
||||
*
|
||||
* @param \Cake\Http\MiddlewareQueue $middleware The MiddlewareQueue to use.
|
||||
* @return \Cake\Http\MiddlewareQueue
|
||||
*/
|
||||
public function pluginMiddleware(MiddlewareQueue $middleware): MiddlewareQueue;
|
||||
|
||||
/**
|
||||
* Run console hooks for plugins
|
||||
*
|
||||
* @param \Cake\Console\CommandCollection $commands The CommandCollection to use.
|
||||
* @return \Cake\Console\CommandCollection
|
||||
*/
|
||||
public function pluginConsole(CommandCollection $commands): CommandCollection;
|
||||
}
|
363
vendor/cakephp/core/PluginCollection.php
vendored
Normal file
363
vendor/cakephp/core/PluginCollection.php
vendored
Normal file
@ -0,0 +1,363 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright 2005-2011, Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.6.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use Cake\Core\Exception\CakeException;
|
||||
use Cake\Core\Exception\MissingPluginException;
|
||||
use Countable;
|
||||
use Generator;
|
||||
use InvalidArgumentException;
|
||||
use Iterator;
|
||||
|
||||
/**
|
||||
* Plugin Collection
|
||||
*
|
||||
* Holds onto plugin objects loaded into an application, and
|
||||
* provides methods for iterating, and finding plugins based
|
||||
* on criteria.
|
||||
*
|
||||
* This class implements the Iterator interface to allow plugins
|
||||
* to be iterated, handling the situation where a plugin's hook
|
||||
* method (usually bootstrap) loads another plugin during iteration.
|
||||
*
|
||||
* While its implementation supported nested iteration it does not
|
||||
* support using `continue` or `break` inside loops.
|
||||
*
|
||||
* @template-implements \Iterator<string, \Cake\Core\PluginInterface>
|
||||
*/
|
||||
class PluginCollection implements Iterator, Countable
|
||||
{
|
||||
/**
|
||||
* Plugin list
|
||||
*
|
||||
* @var array<\Cake\Core\PluginInterface>
|
||||
*/
|
||||
protected $plugins = [];
|
||||
|
||||
/**
|
||||
* Names of plugins
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $names = [];
|
||||
|
||||
/**
|
||||
* Iterator position stack.
|
||||
*
|
||||
* @var array<int>
|
||||
*/
|
||||
protected $positions = [];
|
||||
|
||||
/**
|
||||
* Loop depth
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $loopDepth = -1;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array<\Cake\Core\PluginInterface> $plugins The map of plugins to add to the collection.
|
||||
*/
|
||||
public function __construct(array $plugins = [])
|
||||
{
|
||||
foreach ($plugins as $plugin) {
|
||||
$this->add($plugin);
|
||||
}
|
||||
$this->loadConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the path information stored in vendor/cakephp-plugins.php
|
||||
*
|
||||
* This file is generated by the cakephp/plugin-installer package and used
|
||||
* to locate plugins on the filesystem as applications can use `extra.plugin-paths`
|
||||
* in their composer.json file to move plugin outside of vendor/
|
||||
*
|
||||
* @internal
|
||||
* @return void
|
||||
*/
|
||||
protected function loadConfig(): void
|
||||
{
|
||||
if (Configure::check('plugins')) {
|
||||
return;
|
||||
}
|
||||
$vendorFile = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'cakephp-plugins.php';
|
||||
if (!is_file($vendorFile)) {
|
||||
$vendorFile = dirname(dirname(dirname(dirname(__DIR__)))) . DIRECTORY_SEPARATOR . 'cakephp-plugins.php';
|
||||
if (!is_file($vendorFile)) {
|
||||
Configure::write(['plugins' => []]);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$config = require $vendorFile;
|
||||
Configure::write($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate a plugin path by looking at configuration data.
|
||||
*
|
||||
* This will use the `plugins` Configure key, and fallback to enumerating `App::path('plugins')`
|
||||
*
|
||||
* This method is not part of the official public API as plugins with
|
||||
* no plugin class are being phased out.
|
||||
*
|
||||
* @param string $name The plugin name to locate a path for.
|
||||
* @return string
|
||||
* @throws \Cake\Core\Exception\MissingPluginException when a plugin path cannot be resolved.
|
||||
* @internal
|
||||
*/
|
||||
public function findPath(string $name): string
|
||||
{
|
||||
// Ensure plugin config is loaded each time. This is necessary primarily
|
||||
// for testing because the Configure::clear() call in TestCase::tearDown()
|
||||
// wipes out all configuration including plugin paths config.
|
||||
$this->loadConfig();
|
||||
|
||||
$path = Configure::read('plugins.' . $name);
|
||||
if ($path) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
$pluginPath = str_replace('/', DIRECTORY_SEPARATOR, $name);
|
||||
$paths = App::path('plugins');
|
||||
foreach ($paths as $path) {
|
||||
if (is_dir($path . $pluginPath)) {
|
||||
return $path . $pluginPath . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
}
|
||||
|
||||
throw new MissingPluginException(['plugin' => $name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a plugin to the collection
|
||||
*
|
||||
* Plugins will be keyed by their names.
|
||||
*
|
||||
* @param \Cake\Core\PluginInterface $plugin The plugin to load.
|
||||
* @return $this
|
||||
*/
|
||||
public function add(PluginInterface $plugin)
|
||||
{
|
||||
$name = $plugin->getName();
|
||||
$this->plugins[$name] = $plugin;
|
||||
$this->names = array_keys($this->plugins);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a plugin from the collection if it exists.
|
||||
*
|
||||
* @param string $name The named plugin.
|
||||
* @return $this
|
||||
*/
|
||||
public function remove(string $name)
|
||||
{
|
||||
unset($this->plugins[$name]);
|
||||
$this->names = array_keys($this->plugins);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all plugins from the collection
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->plugins = [];
|
||||
$this->names = [];
|
||||
$this->positions = [];
|
||||
$this->loopDepth = -1;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the named plugin exists in the collection.
|
||||
*
|
||||
* @param string $name The named plugin.
|
||||
* @return bool
|
||||
*/
|
||||
public function has(string $name): bool
|
||||
{
|
||||
return isset($this->plugins[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the a plugin by name.
|
||||
*
|
||||
* If a plugin isn't already loaded it will be autoloaded on first access
|
||||
* and that plugins loaded this way may miss some hook methods.
|
||||
*
|
||||
* @param string $name The plugin to get.
|
||||
* @return \Cake\Core\PluginInterface The plugin.
|
||||
* @throws \Cake\Core\Exception\MissingPluginException when unknown plugins are fetched.
|
||||
*/
|
||||
public function get(string $name): PluginInterface
|
||||
{
|
||||
if ($this->has($name)) {
|
||||
return $this->plugins[$name];
|
||||
}
|
||||
|
||||
$plugin = $this->create($name);
|
||||
$this->add($plugin);
|
||||
|
||||
return $plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a plugin instance from a name/classname and configuration.
|
||||
*
|
||||
* @param string $name The plugin name or classname
|
||||
* @param array<string, mixed> $config Configuration options for the plugin.
|
||||
* @return \Cake\Core\PluginInterface
|
||||
* @throws \Cake\Core\Exception\MissingPluginException When plugin instance could not be created.
|
||||
*/
|
||||
public function create(string $name, array $config = []): PluginInterface
|
||||
{
|
||||
if ($name === '') {
|
||||
throw new CakeException('Cannot create a plugin with empty name');
|
||||
}
|
||||
|
||||
if (strpos($name, '\\') !== false) {
|
||||
/** @var \Cake\Core\PluginInterface */
|
||||
return new $name($config);
|
||||
}
|
||||
|
||||
$config += ['name' => $name];
|
||||
$namespace = str_replace('/', '\\', $name);
|
||||
|
||||
$className = $namespace . '\\' . 'Plugin';
|
||||
// Check for [Vendor/]Foo/Plugin class
|
||||
if (!class_exists($className)) {
|
||||
$pos = strpos($name, '/');
|
||||
if ($pos === false) {
|
||||
$className = $namespace . '\\' . $name . 'Plugin';
|
||||
} else {
|
||||
$className = $namespace . '\\' . substr($name, $pos + 1) . 'Plugin';
|
||||
}
|
||||
|
||||
// Check for [Vendor/]Foo/FooPlugin
|
||||
if (!class_exists($className)) {
|
||||
$className = BasePlugin::class;
|
||||
if (empty($config['path'])) {
|
||||
$config['path'] = $this->findPath($name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @var class-string<\Cake\Core\PluginInterface> $className */
|
||||
return new $className($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of Countable.
|
||||
*
|
||||
* Get the number of plugins in the collection.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->plugins);
|
||||
}
|
||||
|
||||
/**
|
||||
* Part of Iterator Interface
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function next(): void
|
||||
{
|
||||
$this->positions[$this->loopDepth]++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Part of Iterator Interface
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function key(): string
|
||||
{
|
||||
return $this->names[$this->positions[$this->loopDepth]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Part of Iterator Interface
|
||||
*
|
||||
* @return \Cake\Core\PluginInterface
|
||||
*/
|
||||
public function current(): PluginInterface
|
||||
{
|
||||
$position = $this->positions[$this->loopDepth];
|
||||
$name = $this->names[$position];
|
||||
|
||||
return $this->plugins[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Part of Iterator Interface
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rewind(): void
|
||||
{
|
||||
$this->positions[] = 0;
|
||||
$this->loopDepth += 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Part of Iterator Interface
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function valid(): bool
|
||||
{
|
||||
$valid = isset($this->names[$this->positions[$this->loopDepth]]);
|
||||
if (!$valid) {
|
||||
array_pop($this->positions);
|
||||
$this->loopDepth -= 1;
|
||||
}
|
||||
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the plugins to those with the named hook enabled.
|
||||
*
|
||||
* @param string $hook The hook to filter plugins by
|
||||
* @return \Generator<\Cake\Core\PluginInterface> A generator containing matching plugins.
|
||||
* @throws \InvalidArgumentException on invalid hooks
|
||||
*/
|
||||
public function with(string $hook): Generator
|
||||
{
|
||||
if (!in_array($hook, PluginInterface::VALID_HOOKS, true)) {
|
||||
throw new InvalidArgumentException("The `{$hook}` hook is not a known plugin hook.");
|
||||
}
|
||||
foreach ($this as $plugin) {
|
||||
if ($plugin->isEnabled($hook)) {
|
||||
yield $plugin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
135
vendor/cakephp/core/PluginInterface.php
vendored
Normal file
135
vendor/cakephp/core/PluginInterface.php
vendored
Normal file
@ -0,0 +1,135 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright 2005-2011, Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.6.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use Cake\Console\CommandCollection;
|
||||
use Cake\Http\MiddlewareQueue;
|
||||
use Cake\Routing\RouteBuilder;
|
||||
|
||||
/**
|
||||
* Plugin Interface
|
||||
*
|
||||
* @method void services(\Cake\Core\ContainerInterface $container) Register plugin services to
|
||||
* the application's container
|
||||
*/
|
||||
interface PluginInterface
|
||||
{
|
||||
/**
|
||||
* List of valid hooks.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
public const VALID_HOOKS = ['bootstrap', 'console', 'middleware', 'routes', 'services'];
|
||||
|
||||
/**
|
||||
* Get the name of this plugin.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string;
|
||||
|
||||
/**
|
||||
* Get the filesystem path to this plugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPath(): string;
|
||||
|
||||
/**
|
||||
* Get the filesystem path to configuration for this plugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getConfigPath(): string;
|
||||
|
||||
/**
|
||||
* Get the filesystem path to configuration for this plugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getClassPath(): string;
|
||||
|
||||
/**
|
||||
* Get the filesystem path to templates for this plugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTemplatePath(): string;
|
||||
|
||||
/**
|
||||
* Load all the application configuration and bootstrap logic.
|
||||
*
|
||||
* The default implementation of this method will include the `config/bootstrap.php` in the plugin if it exist. You
|
||||
* can override this method to replace that behavior.
|
||||
*
|
||||
* The host application is provided as an argument. This allows you to load additional
|
||||
* plugin dependencies, or attach events.
|
||||
*
|
||||
* @param \Cake\Core\PluginApplicationInterface $app The host application
|
||||
* @return void
|
||||
*/
|
||||
public function bootstrap(PluginApplicationInterface $app): void;
|
||||
|
||||
/**
|
||||
* Add console commands for the plugin.
|
||||
*
|
||||
* @param \Cake\Console\CommandCollection $commands The command collection to update
|
||||
* @return \Cake\Console\CommandCollection
|
||||
*/
|
||||
public function console(CommandCollection $commands): CommandCollection;
|
||||
|
||||
/**
|
||||
* Add middleware for the plugin.
|
||||
*
|
||||
* @param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to update.
|
||||
* @return \Cake\Http\MiddlewareQueue
|
||||
*/
|
||||
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue;
|
||||
|
||||
/**
|
||||
* Add routes for the plugin.
|
||||
*
|
||||
* The default implementation of this method will include the `config/routes.php` in the plugin if it exists. You
|
||||
* can override this method to replace that behavior.
|
||||
*
|
||||
* @param \Cake\Routing\RouteBuilder $routes The route builder to update.
|
||||
* @return void
|
||||
*/
|
||||
public function routes(RouteBuilder $routes): void;
|
||||
|
||||
/**
|
||||
* Disables the named hook
|
||||
*
|
||||
* @param string $hook The hook to disable
|
||||
* @return $this
|
||||
*/
|
||||
public function disable(string $hook);
|
||||
|
||||
/**
|
||||
* Enables the named hook
|
||||
*
|
||||
* @param string $hook The hook to disable
|
||||
* @return $this
|
||||
*/
|
||||
public function enable(string $hook);
|
||||
|
||||
/**
|
||||
* Check if the named hook is enabled
|
||||
*
|
||||
* @param string $hook The hook to check
|
||||
* @return bool
|
||||
*/
|
||||
public function isEnabled(string $hook): bool;
|
||||
}
|
37
vendor/cakephp/core/README.md
vendored
Normal file
37
vendor/cakephp/core/README.md
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
[![Total Downloads](https://img.shields.io/packagist/dt/cakephp/core.svg?style=flat-square)](https://packagist.org/packages/cakephp/core)
|
||||
[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE.txt)
|
||||
|
||||
# CakePHP Core Classes
|
||||
|
||||
A set of classes used for configuration files reading and storing.
|
||||
This repository contains the classes that are used as glue for creating the CakePHP framework.
|
||||
|
||||
## Usage
|
||||
|
||||
You can use the `Configure` class to store arbitrary configuration data:
|
||||
|
||||
```php
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Core\Configure\Engine\PhpConfig;
|
||||
|
||||
Configure::write('Company.name','Pizza, Inc.');
|
||||
Configure::read('Company.name'); // Returns: 'Pizza, Inc.'
|
||||
```
|
||||
|
||||
It also possible to load configuration from external files:
|
||||
|
||||
```php
|
||||
Configure::config('default', new PhpConfig('/path/to/config/folder'));
|
||||
Configure::load('app', 'default', false);
|
||||
Configure::load('other_config', 'default');
|
||||
```
|
||||
|
||||
And write the configuration back into files:
|
||||
|
||||
```php
|
||||
Configure::dump('my_config', 'default');
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Please make sure you check the [official documentation](https://book.cakephp.org/4/en/development/configuration.html)
|
94
vendor/cakephp/core/Retry/CommandRetry.php
vendored
Normal file
94
vendor/cakephp/core/Retry/CommandRetry.php
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.6.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core\Retry;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Allows any action to be retried in case of an exception.
|
||||
*
|
||||
* This class can be parametrized with a strategy, which will be followed
|
||||
* to determine whether the action should be retried.
|
||||
*/
|
||||
class CommandRetry
|
||||
{
|
||||
/**
|
||||
* The strategy to follow should the executed action fail.
|
||||
*
|
||||
* @var \Cake\Core\Retry\RetryStrategyInterface
|
||||
*/
|
||||
protected $strategy;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $maxRetries;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $numRetries;
|
||||
|
||||
/**
|
||||
* Creates the CommandRetry object with the given strategy and retry count
|
||||
*
|
||||
* @param \Cake\Core\Retry\RetryStrategyInterface $strategy The strategy to follow should the action fail
|
||||
* @param int $maxRetries The maximum number of retry attempts allowed
|
||||
*/
|
||||
public function __construct(RetryStrategyInterface $strategy, int $maxRetries = 1)
|
||||
{
|
||||
$this->strategy = $strategy;
|
||||
$this->maxRetries = $maxRetries;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of retries to perform in case of failure
|
||||
*
|
||||
* @param callable $action The callable action to execute with a retry strategy
|
||||
* @return mixed The return value of the passed action callable
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function run(callable $action)
|
||||
{
|
||||
$this->numRetries = 0;
|
||||
while (true) {
|
||||
try {
|
||||
return $action();
|
||||
} catch (Exception $e) {
|
||||
if (
|
||||
$this->numRetries < $this->maxRetries &&
|
||||
$this->strategy->shouldRetry($e, $this->numRetries)
|
||||
) {
|
||||
$this->numRetries++;
|
||||
continue;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last number of retry attemps.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getRetries(): int
|
||||
{
|
||||
return $this->numRetries;
|
||||
}
|
||||
}
|
35
vendor/cakephp/core/Retry/RetryStrategyInterface.php
vendored
Normal file
35
vendor/cakephp/core/Retry/RetryStrategyInterface.php
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.6.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core\Retry;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Used to instruct a CommandRetry object on whether a retry
|
||||
* for an action should be performed
|
||||
*/
|
||||
interface RetryStrategyInterface
|
||||
{
|
||||
/**
|
||||
* Returns true if the action can be retried, false otherwise.
|
||||
*
|
||||
* @param \Exception $exception The exception that caused the action to fail
|
||||
* @param int $retryCount The number of times action has been retried
|
||||
* @return bool Whether it is OK to retry the action
|
||||
*/
|
||||
public function shouldRetry(Exception $exception, int $retryCount): bool;
|
||||
}
|
50
vendor/cakephp/core/ServiceConfig.php
vendored
Normal file
50
vendor/cakephp/core/ServiceConfig.php
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 4.2.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
/**
|
||||
* Read-only wrapper for configuration data
|
||||
*
|
||||
* Intended for use with {@link \Cake\Core\Container} as
|
||||
* a typehintable way for services to have application
|
||||
* configuration injected as arrays cannot be typehinted.
|
||||
*/
|
||||
class ServiceConfig
|
||||
{
|
||||
/**
|
||||
* Read a configuration key
|
||||
*
|
||||
* @param string $path The path to read.
|
||||
* @param mixed $default The default value to use if $path does not exist.
|
||||
* @return mixed The configuration data or $default value.
|
||||
*/
|
||||
public function get(string $path, $default = null)
|
||||
{
|
||||
return Configure::read($path, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if $path exists and has a non-null value.
|
||||
*
|
||||
* @param string $path The path to check.
|
||||
* @return bool True if the configuration data exists.
|
||||
*/
|
||||
public function has(string $path): bool
|
||||
{
|
||||
return Configure::check($path);
|
||||
}
|
||||
}
|
132
vendor/cakephp/core/ServiceProvider.php
vendored
Normal file
132
vendor/cakephp/core/ServiceProvider.php
vendored
Normal file
@ -0,0 +1,132 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 4.2.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use League\Container\DefinitionContainerInterface;
|
||||
use League\Container\ServiceProvider\AbstractServiceProvider;
|
||||
use League\Container\ServiceProvider\BootableServiceProviderInterface;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Container ServiceProvider
|
||||
*
|
||||
* Service provider bundle related services together helping
|
||||
* to organize your application's dependencies. They also help
|
||||
* improve performance of applications with many services by
|
||||
* allowing service registration to be deferred until services are needed.
|
||||
*/
|
||||
abstract class ServiceProvider extends AbstractServiceProvider implements BootableServiceProviderInterface
|
||||
{
|
||||
/**
|
||||
* List of ids of services this provider provides.
|
||||
*
|
||||
* @var array<string>
|
||||
* @see ServiceProvider::provides()
|
||||
*/
|
||||
protected $provides = [];
|
||||
|
||||
/**
|
||||
* Get the container.
|
||||
*
|
||||
* This method's actual return type and documented return type differ
|
||||
* because PHP 7.2 doesn't support return type narrowing.
|
||||
*
|
||||
* @return \Cake\Core\ContainerInterface
|
||||
*/
|
||||
public function getContainer(): DefinitionContainerInterface
|
||||
{
|
||||
$container = parent::getContainer();
|
||||
|
||||
if (!($container instanceof ContainerInterface)) {
|
||||
$message = sprintf(
|
||||
'Unexpected container type. Expected `%s` got `%s` instead.',
|
||||
ContainerInterface::class,
|
||||
getTypeName($container)
|
||||
);
|
||||
throw new RuntimeException($message);
|
||||
}
|
||||
|
||||
return $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate to the bootstrap() method
|
||||
*
|
||||
* This method wraps the league/container function so users
|
||||
* only need to use the CakePHP bootstrap() interface.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
$this->bootstrap($this->getContainer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap hook for ServiceProviders
|
||||
*
|
||||
* This hook should be implemented if your service provider
|
||||
* needs to register additional service providers, load configuration
|
||||
* files or do any other work when the service provider is added to the
|
||||
* container.
|
||||
*
|
||||
* @param \Cake\Core\ContainerInterface $container The container to add services to.
|
||||
* @return void
|
||||
*/
|
||||
public function bootstrap(ContainerInterface $container): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the abstract services() method.
|
||||
*
|
||||
* This method primarily exists as a shim between the interface
|
||||
* that league/container has and the one we want to offer in CakePHP.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
$this->services($this->getContainer());
|
||||
}
|
||||
|
||||
/**
|
||||
* The provides method is a way to let the container know that a service
|
||||
* is provided by this service provider.
|
||||
*
|
||||
* Every service that is registered via this service provider must have an
|
||||
* alias added to this array or it will be ignored.
|
||||
*
|
||||
* @param string $id Identifier.
|
||||
* @return bool
|
||||
*/
|
||||
public function provides(string $id): bool
|
||||
{
|
||||
return in_array($id, $this->provides, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the services in a provider.
|
||||
*
|
||||
* All services registered in this method should also be included in the $provides
|
||||
* property so that services can be located.
|
||||
*
|
||||
* @param \Cake\Core\ContainerInterface $container The container to add services to.
|
||||
* @return void
|
||||
*/
|
||||
abstract public function services(ContainerInterface $container): void;
|
||||
}
|
325
vendor/cakephp/core/StaticConfigTrait.php
vendored
Normal file
325
vendor/cakephp/core/StaticConfigTrait.php
vendored
Normal file
@ -0,0 +1,325 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use BadMethodCallException;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
|
||||
/**
|
||||
* A trait that provides a set of static methods to manage configuration
|
||||
* for classes that provide an adapter facade or need to have sets of
|
||||
* configuration data registered and manipulated.
|
||||
*
|
||||
* Implementing objects are expected to declare a static `$_dsnClassMap` property.
|
||||
*/
|
||||
trait StaticConfigTrait
|
||||
{
|
||||
/**
|
||||
* Configuration sets.
|
||||
*
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
protected static $_config = [];
|
||||
|
||||
/**
|
||||
* This method can be used to define configuration adapters for an application.
|
||||
*
|
||||
* To change an adapter's configuration at runtime, first drop the adapter and then
|
||||
* reconfigure it.
|
||||
*
|
||||
* Adapters will not be constructed until the first operation is done.
|
||||
*
|
||||
* ### Usage
|
||||
*
|
||||
* Assuming that the class' name is `Cache` the following scenarios
|
||||
* are supported:
|
||||
*
|
||||
* Setting a cache engine up.
|
||||
*
|
||||
* ```
|
||||
* Cache::setConfig('default', $settings);
|
||||
* ```
|
||||
*
|
||||
* Injecting a constructed adapter in:
|
||||
*
|
||||
* ```
|
||||
* Cache::setConfig('default', $instance);
|
||||
* ```
|
||||
*
|
||||
* Configure multiple adapters at once:
|
||||
*
|
||||
* ```
|
||||
* Cache::setConfig($arrayOfConfig);
|
||||
* ```
|
||||
*
|
||||
* @param array<string, mixed>|string $key The name of the configuration, or an array of multiple configs.
|
||||
* @param mixed $config Configuration value. Generally an array of name => configuration data for adapter.
|
||||
* @throws \BadMethodCallException When trying to modify an existing config.
|
||||
* @throws \LogicException When trying to store an invalid structured config array.
|
||||
* @return void
|
||||
*/
|
||||
public static function setConfig($key, $config = null): void
|
||||
{
|
||||
if ($config === null) {
|
||||
if (!is_array($key)) {
|
||||
throw new LogicException('If config is null, key must be an array.');
|
||||
}
|
||||
foreach ($key as $name => $settings) {
|
||||
static::setConfig($name, $settings);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset(static::$_config[$key])) {
|
||||
/** @psalm-suppress PossiblyInvalidArgument */
|
||||
throw new BadMethodCallException(sprintf('Cannot reconfigure existing key "%s"', $key));
|
||||
}
|
||||
|
||||
if (is_object($config)) {
|
||||
$config = ['className' => $config];
|
||||
}
|
||||
|
||||
if (is_array($config) && isset($config['url'])) {
|
||||
$parsed = static::parseDsn($config['url']);
|
||||
unset($config['url']);
|
||||
$config = $parsed + $config;
|
||||
}
|
||||
|
||||
if (isset($config['engine']) && empty($config['className'])) {
|
||||
$config['className'] = $config['engine'];
|
||||
unset($config['engine']);
|
||||
}
|
||||
/** @psalm-suppress InvalidPropertyAssignmentValue */
|
||||
static::$_config[$key] = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads existing configuration.
|
||||
*
|
||||
* @param string $key The name of the configuration.
|
||||
* @return mixed|null Configuration data at the named key or null if the key does not exist.
|
||||
*/
|
||||
public static function getConfig(string $key)
|
||||
{
|
||||
return static::$_config[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads existing configuration for a specific key.
|
||||
*
|
||||
* The config value for this key must exist, it can never be null.
|
||||
*
|
||||
* @param string $key The name of the configuration.
|
||||
* @return mixed Configuration data at the named key.
|
||||
* @throws \InvalidArgumentException If value does not exist.
|
||||
*/
|
||||
public static function getConfigOrFail(string $key)
|
||||
{
|
||||
if (!isset(static::$_config[$key])) {
|
||||
throw new InvalidArgumentException(sprintf('Expected configuration `%s` not found.', $key));
|
||||
}
|
||||
|
||||
return static::$_config[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops a constructed adapter.
|
||||
*
|
||||
* If you wish to modify an existing configuration, you should drop it,
|
||||
* change configuration and then re-add it.
|
||||
*
|
||||
* If the implementing objects supports a `$_registry` object the named configuration
|
||||
* will also be unloaded from the registry.
|
||||
*
|
||||
* @param string $config An existing configuration you wish to remove.
|
||||
* @return bool Success of the removal, returns false when the config does not exist.
|
||||
*/
|
||||
public static function drop(string $config): bool
|
||||
{
|
||||
if (!isset(static::$_config[$config])) {
|
||||
return false;
|
||||
}
|
||||
/** @psalm-suppress RedundantPropertyInitializationCheck */
|
||||
if (isset(static::$_registry)) {
|
||||
static::$_registry->unload($config);
|
||||
}
|
||||
unset(static::$_config[$config]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing the named configurations
|
||||
*
|
||||
* @return array<string> Array of configurations.
|
||||
*/
|
||||
public static function configured(): array
|
||||
{
|
||||
$configurations = array_keys(static::$_config);
|
||||
|
||||
return array_map(function ($key) {
|
||||
return (string)$key;
|
||||
}, $configurations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a DSN into a valid connection configuration
|
||||
*
|
||||
* This method allows setting a DSN using formatting similar to that used by PEAR::DB.
|
||||
* The following is an example of its usage:
|
||||
*
|
||||
* ```
|
||||
* $dsn = 'mysql://user:pass@localhost/database?';
|
||||
* $config = ConnectionManager::parseDsn($dsn);
|
||||
*
|
||||
* $dsn = 'Cake\Log\Engine\FileLog://?types=notice,info,debug&file=debug&path=LOGS';
|
||||
* $config = Log::parseDsn($dsn);
|
||||
*
|
||||
* $dsn = 'smtp://user:secret@localhost:25?timeout=30&client=null&tls=null';
|
||||
* $config = Email::parseDsn($dsn);
|
||||
*
|
||||
* $dsn = 'file:///?className=\My\Cache\Engine\FileEngine';
|
||||
* $config = Cache::parseDsn($dsn);
|
||||
*
|
||||
* $dsn = 'File://?prefix=myapp_cake_core_&serialize=true&duration=+2 minutes&path=/tmp/persistent/';
|
||||
* $config = Cache::parseDsn($dsn);
|
||||
* ```
|
||||
*
|
||||
* For all classes, the value of `scheme` is set as the value of both the `className`
|
||||
* unless they have been otherwise specified.
|
||||
*
|
||||
* Note that querystring arguments are also parsed and set as values in the returned configuration.
|
||||
*
|
||||
* @param string $dsn The DSN string to convert to a configuration array
|
||||
* @return array<string, mixed> The configuration array to be stored after parsing the DSN
|
||||
* @throws \InvalidArgumentException If not passed a string, or passed an invalid string
|
||||
*/
|
||||
public static function parseDsn(string $dsn): array
|
||||
{
|
||||
if (empty($dsn)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$pattern = <<<'REGEXP'
|
||||
{
|
||||
^
|
||||
(?P<_scheme>
|
||||
(?P<scheme>[\w\\\\]+)://
|
||||
)
|
||||
(?P<_username>
|
||||
(?P<username>.*?)
|
||||
(?P<_password>
|
||||
:(?P<password>.*?)
|
||||
)?
|
||||
@
|
||||
)?
|
||||
(?P<_host>
|
||||
(?P<host>[^?#/:@]+)
|
||||
(?P<_port>
|
||||
:(?P<port>\d+)
|
||||
)?
|
||||
)?
|
||||
(?P<_path>
|
||||
(?P<path>/[^?#]*)
|
||||
)?
|
||||
(?P<_query>
|
||||
\?(?P<query>[^#]*)
|
||||
)?
|
||||
(?P<_fragment>
|
||||
\#(?P<fragment>.*)
|
||||
)?
|
||||
$
|
||||
}x
|
||||
REGEXP;
|
||||
|
||||
preg_match($pattern, $dsn, $parsed);
|
||||
|
||||
if (!$parsed) {
|
||||
throw new InvalidArgumentException("The DSN string '{$dsn}' could not be parsed.");
|
||||
}
|
||||
|
||||
$exists = [];
|
||||
foreach ($parsed as $k => $v) {
|
||||
if (is_int($k)) {
|
||||
unset($parsed[$k]);
|
||||
} elseif (strpos($k, '_') === 0) {
|
||||
$exists[substr($k, 1)] = ($v !== '');
|
||||
unset($parsed[$k]);
|
||||
} elseif ($v === '' && !$exists[$k]) {
|
||||
unset($parsed[$k]);
|
||||
}
|
||||
}
|
||||
|
||||
$query = '';
|
||||
|
||||
if (isset($parsed['query'])) {
|
||||
$query = $parsed['query'];
|
||||
unset($parsed['query']);
|
||||
}
|
||||
|
||||
parse_str($query, $queryArgs);
|
||||
|
||||
foreach ($queryArgs as $key => $value) {
|
||||
if ($value === 'true') {
|
||||
$queryArgs[$key] = true;
|
||||
} elseif ($value === 'false') {
|
||||
$queryArgs[$key] = false;
|
||||
} elseif ($value === 'null') {
|
||||
$queryArgs[$key] = null;
|
||||
}
|
||||
}
|
||||
|
||||
$parsed = $queryArgs + $parsed;
|
||||
|
||||
if (empty($parsed['className'])) {
|
||||
$classMap = static::getDsnClassMap();
|
||||
|
||||
$parsed['className'] = $parsed['scheme'];
|
||||
if (isset($classMap[$parsed['scheme']])) {
|
||||
/** @psalm-suppress PossiblyNullArrayOffset */
|
||||
$parsed['className'] = $classMap[$parsed['scheme']];
|
||||
}
|
||||
}
|
||||
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the DSN class map for this class.
|
||||
*
|
||||
* @param array<string, string> $map Additions/edits to the class map to apply.
|
||||
* @return void
|
||||
* @psalm-param array<string, class-string> $map
|
||||
*/
|
||||
public static function setDsnClassMap(array $map): void
|
||||
{
|
||||
static::$_dsnClassMap = $map + static::$_dsnClassMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DSN class map for this class.
|
||||
*
|
||||
* @return array<string, string>
|
||||
* @psalm-return array<string, class-string>
|
||||
*/
|
||||
public static function getDsnClassMap(): array
|
||||
{
|
||||
return static::$_dsnClassMap;
|
||||
}
|
||||
}
|
181
vendor/cakephp/core/TestSuite/ContainerStubTrait.php
vendored
Normal file
181
vendor/cakephp/core/TestSuite/ContainerStubTrait.php
vendored
Normal file
@ -0,0 +1,181 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @since 4.2.0
|
||||
* @license https://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core\TestSuite;
|
||||
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Core\ContainerInterface;
|
||||
use Cake\Event\EventInterface;
|
||||
use Closure;
|
||||
use League\Container\Exception\NotFoundException;
|
||||
use LogicException;
|
||||
|
||||
/**
|
||||
* A set of methods used for defining container services
|
||||
* in test cases.
|
||||
*
|
||||
* This trait leverages the `Application.buildContainer` event
|
||||
* to inject the mocked services into the container that the
|
||||
* application uses.
|
||||
*/
|
||||
trait ContainerStubTrait
|
||||
{
|
||||
/**
|
||||
* The customized application class name.
|
||||
*
|
||||
* @psalm-var class-string<\Cake\Core\HttpApplicationInterface>|class-string<\Cake\Core\ConsoleApplicationInterface>|null
|
||||
* @var string|null
|
||||
*/
|
||||
protected $_appClass;
|
||||
|
||||
/**
|
||||
* The customized application constructor arguments.
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
protected $_appArgs;
|
||||
|
||||
/**
|
||||
* The collection of container services.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $containerServices = [];
|
||||
|
||||
/**
|
||||
* Configure the application class to use in integration tests.
|
||||
*
|
||||
* @param string $class The application class name.
|
||||
* @param array|null $constructorArgs The constructor arguments for your application class.
|
||||
* @return void
|
||||
* @psalm-param class-string<\Cake\Core\HttpApplicationInterface>|class-string<\Cake\Core\ConsoleApplicationInterface> $class
|
||||
*/
|
||||
public function configApplication(string $class, ?array $constructorArgs): void
|
||||
{
|
||||
$this->_appClass = $class;
|
||||
$this->_appArgs = $constructorArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an application instance.
|
||||
*
|
||||
* Uses the configuration set in `configApplication()`.
|
||||
*
|
||||
* @return \Cake\Core\HttpApplicationInterface|\Cake\Core\ConsoleApplicationInterface
|
||||
*/
|
||||
protected function createApp()
|
||||
{
|
||||
if ($this->_appClass) {
|
||||
$appClass = $this->_appClass;
|
||||
} else {
|
||||
/** @psalm-var class-string<\Cake\Http\BaseApplication> */
|
||||
$appClass = Configure::read('App.namespace') . '\Application';
|
||||
}
|
||||
if (!class_exists($appClass)) {
|
||||
throw new LogicException("Cannot load `{$appClass}` for use in integration testing.");
|
||||
}
|
||||
$appArgs = $this->_appArgs ?: [CONFIG];
|
||||
|
||||
$app = new $appClass(...$appArgs);
|
||||
if (!empty($this->containerServices) && method_exists($app, 'getEventManager')) {
|
||||
$app->getEventManager()->on('Application.buildContainer', [$this, 'modifyContainer']);
|
||||
}
|
||||
|
||||
return $app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a mocked service to the container.
|
||||
*
|
||||
* When the container is created the provided classname
|
||||
* will be mapped to the factory function. The factory
|
||||
* function will be used to create mocked services.
|
||||
*
|
||||
* @param string $class The class or interface you want to define.
|
||||
* @param \Closure $factory The factory function for mocked services.
|
||||
* @return $this
|
||||
*/
|
||||
public function mockService(string $class, Closure $factory)
|
||||
{
|
||||
$this->containerServices[$class] = $factory;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a mocked service to the container.
|
||||
*
|
||||
* @param string $class The class or interface you want to remove.
|
||||
* @return $this
|
||||
*/
|
||||
public function removeMockService(string $class)
|
||||
{
|
||||
unset($this->containerServices[$class]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the application's container with one containing mocks.
|
||||
*
|
||||
* If any mocked services are defined, the application's container
|
||||
* will be replaced with one containing mocks. The original
|
||||
* container will be set as a delegate to the mock container.
|
||||
*
|
||||
* @param \Cake\Event\EventInterface $event The event
|
||||
* @param \Cake\Core\ContainerInterface $container The container to wrap.
|
||||
* @return \Cake\Core\ContainerInterface|null
|
||||
*/
|
||||
public function modifyContainer(EventInterface $event, ContainerInterface $container): ?ContainerInterface
|
||||
{
|
||||
if (empty($this->containerServices)) {
|
||||
return null;
|
||||
}
|
||||
foreach ($this->containerServices as $key => $factory) {
|
||||
if ($container->has($key)) {
|
||||
try {
|
||||
$container->extend($key)->setConcrete($factory);
|
||||
} catch (NotFoundException $e) {
|
||||
$container->add($key, $factory);
|
||||
}
|
||||
} else {
|
||||
$container->add($key, $factory);
|
||||
}
|
||||
}
|
||||
|
||||
return $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears any mocks that were defined and cleans
|
||||
* up application class configuration.
|
||||
*
|
||||
* @after
|
||||
* @return void
|
||||
*/
|
||||
public function cleanupContainer(): void
|
||||
{
|
||||
$this->_appArgs = null;
|
||||
$this->_appClass = null;
|
||||
$this->containerServices = [];
|
||||
}
|
||||
}
|
||||
|
||||
// phpcs:disable
|
||||
class_alias(
|
||||
'Cake\Core\TestSuite\ContainerStubTrait',
|
||||
'Cake\TestSuite\ContainerStubTrait'
|
||||
);
|
||||
// phpcs:enable
|
44
vendor/cakephp/core/composer.json
vendored
Normal file
44
vendor/cakephp/core/composer.json
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "cakephp/core",
|
||||
"description": "CakePHP Framework Core classes",
|
||||
"type": "library",
|
||||
"keywords": [
|
||||
"cakephp",
|
||||
"framework",
|
||||
"core"
|
||||
],
|
||||
"homepage": "https://cakephp.org",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "CakePHP Community",
|
||||
"homepage": "https://github.com/cakephp/core/graphs/contributors"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/cakephp/cakephp/issues",
|
||||
"forum": "https://stackoverflow.com/tags/cakephp",
|
||||
"irc": "irc://irc.freenode.org/cakephp",
|
||||
"source": "https://github.com/cakephp/core"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.4.0",
|
||||
"cakephp/utility": "^4.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/container-implementation": "^1.0 || ^2.0"
|
||||
},
|
||||
"suggest": {
|
||||
"cakephp/event": "To use PluginApplicationInterface or plugin applications.",
|
||||
"cakephp/cache": "To use Configure::store() and restore().",
|
||||
"league/container": "To use Container and ServiceProvider classes"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Cake\\Core\\": "."
|
||||
},
|
||||
"files": [
|
||||
"functions.php"
|
||||
]
|
||||
}
|
||||
}
|
340
vendor/cakephp/core/functions.php
vendored
Normal file
340
vendor/cakephp/core/functions.php
vendored
Normal file
@ -0,0 +1,340 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 4.5.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
namespace Cake\Core;
|
||||
|
||||
if (!function_exists('Cake\Core\h')) {
|
||||
/**
|
||||
* Convenience method for htmlspecialchars.
|
||||
*
|
||||
* @param mixed $text Text to wrap through htmlspecialchars. Also works with arrays, and objects.
|
||||
* Arrays will be mapped and have all their elements escaped. Objects will be string cast if they
|
||||
* implement a `__toString` method. Otherwise, the class name will be used.
|
||||
* Other scalar types will be returned unchanged.
|
||||
* @param bool $double Encode existing html entities.
|
||||
* @param string|null $charset Character set to use when escaping.
|
||||
* Defaults to config value in `mb_internal_encoding()` or 'UTF-8'.
|
||||
* @return mixed Wrapped text.
|
||||
* @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#h
|
||||
*/
|
||||
function h($text, bool $double = true, ?string $charset = null)
|
||||
{
|
||||
if (is_string($text)) {
|
||||
//optimize for strings
|
||||
} elseif (is_array($text)) {
|
||||
$texts = [];
|
||||
foreach ($text as $k => $t) {
|
||||
$texts[$k] = h($t, $double, $charset);
|
||||
}
|
||||
|
||||
return $texts;
|
||||
} elseif (is_object($text)) {
|
||||
if (method_exists($text, '__toString')) {
|
||||
$text = $text->__toString();
|
||||
} else {
|
||||
$text = '(object)' . get_class($text);
|
||||
}
|
||||
} elseif ($text === null || is_scalar($text)) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
static $defaultCharset = false;
|
||||
if ($defaultCharset === false) {
|
||||
$defaultCharset = mb_internal_encoding() ?: 'UTF-8';
|
||||
}
|
||||
|
||||
return htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, $charset ?: $defaultCharset, $double);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('Cake\Core\pluginSplit')) {
|
||||
/**
|
||||
* Splits a dot syntax plugin name into its plugin and class name.
|
||||
* If $name does not have a dot, then index 0 will be null.
|
||||
*
|
||||
* Commonly used like
|
||||
* ```
|
||||
* list($plugin, $name) = pluginSplit($name);
|
||||
* ```
|
||||
*
|
||||
* @param string $name The name you want to plugin split.
|
||||
* @param bool $dotAppend Set to true if you want the plugin to have a '.' appended to it.
|
||||
* @param string|null $plugin Optional default plugin to use if no plugin is found. Defaults to null.
|
||||
* @return array Array with 2 indexes. 0 => plugin name, 1 => class name.
|
||||
* @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#pluginSplit
|
||||
* @psalm-return array{string|null, string}
|
||||
*/
|
||||
function pluginSplit(string $name, bool $dotAppend = false, ?string $plugin = null): array
|
||||
{
|
||||
if (strpos($name, '.') !== false) {
|
||||
$parts = explode('.', $name, 2);
|
||||
if ($dotAppend) {
|
||||
$parts[0] .= '.';
|
||||
}
|
||||
|
||||
/** @psalm-var array{string, string} */
|
||||
return $parts;
|
||||
}
|
||||
|
||||
return [$plugin, $name];
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('Cake\Core\namespaceSplit')) {
|
||||
/**
|
||||
* Split the namespace from the classname.
|
||||
*
|
||||
* Commonly used like `list($namespace, $className) = namespaceSplit($class);`.
|
||||
*
|
||||
* @param string $class The full class name, ie `Cake\Core\App`.
|
||||
* @return array<string> Array with 2 indexes. 0 => namespace, 1 => classname.
|
||||
*/
|
||||
function namespaceSplit(string $class): array
|
||||
{
|
||||
$pos = strrpos($class, '\\');
|
||||
if ($pos === false) {
|
||||
return ['', $class];
|
||||
}
|
||||
|
||||
return [substr($class, 0, $pos), substr($class, $pos + 1)];
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('Cake\Core\pr')) {
|
||||
/**
|
||||
* print_r() convenience function.
|
||||
*
|
||||
* In terminals this will act similar to using print_r() directly, when not run on CLI
|
||||
* print_r() will also wrap `<pre>` tags around the output of given variable. Similar to debug().
|
||||
*
|
||||
* This function returns the same variable that was passed.
|
||||
*
|
||||
* @param mixed $var Variable to print out.
|
||||
* @return mixed the same $var that was passed to this function
|
||||
* @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#pr
|
||||
* @see debug()
|
||||
*/
|
||||
function pr($var)
|
||||
{
|
||||
if (!Configure::read('debug')) {
|
||||
return $var;
|
||||
}
|
||||
|
||||
$template = PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg' ? '<pre class="pr">%s</pre>' : "\n%s\n\n";
|
||||
printf($template, trim(print_r($var, true)));
|
||||
|
||||
return $var;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('Cake\Core\pj')) {
|
||||
/**
|
||||
* JSON pretty print convenience function.
|
||||
*
|
||||
* In terminals this will act similar to using json_encode() with JSON_PRETTY_PRINT directly, when not run on CLI
|
||||
* will also wrap `<pre>` tags around the output of given variable. Similar to pr().
|
||||
*
|
||||
* This function returns the same variable that was passed.
|
||||
*
|
||||
* @param mixed $var Variable to print out.
|
||||
* @return mixed the same $var that was passed to this function
|
||||
* @see pr()
|
||||
* @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#pj
|
||||
*/
|
||||
function pj($var)
|
||||
{
|
||||
if (!Configure::read('debug')) {
|
||||
return $var;
|
||||
}
|
||||
|
||||
$template = PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg' ? '<pre class="pj">%s</pre>' : "\n%s\n\n";
|
||||
printf($template, trim(json_encode($var, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)));
|
||||
|
||||
return $var;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('Cake\Core\env')) {
|
||||
/**
|
||||
* Gets an environment variable from available sources, and provides emulation
|
||||
* for unsupported or inconsistent environment variables (i.e. DOCUMENT_ROOT on
|
||||
* IIS, or SCRIPT_NAME in CGI mode). Also exposes some additional custom
|
||||
* environment information.
|
||||
*
|
||||
* @param string $key Environment variable name.
|
||||
* @param string|bool|null $default Specify a default value in case the environment variable is not defined.
|
||||
* @return string|bool|null Environment variable setting.
|
||||
* @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#env
|
||||
*/
|
||||
function env(string $key, $default = null)
|
||||
{
|
||||
if ($key === 'HTTPS') {
|
||||
if (isset($_SERVER['HTTPS'])) {
|
||||
return !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
|
||||
}
|
||||
|
||||
return strpos((string)env('SCRIPT_URI'), 'https://') === 0;
|
||||
}
|
||||
|
||||
if ($key === 'SCRIPT_NAME' && env('CGI_MODE') && isset($_ENV['SCRIPT_URL'])) {
|
||||
$key = 'SCRIPT_URL';
|
||||
}
|
||||
|
||||
/** @var string|null $val */
|
||||
$val = $_SERVER[$key] ?? $_ENV[$key] ?? null;
|
||||
if ($val == null && getenv($key) !== false) {
|
||||
/** @var string|false $val */
|
||||
$val = getenv($key);
|
||||
}
|
||||
|
||||
if ($key === 'REMOTE_ADDR' && $val === env('SERVER_ADDR')) {
|
||||
$addr = env('HTTP_PC_REMOTE_ADDR');
|
||||
if ($addr !== null) {
|
||||
$val = $addr;
|
||||
}
|
||||
}
|
||||
|
||||
if ($val !== null) {
|
||||
return $val;
|
||||
}
|
||||
|
||||
switch ($key) {
|
||||
case 'DOCUMENT_ROOT':
|
||||
$name = (string)env('SCRIPT_NAME');
|
||||
$filename = (string)env('SCRIPT_FILENAME');
|
||||
$offset = 0;
|
||||
if (!strpos($name, '.php')) {
|
||||
$offset = 4;
|
||||
}
|
||||
|
||||
return substr($filename, 0, -(strlen($name) + $offset));
|
||||
case 'PHP_SELF':
|
||||
return str_replace((string)env('DOCUMENT_ROOT'), '', (string)env('SCRIPT_FILENAME'));
|
||||
case 'CGI_MODE':
|
||||
return PHP_SAPI === 'cgi';
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('Cake\Core\triggerWarning')) {
|
||||
/**
|
||||
* Triggers an E_USER_WARNING.
|
||||
*
|
||||
* @param string $message The warning message.
|
||||
* @return void
|
||||
*/
|
||||
function triggerWarning(string $message): void
|
||||
{
|
||||
$trace = debug_backtrace();
|
||||
if (isset($trace[1])) {
|
||||
$frame = $trace[1];
|
||||
$frame += ['file' => '[internal]', 'line' => '??'];
|
||||
$message = sprintf(
|
||||
'%s - %s, line: %s',
|
||||
$message,
|
||||
$frame['file'],
|
||||
$frame['line']
|
||||
);
|
||||
}
|
||||
trigger_error($message, E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('Cake\Core\deprecationWarning')) {
|
||||
/**
|
||||
* Helper method for outputting deprecation warnings
|
||||
*
|
||||
* @param string $message The message to output as a deprecation warning.
|
||||
* @param int $stackFrame The stack frame to include in the error. Defaults to 1
|
||||
* as that should point to application/plugin code.
|
||||
* @return void
|
||||
*/
|
||||
function deprecationWarning(string $message, int $stackFrame = 1): void
|
||||
{
|
||||
if (!(error_reporting() & E_USER_DEPRECATED)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$trace = debug_backtrace();
|
||||
if (isset($trace[$stackFrame])) {
|
||||
$frame = $trace[$stackFrame];
|
||||
$frame += ['file' => '[internal]', 'line' => '??'];
|
||||
|
||||
// Assuming we're installed in vendor/cakephp/cakephp/src/Core/functions.php
|
||||
$root = dirname(__DIR__, 5);
|
||||
if (defined('ROOT')) {
|
||||
$root = ROOT;
|
||||
}
|
||||
$relative = str_replace(
|
||||
DIRECTORY_SEPARATOR,
|
||||
'/',
|
||||
substr($frame['file'], strlen($root) + 1)
|
||||
);
|
||||
$patterns = (array)Configure::read('Error.ignoredDeprecationPaths');
|
||||
foreach ($patterns as $pattern) {
|
||||
$pattern = str_replace(DIRECTORY_SEPARATOR, '/', $pattern);
|
||||
if (fnmatch($pattern, $relative)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$message = sprintf(
|
||||
"%s\n%s, line: %s\n" . 'You can disable all deprecation warnings by setting `Error.errorLevel` to ' .
|
||||
'`E_ALL & ~E_USER_DEPRECATED`. Adding `%s` to `Error.ignoredDeprecationPaths` ' .
|
||||
'in your `config/app.php` config will mute deprecations from that file only.',
|
||||
$message,
|
||||
$frame['file'],
|
||||
$frame['line'],
|
||||
$relative
|
||||
);
|
||||
}
|
||||
|
||||
static $errors = [];
|
||||
$checksum = md5($message);
|
||||
$duplicate = (bool)Configure::read('Error.allowDuplicateDeprecations', false);
|
||||
if (isset($errors[$checksum]) && !$duplicate) {
|
||||
return;
|
||||
}
|
||||
if (!$duplicate) {
|
||||
$errors[$checksum] = true;
|
||||
}
|
||||
|
||||
trigger_error($message, E_USER_DEPRECATED);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('Cake\Core\getTypeName')) {
|
||||
/**
|
||||
* Returns the objects class or var type of it's not an object
|
||||
*
|
||||
* @param mixed $var Variable to check
|
||||
* @return string Returns the class name or variable type
|
||||
*/
|
||||
function getTypeName($var): string
|
||||
{
|
||||
return is_object($var) ? get_class($var) : gettype($var);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Include global functions.
|
||||
*/
|
||||
if (!getenv('CAKE_DISABLE_GLOBAL_FUNCS')) {
|
||||
include 'functions_global.php';
|
||||
}
|
190
vendor/cakephp/core/functions_global.php
vendored
Normal file
190
vendor/cakephp/core/functions_global.php
vendored
Normal file
@ -0,0 +1,190 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
use function Cake\Core\deprecationWarning as cakeDeprecationWarning;
|
||||
use function Cake\Core\env as cakeEnv;
|
||||
use function Cake\Core\getTypeName as cakeGetTypeName;
|
||||
use function Cake\Core\h as cakeH;
|
||||
use function Cake\Core\namespaceSplit as cakeNamespaceSplit;
|
||||
use function Cake\Core\pj as cakePj;
|
||||
use function Cake\Core\pluginSplit as cakePluginSplit;
|
||||
use function Cake\Core\pr as cakePr;
|
||||
use function Cake\Core\triggerWarning as cakeTriggerWarning;
|
||||
|
||||
if (!defined('DS')) {
|
||||
/**
|
||||
* Defines DS as short form of DIRECTORY_SEPARATOR.
|
||||
*/
|
||||
define('DS', DIRECTORY_SEPARATOR);
|
||||
}
|
||||
|
||||
if (!function_exists('h')) {
|
||||
/**
|
||||
* Convenience method for htmlspecialchars.
|
||||
*
|
||||
* @param mixed $text Text to wrap through htmlspecialchars. Also works with arrays, and objects.
|
||||
* Arrays will be mapped and have all their elements escaped. Objects will be string cast if they
|
||||
* implement a `__toString` method. Otherwise, the class name will be used.
|
||||
* Other scalar types will be returned unchanged.
|
||||
* @param bool $double Encode existing html entities.
|
||||
* @param string|null $charset Character set to use when escaping.
|
||||
* Defaults to config value in `mb_internal_encoding()` or 'UTF-8'.
|
||||
* @return mixed Wrapped text.
|
||||
* @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#h
|
||||
*/
|
||||
function h($text, bool $double = true, ?string $charset = null)
|
||||
{
|
||||
return cakeH($text, $double, $charset);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('pluginSplit')) {
|
||||
/**
|
||||
* Splits a dot syntax plugin name into its plugin and class name.
|
||||
* If $name does not have a dot, then index 0 will be null.
|
||||
*
|
||||
* Commonly used like
|
||||
* ```
|
||||
* list($plugin, $name) = pluginSplit($name);
|
||||
* ```
|
||||
*
|
||||
* @param string $name The name you want to plugin split.
|
||||
* @param bool $dotAppend Set to true if you want the plugin to have a '.' appended to it.
|
||||
* @param string|null $plugin Optional default plugin to use if no plugin is found. Defaults to null.
|
||||
* @return array Array with 2 indexes. 0 => plugin name, 1 => class name.
|
||||
* @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#pluginSplit
|
||||
* @psalm-return array{string|null, string}
|
||||
*/
|
||||
function pluginSplit(string $name, bool $dotAppend = false, ?string $plugin = null): array
|
||||
{
|
||||
return cakePluginSplit($name, $dotAppend, $plugin);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('namespaceSplit')) {
|
||||
/**
|
||||
* Split the namespace from the classname.
|
||||
*
|
||||
* Commonly used like `list($namespace, $className) = namespaceSplit($class);`.
|
||||
*
|
||||
* @param string $class The full class name, ie `Cake\Core\App`.
|
||||
* @return array<string> Array with 2 indexes. 0 => namespace, 1 => classname.
|
||||
*/
|
||||
function namespaceSplit(string $class): array
|
||||
{
|
||||
return cakeNamespaceSplit($class);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('pr')) {
|
||||
/**
|
||||
* print_r() convenience function.
|
||||
*
|
||||
* In terminals this will act similar to using print_r() directly, when not run on CLI
|
||||
* print_r() will also wrap `<pre>` tags around the output of given variable. Similar to debug().
|
||||
*
|
||||
* This function returns the same variable that was passed.
|
||||
*
|
||||
* @param mixed $var Variable to print out.
|
||||
* @return mixed the same $var that was passed to this function
|
||||
* @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#pr
|
||||
* @see debug()
|
||||
*/
|
||||
function pr($var)
|
||||
{
|
||||
return cakePr($var);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('pj')) {
|
||||
/**
|
||||
* JSON pretty print convenience function.
|
||||
*
|
||||
* In terminals this will act similar to using json_encode() with JSON_PRETTY_PRINT directly, when not run on CLI
|
||||
* will also wrap `<pre>` tags around the output of given variable. Similar to pr().
|
||||
*
|
||||
* This function returns the same variable that was passed.
|
||||
*
|
||||
* @param mixed $var Variable to print out.
|
||||
* @return mixed the same $var that was passed to this function
|
||||
* @see pr()
|
||||
* @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#pj
|
||||
*/
|
||||
function pj($var)
|
||||
{
|
||||
return cakePj($var);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('env')) {
|
||||
/**
|
||||
* Gets an environment variable from available sources, and provides emulation
|
||||
* for unsupported or inconsistent environment variables (i.e. DOCUMENT_ROOT on
|
||||
* IIS, or SCRIPT_NAME in CGI mode). Also exposes some additional custom
|
||||
* environment information.
|
||||
*
|
||||
* @param string $key Environment variable name.
|
||||
* @param string|bool|null $default Specify a default value in case the environment variable is not defined.
|
||||
* @return string|bool|null Environment variable setting.
|
||||
* @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#env
|
||||
*/
|
||||
function env(string $key, $default = null)
|
||||
{
|
||||
return cakeEnv($key, $default);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('triggerWarning')) {
|
||||
/**
|
||||
* Triggers an E_USER_WARNING.
|
||||
*
|
||||
* @param string $message The warning message.
|
||||
* @return void
|
||||
*/
|
||||
function triggerWarning(string $message): void
|
||||
{
|
||||
cakeTriggerWarning($message);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('deprecationWarning')) {
|
||||
/**
|
||||
* Helper method for outputting deprecation warnings
|
||||
*
|
||||
* @param string $message The message to output as a deprecation warning.
|
||||
* @param int $stackFrame The stack frame to include in the error. Defaults to 1
|
||||
* as that should point to application/plugin code.
|
||||
* @return void
|
||||
*/
|
||||
function deprecationWarning(string $message, int $stackFrame = 1): void
|
||||
{
|
||||
cakeDeprecationWarning($message, $stackFrame + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('getTypeName')) {
|
||||
/**
|
||||
* Returns the objects class or var type of it's not an object
|
||||
*
|
||||
* @param mixed $var Variable to check
|
||||
* @return string Returns the class name or variable type
|
||||
*/
|
||||
function getTypeName($var): string
|
||||
{
|
||||
return cakeGetTypeName($var);
|
||||
}
|
||||
}
|
1197
vendor/cakephp/database/Connection.php
vendored
Normal file
1197
vendor/cakephp/database/Connection.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
49
vendor/cakephp/database/ConstraintsInterface.php
vendored
Normal file
49
vendor/cakephp/database/ConstraintsInterface.php
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<?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.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Database;
|
||||
|
||||
use Cake\Datasource\ConnectionInterface;
|
||||
|
||||
/**
|
||||
* Defines the interface for a fixture that needs to manage constraints.
|
||||
*
|
||||
* If an implementation of `Cake\Datasource\FixtureInterface` also implements
|
||||
* this interface, the FixtureManager will use these methods to manage
|
||||
* a fixtures constraints.
|
||||
*/
|
||||
interface ConstraintsInterface
|
||||
{
|
||||
/**
|
||||
* Build and execute SQL queries necessary to create the constraints for the
|
||||
* fixture
|
||||
*
|
||||
* @param \Cake\Datasource\ConnectionInterface $connection An instance of the database
|
||||
* into which the constraints will be created.
|
||||
* @return bool on success or if there are no constraints to create, or false on failure
|
||||
*/
|
||||
public function createConstraints(ConnectionInterface $connection): bool;
|
||||
|
||||
/**
|
||||
* Build and execute SQL queries necessary to drop the constraints for the
|
||||
* fixture
|
||||
*
|
||||
* @param \Cake\Datasource\ConnectionInterface $connection An instance of the database
|
||||
* into which the constraints will be dropped.
|
||||
* @return bool on success or if there are no constraints to drop, or false on failure
|
||||
*/
|
||||
public function dropConstraints(ConnectionInterface $connection): bool;
|
||||
}
|
559
vendor/cakephp/database/Driver.php
vendored
Normal file
559
vendor/cakephp/database/Driver.php
vendored
Normal file
@ -0,0 +1,559 @@
|
||||
<?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\Database;
|
||||
|
||||
use Cake\Core\App;
|
||||
use Cake\Core\Retry\CommandRetry;
|
||||
use Cake\Database\Exception\MissingConnectionException;
|
||||
use Cake\Database\Retry\ErrorCodeWaitStrategy;
|
||||
use Cake\Database\Schema\SchemaDialect;
|
||||
use Cake\Database\Schema\TableSchema;
|
||||
use Cake\Database\Statement\PDOStatement;
|
||||
use Closure;
|
||||
use InvalidArgumentException;
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use function Cake\Core\deprecationWarning;
|
||||
|
||||
/**
|
||||
* Represents a database driver containing all specificities for
|
||||
* a database engine including its SQL dialect.
|
||||
*/
|
||||
abstract class Driver implements DriverInterface
|
||||
{
|
||||
/**
|
||||
* @var int|null Maximum alias length or null if no limit
|
||||
*/
|
||||
protected const MAX_ALIAS_LENGTH = null;
|
||||
|
||||
/**
|
||||
* @var array<int> DB-specific error codes that allow connect retry
|
||||
*/
|
||||
protected const RETRY_ERROR_CODES = [];
|
||||
|
||||
/**
|
||||
* Instance of PDO.
|
||||
*
|
||||
* @var \PDO
|
||||
*/
|
||||
protected $_connection;
|
||||
|
||||
/**
|
||||
* Configuration data.
|
||||
*
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
protected $_config;
|
||||
|
||||
/**
|
||||
* Base configuration that is merged into the user
|
||||
* supplied configuration data.
|
||||
*
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
protected $_baseConfig = [];
|
||||
|
||||
/**
|
||||
* Indicates whether the driver is doing automatic identifier quoting
|
||||
* for all queries
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $_autoQuoting = false;
|
||||
|
||||
/**
|
||||
* The server version
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $_version;
|
||||
|
||||
/**
|
||||
* The last number of connection retry attempts.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $connectRetries = 0;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array<string, mixed> $config The configuration for the driver.
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function __construct(array $config = [])
|
||||
{
|
||||
if (empty($config['username']) && !empty($config['login'])) {
|
||||
throw new InvalidArgumentException(
|
||||
'Please pass "username" instead of "login" for connecting to the database'
|
||||
);
|
||||
}
|
||||
$config += $this->_baseConfig;
|
||||
$this->_config = $config;
|
||||
if (!empty($config['quoteIdentifiers'])) {
|
||||
$this->enableAutoQuoting();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration data used to create the driver.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function config(): array
|
||||
{
|
||||
return $this->_config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes a connection to the database server
|
||||
*
|
||||
* @param string $dsn A Driver-specific PDO-DSN
|
||||
* @param array<string, mixed> $config configuration to be used for creating connection
|
||||
* @return bool true on success
|
||||
*/
|
||||
protected function _connect(string $dsn, array $config): bool
|
||||
{
|
||||
$action = function () use ($dsn, $config) {
|
||||
$this->setConnection(new PDO(
|
||||
$dsn,
|
||||
$config['username'] ?: null,
|
||||
$config['password'] ?: null,
|
||||
$config['flags']
|
||||
));
|
||||
};
|
||||
|
||||
$retry = new CommandRetry(new ErrorCodeWaitStrategy(static::RETRY_ERROR_CODES, 5), 4);
|
||||
try {
|
||||
$retry->run($action);
|
||||
} catch (PDOException $e) {
|
||||
throw new MissingConnectionException(
|
||||
[
|
||||
'driver' => App::shortName(static::class, 'Database/Driver'),
|
||||
'reason' => $e->getMessage(),
|
||||
],
|
||||
null,
|
||||
$e
|
||||
);
|
||||
} finally {
|
||||
$this->connectRetries = $retry->getRetries();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
abstract public function connect(): bool;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function disconnect(): void
|
||||
{
|
||||
/** @psalm-suppress PossiblyNullPropertyAssignmentValue */
|
||||
$this->_connection = null;
|
||||
$this->_version = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns connected server version.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function version(): string
|
||||
{
|
||||
if ($this->_version === null) {
|
||||
$this->connect();
|
||||
$this->_version = (string)$this->_connection->getAttribute(PDO::ATTR_SERVER_VERSION);
|
||||
}
|
||||
|
||||
return $this->_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internal PDO connection instance.
|
||||
*
|
||||
* @return \PDO
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
if ($this->_connection === null) {
|
||||
throw new MissingConnectionException([
|
||||
'driver' => App::shortName(static::class, 'Database/Driver'),
|
||||
'reason' => 'Unknown',
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->_connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the internal PDO connection instance.
|
||||
*
|
||||
* @param \PDO $connection PDO instance.
|
||||
* @return $this
|
||||
* @psalm-suppress MoreSpecificImplementedParamType
|
||||
*/
|
||||
public function setConnection($connection)
|
||||
{
|
||||
$this->_connection = $connection;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
abstract public function enabled(): bool;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function prepare($query): StatementInterface
|
||||
{
|
||||
$this->connect();
|
||||
$statement = $this->_connection->prepare($query instanceof Query ? $query->sql() : $query);
|
||||
|
||||
return new PDOStatement($statement, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function beginTransaction(): bool
|
||||
{
|
||||
$this->connect();
|
||||
if ($this->_connection->inTransaction()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->_connection->beginTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function commitTransaction(): bool
|
||||
{
|
||||
$this->connect();
|
||||
if (!$this->_connection->inTransaction()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->_connection->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function rollbackTransaction(): bool
|
||||
{
|
||||
$this->connect();
|
||||
if (!$this->_connection->inTransaction()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->_connection->rollBack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a transaction is active for connection.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function inTransaction(): bool
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
return $this->_connection->inTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function supportsSavePoints(): bool
|
||||
{
|
||||
deprecationWarning('Feature support checks are now implemented by `supports()` with FEATURE_* constants.');
|
||||
|
||||
return $this->supports(static::FEATURE_SAVEPOINT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the server supports common table expressions.
|
||||
*
|
||||
* @return bool
|
||||
* @deprecated 4.3.0 Use `supports(DriverInterface::FEATURE_QUOTE)` instead
|
||||
*/
|
||||
public function supportsCTEs(): bool
|
||||
{
|
||||
deprecationWarning('Feature support checks are now implemented by `supports()` with FEATURE_* constants.');
|
||||
|
||||
return $this->supports(static::FEATURE_CTE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function quote($value, $type = PDO::PARAM_STR): string
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
return $this->_connection->quote((string)$value, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the driver supports quoting, as PDO_ODBC does not support it.
|
||||
*
|
||||
* @return bool
|
||||
* @deprecated 4.3.0 Use `supports(DriverInterface::FEATURE_QUOTE)` instead
|
||||
*/
|
||||
public function supportsQuoting(): bool
|
||||
{
|
||||
deprecationWarning('Feature support checks are now implemented by `supports()` with FEATURE_* constants.');
|
||||
|
||||
return $this->supports(static::FEATURE_QUOTE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
abstract public function queryTranslator(string $type): Closure;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
abstract public function schemaDialect(): SchemaDialect;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
abstract public function quoteIdentifier(string $identifier): string;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function schemaValue($value): string
|
||||
{
|
||||
if ($value === null) {
|
||||
return 'NULL';
|
||||
}
|
||||
if ($value === false) {
|
||||
return 'FALSE';
|
||||
}
|
||||
if ($value === true) {
|
||||
return 'TRUE';
|
||||
}
|
||||
if (is_float($value)) {
|
||||
return str_replace(',', '.', (string)$value);
|
||||
}
|
||||
/** @psalm-suppress InvalidArgument */
|
||||
if (
|
||||
(
|
||||
is_int($value) ||
|
||||
$value === '0'
|
||||
) ||
|
||||
(
|
||||
is_numeric($value) &&
|
||||
strpos($value, ',') === false &&
|
||||
substr($value, 0, 1) !== '0' &&
|
||||
strpos($value, 'e') === false
|
||||
)
|
||||
) {
|
||||
return (string)$value;
|
||||
}
|
||||
|
||||
return $this->_connection->quote((string)$value, PDO::PARAM_STR);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function schema(): string
|
||||
{
|
||||
return $this->_config['schema'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function lastInsertId(?string $table = null, ?string $column = null)
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
if ($this->_connection instanceof PDO) {
|
||||
return $this->_connection->lastInsertId($table);
|
||||
}
|
||||
|
||||
return $this->_connection->lastInsertId($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function isConnected(): bool
|
||||
{
|
||||
if ($this->_connection === null) {
|
||||
$connected = false;
|
||||
} else {
|
||||
try {
|
||||
$connected = (bool)$this->_connection->query('SELECT 1');
|
||||
} catch (PDOException $e) {
|
||||
$connected = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $connected;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function enableAutoQuoting(bool $enable = true)
|
||||
{
|
||||
$this->_autoQuoting = $enable;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function disableAutoQuoting()
|
||||
{
|
||||
$this->_autoQuoting = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function isAutoQuotingEnabled(): bool
|
||||
{
|
||||
return $this->_autoQuoting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the driver supports the feature.
|
||||
*
|
||||
* Defaults to true for FEATURE_QUOTE and FEATURE_SAVEPOINT.
|
||||
*
|
||||
* @param string $feature Driver feature name
|
||||
* @return bool
|
||||
*/
|
||||
public function supports(string $feature): bool
|
||||
{
|
||||
switch ($feature) {
|
||||
case static::FEATURE_DISABLE_CONSTRAINT_WITHOUT_TRANSACTION:
|
||||
case static::FEATURE_QUOTE:
|
||||
case static::FEATURE_SAVEPOINT:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function compileQuery(Query $query, ValueBinder $binder): array
|
||||
{
|
||||
$processor = $this->newCompiler();
|
||||
$translator = $this->queryTranslator($query->type());
|
||||
$query = $translator($query);
|
||||
|
||||
return [$query, $processor->compile($query, $binder)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function newCompiler(): QueryCompiler
|
||||
{
|
||||
return new QueryCompiler();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function newTableSchema(string $table, array $columns = []): TableSchema
|
||||
{
|
||||
$className = TableSchema::class;
|
||||
if (isset($this->_config['tableSchema'])) {
|
||||
/** @var class-string<\Cake\Database\Schema\TableSchema> $className */
|
||||
$className = $this->_config['tableSchema'];
|
||||
}
|
||||
|
||||
return new $className($table, $columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum alias length allowed.
|
||||
* This can be different from the maximum identifier length for columns.
|
||||
*
|
||||
* @return int|null Maximum alias length or null if no limit
|
||||
*/
|
||||
public function getMaxAliasLength(): ?int
|
||||
{
|
||||
return static::MAX_ALIAS_LENGTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of connection retry attempts made.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getConnectRetries(): int
|
||||
{
|
||||
return $this->connectRetries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the connection role this driver performs.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRole(): string
|
||||
{
|
||||
return $this->_config['_role'] ?? Connection::ROLE_WRITE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
/** @psalm-suppress PossiblyNullPropertyAssignmentValue */
|
||||
$this->_connection = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array that can be used to describe the internal state of this
|
||||
* object.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return [
|
||||
'connected' => $this->_connection !== null,
|
||||
'role' => $this->getRole(),
|
||||
];
|
||||
}
|
||||
}
|
345
vendor/cakephp/database/Driver/Mysql.php
vendored
Normal file
345
vendor/cakephp/database/Driver/Mysql.php
vendored
Normal file
@ -0,0 +1,345 @@
|
||||
<?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\Database\Driver;
|
||||
|
||||
use Cake\Database\Driver;
|
||||
use Cake\Database\Query;
|
||||
use Cake\Database\Schema\MysqlSchemaDialect;
|
||||
use Cake\Database\Schema\SchemaDialect;
|
||||
use Cake\Database\Statement\MysqlStatement;
|
||||
use Cake\Database\StatementInterface;
|
||||
use PDO;
|
||||
use function Cake\Core\deprecationWarning;
|
||||
|
||||
/**
|
||||
* MySQL Driver
|
||||
*/
|
||||
class Mysql extends Driver
|
||||
{
|
||||
use SqlDialectTrait;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected const MAX_ALIAS_LENGTH = 256;
|
||||
|
||||
/**
|
||||
* Server type MySQL
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected const SERVER_TYPE_MYSQL = 'mysql';
|
||||
|
||||
/**
|
||||
* Server type MariaDB
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected const SERVER_TYPE_MARIADB = 'mariadb';
|
||||
|
||||
/**
|
||||
* Base configuration settings for MySQL driver
|
||||
*
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
protected $_baseConfig = [
|
||||
'persistent' => true,
|
||||
'host' => 'localhost',
|
||||
'username' => 'root',
|
||||
'password' => '',
|
||||
'database' => 'cake',
|
||||
'port' => '3306',
|
||||
'flags' => [],
|
||||
'encoding' => 'utf8mb4',
|
||||
'timezone' => null,
|
||||
'init' => [],
|
||||
];
|
||||
|
||||
/**
|
||||
* The schema dialect for this driver
|
||||
*
|
||||
* @var \Cake\Database\Schema\MysqlSchemaDialect|null
|
||||
*/
|
||||
protected $_schemaDialect;
|
||||
|
||||
/**
|
||||
* String used to start a database identifier quoting to make it safe
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_startQuote = '`';
|
||||
|
||||
/**
|
||||
* String used to end a database identifier quoting to make it safe
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_endQuote = '`';
|
||||
|
||||
/**
|
||||
* Server type.
|
||||
*
|
||||
* If the underlying server is MariaDB, its value will get set to `'mariadb'`
|
||||
* after `version()` method is called.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $serverType = self::SERVER_TYPE_MYSQL;
|
||||
|
||||
/**
|
||||
* Mapping of feature to db server version for feature availability checks.
|
||||
*
|
||||
* @var array<string, array<string, string>>
|
||||
*/
|
||||
protected $featureVersions = [
|
||||
'mysql' => [
|
||||
'json' => '5.7.0',
|
||||
'cte' => '8.0.0',
|
||||
'window' => '8.0.0',
|
||||
],
|
||||
'mariadb' => [
|
||||
'json' => '10.2.7',
|
||||
'cte' => '10.2.1',
|
||||
'window' => '10.2.0',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Establishes a connection to the database server
|
||||
*
|
||||
* @return bool true on success
|
||||
*/
|
||||
public function connect(): bool
|
||||
{
|
||||
if ($this->_connection) {
|
||||
return true;
|
||||
}
|
||||
$config = $this->_config;
|
||||
|
||||
if ($config['timezone'] === 'UTC') {
|
||||
$config['timezone'] = '+0:00';
|
||||
}
|
||||
|
||||
if (!empty($config['timezone'])) {
|
||||
$config['init'][] = sprintf("SET time_zone = '%s'", $config['timezone']);
|
||||
}
|
||||
|
||||
$config['flags'] += [
|
||||
PDO::ATTR_PERSISTENT => $config['persistent'],
|
||||
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
];
|
||||
|
||||
if (!empty($config['ssl_key']) && !empty($config['ssl_cert'])) {
|
||||
$config['flags'][PDO::MYSQL_ATTR_SSL_KEY] = $config['ssl_key'];
|
||||
$config['flags'][PDO::MYSQL_ATTR_SSL_CERT] = $config['ssl_cert'];
|
||||
}
|
||||
if (!empty($config['ssl_ca'])) {
|
||||
$config['flags'][PDO::MYSQL_ATTR_SSL_CA] = $config['ssl_ca'];
|
||||
}
|
||||
|
||||
if (empty($config['unix_socket'])) {
|
||||
$dsn = "mysql:host={$config['host']};port={$config['port']};dbname={$config['database']}";
|
||||
} else {
|
||||
$dsn = "mysql:unix_socket={$config['unix_socket']};dbname={$config['database']}";
|
||||
}
|
||||
|
||||
if (!empty($config['encoding'])) {
|
||||
$dsn .= ";charset={$config['encoding']}";
|
||||
}
|
||||
|
||||
$this->_connect($dsn, $config);
|
||||
|
||||
if (!empty($config['init'])) {
|
||||
$connection = $this->getConnection();
|
||||
foreach ((array)$config['init'] as $command) {
|
||||
$connection->exec($command);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether php is able to use this driver for connecting to database
|
||||
*
|
||||
* @return bool true if it is valid to use this driver
|
||||
*/
|
||||
public function enabled(): bool
|
||||
{
|
||||
return in_array('mysql', PDO::getAvailableDrivers(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a sql statement to be executed
|
||||
*
|
||||
* @param \Cake\Database\Query|string $query The query to prepare.
|
||||
* @return \Cake\Database\StatementInterface
|
||||
*/
|
||||
public function prepare($query): StatementInterface
|
||||
{
|
||||
$this->connect();
|
||||
$isObject = $query instanceof Query;
|
||||
/**
|
||||
* @psalm-suppress PossiblyInvalidMethodCall
|
||||
* @psalm-suppress PossiblyInvalidArgument
|
||||
*/
|
||||
$statement = $this->_connection->prepare($isObject ? $query->sql() : $query);
|
||||
$result = new MysqlStatement($statement, $this);
|
||||
/** @psalm-suppress PossiblyInvalidMethodCall */
|
||||
if ($isObject && $query->isBufferedResultsEnabled() === false) {
|
||||
$result->bufferResults(false);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function schemaDialect(): SchemaDialect
|
||||
{
|
||||
if ($this->_schemaDialect === null) {
|
||||
$this->_schemaDialect = new MysqlSchemaDialect($this);
|
||||
}
|
||||
|
||||
return $this->_schemaDialect;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function schema(): string
|
||||
{
|
||||
return $this->_config['database'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function disableForeignKeySQL(): string
|
||||
{
|
||||
return 'SET foreign_key_checks = 0';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function enableForeignKeySQL(): string
|
||||
{
|
||||
return 'SET foreign_key_checks = 1';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function supports(string $feature): bool
|
||||
{
|
||||
switch ($feature) {
|
||||
case static::FEATURE_CTE:
|
||||
case static::FEATURE_JSON:
|
||||
case static::FEATURE_WINDOW:
|
||||
return version_compare(
|
||||
$this->version(),
|
||||
$this->featureVersions[$this->serverType][$feature],
|
||||
'>='
|
||||
);
|
||||
}
|
||||
|
||||
return parent::supports($feature);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function supportsDynamicConstraints(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the connected server is MariaDB.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isMariadb(): bool
|
||||
{
|
||||
$this->version();
|
||||
|
||||
return $this->serverType === static::SERVER_TYPE_MARIADB;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns connected server version.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function version(): string
|
||||
{
|
||||
if ($this->_version === null) {
|
||||
$this->connect();
|
||||
$this->_version = (string)$this->_connection->getAttribute(PDO::ATTR_SERVER_VERSION);
|
||||
|
||||
if (strpos($this->_version, 'MariaDB') !== false) {
|
||||
$this->serverType = static::SERVER_TYPE_MARIADB;
|
||||
preg_match('/^(?:5\.5\.5-)?(\d+\.\d+\.\d+.*-MariaDB[^:]*)/', $this->_version, $matches);
|
||||
$this->_version = $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the server supports common table expressions.
|
||||
*
|
||||
* @return bool
|
||||
* @deprecated 4.3.0 Use `supports(DriverInterface::FEATURE_CTE)` instead
|
||||
*/
|
||||
public function supportsCTEs(): bool
|
||||
{
|
||||
deprecationWarning('Feature support checks are now implemented by `supports()` with FEATURE_* constants.');
|
||||
|
||||
return $this->supports(static::FEATURE_CTE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the server supports native JSON columns
|
||||
*
|
||||
* @return bool
|
||||
* @deprecated 4.3.0 Use `supports(DriverInterface::FEATURE_JSON)` instead
|
||||
*/
|
||||
public function supportsNativeJson(): bool
|
||||
{
|
||||
deprecationWarning('Feature support checks are now implemented by `supports()` with FEATURE_* constants.');
|
||||
|
||||
return $this->supports(static::FEATURE_JSON);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the connected server supports window functions.
|
||||
*
|
||||
* @return bool
|
||||
* @deprecated 4.3.0 Use `supports(DriverInterface::FEATURE_WINDOW)` instead
|
||||
*/
|
||||
public function supportsWindowFunctions(): bool
|
||||
{
|
||||
deprecationWarning('Feature support checks are now implemented by `supports()` with FEATURE_* constants.');
|
||||
|
||||
return $this->supports(static::FEATURE_WINDOW);
|
||||
}
|
||||
}
|
348
vendor/cakephp/database/Driver/Postgres.php
vendored
Normal file
348
vendor/cakephp/database/Driver/Postgres.php
vendored
Normal file
@ -0,0 +1,348 @@
|
||||
<?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\Database\Driver;
|
||||
|
||||
use Cake\Database\Driver;
|
||||
use Cake\Database\Expression\FunctionExpression;
|
||||
use Cake\Database\Expression\IdentifierExpression;
|
||||
use Cake\Database\Expression\StringExpression;
|
||||
use Cake\Database\PostgresCompiler;
|
||||
use Cake\Database\Query;
|
||||
use Cake\Database\QueryCompiler;
|
||||
use Cake\Database\Schema\PostgresSchemaDialect;
|
||||
use Cake\Database\Schema\SchemaDialect;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* Class Postgres
|
||||
*/
|
||||
class Postgres extends Driver
|
||||
{
|
||||
use SqlDialectTrait;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected const MAX_ALIAS_LENGTH = 63;
|
||||
|
||||
/**
|
||||
* Base configuration settings for Postgres driver
|
||||
*
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
protected $_baseConfig = [
|
||||
'persistent' => true,
|
||||
'host' => 'localhost',
|
||||
'username' => 'root',
|
||||
'password' => '',
|
||||
'database' => 'cake',
|
||||
'schema' => 'public',
|
||||
'port' => 5432,
|
||||
'encoding' => 'utf8',
|
||||
'timezone' => null,
|
||||
'flags' => [],
|
||||
'init' => [],
|
||||
];
|
||||
|
||||
/**
|
||||
* The schema dialect class for this driver
|
||||
*
|
||||
* @var \Cake\Database\Schema\PostgresSchemaDialect|null
|
||||
*/
|
||||
protected $_schemaDialect;
|
||||
|
||||
/**
|
||||
* String used to start a database identifier quoting to make it safe
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_startQuote = '"';
|
||||
|
||||
/**
|
||||
* String used to end a database identifier quoting to make it safe
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_endQuote = '"';
|
||||
|
||||
/**
|
||||
* Establishes a connection to the database server
|
||||
*
|
||||
* @return bool true on success
|
||||
*/
|
||||
public function connect(): bool
|
||||
{
|
||||
if ($this->_connection) {
|
||||
return true;
|
||||
}
|
||||
$config = $this->_config;
|
||||
$config['flags'] += [
|
||||
PDO::ATTR_PERSISTENT => $config['persistent'],
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
];
|
||||
if (empty($config['unix_socket'])) {
|
||||
$dsn = "pgsql:host={$config['host']};port={$config['port']};dbname={$config['database']}";
|
||||
} else {
|
||||
$dsn = "pgsql:dbname={$config['database']}";
|
||||
}
|
||||
|
||||
$this->_connect($dsn, $config);
|
||||
$this->_connection = $connection = $this->getConnection();
|
||||
if (!empty($config['encoding'])) {
|
||||
$this->setEncoding($config['encoding']);
|
||||
}
|
||||
|
||||
if (!empty($config['schema'])) {
|
||||
$this->setSchema($config['schema']);
|
||||
}
|
||||
|
||||
if (!empty($config['timezone'])) {
|
||||
$config['init'][] = sprintf('SET timezone = %s', $connection->quote($config['timezone']));
|
||||
}
|
||||
|
||||
foreach ($config['init'] as $command) {
|
||||
$connection->exec($command);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether php is able to use this driver for connecting to database
|
||||
*
|
||||
* @return bool true if it is valid to use this driver
|
||||
*/
|
||||
public function enabled(): bool
|
||||
{
|
||||
return in_array('pgsql', PDO::getAvailableDrivers(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function schemaDialect(): SchemaDialect
|
||||
{
|
||||
if ($this->_schemaDialect === null) {
|
||||
$this->_schemaDialect = new PostgresSchemaDialect($this);
|
||||
}
|
||||
|
||||
return $this->_schemaDialect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets connection encoding
|
||||
*
|
||||
* @param string $encoding The encoding to use.
|
||||
* @return void
|
||||
*/
|
||||
public function setEncoding(string $encoding): void
|
||||
{
|
||||
$this->connect();
|
||||
$this->_connection->exec('SET NAMES ' . $this->_connection->quote($encoding));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets connection default schema, if any relation defined in a query is not fully qualified
|
||||
* postgres will fallback to looking the relation into defined default schema
|
||||
*
|
||||
* @param string $schema The schema names to set `search_path` to.
|
||||
* @return void
|
||||
*/
|
||||
public function setSchema(string $schema): void
|
||||
{
|
||||
$this->connect();
|
||||
$this->_connection->exec('SET search_path TO ' . $this->_connection->quote($schema));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function disableForeignKeySQL(): string
|
||||
{
|
||||
return 'SET CONSTRAINTS ALL DEFERRED';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function enableForeignKeySQL(): string
|
||||
{
|
||||
return 'SET CONSTRAINTS ALL IMMEDIATE';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function supports(string $feature): bool
|
||||
{
|
||||
switch ($feature) {
|
||||
case static::FEATURE_CTE:
|
||||
case static::FEATURE_JSON:
|
||||
case static::FEATURE_TRUNCATE_WITH_CONSTRAINTS:
|
||||
case static::FEATURE_WINDOW:
|
||||
return true;
|
||||
|
||||
case static::FEATURE_DISABLE_CONSTRAINT_WITHOUT_TRANSACTION:
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent::supports($feature);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function supportsDynamicConstraints(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function _transformDistinct(Query $query): Query
|
||||
{
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function _insertQueryTranslator(Query $query): Query
|
||||
{
|
||||
if (!$query->clause('epilog')) {
|
||||
$query->epilog('RETURNING *');
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function _expressionTranslators(): array
|
||||
{
|
||||
return [
|
||||
IdentifierExpression::class => '_transformIdentifierExpression',
|
||||
FunctionExpression::class => '_transformFunctionExpression',
|
||||
StringExpression::class => '_transformStringExpression',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes identifer expression into postgresql format.
|
||||
*
|
||||
* @param \Cake\Database\Expression\IdentifierExpression $expression The expression to tranform.
|
||||
* @return void
|
||||
*/
|
||||
protected function _transformIdentifierExpression(IdentifierExpression $expression): void
|
||||
{
|
||||
$collation = $expression->getCollation();
|
||||
if ($collation) {
|
||||
// use trim() to work around expression being transformed multiple times
|
||||
$expression->setCollation('"' . trim($collation, '"') . '"');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a FunctionExpression and changes it so that it conforms to this
|
||||
* SQL dialect.
|
||||
*
|
||||
* @param \Cake\Database\Expression\FunctionExpression $expression The function expression to convert
|
||||
* to postgres SQL.
|
||||
* @return void
|
||||
*/
|
||||
protected function _transformFunctionExpression(FunctionExpression $expression): void
|
||||
{
|
||||
switch ($expression->getName()) {
|
||||
case 'CONCAT':
|
||||
// CONCAT function is expressed as exp1 || exp2
|
||||
$expression->setName('')->setConjunction(' ||');
|
||||
break;
|
||||
case 'DATEDIFF':
|
||||
$expression
|
||||
->setName('')
|
||||
->setConjunction('-')
|
||||
->iterateParts(function ($p) {
|
||||
if (is_string($p)) {
|
||||
$p = ['value' => [$p => 'literal'], 'type' => null];
|
||||
} else {
|
||||
$p['value'] = [$p['value']];
|
||||
}
|
||||
|
||||
return new FunctionExpression('DATE', $p['value'], [$p['type']]);
|
||||
});
|
||||
break;
|
||||
case 'CURRENT_DATE':
|
||||
$time = new FunctionExpression('LOCALTIMESTAMP', [' 0 ' => 'literal']);
|
||||
$expression->setName('CAST')->setConjunction(' AS ')->add([$time, 'date' => 'literal']);
|
||||
break;
|
||||
case 'CURRENT_TIME':
|
||||
$time = new FunctionExpression('LOCALTIMESTAMP', [' 0 ' => 'literal']);
|
||||
$expression->setName('CAST')->setConjunction(' AS ')->add([$time, 'time' => 'literal']);
|
||||
break;
|
||||
case 'NOW':
|
||||
$expression->setName('LOCALTIMESTAMP')->add([' 0 ' => 'literal']);
|
||||
break;
|
||||
case 'RAND':
|
||||
$expression->setName('RANDOM');
|
||||
break;
|
||||
case 'DATE_ADD':
|
||||
$expression
|
||||
->setName('')
|
||||
->setConjunction(' + INTERVAL')
|
||||
->iterateParts(function ($p, $key) {
|
||||
if ($key === 1) {
|
||||
$p = sprintf("'%s'", $p);
|
||||
}
|
||||
|
||||
return $p;
|
||||
});
|
||||
break;
|
||||
case 'DAYOFWEEK':
|
||||
$expression
|
||||
->setName('EXTRACT')
|
||||
->setConjunction(' ')
|
||||
->add(['DOW FROM' => 'literal'], [], true)
|
||||
->add([') + (1' => 'literal']); // Postgres starts on index 0 but Sunday should be 1
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes string expression into postgresql format.
|
||||
*
|
||||
* @param \Cake\Database\Expression\StringExpression $expression The string expression to tranform.
|
||||
* @return void
|
||||
*/
|
||||
protected function _transformStringExpression(StringExpression $expression): void
|
||||
{
|
||||
// use trim() to work around expression being transformed multiple times
|
||||
$expression->setCollation('"' . trim($expression->getCollation(), '"') . '"');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return \Cake\Database\PostgresCompiler
|
||||
*/
|
||||
public function newCompiler(): QueryCompiler
|
||||
{
|
||||
return new PostgresCompiler();
|
||||
}
|
||||
}
|
315
vendor/cakephp/database/Driver/SqlDialectTrait.php
vendored
Normal file
315
vendor/cakephp/database/Driver/SqlDialectTrait.php
vendored
Normal file
@ -0,0 +1,315 @@
|
||||
<?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\Database\Driver;
|
||||
|
||||
use Cake\Database\Expression\ComparisonExpression;
|
||||
use Cake\Database\Expression\IdentifierExpression;
|
||||
use Cake\Database\IdentifierQuoter;
|
||||
use Cake\Database\Query;
|
||||
use Closure;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Sql dialect trait
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait SqlDialectTrait
|
||||
{
|
||||
/**
|
||||
* Quotes a database identifier (a column name, table name, etc..) to
|
||||
* be used safely in queries without the risk of using reserved words
|
||||
*
|
||||
* @param string $identifier The identifier to quote.
|
||||
* @return string
|
||||
*/
|
||||
public function quoteIdentifier(string $identifier): string
|
||||
{
|
||||
$identifier = trim($identifier);
|
||||
|
||||
if ($identifier === '*' || $identifier === '') {
|
||||
return $identifier;
|
||||
}
|
||||
|
||||
// string
|
||||
if (preg_match('/^[\w-]+$/u', $identifier)) {
|
||||
return $this->_startQuote . $identifier . $this->_endQuote;
|
||||
}
|
||||
|
||||
// string.string
|
||||
if (preg_match('/^[\w-]+\.[^ \*]*$/u', $identifier)) {
|
||||
$items = explode('.', $identifier);
|
||||
|
||||
return $this->_startQuote . implode($this->_endQuote . '.' . $this->_startQuote, $items) . $this->_endQuote;
|
||||
}
|
||||
|
||||
// string.*
|
||||
if (preg_match('/^[\w-]+\.\*$/u', $identifier)) {
|
||||
return $this->_startQuote . str_replace('.*', $this->_endQuote . '.*', $identifier);
|
||||
}
|
||||
|
||||
// Functions
|
||||
if (preg_match('/^([\w-]+)\((.*)\)$/', $identifier, $matches)) {
|
||||
return $matches[1] . '(' . $this->quoteIdentifier($matches[2]) . ')';
|
||||
}
|
||||
|
||||
// Alias.field AS thing
|
||||
if (preg_match('/^([\w-]+(\.[\w\s-]+|\(.*\))*)\s+AS\s*([\w-]+)$/ui', $identifier, $matches)) {
|
||||
return $this->quoteIdentifier($matches[1]) . ' AS ' . $this->quoteIdentifier($matches[3]);
|
||||
}
|
||||
|
||||
// string.string with spaces
|
||||
if (preg_match('/^([\w-]+\.[\w][\w\s-]*[\w])(.*)/u', $identifier, $matches)) {
|
||||
$items = explode('.', $matches[1]);
|
||||
$field = implode($this->_endQuote . '.' . $this->_startQuote, $items);
|
||||
|
||||
return $this->_startQuote . $field . $this->_endQuote . $matches[2];
|
||||
}
|
||||
|
||||
if (preg_match('/^[\w\s-]*[\w-]+/u', $identifier)) {
|
||||
return $this->_startQuote . $identifier . $this->_endQuote;
|
||||
}
|
||||
|
||||
return $identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a callable function that will be used to transform a passed Query object.
|
||||
* This function, in turn, will return an instance of a Query object that has been
|
||||
* transformed to accommodate any specificities of the SQL dialect in use.
|
||||
*
|
||||
* @param string $type the type of query to be transformed
|
||||
* (select, insert, update, delete)
|
||||
* @return \Closure
|
||||
*/
|
||||
public function queryTranslator(string $type): Closure
|
||||
{
|
||||
return function ($query) use ($type) {
|
||||
if ($this->isAutoQuotingEnabled()) {
|
||||
$query = (new IdentifierQuoter($this))->quote($query);
|
||||
}
|
||||
|
||||
/** @var \Cake\ORM\Query $query */
|
||||
$query = $this->{'_' . $type . 'QueryTranslator'}($query);
|
||||
$translators = $this->_expressionTranslators();
|
||||
if (!$translators) {
|
||||
return $query;
|
||||
}
|
||||
|
||||
$query->traverseExpressions(function ($expression) use ($translators, $query): void {
|
||||
foreach ($translators as $class => $method) {
|
||||
if ($expression instanceof $class) {
|
||||
$this->{$method}($expression, $query);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return $query;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an associative array of methods that will transform Expression
|
||||
* objects to conform with the specific SQL dialect. Keys are class names
|
||||
* and values a method in this class.
|
||||
*
|
||||
* @psalm-return array<class-string, string>
|
||||
* @return array<string>
|
||||
*/
|
||||
protected function _expressionTranslators(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply translation steps to select queries.
|
||||
*
|
||||
* @param \Cake\Database\Query $query The query to translate
|
||||
* @return \Cake\Database\Query The modified query
|
||||
*/
|
||||
protected function _selectQueryTranslator(Query $query): Query
|
||||
{
|
||||
return $this->_transformDistinct($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the passed query after rewriting the DISTINCT clause, so that drivers
|
||||
* that do not support the "ON" part can provide the actual way it should be done
|
||||
*
|
||||
* @param \Cake\Database\Query $query The query to be transformed
|
||||
* @return \Cake\Database\Query
|
||||
*/
|
||||
protected function _transformDistinct(Query $query): Query
|
||||
{
|
||||
if (is_array($query->clause('distinct'))) {
|
||||
$query->group($query->clause('distinct'), true);
|
||||
$query->distinct(false);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply translation steps to delete queries.
|
||||
*
|
||||
* Chops out aliases on delete query conditions as most database dialects do not
|
||||
* support aliases in delete queries. This also removes aliases
|
||||
* in table names as they frequently don't work either.
|
||||
*
|
||||
* We are intentionally not supporting deletes with joins as they have even poorer support.
|
||||
*
|
||||
* @param \Cake\Database\Query $query The query to translate
|
||||
* @return \Cake\Database\Query The modified query
|
||||
*/
|
||||
protected function _deleteQueryTranslator(Query $query): Query
|
||||
{
|
||||
$hadAlias = false;
|
||||
$tables = [];
|
||||
foreach ($query->clause('from') as $alias => $table) {
|
||||
if (is_string($alias)) {
|
||||
$hadAlias = true;
|
||||
}
|
||||
$tables[] = $table;
|
||||
}
|
||||
if ($hadAlias) {
|
||||
$query->from($tables, true);
|
||||
}
|
||||
|
||||
if (!$hadAlias) {
|
||||
return $query;
|
||||
}
|
||||
|
||||
return $this->_removeAliasesFromConditions($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply translation steps to update queries.
|
||||
*
|
||||
* Chops out aliases on update query conditions as not all database dialects do support
|
||||
* aliases in update queries.
|
||||
*
|
||||
* Just like for delete queries, joins are currently not supported for update queries.
|
||||
*
|
||||
* @param \Cake\Database\Query $query The query to translate
|
||||
* @return \Cake\Database\Query The modified query
|
||||
*/
|
||||
protected function _updateQueryTranslator(Query $query): Query
|
||||
{
|
||||
return $this->_removeAliasesFromConditions($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes aliases from the `WHERE` clause of a query.
|
||||
*
|
||||
* @param \Cake\Database\Query $query The query to process.
|
||||
* @return \Cake\Database\Query The modified query.
|
||||
* @throws \RuntimeException In case the processed query contains any joins, as removing
|
||||
* aliases from the conditions can break references to the joined tables.
|
||||
*/
|
||||
protected function _removeAliasesFromConditions(Query $query): Query
|
||||
{
|
||||
if ($query->clause('join')) {
|
||||
throw new RuntimeException(
|
||||
'Aliases are being removed from conditions for UPDATE/DELETE queries, ' .
|
||||
'this can break references to joined tables.'
|
||||
);
|
||||
}
|
||||
|
||||
$conditions = $query->clause('where');
|
||||
if ($conditions) {
|
||||
$conditions->traverse(function ($expression) {
|
||||
if ($expression instanceof ComparisonExpression) {
|
||||
$field = $expression->getField();
|
||||
if (
|
||||
is_string($field) &&
|
||||
strpos($field, '.') !== false
|
||||
) {
|
||||
[, $unaliasedField] = explode('.', $field, 2);
|
||||
$expression->setField($unaliasedField);
|
||||
}
|
||||
|
||||
return $expression;
|
||||
}
|
||||
|
||||
if ($expression instanceof IdentifierExpression) {
|
||||
$identifier = $expression->getIdentifier();
|
||||
if (strpos($identifier, '.') !== false) {
|
||||
[, $unaliasedIdentifier] = explode('.', $identifier, 2);
|
||||
$expression->setIdentifier($unaliasedIdentifier);
|
||||
}
|
||||
|
||||
return $expression;
|
||||
}
|
||||
|
||||
return $expression;
|
||||
});
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply translation steps to insert queries.
|
||||
*
|
||||
* @param \Cake\Database\Query $query The query to translate
|
||||
* @return \Cake\Database\Query The modified query
|
||||
*/
|
||||
protected function _insertQueryTranslator(Query $query): Query
|
||||
{
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a SQL snippet for creating a new transaction savepoint
|
||||
*
|
||||
* @param string|int $name save point name
|
||||
* @return string
|
||||
*/
|
||||
public function savePointSQL($name): string
|
||||
{
|
||||
return 'SAVEPOINT LEVEL' . $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a SQL snippet for releasing a previously created save point
|
||||
*
|
||||
* @param string|int $name save point name
|
||||
* @return string
|
||||
*/
|
||||
public function releaseSavePointSQL($name): string
|
||||
{
|
||||
return 'RELEASE SAVEPOINT LEVEL' . $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a SQL snippet for rollbacking a previously created save point
|
||||
*
|
||||
* @param string|int $name save point name
|
||||
* @return string
|
||||
*/
|
||||
public function rollbackSavePointSQL($name): string
|
||||
{
|
||||
return 'ROLLBACK TO SAVEPOINT LEVEL' . $name;
|
||||
}
|
||||
}
|
||||
|
||||
// phpcs:disable
|
||||
class_alias(
|
||||
'Cake\Database\Driver\SqlDialectTrait',
|
||||
'Cake\Database\SqlDialectTrait'
|
||||
);
|
||||
// phpcs:enable
|
385
vendor/cakephp/database/Driver/Sqlite.php
vendored
Normal file
385
vendor/cakephp/database/Driver/Sqlite.php
vendored
Normal file
@ -0,0 +1,385 @@
|
||||
<?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\Database\Driver;
|
||||
|
||||
use Cake\Database\Driver;
|
||||
use Cake\Database\Expression\FunctionExpression;
|
||||
use Cake\Database\Expression\TupleComparison;
|
||||
use Cake\Database\Query;
|
||||
use Cake\Database\QueryCompiler;
|
||||
use Cake\Database\Schema\SchemaDialect;
|
||||
use Cake\Database\Schema\SqliteSchemaDialect;
|
||||
use Cake\Database\SqliteCompiler;
|
||||
use Cake\Database\Statement\PDOStatement;
|
||||
use Cake\Database\Statement\SqliteStatement;
|
||||
use Cake\Database\StatementInterface;
|
||||
use InvalidArgumentException;
|
||||
use PDO;
|
||||
use RuntimeException;
|
||||
use function Cake\Core\deprecationWarning;
|
||||
|
||||
/**
|
||||
* Class Sqlite
|
||||
*/
|
||||
class Sqlite extends Driver
|
||||
{
|
||||
use SqlDialectTrait;
|
||||
use TupleComparisonTranslatorTrait;
|
||||
|
||||
/**
|
||||
* Base configuration settings for Sqlite driver
|
||||
*
|
||||
* - `mask` The mask used for created database
|
||||
*
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
protected $_baseConfig = [
|
||||
'persistent' => false,
|
||||
'username' => null,
|
||||
'password' => null,
|
||||
'database' => ':memory:',
|
||||
'encoding' => 'utf8',
|
||||
'mask' => 0644,
|
||||
'cache' => null,
|
||||
'mode' => null,
|
||||
'flags' => [],
|
||||
'init' => [],
|
||||
];
|
||||
|
||||
/**
|
||||
* The schema dialect class for this driver
|
||||
*
|
||||
* @var \Cake\Database\Schema\SqliteSchemaDialect|null
|
||||
*/
|
||||
protected $_schemaDialect;
|
||||
|
||||
/**
|
||||
* Whether the connected server supports window functions.
|
||||
*
|
||||
* @var bool|null
|
||||
*/
|
||||
protected $_supportsWindowFunctions;
|
||||
|
||||
/**
|
||||
* String used to start a database identifier quoting to make it safe
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_startQuote = '"';
|
||||
|
||||
/**
|
||||
* String used to end a database identifier quoting to make it safe
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_endQuote = '"';
|
||||
|
||||
/**
|
||||
* Mapping of date parts.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $_dateParts = [
|
||||
'day' => 'd',
|
||||
'hour' => 'H',
|
||||
'month' => 'm',
|
||||
'minute' => 'M',
|
||||
'second' => 'S',
|
||||
'week' => 'W',
|
||||
'year' => 'Y',
|
||||
];
|
||||
|
||||
/**
|
||||
* Mapping of feature to db server version for feature availability checks.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $featureVersions = [
|
||||
'cte' => '3.8.3',
|
||||
'window' => '3.28.0',
|
||||
];
|
||||
|
||||
/**
|
||||
* Establishes a connection to the database server
|
||||
*
|
||||
* @return bool true on success
|
||||
*/
|
||||
public function connect(): bool
|
||||
{
|
||||
if ($this->_connection) {
|
||||
return true;
|
||||
}
|
||||
$config = $this->_config;
|
||||
$config['flags'] += [
|
||||
PDO::ATTR_PERSISTENT => $config['persistent'],
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
];
|
||||
if (!is_string($config['database']) || $config['database'] === '') {
|
||||
$name = $config['name'] ?? 'unknown';
|
||||
throw new InvalidArgumentException(
|
||||
"The `database` key for the `{$name}` SQLite connection needs to be a non-empty string."
|
||||
);
|
||||
}
|
||||
|
||||
$chmodFile = false;
|
||||
if ($config['database'] !== ':memory:' && $config['mode'] !== 'memory') {
|
||||
$chmodFile = !file_exists($config['database']);
|
||||
}
|
||||
|
||||
$params = [];
|
||||
if ($config['cache']) {
|
||||
$params[] = 'cache=' . $config['cache'];
|
||||
}
|
||||
if ($config['mode']) {
|
||||
$params[] = 'mode=' . $config['mode'];
|
||||
}
|
||||
|
||||
if ($params) {
|
||||
if (PHP_VERSION_ID < 80100) {
|
||||
throw new RuntimeException('SQLite URI support requires PHP 8.1.');
|
||||
}
|
||||
$dsn = 'sqlite:file:' . $config['database'] . '?' . implode('&', $params);
|
||||
} else {
|
||||
$dsn = 'sqlite:' . $config['database'];
|
||||
}
|
||||
|
||||
$this->_connect($dsn, $config);
|
||||
if ($chmodFile) {
|
||||
// phpcs:disable
|
||||
@chmod($config['database'], $config['mask']);
|
||||
// phpcs:enable
|
||||
}
|
||||
|
||||
if (!empty($config['init'])) {
|
||||
foreach ((array)$config['init'] as $command) {
|
||||
$this->getConnection()->exec($command);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether php is able to use this driver for connecting to database
|
||||
*
|
||||
* @return bool true if it is valid to use this driver
|
||||
*/
|
||||
public function enabled(): bool
|
||||
{
|
||||
return in_array('sqlite', PDO::getAvailableDrivers(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a sql statement to be executed
|
||||
*
|
||||
* @param \Cake\Database\Query|string $query The query to prepare.
|
||||
* @return \Cake\Database\StatementInterface
|
||||
*/
|
||||
public function prepare($query): StatementInterface
|
||||
{
|
||||
$this->connect();
|
||||
$isObject = $query instanceof Query;
|
||||
/**
|
||||
* @psalm-suppress PossiblyInvalidMethodCall
|
||||
* @psalm-suppress PossiblyInvalidArgument
|
||||
*/
|
||||
$statement = $this->_connection->prepare($isObject ? $query->sql() : $query);
|
||||
$result = new SqliteStatement(new PDOStatement($statement, $this), $this);
|
||||
/** @psalm-suppress PossiblyInvalidMethodCall */
|
||||
if ($isObject && $query->isBufferedResultsEnabled() === false) {
|
||||
$result->bufferResults(false);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function disableForeignKeySQL(): string
|
||||
{
|
||||
return 'PRAGMA foreign_keys = OFF';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function enableForeignKeySQL(): string
|
||||
{
|
||||
return 'PRAGMA foreign_keys = ON';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function supports(string $feature): bool
|
||||
{
|
||||
switch ($feature) {
|
||||
case static::FEATURE_CTE:
|
||||
case static::FEATURE_WINDOW:
|
||||
return version_compare(
|
||||
$this->version(),
|
||||
$this->featureVersions[$feature],
|
||||
'>='
|
||||
);
|
||||
|
||||
case static::FEATURE_TRUNCATE_WITH_CONSTRAINTS:
|
||||
return true;
|
||||
}
|
||||
|
||||
return parent::supports($feature);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function supportsDynamicConstraints(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function schemaDialect(): SchemaDialect
|
||||
{
|
||||
if ($this->_schemaDialect === null) {
|
||||
$this->_schemaDialect = new SqliteSchemaDialect($this);
|
||||
}
|
||||
|
||||
return $this->_schemaDialect;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function newCompiler(): QueryCompiler
|
||||
{
|
||||
return new SqliteCompiler();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function _expressionTranslators(): array
|
||||
{
|
||||
return [
|
||||
FunctionExpression::class => '_transformFunctionExpression',
|
||||
TupleComparison::class => '_transformTupleComparison',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a FunctionExpression and changes it so that it conforms to this
|
||||
* SQL dialect.
|
||||
*
|
||||
* @param \Cake\Database\Expression\FunctionExpression $expression The function expression to convert to TSQL.
|
||||
* @return void
|
||||
*/
|
||||
protected function _transformFunctionExpression(FunctionExpression $expression): void
|
||||
{
|
||||
switch ($expression->getName()) {
|
||||
case 'CONCAT':
|
||||
// CONCAT function is expressed as exp1 || exp2
|
||||
$expression->setName('')->setConjunction(' ||');
|
||||
break;
|
||||
case 'DATEDIFF':
|
||||
$expression
|
||||
->setName('ROUND')
|
||||
->setConjunction('-')
|
||||
->iterateParts(function ($p) {
|
||||
return new FunctionExpression('JULIANDAY', [$p['value']], [$p['type']]);
|
||||
});
|
||||
break;
|
||||
case 'NOW':
|
||||
$expression->setName('DATETIME')->add(["'now'" => 'literal']);
|
||||
break;
|
||||
case 'RAND':
|
||||
$expression
|
||||
->setName('ABS')
|
||||
->add(['RANDOM() % 1' => 'literal'], [], true);
|
||||
break;
|
||||
case 'CURRENT_DATE':
|
||||
$expression->setName('DATE')->add(["'now'" => 'literal']);
|
||||
break;
|
||||
case 'CURRENT_TIME':
|
||||
$expression->setName('TIME')->add(["'now'" => 'literal']);
|
||||
break;
|
||||
case 'EXTRACT':
|
||||
$expression
|
||||
->setName('STRFTIME')
|
||||
->setConjunction(' ,')
|
||||
->iterateParts(function ($p, $key) {
|
||||
if ($key === 0) {
|
||||
$value = rtrim(strtolower($p), 's');
|
||||
if (isset($this->_dateParts[$value])) {
|
||||
$p = ['value' => '%' . $this->_dateParts[$value], 'type' => null];
|
||||
}
|
||||
}
|
||||
|
||||
return $p;
|
||||
});
|
||||
break;
|
||||
case 'DATE_ADD':
|
||||
$expression
|
||||
->setName('DATE')
|
||||
->setConjunction(',')
|
||||
->iterateParts(function ($p, $key) {
|
||||
if ($key === 1) {
|
||||
$p = ['value' => $p, 'type' => null];
|
||||
}
|
||||
|
||||
return $p;
|
||||
});
|
||||
break;
|
||||
case 'DAYOFWEEK':
|
||||
$expression
|
||||
->setName('STRFTIME')
|
||||
->setConjunction(' ')
|
||||
->add(["'%w', " => 'literal'], [], true)
|
||||
->add([') + (1' => 'literal']); // Sqlite starts on index 0 but Sunday should be 1
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the server supports common table expressions.
|
||||
*
|
||||
* @return bool
|
||||
* @deprecated 4.3.0 Use `supports(DriverInterface::FEATURE_CTE)` instead
|
||||
*/
|
||||
public function supportsCTEs(): bool
|
||||
{
|
||||
deprecationWarning('Feature support checks are now implemented by `supports()` with FEATURE_* constants.');
|
||||
|
||||
return $this->supports(static::FEATURE_CTE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the connected server supports window functions.
|
||||
*
|
||||
* @return bool
|
||||
* @deprecated 4.3.0 Use `supports(DriverInterface::FEATURE_WINDOW)` instead
|
||||
*/
|
||||
public function supportsWindowFunctions(): bool
|
||||
{
|
||||
deprecationWarning('Feature support checks are now implemented by `supports()` with FEATURE_* constants.');
|
||||
|
||||
return $this->supports(static::FEATURE_WINDOW);
|
||||
}
|
||||
}
|
569
vendor/cakephp/database/Driver/Sqlserver.php
vendored
Normal file
569
vendor/cakephp/database/Driver/Sqlserver.php
vendored
Normal file
@ -0,0 +1,569 @@
|
||||
<?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\Database\Driver;
|
||||
|
||||
use Cake\Database\Driver;
|
||||
use Cake\Database\Expression\FunctionExpression;
|
||||
use Cake\Database\Expression\OrderByExpression;
|
||||
use Cake\Database\Expression\OrderClauseExpression;
|
||||
use Cake\Database\Expression\TupleComparison;
|
||||
use Cake\Database\Expression\UnaryExpression;
|
||||
use Cake\Database\ExpressionInterface;
|
||||
use Cake\Database\Query;
|
||||
use Cake\Database\QueryCompiler;
|
||||
use Cake\Database\Schema\SchemaDialect;
|
||||
use Cake\Database\Schema\SqlserverSchemaDialect;
|
||||
use Cake\Database\SqlserverCompiler;
|
||||
use Cake\Database\Statement\SqlserverStatement;
|
||||
use Cake\Database\StatementInterface;
|
||||
use InvalidArgumentException;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* SQLServer driver.
|
||||
*/
|
||||
class Sqlserver extends Driver
|
||||
{
|
||||
use SqlDialectTrait;
|
||||
use TupleComparisonTranslatorTrait;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected const MAX_ALIAS_LENGTH = 128;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected const RETRY_ERROR_CODES = [
|
||||
40613, // Azure Sql Database paused
|
||||
];
|
||||
|
||||
/**
|
||||
* Base configuration settings for Sqlserver driver
|
||||
*
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
protected $_baseConfig = [
|
||||
'host' => 'localhost\SQLEXPRESS',
|
||||
'username' => '',
|
||||
'password' => '',
|
||||
'database' => 'cake',
|
||||
'port' => '',
|
||||
// PDO::SQLSRV_ENCODING_UTF8
|
||||
'encoding' => 65001,
|
||||
'flags' => [],
|
||||
'init' => [],
|
||||
'settings' => [],
|
||||
'attributes' => [],
|
||||
'app' => null,
|
||||
'connectionPooling' => null,
|
||||
'failoverPartner' => null,
|
||||
'loginTimeout' => null,
|
||||
'multiSubnetFailover' => null,
|
||||
'encrypt' => null,
|
||||
'trustServerCertificate' => null,
|
||||
];
|
||||
|
||||
/**
|
||||
* The schema dialect class for this driver
|
||||
*
|
||||
* @var \Cake\Database\Schema\SqlserverSchemaDialect|null
|
||||
*/
|
||||
protected $_schemaDialect;
|
||||
|
||||
/**
|
||||
* String used to start a database identifier quoting to make it safe
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_startQuote = '[';
|
||||
|
||||
/**
|
||||
* String used to end a database identifier quoting to make it safe
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_endQuote = ']';
|
||||
|
||||
/**
|
||||
* Establishes a connection to the database server.
|
||||
*
|
||||
* Please note that the PDO::ATTR_PERSISTENT attribute is not supported by
|
||||
* the SQL Server PHP PDO drivers. As a result you cannot use the
|
||||
* persistent config option when connecting to a SQL Server (for more
|
||||
* information see: https://github.com/Microsoft/msphpsql/issues/65).
|
||||
*
|
||||
* @throws \InvalidArgumentException if an unsupported setting is in the driver config
|
||||
* @return bool true on success
|
||||
*/
|
||||
public function connect(): bool
|
||||
{
|
||||
if ($this->_connection) {
|
||||
return true;
|
||||
}
|
||||
$config = $this->_config;
|
||||
|
||||
if (isset($config['persistent']) && $config['persistent']) {
|
||||
throw new InvalidArgumentException(
|
||||
'Config setting "persistent" cannot be set to true, '
|
||||
. 'as the Sqlserver PDO driver does not support PDO::ATTR_PERSISTENT'
|
||||
);
|
||||
}
|
||||
|
||||
$config['flags'] += [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
];
|
||||
|
||||
if (!empty($config['encoding'])) {
|
||||
$config['flags'][PDO::SQLSRV_ATTR_ENCODING] = $config['encoding'];
|
||||
}
|
||||
$port = '';
|
||||
if ($config['port']) {
|
||||
$port = ',' . $config['port'];
|
||||
}
|
||||
|
||||
$dsn = "sqlsrv:Server={$config['host']}{$port};Database={$config['database']};MultipleActiveResultSets=false";
|
||||
if ($config['app'] !== null) {
|
||||
$dsn .= ";APP={$config['app']}";
|
||||
}
|
||||
if ($config['connectionPooling'] !== null) {
|
||||
$dsn .= ";ConnectionPooling={$config['connectionPooling']}";
|
||||
}
|
||||
if ($config['failoverPartner'] !== null) {
|
||||
$dsn .= ";Failover_Partner={$config['failoverPartner']}";
|
||||
}
|
||||
if ($config['loginTimeout'] !== null) {
|
||||
$dsn .= ";LoginTimeout={$config['loginTimeout']}";
|
||||
}
|
||||
if ($config['multiSubnetFailover'] !== null) {
|
||||
$dsn .= ";MultiSubnetFailover={$config['multiSubnetFailover']}";
|
||||
}
|
||||
if ($config['encrypt'] !== null) {
|
||||
$dsn .= ";Encrypt={$config['encrypt']}";
|
||||
}
|
||||
if ($config['trustServerCertificate'] !== null) {
|
||||
$dsn .= ";TrustServerCertificate={$config['trustServerCertificate']}";
|
||||
}
|
||||
$this->_connect($dsn, $config);
|
||||
|
||||
$connection = $this->getConnection();
|
||||
if (!empty($config['init'])) {
|
||||
foreach ((array)$config['init'] as $command) {
|
||||
$connection->exec($command);
|
||||
}
|
||||
}
|
||||
if (!empty($config['settings']) && is_array($config['settings'])) {
|
||||
foreach ($config['settings'] as $key => $value) {
|
||||
$connection->exec("SET {$key} {$value}");
|
||||
}
|
||||
}
|
||||
if (!empty($config['attributes']) && is_array($config['attributes'])) {
|
||||
foreach ($config['attributes'] as $key => $value) {
|
||||
$connection->setAttribute($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether PHP is able to use this driver for connecting to database
|
||||
*
|
||||
* @return bool true if it is valid to use this driver
|
||||
*/
|
||||
public function enabled(): bool
|
||||
{
|
||||
return in_array('sqlsrv', PDO::getAvailableDrivers(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a sql statement to be executed
|
||||
*
|
||||
* @param \Cake\Database\Query|string $query The query to prepare.
|
||||
* @return \Cake\Database\StatementInterface
|
||||
*/
|
||||
public function prepare($query): StatementInterface
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
$sql = $query;
|
||||
$options = [
|
||||
PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL,
|
||||
PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE => PDO::SQLSRV_CURSOR_BUFFERED,
|
||||
];
|
||||
if ($query instanceof Query) {
|
||||
$sql = $query->sql();
|
||||
if (count($query->getValueBinder()->bindings()) > 2100) {
|
||||
throw new InvalidArgumentException(
|
||||
'Exceeded maximum number of parameters (2100) for prepared statements in Sql Server. ' .
|
||||
'This is probably due to a very large WHERE IN () clause which generates a parameter ' .
|
||||
'for each value in the array. ' .
|
||||
'If using an Association, try changing the `strategy` from select to subquery.'
|
||||
);
|
||||
}
|
||||
|
||||
if (!$query->isBufferedResultsEnabled()) {
|
||||
$options = [];
|
||||
}
|
||||
}
|
||||
|
||||
/** @psalm-suppress PossiblyInvalidArgument */
|
||||
$statement = $this->_connection->prepare($sql, $options);
|
||||
|
||||
return new SqlserverStatement($statement, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function savePointSQL($name): string
|
||||
{
|
||||
return 'SAVE TRANSACTION t' . $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function releaseSavePointSQL($name): string
|
||||
{
|
||||
// SQLServer has no release save point operation.
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function rollbackSavePointSQL($name): string
|
||||
{
|
||||
return 'ROLLBACK TRANSACTION t' . $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function disableForeignKeySQL(): string
|
||||
{
|
||||
return 'EXEC sp_MSforeachtable "ALTER TABLE ? NOCHECK CONSTRAINT all"';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function enableForeignKeySQL(): string
|
||||
{
|
||||
return 'EXEC sp_MSforeachtable "ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function supports(string $feature): bool
|
||||
{
|
||||
switch ($feature) {
|
||||
case static::FEATURE_CTE:
|
||||
case static::FEATURE_TRUNCATE_WITH_CONSTRAINTS:
|
||||
case static::FEATURE_WINDOW:
|
||||
return true;
|
||||
|
||||
case static::FEATURE_QUOTE:
|
||||
$this->connect();
|
||||
|
||||
return $this->_connection->getAttribute(PDO::ATTR_DRIVER_NAME) !== 'odbc';
|
||||
}
|
||||
|
||||
return parent::supports($feature);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function supportsDynamicConstraints(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function schemaDialect(): SchemaDialect
|
||||
{
|
||||
if ($this->_schemaDialect === null) {
|
||||
$this->_schemaDialect = new SqlserverSchemaDialect($this);
|
||||
}
|
||||
|
||||
return $this->_schemaDialect;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return \Cake\Database\SqlserverCompiler
|
||||
*/
|
||||
public function newCompiler(): QueryCompiler
|
||||
{
|
||||
return new SqlserverCompiler();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function _selectQueryTranslator(Query $query): Query
|
||||
{
|
||||
$limit = $query->clause('limit');
|
||||
$offset = $query->clause('offset');
|
||||
|
||||
if ($limit && $offset === null) {
|
||||
$query->modifier(['_auto_top_' => sprintf('TOP %d', $limit)]);
|
||||
}
|
||||
|
||||
if ($offset !== null && !$query->clause('order')) {
|
||||
$query->order($query->newExpr()->add('(SELECT NULL)'));
|
||||
}
|
||||
|
||||
if ($this->version() < 11 && $offset !== null) {
|
||||
return $this->_pagingSubquery($query, $limit, $offset);
|
||||
}
|
||||
|
||||
return $this->_transformDistinct($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a paging subquery for older versions of SQLserver.
|
||||
*
|
||||
* Prior to SQLServer 2012 there was no equivalent to LIMIT OFFSET, so a subquery must
|
||||
* be used.
|
||||
*
|
||||
* @param \Cake\Database\Query $original The query to wrap in a subquery.
|
||||
* @param int|null $limit The number of rows to fetch.
|
||||
* @param int|null $offset The number of rows to offset.
|
||||
* @return \Cake\Database\Query Modified query object.
|
||||
*/
|
||||
protected function _pagingSubquery(Query $original, ?int $limit, ?int $offset): Query
|
||||
{
|
||||
$field = '_cake_paging_._cake_page_rownum_';
|
||||
|
||||
if ($original->clause('order')) {
|
||||
// SQL server does not support column aliases in OVER clauses. But
|
||||
// the only practical way to specify the use of calculated columns
|
||||
// is with their alias. So substitute the select SQL in place of
|
||||
// any column aliases for those entries in the order clause.
|
||||
$select = $original->clause('select');
|
||||
$order = new OrderByExpression();
|
||||
$original
|
||||
->clause('order')
|
||||
->iterateParts(function ($direction, $orderBy) use ($select, $order) {
|
||||
$key = $orderBy;
|
||||
if (
|
||||
isset($select[$orderBy]) &&
|
||||
$select[$orderBy] instanceof ExpressionInterface
|
||||
) {
|
||||
$order->add(new OrderClauseExpression($select[$orderBy], $direction));
|
||||
} else {
|
||||
$order->add([$key => $direction]);
|
||||
}
|
||||
|
||||
// Leave original order clause unchanged.
|
||||
return $orderBy;
|
||||
});
|
||||
} else {
|
||||
$order = new OrderByExpression('(SELECT NULL)');
|
||||
}
|
||||
|
||||
$query = clone $original;
|
||||
$query->select([
|
||||
'_cake_page_rownum_' => new UnaryExpression('ROW_NUMBER() OVER', $order),
|
||||
])->limit(null)
|
||||
->offset(null)
|
||||
->order([], true);
|
||||
|
||||
$outer = new Query($query->getConnection());
|
||||
$outer->select('*')
|
||||
->from(['_cake_paging_' => $query]);
|
||||
|
||||
if ($offset) {
|
||||
$outer->where(["$field > " . $offset]);
|
||||
}
|
||||
if ($limit) {
|
||||
$value = (int)$offset + $limit;
|
||||
$outer->where(["$field <= $value"]);
|
||||
}
|
||||
|
||||
// Decorate the original query as that is what the
|
||||
// end developer will be calling execute() on originally.
|
||||
$original->decorateResults(function ($row) {
|
||||
if (isset($row['_cake_page_rownum_'])) {
|
||||
unset($row['_cake_page_rownum_']);
|
||||
}
|
||||
|
||||
return $row;
|
||||
});
|
||||
|
||||
return $outer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function _transformDistinct(Query $query): Query
|
||||
{
|
||||
if (!is_array($query->clause('distinct'))) {
|
||||
return $query;
|
||||
}
|
||||
|
||||
$original = $query;
|
||||
$query = clone $original;
|
||||
|
||||
$distinct = $query->clause('distinct');
|
||||
$query->distinct(false);
|
||||
|
||||
$order = new OrderByExpression($distinct);
|
||||
$query
|
||||
->select(function ($q) use ($distinct, $order) {
|
||||
$over = $q->newExpr('ROW_NUMBER() OVER')
|
||||
->add('(PARTITION BY')
|
||||
->add($q->newExpr()->add($distinct)->setConjunction(','))
|
||||
->add($order)
|
||||
->add(')')
|
||||
->setConjunction(' ');
|
||||
|
||||
return [
|
||||
'_cake_distinct_pivot_' => $over,
|
||||
];
|
||||
})
|
||||
->limit(null)
|
||||
->offset(null)
|
||||
->order([], true);
|
||||
|
||||
$outer = new Query($query->getConnection());
|
||||
$outer->select('*')
|
||||
->from(['_cake_distinct_' => $query])
|
||||
->where(['_cake_distinct_pivot_' => 1]);
|
||||
|
||||
// Decorate the original query as that is what the
|
||||
// end developer will be calling execute() on originally.
|
||||
$original->decorateResults(function ($row) {
|
||||
if (isset($row['_cake_distinct_pivot_'])) {
|
||||
unset($row['_cake_distinct_pivot_']);
|
||||
}
|
||||
|
||||
return $row;
|
||||
});
|
||||
|
||||
return $outer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function _expressionTranslators(): array
|
||||
{
|
||||
return [
|
||||
FunctionExpression::class => '_transformFunctionExpression',
|
||||
TupleComparison::class => '_transformTupleComparison',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a FunctionExpression and changes it so that it conforms to this
|
||||
* SQL dialect.
|
||||
*
|
||||
* @param \Cake\Database\Expression\FunctionExpression $expression The function expression to convert to TSQL.
|
||||
* @return void
|
||||
*/
|
||||
protected function _transformFunctionExpression(FunctionExpression $expression): void
|
||||
{
|
||||
switch ($expression->getName()) {
|
||||
case 'CONCAT':
|
||||
// CONCAT function is expressed as exp1 + exp2
|
||||
$expression->setName('')->setConjunction(' +');
|
||||
break;
|
||||
case 'DATEDIFF':
|
||||
/** @var bool $hasDay */
|
||||
$hasDay = false;
|
||||
$visitor = function ($value) use (&$hasDay) {
|
||||
if ($value === 'day') {
|
||||
$hasDay = true;
|
||||
}
|
||||
|
||||
return $value;
|
||||
};
|
||||
$expression->iterateParts($visitor);
|
||||
|
||||
if (!$hasDay) {
|
||||
$expression->add(['day' => 'literal'], [], true);
|
||||
}
|
||||
break;
|
||||
case 'CURRENT_DATE':
|
||||
$time = new FunctionExpression('GETUTCDATE');
|
||||
$expression->setName('CONVERT')->add(['date' => 'literal', $time]);
|
||||
break;
|
||||
case 'CURRENT_TIME':
|
||||
$time = new FunctionExpression('GETUTCDATE');
|
||||
$expression->setName('CONVERT')->add(['time' => 'literal', $time]);
|
||||
break;
|
||||
case 'NOW':
|
||||
$expression->setName('GETUTCDATE');
|
||||
break;
|
||||
case 'EXTRACT':
|
||||
$expression->setName('DATEPART')->setConjunction(' ,');
|
||||
break;
|
||||
case 'DATE_ADD':
|
||||
$params = [];
|
||||
$visitor = function ($p, $key) use (&$params) {
|
||||
if ($key === 0) {
|
||||
$params[2] = $p;
|
||||
} else {
|
||||
$valueUnit = explode(' ', $p);
|
||||
$params[0] = rtrim($valueUnit[1], 's');
|
||||
$params[1] = $valueUnit[0];
|
||||
}
|
||||
|
||||
return $p;
|
||||
};
|
||||
$manipulator = function ($p, $key) use (&$params) {
|
||||
return $params[$key];
|
||||
};
|
||||
|
||||
$expression
|
||||
->setName('DATEADD')
|
||||
->setConjunction(',')
|
||||
->iterateParts($visitor)
|
||||
->iterateParts($manipulator)
|
||||
->add([$params[2] => 'literal']);
|
||||
break;
|
||||
case 'DAYOFWEEK':
|
||||
$expression
|
||||
->setName('DATEPART')
|
||||
->setConjunction(' ')
|
||||
->add(['weekday, ' => 'literal'], [], true);
|
||||
break;
|
||||
case 'SUBSTR':
|
||||
$expression->setName('SUBSTRING');
|
||||
if (count($expression) < 4) {
|
||||
$params = [];
|
||||
$expression
|
||||
->iterateParts(function ($p) use (&$params) {
|
||||
return $params[] = $p;
|
||||
})
|
||||
->add([new FunctionExpression('LEN', [$params[0]]), ['string']]);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
113
vendor/cakephp/database/Driver/TupleComparisonTranslatorTrait.php
vendored
Normal file
113
vendor/cakephp/database/Driver/TupleComparisonTranslatorTrait.php
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
<?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\Database\Driver;
|
||||
|
||||
use Cake\Database\Expression\IdentifierExpression;
|
||||
use Cake\Database\Expression\QueryExpression;
|
||||
use Cake\Database\Expression\TupleComparison;
|
||||
use Cake\Database\Query;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Provides a translator method for tuple comparisons
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait TupleComparisonTranslatorTrait
|
||||
{
|
||||
/**
|
||||
* Receives a TupleExpression and changes it so that it conforms to this
|
||||
* SQL dialect.
|
||||
*
|
||||
* It transforms expressions looking like '(a, b) IN ((c, d), (e, f))' into an
|
||||
* equivalent expression of the form '((a = c) AND (b = d)) OR ((a = e) AND (b = f))'.
|
||||
*
|
||||
* It can also transform transform expressions where the right hand side is a query
|
||||
* selecting the same amount of columns as the elements in the left hand side of
|
||||
* the expression:
|
||||
*
|
||||
* (a, b) IN (SELECT c, d FROM a_table) is transformed into
|
||||
*
|
||||
* 1 = (SELECT 1 FROM a_table WHERE (a = c) AND (b = d))
|
||||
*
|
||||
* @param \Cake\Database\Expression\TupleComparison $expression The expression to transform
|
||||
* @param \Cake\Database\Query $query The query to update.
|
||||
* @return void
|
||||
*/
|
||||
protected function _transformTupleComparison(TupleComparison $expression, Query $query): void
|
||||
{
|
||||
$fields = $expression->getField();
|
||||
|
||||
if (!is_array($fields)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$operator = strtoupper($expression->getOperator());
|
||||
if (!in_array($operator, ['IN', '='])) {
|
||||
throw new RuntimeException(
|
||||
sprintf(
|
||||
'Tuple comparison transform only supports the `IN` and `=` operators, `%s` given.',
|
||||
$operator
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$value = $expression->getValue();
|
||||
$true = new QueryExpression('1');
|
||||
|
||||
if ($value instanceof Query) {
|
||||
$selected = array_values($value->clause('select'));
|
||||
foreach ($fields as $i => $field) {
|
||||
$value->andWhere([$field => new IdentifierExpression($selected[$i])]);
|
||||
}
|
||||
$value->select($true, true);
|
||||
$expression->setField($true);
|
||||
$expression->setOperator('=');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$type = $expression->getType();
|
||||
if ($type) {
|
||||
/** @var array<string, string> $typeMap */
|
||||
$typeMap = array_combine($fields, $type) ?: [];
|
||||
} else {
|
||||
$typeMap = [];
|
||||
}
|
||||
|
||||
$surrogate = $query->getConnection()
|
||||
->selectQuery($true);
|
||||
|
||||
if (!is_array(current($value))) {
|
||||
$value = [$value];
|
||||
}
|
||||
|
||||
$conditions = ['OR' => []];
|
||||
foreach ($value as $tuple) {
|
||||
$item = [];
|
||||
foreach (array_values($tuple) as $i => $value2) {
|
||||
$item[] = [$fields[$i] => $value2];
|
||||
}
|
||||
$conditions['OR'][] = $item;
|
||||
}
|
||||
$surrogate->where($conditions, $typeMap);
|
||||
|
||||
$expression->setField($true);
|
||||
$expression->setValue($surrogate);
|
||||
$expression->setOperator('=');
|
||||
}
|
||||
}
|
336
vendor/cakephp/database/DriverInterface.php
vendored
Normal file
336
vendor/cakephp/database/DriverInterface.php
vendored
Normal file
@ -0,0 +1,336 @@
|
||||
<?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\Database;
|
||||
|
||||
use Cake\Database\Schema\SchemaDialect;
|
||||
use Cake\Database\Schema\TableSchema;
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* Interface for database driver.
|
||||
*
|
||||
* @method int|null getMaxAliasLength() Returns the maximum alias length allowed.
|
||||
* @method int getConnectRetries() Returns the number of connection retry attempts made.
|
||||
* @method bool supports(string $feature) Checks whether a feature is supported by the driver.
|
||||
* @method bool inTransaction() Returns whether a transaction is active.
|
||||
* @method array config() Get the configuration data used to create the driver.
|
||||
* @method string getRole() Returns the connection role this driver prforms.
|
||||
*/
|
||||
interface DriverInterface
|
||||
{
|
||||
/**
|
||||
* Common Table Expressions (with clause) support.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const FEATURE_CTE = 'cte';
|
||||
|
||||
/**
|
||||
* Disabling constraints without being in transaction support.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const FEATURE_DISABLE_CONSTRAINT_WITHOUT_TRANSACTION = 'disable-constraint-without-transaction';
|
||||
|
||||
/**
|
||||
* Native JSON data type support.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const FEATURE_JSON = 'json';
|
||||
|
||||
/**
|
||||
* PDO::quote() support.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const FEATURE_QUOTE = 'quote';
|
||||
|
||||
/**
|
||||
* Transaction savepoint support.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const FEATURE_SAVEPOINT = 'savepoint';
|
||||
|
||||
/**
|
||||
* Truncate with foreign keys attached support.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const FEATURE_TRUNCATE_WITH_CONSTRAINTS = 'truncate-with-constraints';
|
||||
|
||||
/**
|
||||
* Window function support (all or partial clauses).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const FEATURE_WINDOW = 'window';
|
||||
|
||||
/**
|
||||
* Establishes a connection to the database server.
|
||||
*
|
||||
* @throws \Cake\Database\Exception\MissingConnectionException If database connection could not be established.
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
public function connect(): bool;
|
||||
|
||||
/**
|
||||
* Disconnects from database server.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function disconnect(): void;
|
||||
|
||||
/**
|
||||
* Returns correct connection resource or object that is internally used.
|
||||
*
|
||||
* @return object Connection object used internally.
|
||||
*/
|
||||
public function getConnection();
|
||||
|
||||
/**
|
||||
* Set the internal connection object.
|
||||
*
|
||||
* @param object $connection The connection instance.
|
||||
* @return $this
|
||||
*/
|
||||
public function setConnection($connection);
|
||||
|
||||
/**
|
||||
* Returns whether php is able to use this driver for connecting to database.
|
||||
*
|
||||
* @return bool True if it is valid to use this driver.
|
||||
*/
|
||||
public function enabled(): bool;
|
||||
|
||||
/**
|
||||
* Prepares a sql statement to be executed.
|
||||
*
|
||||
* @param \Cake\Database\Query|string $query The query to turn into a prepared statement.
|
||||
* @return \Cake\Database\StatementInterface
|
||||
*/
|
||||
public function prepare($query): StatementInterface;
|
||||
|
||||
/**
|
||||
* Starts a transaction.
|
||||
*
|
||||
* @return bool True on success, false otherwise.
|
||||
*/
|
||||
public function beginTransaction(): bool;
|
||||
|
||||
/**
|
||||
* Commits a transaction.
|
||||
*
|
||||
* @return bool True on success, false otherwise.
|
||||
*/
|
||||
public function commitTransaction(): bool;
|
||||
|
||||
/**
|
||||
* Rollbacks a transaction.
|
||||
*
|
||||
* @return bool True on success, false otherwise.
|
||||
*/
|
||||
public function rollbackTransaction(): bool;
|
||||
|
||||
/**
|
||||
* Get the SQL for releasing a save point.
|
||||
*
|
||||
* @param string|int $name Save point name or id
|
||||
* @return string
|
||||
*/
|
||||
public function releaseSavePointSQL($name): string;
|
||||
|
||||
/**
|
||||
* Get the SQL for creating a save point.
|
||||
*
|
||||
* @param string|int $name Save point name or id
|
||||
* @return string
|
||||
*/
|
||||
public function savePointSQL($name): string;
|
||||
|
||||
/**
|
||||
* Get the SQL for rollingback a save point.
|
||||
*
|
||||
* @param string|int $name Save point name or id
|
||||
* @return string
|
||||
*/
|
||||
public function rollbackSavePointSQL($name): string;
|
||||
|
||||
/**
|
||||
* Get the SQL for disabling foreign keys.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function disableForeignKeySQL(): string;
|
||||
|
||||
/**
|
||||
* Get the SQL for enabling foreign keys.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function enableForeignKeySQL(): string;
|
||||
|
||||
/**
|
||||
* Returns whether the driver supports adding or dropping constraints
|
||||
* to already created tables.
|
||||
*
|
||||
* @return bool True if driver supports dynamic constraints.
|
||||
* @deprecated 4.3.0 Fixtures no longer dynamically drop and create constraints.
|
||||
*/
|
||||
public function supportsDynamicConstraints(): bool;
|
||||
|
||||
/**
|
||||
* Returns whether this driver supports save points for nested transactions.
|
||||
*
|
||||
* @return bool True if save points are supported, false otherwise.
|
||||
* @deprecated 4.3.0 Use `supports(DriverInterface::FEATURE_SAVEPOINT)` instead
|
||||
*/
|
||||
public function supportsSavePoints(): bool;
|
||||
|
||||
/**
|
||||
* Returns a value in a safe representation to be used in a query string
|
||||
*
|
||||
* @param mixed $value The value to quote.
|
||||
* @param int $type Must be one of the \PDO::PARAM_* constants
|
||||
* @return string
|
||||
*/
|
||||
public function quote($value, $type): string;
|
||||
|
||||
/**
|
||||
* Checks if the driver supports quoting.
|
||||
*
|
||||
* @return bool
|
||||
* @deprecated 4.3.0 Use `supports(DriverInterface::FEATURE_QUOTE)` instead
|
||||
*/
|
||||
public function supportsQuoting(): bool;
|
||||
|
||||
/**
|
||||
* Returns a callable function that will be used to transform a passed Query object.
|
||||
* This function, in turn, will return an instance of a Query object that has been
|
||||
* transformed to accommodate any specificities of the SQL dialect in use.
|
||||
*
|
||||
* @param string $type The type of query to be transformed
|
||||
* (select, insert, update, delete).
|
||||
* @return \Closure
|
||||
*/
|
||||
public function queryTranslator(string $type): Closure;
|
||||
|
||||
/**
|
||||
* Get the schema dialect.
|
||||
*
|
||||
* Used by {@link \Cake\Database\Schema} package to reflect schema and
|
||||
* generate schema.
|
||||
*
|
||||
* If all the tables that use this Driver specify their
|
||||
* own schemas, then this may return null.
|
||||
*
|
||||
* @return \Cake\Database\Schema\SchemaDialect
|
||||
*/
|
||||
public function schemaDialect(): SchemaDialect;
|
||||
|
||||
/**
|
||||
* Quotes a database identifier (a column name, table name, etc..) to
|
||||
* be used safely in queries without the risk of using reserved words.
|
||||
*
|
||||
* @param string $identifier The identifier expression to quote.
|
||||
* @return string
|
||||
*/
|
||||
public function quoteIdentifier(string $identifier): string;
|
||||
|
||||
/**
|
||||
* Escapes values for use in schema definitions.
|
||||
*
|
||||
* @param mixed $value The value to escape.
|
||||
* @return string String for use in schema definitions.
|
||||
*/
|
||||
public function schemaValue($value): string;
|
||||
|
||||
/**
|
||||
* Returns the schema name that's being used.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function schema(): string;
|
||||
|
||||
/**
|
||||
* Returns last id generated for a table or sequence in database.
|
||||
*
|
||||
* @param string|null $table table name or sequence to get last insert value from.
|
||||
* @param string|null $column the name of the column representing the primary key.
|
||||
* @return string|int
|
||||
*/
|
||||
public function lastInsertId(?string $table = null, ?string $column = null);
|
||||
|
||||
/**
|
||||
* Checks whether the driver is connected.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isConnected(): bool;
|
||||
|
||||
/**
|
||||
* Sets whether this driver should automatically quote identifiers
|
||||
* in queries.
|
||||
*
|
||||
* @param bool $enable Whether to enable auto quoting
|
||||
* @return $this
|
||||
*/
|
||||
public function enableAutoQuoting(bool $enable = true);
|
||||
|
||||
/**
|
||||
* Disable auto quoting of identifiers in queries.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function disableAutoQuoting();
|
||||
|
||||
/**
|
||||
* Returns whether this driver should automatically quote identifiers
|
||||
* in queries.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isAutoQuotingEnabled(): bool;
|
||||
|
||||
/**
|
||||
* Transforms the passed query to this Driver's dialect and returns an instance
|
||||
* of the transformed query and the full compiled SQL string.
|
||||
*
|
||||
* @param \Cake\Database\Query $query The query to compile.
|
||||
* @param \Cake\Database\ValueBinder $binder The value binder to use.
|
||||
* @return array containing 2 entries. The first entity is the transformed query
|
||||
* and the second one the compiled SQL.
|
||||
*/
|
||||
public function compileQuery(Query $query, ValueBinder $binder): array;
|
||||
|
||||
/**
|
||||
* Returns an instance of a QueryCompiler.
|
||||
*
|
||||
* @return \Cake\Database\QueryCompiler
|
||||
*/
|
||||
public function newCompiler(): QueryCompiler;
|
||||
|
||||
/**
|
||||
* Constructs new TableSchema.
|
||||
*
|
||||
* @param string $table The table name.
|
||||
* @param array $columns The list of columns for the schema.
|
||||
* @return \Cake\Database\Schema\TableSchema
|
||||
*/
|
||||
public function newTableSchema(string $table, array $columns = []): TableSchema;
|
||||
}
|
10
vendor/cakephp/database/Exception.php
vendored
Normal file
10
vendor/cakephp/database/Exception.php
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use function Cake\Core\deprecationWarning;
|
||||
|
||||
deprecationWarning(
|
||||
'Since 4.2.0: Cake\Database\Exception is deprecated. ' .
|
||||
'Use Cake\Database\Exception\DatabaseException instead.'
|
||||
);
|
||||
class_exists('Cake\Database\Exception\DatabaseException');
|
37
vendor/cakephp/database/Exception/DatabaseException.php
vendored
Normal file
37
vendor/cakephp/database/Exception/DatabaseException.php
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
<?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\Database\Exception;
|
||||
|
||||
use Cake\Core\Exception\CakeException;
|
||||
|
||||
/**
|
||||
* Exception for the database package.
|
||||
*/
|
||||
class DatabaseException extends CakeException
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $_messageTemplate = '%s';
|
||||
}
|
||||
|
||||
// phpcs:disable
|
||||
class_alias(
|
||||
'Cake\Database\Exception\DatabaseException',
|
||||
'Cake\Database\Exception'
|
||||
);
|
||||
// phpcs:enable
|
30
vendor/cakephp/database/Exception/MissingConnectionException.php
vendored
Normal file
30
vendor/cakephp/database/Exception/MissingConnectionException.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?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\Database\Exception;
|
||||
|
||||
use Cake\Core\Exception\CakeException;
|
||||
|
||||
/**
|
||||
* Class MissingConnectionException
|
||||
*/
|
||||
class MissingConnectionException extends CakeException
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $_messageTemplate = 'Connection to %s could not be established: %s';
|
||||
}
|
30
vendor/cakephp/database/Exception/MissingDriverException.php
vendored
Normal file
30
vendor/cakephp/database/Exception/MissingDriverException.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?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\Database\Exception;
|
||||
|
||||
use Cake\Core\Exception\CakeException;
|
||||
|
||||
/**
|
||||
* Class MissingDriverException
|
||||
*/
|
||||
class MissingDriverException extends CakeException
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $_messageTemplate = 'Could not find driver `%s` for connection `%s`.';
|
||||
}
|
31
vendor/cakephp/database/Exception/MissingExtensionException.php
vendored
Normal file
31
vendor/cakephp/database/Exception/MissingExtensionException.php
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
<?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\Database\Exception;
|
||||
|
||||
use Cake\Core\Exception\CakeException;
|
||||
|
||||
/**
|
||||
* Class MissingExtensionException
|
||||
*/
|
||||
class MissingExtensionException extends CakeException
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
// phpcs:ignore Generic.Files.LineLength
|
||||
protected $_messageTemplate = 'Database driver %s cannot be used due to a missing PHP extension or unmet dependency. Requested by connection "%s"';
|
||||
}
|
41
vendor/cakephp/database/Exception/NestedTransactionRollbackException.php
vendored
Normal file
41
vendor/cakephp/database/Exception/NestedTransactionRollbackException.php
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
<?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.4.3
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Database\Exception;
|
||||
|
||||
use Cake\Core\Exception\CakeException;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Class NestedTransactionRollbackException
|
||||
*/
|
||||
class NestedTransactionRollbackException extends CakeException
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string|null $message If no message is given a default meesage will be used.
|
||||
* @param int|null $code Status code, defaults to 500.
|
||||
* @param \Throwable|null $previous the previous exception.
|
||||
*/
|
||||
public function __construct(?string $message = null, ?int $code = 500, ?Throwable $previous = null)
|
||||
{
|
||||
if ($message === null) {
|
||||
$message = 'Cannot commit transaction - rollback() has been already called in the nested transaction';
|
||||
}
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
253
vendor/cakephp/database/Expression/AggregateExpression.php
vendored
Normal file
253
vendor/cakephp/database/Expression/AggregateExpression.php
vendored
Normal file
@ -0,0 +1,253 @@
|
||||
<?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.1.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Database\Expression;
|
||||
|
||||
use Cake\Database\ValueBinder;
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* This represents an SQL aggregate function expression in an SQL statement.
|
||||
* Calls can be constructed by passing the name of the function and a list of params.
|
||||
* For security reasons, all params passed are quoted by default unless
|
||||
* explicitly told otherwise.
|
||||
*/
|
||||
class AggregateExpression extends FunctionExpression implements WindowInterface
|
||||
{
|
||||
/**
|
||||
* @var \Cake\Database\Expression\QueryExpression
|
||||
*/
|
||||
protected $filter;
|
||||
|
||||
/**
|
||||
* @var \Cake\Database\Expression\WindowExpression
|
||||
*/
|
||||
protected $window;
|
||||
|
||||
/**
|
||||
* Adds conditions to the FILTER clause. The conditions are the same format as
|
||||
* `Query::where()`.
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|\Closure|array|string $conditions The conditions to filter on.
|
||||
* @param array<string, string> $types Associative array of type names used to bind values to query
|
||||
* @return $this
|
||||
* @see \Cake\Database\Query::where()
|
||||
*/
|
||||
public function filter($conditions, array $types = [])
|
||||
{
|
||||
if ($this->filter === null) {
|
||||
$this->filter = new QueryExpression();
|
||||
}
|
||||
|
||||
if ($conditions instanceof Closure) {
|
||||
$conditions = $conditions(new QueryExpression());
|
||||
}
|
||||
|
||||
$this->filter->add($conditions, $types);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an empty `OVER()` window expression or a named window epression.
|
||||
*
|
||||
* @param string|null $name Window name
|
||||
* @return $this
|
||||
*/
|
||||
public function over(?string $name = null)
|
||||
{
|
||||
if ($this->window === null) {
|
||||
$this->window = new WindowExpression();
|
||||
}
|
||||
if ($name) {
|
||||
// Set name manually in case this was chained from FunctionsBuilder wrapper
|
||||
$this->window->name($name);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function partition($partitions)
|
||||
{
|
||||
$this->over();
|
||||
$this->window->partition($partitions);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function order($fields)
|
||||
{
|
||||
$this->over();
|
||||
$this->window->order($fields);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function range($start, $end = 0)
|
||||
{
|
||||
$this->over();
|
||||
$this->window->range($start, $end);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function rows(?int $start, ?int $end = 0)
|
||||
{
|
||||
$this->over();
|
||||
$this->window->rows($start, $end);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function groups(?int $start, ?int $end = 0)
|
||||
{
|
||||
$this->over();
|
||||
$this->window->groups($start, $end);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function frame(
|
||||
string $type,
|
||||
$startOffset,
|
||||
string $startDirection,
|
||||
$endOffset,
|
||||
string $endDirection
|
||||
) {
|
||||
$this->over();
|
||||
$this->window->frame($type, $startOffset, $startDirection, $endOffset, $endDirection);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function excludeCurrent()
|
||||
{
|
||||
$this->over();
|
||||
$this->window->excludeCurrent();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function excludeGroup()
|
||||
{
|
||||
$this->over();
|
||||
$this->window->excludeGroup();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function excludeTies()
|
||||
{
|
||||
$this->over();
|
||||
$this->window->excludeTies();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function sql(ValueBinder $binder): string
|
||||
{
|
||||
$sql = parent::sql($binder);
|
||||
if ($this->filter !== null) {
|
||||
$sql .= ' FILTER (WHERE ' . $this->filter->sql($binder) . ')';
|
||||
}
|
||||
if ($this->window !== null) {
|
||||
if ($this->window->isNamedOnly()) {
|
||||
$sql .= ' OVER ' . $this->window->sql($binder);
|
||||
} else {
|
||||
$sql .= ' OVER (' . $this->window->sql($binder) . ')';
|
||||
}
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function traverse(Closure $callback)
|
||||
{
|
||||
parent::traverse($callback);
|
||||
if ($this->filter !== null) {
|
||||
$callback($this->filter);
|
||||
$this->filter->traverse($callback);
|
||||
}
|
||||
if ($this->window !== null) {
|
||||
$callback($this->window);
|
||||
$this->window->traverse($callback);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
$count = parent::count();
|
||||
if ($this->window !== null) {
|
||||
$count = $count + 1;
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone this object and its subtree of expressions.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
parent::__clone();
|
||||
if ($this->filter !== null) {
|
||||
$this->filter = clone $this->filter;
|
||||
}
|
||||
if ($this->window !== null) {
|
||||
$this->window = clone $this->window;
|
||||
}
|
||||
}
|
||||
}
|
144
vendor/cakephp/database/Expression/BetweenExpression.php
vendored
Normal file
144
vendor/cakephp/database/Expression/BetweenExpression.php
vendored
Normal file
@ -0,0 +1,144 @@
|
||||
<?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\Database\Expression;
|
||||
|
||||
use Cake\Database\ExpressionInterface;
|
||||
use Cake\Database\Type\ExpressionTypeCasterTrait;
|
||||
use Cake\Database\ValueBinder;
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* An expression object that represents a SQL BETWEEN snippet
|
||||
*/
|
||||
class BetweenExpression implements ExpressionInterface, FieldInterface
|
||||
{
|
||||
use ExpressionTypeCasterTrait;
|
||||
use FieldTrait;
|
||||
|
||||
/**
|
||||
* The first value in the expression
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $_from;
|
||||
|
||||
/**
|
||||
* The second value in the expression
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $_to;
|
||||
|
||||
/**
|
||||
* The data type for the from and to arguments
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $_type;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|string $field The field name to compare for values inbetween the range.
|
||||
* @param mixed $from The initial value of the range.
|
||||
* @param mixed $to The ending value in the comparison range.
|
||||
* @param string|null $type The data type name to bind the values with.
|
||||
*/
|
||||
public function __construct($field, $from, $to, $type = null)
|
||||
{
|
||||
if ($type !== null) {
|
||||
$from = $this->_castToExpression($from, $type);
|
||||
$to = $this->_castToExpression($to, $type);
|
||||
}
|
||||
|
||||
$this->_field = $field;
|
||||
$this->_from = $from;
|
||||
$this->_to = $to;
|
||||
$this->_type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function sql(ValueBinder $binder): string
|
||||
{
|
||||
$parts = [
|
||||
'from' => $this->_from,
|
||||
'to' => $this->_to,
|
||||
];
|
||||
|
||||
/** @var \Cake\Database\ExpressionInterface|string $field */
|
||||
$field = $this->_field;
|
||||
if ($field instanceof ExpressionInterface) {
|
||||
$field = $field->sql($binder);
|
||||
}
|
||||
|
||||
foreach ($parts as $name => $part) {
|
||||
if ($part instanceof ExpressionInterface) {
|
||||
$parts[$name] = $part->sql($binder);
|
||||
continue;
|
||||
}
|
||||
$parts[$name] = $this->_bindValue($part, $binder, $this->_type);
|
||||
}
|
||||
|
||||
return sprintf('%s BETWEEN %s AND %s', $field, $parts['from'], $parts['to']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function traverse(Closure $callback)
|
||||
{
|
||||
foreach ([$this->_field, $this->_from, $this->_to] as $part) {
|
||||
if ($part instanceof ExpressionInterface) {
|
||||
$callback($part);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a value in the placeholder generator and returns the generated placeholder
|
||||
*
|
||||
* @param mixed $value The value to bind
|
||||
* @param \Cake\Database\ValueBinder $binder The value binder to use
|
||||
* @param string $type The type of $value
|
||||
* @return string generated placeholder
|
||||
*/
|
||||
protected function _bindValue($value, $binder, $type): string
|
||||
{
|
||||
$placeholder = $binder->placeholder('c');
|
||||
$binder->bind($placeholder, $value, $type);
|
||||
|
||||
return $placeholder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do a deep clone of this expression.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
foreach (['_field', '_from', '_to'] as $part) {
|
||||
if ($this->{$part} instanceof ExpressionInterface) {
|
||||
$this->{$part} = clone $this->{$part};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
251
vendor/cakephp/database/Expression/CaseExpression.php
vendored
Normal file
251
vendor/cakephp/database/Expression/CaseExpression.php
vendored
Normal file
@ -0,0 +1,251 @@
|
||||
<?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\Database\Expression;
|
||||
|
||||
use Cake\Database\ExpressionInterface;
|
||||
use Cake\Database\Type\ExpressionTypeCasterTrait;
|
||||
use Cake\Database\ValueBinder;
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* This class represents a SQL Case statement
|
||||
*
|
||||
* @deprecated 4.3.0 Use QueryExpression::case() or CaseStatementExpression instead
|
||||
*/
|
||||
class CaseExpression implements ExpressionInterface
|
||||
{
|
||||
use ExpressionTypeCasterTrait;
|
||||
|
||||
/**
|
||||
* A list of strings or other expression objects that represent the conditions of
|
||||
* the case statement. For example one key of the array might look like "sum > :value"
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_conditions = [];
|
||||
|
||||
/**
|
||||
* Values that are associated with the conditions in the $_conditions array.
|
||||
* Each value represents the 'true' value for the condition with the corresponding key.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_values = [];
|
||||
|
||||
/**
|
||||
* The `ELSE` value for the case statement. If null then no `ELSE` will be included.
|
||||
*
|
||||
* @var \Cake\Database\ExpressionInterface|array|string|null
|
||||
*/
|
||||
protected $_elseValue;
|
||||
|
||||
/**
|
||||
* Constructs the case expression
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|array $conditions The conditions to test. Must be a ExpressionInterface
|
||||
* instance, or an array of ExpressionInterface instances.
|
||||
* @param \Cake\Database\ExpressionInterface|array $values Associative array of values to be associated with the
|
||||
* conditions passed in $conditions. If there are more $values than $conditions,
|
||||
* the last $value is used as the `ELSE` value.
|
||||
* @param array<string> $types Associative array of types to be associated with the values
|
||||
* passed in $values
|
||||
*/
|
||||
public function __construct($conditions = [], $values = [], $types = [])
|
||||
{
|
||||
$conditions = is_array($conditions) ? $conditions : [$conditions];
|
||||
$values = is_array($values) ? $values : [$values];
|
||||
$types = is_array($types) ? $types : [$types];
|
||||
|
||||
if (!empty($conditions)) {
|
||||
$this->add($conditions, $values, $types);
|
||||
}
|
||||
|
||||
if (count($values) > count($conditions)) {
|
||||
end($values);
|
||||
$key = key($values);
|
||||
$this->elseValue($values[$key], $types[$key] ?? null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds one or more conditions and their respective true values to the case object.
|
||||
* Conditions must be a one dimensional array or a QueryExpression.
|
||||
* The trueValues must be a similar structure, but may contain a string value.
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|array $conditions Must be a ExpressionInterface instance,
|
||||
* or an array of ExpressionInterface instances.
|
||||
* @param \Cake\Database\ExpressionInterface|array $values Associative array of values of each condition
|
||||
* @param array<string> $types Associative array of types to be associated with the values
|
||||
* @return $this
|
||||
*/
|
||||
public function add($conditions = [], $values = [], $types = [])
|
||||
{
|
||||
$conditions = is_array($conditions) ? $conditions : [$conditions];
|
||||
$values = is_array($values) ? $values : [$values];
|
||||
$types = is_array($types) ? $types : [$types];
|
||||
|
||||
$this->_addExpressions($conditions, $values, $types);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over the passed in conditions and ensures that there is a matching true value for each.
|
||||
* If no matching true value, then it is defaulted to '1'.
|
||||
*
|
||||
* @param array $conditions Array of ExpressionInterface instances.
|
||||
* @param array<mixed> $values Associative array of values of each condition
|
||||
* @param array<string> $types Associative array of types to be associated with the values
|
||||
* @return void
|
||||
*/
|
||||
protected function _addExpressions(array $conditions, array $values, array $types): void
|
||||
{
|
||||
$rawValues = array_values($values);
|
||||
$keyValues = array_keys($values);
|
||||
|
||||
foreach ($conditions as $k => $c) {
|
||||
$numericKey = is_numeric($k);
|
||||
|
||||
if ($numericKey && empty($c)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$c instanceof ExpressionInterface) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->_conditions[] = $c;
|
||||
$value = $rawValues[$k] ?? 1;
|
||||
|
||||
if ($value === 'literal') {
|
||||
$value = $keyValues[$k];
|
||||
$this->_values[] = $value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($value === 'identifier') {
|
||||
/** @var string $identifier */
|
||||
$identifier = $keyValues[$k];
|
||||
$value = new IdentifierExpression($identifier);
|
||||
$this->_values[] = $value;
|
||||
continue;
|
||||
}
|
||||
|
||||
$type = $types[$k] ?? null;
|
||||
|
||||
if ($type !== null && !$value instanceof ExpressionInterface) {
|
||||
$value = $this->_castToExpression($value, $type);
|
||||
}
|
||||
|
||||
if ($value instanceof ExpressionInterface) {
|
||||
$this->_values[] = $value;
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->_values[] = ['value' => $value, 'type' => $type];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default value
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|array|string|null $value Value to set
|
||||
* @param string|null $type Type of value
|
||||
* @return void
|
||||
*/
|
||||
public function elseValue($value = null, ?string $type = null): void
|
||||
{
|
||||
if (is_array($value)) {
|
||||
end($value);
|
||||
$value = key($value);
|
||||
}
|
||||
|
||||
if ($value !== null && !$value instanceof ExpressionInterface) {
|
||||
$value = $this->_castToExpression($value, $type);
|
||||
}
|
||||
|
||||
if (!$value instanceof ExpressionInterface) {
|
||||
$value = ['value' => $value, 'type' => $type];
|
||||
}
|
||||
|
||||
$this->_elseValue = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the relevant parts into sql
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|array|string $part The part to compile
|
||||
* @param \Cake\Database\ValueBinder $binder Sql generator
|
||||
* @return string
|
||||
*/
|
||||
protected function _compile($part, ValueBinder $binder): string
|
||||
{
|
||||
if ($part instanceof ExpressionInterface) {
|
||||
$part = $part->sql($binder);
|
||||
} elseif (is_array($part)) {
|
||||
$placeholder = $binder->placeholder('param');
|
||||
$binder->bind($placeholder, $part['value'], $part['type']);
|
||||
$part = $placeholder;
|
||||
}
|
||||
|
||||
return $part;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the Node into a SQL string fragment.
|
||||
*
|
||||
* @param \Cake\Database\ValueBinder $binder Placeholder generator object
|
||||
* @return string
|
||||
*/
|
||||
public function sql(ValueBinder $binder): string
|
||||
{
|
||||
$parts = [];
|
||||
$parts[] = 'CASE';
|
||||
foreach ($this->_conditions as $k => $part) {
|
||||
$value = $this->_values[$k];
|
||||
$parts[] = 'WHEN ' . $this->_compile($part, $binder) . ' THEN ' . $this->_compile($value, $binder);
|
||||
}
|
||||
if ($this->_elseValue !== null) {
|
||||
$parts[] = 'ELSE';
|
||||
$parts[] = $this->_compile($this->_elseValue, $binder);
|
||||
}
|
||||
$parts[] = 'END';
|
||||
|
||||
return implode(' ', $parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function traverse(Closure $callback)
|
||||
{
|
||||
foreach (['_conditions', '_values'] as $part) {
|
||||
foreach ($this->{$part} as $c) {
|
||||
if ($c instanceof ExpressionInterface) {
|
||||
$callback($c);
|
||||
$c->traverse($callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($this->_elseValue instanceof ExpressionInterface) {
|
||||
$callback($this->_elseValue);
|
||||
$this->_elseValue->traverse($callback);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
109
vendor/cakephp/database/Expression/CaseExpressionTrait.php
vendored
Normal file
109
vendor/cakephp/database/Expression/CaseExpressionTrait.php
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
<?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.3.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Database\Expression;
|
||||
|
||||
use Cake\Chronos\ChronosDate;
|
||||
use Cake\Chronos\MutableDate;
|
||||
use Cake\Database\ExpressionInterface;
|
||||
use Cake\Database\Query;
|
||||
use Cake\Database\TypedResultInterface;
|
||||
use Cake\Database\ValueBinder;
|
||||
use DateTimeInterface;
|
||||
|
||||
/**
|
||||
* Trait that holds shared functionality for case related expressions.
|
||||
*
|
||||
* @property \Cake\Database\TypeMap $_typeMap The type map to use when using an array of conditions for the `WHEN`
|
||||
* value.
|
||||
* @internal
|
||||
*/
|
||||
trait CaseExpressionTrait
|
||||
{
|
||||
/**
|
||||
* Infers the abstract type for the given value.
|
||||
*
|
||||
* @param mixed $value The value for which to infer the type.
|
||||
* @return string|null The abstract type, or `null` if it could not be inferred.
|
||||
*/
|
||||
protected function inferType($value): ?string
|
||||
{
|
||||
$type = null;
|
||||
|
||||
if (is_string($value)) {
|
||||
$type = 'string';
|
||||
} elseif (is_int($value)) {
|
||||
$type = 'integer';
|
||||
} elseif (is_float($value)) {
|
||||
$type = 'float';
|
||||
} elseif (is_bool($value)) {
|
||||
$type = 'boolean';
|
||||
} elseif (
|
||||
$value instanceof ChronosDate ||
|
||||
$value instanceof MutableDate
|
||||
) {
|
||||
$type = 'date';
|
||||
} elseif ($value instanceof DateTimeInterface) {
|
||||
$type = 'datetime';
|
||||
} elseif (
|
||||
is_object($value) &&
|
||||
method_exists($value, '__toString')
|
||||
) {
|
||||
$type = 'string';
|
||||
} elseif (
|
||||
$this->_typeMap !== null &&
|
||||
$value instanceof IdentifierExpression
|
||||
) {
|
||||
$type = $this->_typeMap->type($value->getIdentifier());
|
||||
} elseif ($value instanceof TypedResultInterface) {
|
||||
$type = $value->getReturnType();
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a nullable value to SQL.
|
||||
*
|
||||
* @param \Cake\Database\ValueBinder $binder The value binder to use.
|
||||
* @param \Cake\Database\ExpressionInterface|object|scalar|null $value The value to compile.
|
||||
* @param string|null $type The value type.
|
||||
* @return string
|
||||
*/
|
||||
protected function compileNullableValue(ValueBinder $binder, $value, ?string $type = null): string
|
||||
{
|
||||
if (
|
||||
$type !== null &&
|
||||
!($value instanceof ExpressionInterface)
|
||||
) {
|
||||
$value = $this->_castToExpression($value, $type);
|
||||
}
|
||||
|
||||
if ($value === null) {
|
||||
$value = 'NULL';
|
||||
} elseif ($value instanceof Query) {
|
||||
$value = sprintf('(%s)', $value->sql($binder));
|
||||
} elseif ($value instanceof ExpressionInterface) {
|
||||
$value = $value->sql($binder);
|
||||
} else {
|
||||
$placeholder = $binder->placeholder('c');
|
||||
$binder->bind($placeholder, $value, $type);
|
||||
$value = $placeholder;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
597
vendor/cakephp/database/Expression/CaseStatementExpression.php
vendored
Normal file
597
vendor/cakephp/database/Expression/CaseStatementExpression.php
vendored
Normal file
@ -0,0 +1,597 @@
|
||||
<?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.3.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Database\Expression;
|
||||
|
||||
use Cake\Database\ExpressionInterface;
|
||||
use Cake\Database\Type\ExpressionTypeCasterTrait;
|
||||
use Cake\Database\TypedResultInterface;
|
||||
use Cake\Database\TypeMapTrait;
|
||||
use Cake\Database\ValueBinder;
|
||||
use Closure;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
use function Cake\Core\getTypeName;
|
||||
|
||||
/**
|
||||
* Represents a SQL case statement with a fluid API
|
||||
*/
|
||||
class CaseStatementExpression implements ExpressionInterface, TypedResultInterface
|
||||
{
|
||||
use CaseExpressionTrait;
|
||||
use ExpressionTypeCasterTrait;
|
||||
use TypeMapTrait;
|
||||
|
||||
/**
|
||||
* The names of the clauses that are valid for use with the
|
||||
* `clause()` method.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $validClauseNames = [
|
||||
'value',
|
||||
'when',
|
||||
'else',
|
||||
];
|
||||
|
||||
/**
|
||||
* Whether this is a simple case expression.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $isSimpleVariant = false;
|
||||
|
||||
/**
|
||||
* The case value.
|
||||
*
|
||||
* @var \Cake\Database\ExpressionInterface|object|scalar|null
|
||||
*/
|
||||
protected $value = null;
|
||||
|
||||
/**
|
||||
* The case value type.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $valueType = null;
|
||||
|
||||
/**
|
||||
* The `WHEN ... THEN ...` expressions.
|
||||
*
|
||||
* @var array<\Cake\Database\Expression\WhenThenExpression>
|
||||
*/
|
||||
protected $when = [];
|
||||
|
||||
/**
|
||||
* Buffer that holds values and types for use with `then()`.
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
protected $whenBuffer = null;
|
||||
|
||||
/**
|
||||
* The else part result value.
|
||||
*
|
||||
* @var \Cake\Database\ExpressionInterface|object|scalar|null
|
||||
*/
|
||||
protected $else = null;
|
||||
|
||||
/**
|
||||
* The else part result type.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $elseType = null;
|
||||
|
||||
/**
|
||||
* The return type.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $returnType = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* When a value is set, the syntax generated is
|
||||
* `CASE case_value WHEN when_value ... END` (simple case),
|
||||
* where the `when_value`'s are compared against the
|
||||
* `case_value`.
|
||||
*
|
||||
* When no value is set, the syntax generated is
|
||||
* `CASE WHEN when_conditions ... END` (searched case),
|
||||
* where the conditions hold the comparisons.
|
||||
*
|
||||
* Note that `null` is a valid case value, and thus should
|
||||
* only be passed if you actually want to create the simple
|
||||
* case expression variant!
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|object|scalar|null $value The case value.
|
||||
* @param string|null $type The case value type. If no type is provided, the type will be tried to be inferred
|
||||
* from the value.
|
||||
*/
|
||||
public function __construct($value = null, ?string $type = null)
|
||||
{
|
||||
if (func_num_args() > 0) {
|
||||
if (
|
||||
$value !== null &&
|
||||
!is_scalar($value) &&
|
||||
!(is_object($value) && !($value instanceof Closure))
|
||||
) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'The `$value` argument must be either `null`, a scalar value, an object, ' .
|
||||
'or an instance of `\%s`, `%s` given.',
|
||||
ExpressionInterface::class,
|
||||
getTypeName($value)
|
||||
));
|
||||
}
|
||||
|
||||
$this->value = $value;
|
||||
|
||||
if (
|
||||
$value !== null &&
|
||||
$type === null &&
|
||||
!($value instanceof ExpressionInterface)
|
||||
) {
|
||||
$type = $this->inferType($value);
|
||||
}
|
||||
$this->valueType = $type;
|
||||
|
||||
$this->isSimpleVariant = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the `WHEN` value for a `WHEN ... THEN ...` expression, or a
|
||||
* self-contained expression that holds both the value for `WHEN`
|
||||
* and the value for `THEN`.
|
||||
*
|
||||
* ### Order based syntax
|
||||
*
|
||||
* When passing a value other than a self-contained
|
||||
* `\Cake\Database\Expression\WhenThenExpression`,
|
||||
* instance, the `WHEN ... THEN ...` statement must be closed off with
|
||||
* a call to `then()` before invoking `when()` again or `else()`:
|
||||
*
|
||||
* ```
|
||||
* $queryExpression
|
||||
* ->case($query->identifier('Table.column'))
|
||||
* ->when(true)
|
||||
* ->then('Yes')
|
||||
* ->when(false)
|
||||
* ->then('No')
|
||||
* ->else('Maybe');
|
||||
* ```
|
||||
*
|
||||
* ### Self-contained expressions
|
||||
*
|
||||
* When passing an instance of `\Cake\Database\Expression\WhenThenExpression`,
|
||||
* being it directly, or via a callable, then there is no need to close
|
||||
* using `then()` on this object, instead the statement will be closed
|
||||
* on the `\Cake\Database\Expression\WhenThenExpression`
|
||||
* object using
|
||||
* `\Cake\Database\Expression\WhenThenExpression::then()`.
|
||||
*
|
||||
* Callables will receive an instance of `\Cake\Database\Expression\WhenThenExpression`,
|
||||
* and must return one, being it the same object, or a custom one:
|
||||
*
|
||||
* ```
|
||||
* $queryExpression
|
||||
* ->case()
|
||||
* ->when(function (\Cake\Database\Expression\WhenThenExpression $whenThen) {
|
||||
* return $whenThen
|
||||
* ->when(['Table.column' => true])
|
||||
* ->then('Yes');
|
||||
* })
|
||||
* ->when(function (\Cake\Database\Expression\WhenThenExpression $whenThen) {
|
||||
* return $whenThen
|
||||
* ->when(['Table.column' => false])
|
||||
* ->then('No');
|
||||
* })
|
||||
* ->else('Maybe');
|
||||
* ```
|
||||
*
|
||||
* ### Type handling
|
||||
*
|
||||
* The types provided via the `$type` argument will be merged with the
|
||||
* type map set for this expression. When using callables for `$when`,
|
||||
* the `\Cake\Database\Expression\WhenThenExpression`
|
||||
* instance received by the callables will inherit that type map, however
|
||||
* the types passed here will _not_ be merged in case of using callables,
|
||||
* instead the types must be passed in
|
||||
* `\Cake\Database\Expression\WhenThenExpression::when()`:
|
||||
*
|
||||
* ```
|
||||
* $queryExpression
|
||||
* ->case()
|
||||
* ->when(function (\Cake\Database\Expression\WhenThenExpression $whenThen) {
|
||||
* return $whenThen
|
||||
* ->when(['unmapped_column' => true], ['unmapped_column' => 'bool'])
|
||||
* ->then('Yes');
|
||||
* })
|
||||
* ->when(function (\Cake\Database\Expression\WhenThenExpression $whenThen) {
|
||||
* return $whenThen
|
||||
* ->when(['unmapped_column' => false], ['unmapped_column' => 'bool'])
|
||||
* ->then('No');
|
||||
* })
|
||||
* ->else('Maybe');
|
||||
* ```
|
||||
*
|
||||
* ### User data safety
|
||||
*
|
||||
* When passing user data, be aware that allowing a user defined array
|
||||
* to be passed, is a potential SQL injection vulnerability, as it
|
||||
* allows for raw SQL to slip in!
|
||||
*
|
||||
* The following is _unsafe_ usage that must be avoided:
|
||||
*
|
||||
* ```
|
||||
* $case
|
||||
* ->when($userData)
|
||||
* ```
|
||||
*
|
||||
* A safe variant for the above would be to define a single type for
|
||||
* the value:
|
||||
*
|
||||
* ```
|
||||
* $case
|
||||
* ->when($userData, 'integer')
|
||||
* ```
|
||||
*
|
||||
* This way an exception would be triggered when an array is passed for
|
||||
* the value, thus preventing raw SQL from slipping in, and all other
|
||||
* types of values would be forced to be bound as an integer.
|
||||
*
|
||||
* Another way to safely pass user data is when using a conditions
|
||||
* array, and passing user data only on the value side of the array
|
||||
* entries, which will cause them to be bound:
|
||||
*
|
||||
* ```
|
||||
* $case
|
||||
* ->when([
|
||||
* 'Table.column' => $userData,
|
||||
* ])
|
||||
* ```
|
||||
*
|
||||
* Lastly, data can also be bound manually:
|
||||
*
|
||||
* ```
|
||||
* $query
|
||||
* ->select([
|
||||
* 'val' => $query->newExpr()
|
||||
* ->case()
|
||||
* ->when($query->newExpr(':userData'))
|
||||
* ->then(123)
|
||||
* ])
|
||||
* ->bind(':userData', $userData, 'integer')
|
||||
* ```
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|\Closure|object|array|scalar $when The `WHEN` value. When using an
|
||||
* array of conditions, it must be compatible with `\Cake\Database\Query::where()`. Note that this argument is
|
||||
* _not_ completely safe for use with user data, as a user supplied array would allow for raw SQL to slip in! If
|
||||
* you plan to use user data, either pass a single type for the `$type` argument (which forces the `$when` value to
|
||||
* be a non-array, and then always binds the data), use a conditions array where the user data is only passed on
|
||||
* the value side of the array entries, or custom bindings!
|
||||
* @param array<string, string>|string|null $type The when value type. Either an associative array when using array style
|
||||
* conditions, or else a string. If no type is provided, the type will be tried to be inferred from the value.
|
||||
* @return $this
|
||||
* @throws \LogicException In case this a closing `then()` call is required before calling this method.
|
||||
* @throws \LogicException In case the callable doesn't return an instance of
|
||||
* `\Cake\Database\Expression\WhenThenExpression`.
|
||||
*/
|
||||
public function when($when, $type = null)
|
||||
{
|
||||
if ($this->whenBuffer !== null) {
|
||||
throw new LogicException('Cannot call `when()` between `when()` and `then()`.');
|
||||
}
|
||||
|
||||
if ($when instanceof Closure) {
|
||||
$when = $when(new WhenThenExpression($this->getTypeMap()));
|
||||
if (!($when instanceof WhenThenExpression)) {
|
||||
throw new LogicException(sprintf(
|
||||
'`when()` callables must return an instance of `\%s`, `%s` given.',
|
||||
WhenThenExpression::class,
|
||||
getTypeName($when)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if ($when instanceof WhenThenExpression) {
|
||||
$this->when[] = $when;
|
||||
} else {
|
||||
$this->whenBuffer = ['when' => $when, 'type' => $type];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the `THEN` result value for the last `WHEN ... THEN ...`
|
||||
* statement that was opened using `when()`.
|
||||
*
|
||||
* ### Order based syntax
|
||||
*
|
||||
* This method can only be invoked in case `when()` was previously
|
||||
* used with a value other than a closure or an instance of
|
||||
* `\Cake\Database\Expression\WhenThenExpression`:
|
||||
*
|
||||
* ```
|
||||
* $case
|
||||
* ->when(['Table.column' => true])
|
||||
* ->then('Yes')
|
||||
* ->when(['Table.column' => false])
|
||||
* ->then('No')
|
||||
* ->else('Maybe');
|
||||
* ```
|
||||
*
|
||||
* The following would all fail with an exception:
|
||||
*
|
||||
* ```
|
||||
* $case
|
||||
* ->when(['Table.column' => true])
|
||||
* ->when(['Table.column' => false])
|
||||
* // ...
|
||||
* ```
|
||||
*
|
||||
* ```
|
||||
* $case
|
||||
* ->when(['Table.column' => true])
|
||||
* ->else('Maybe')
|
||||
* // ...
|
||||
* ```
|
||||
*
|
||||
* ```
|
||||
* $case
|
||||
* ->then('Yes')
|
||||
* // ...
|
||||
* ```
|
||||
*
|
||||
* ```
|
||||
* $case
|
||||
* ->when(['Table.column' => true])
|
||||
* ->then('Yes')
|
||||
* ->then('No')
|
||||
* // ...
|
||||
* ```
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|object|scalar|null $result The result value.
|
||||
* @param string|null $type The result type. If no type is provided, the type will be tried to be inferred from the
|
||||
* value.
|
||||
* @return $this
|
||||
* @throws \LogicException In case `when()` wasn't previously called with a value other than a closure or an
|
||||
* instance of `\Cake\Database\Expression\WhenThenExpression`.
|
||||
*/
|
||||
public function then($result, ?string $type = null)
|
||||
{
|
||||
if ($this->whenBuffer === null) {
|
||||
throw new LogicException('Cannot call `then()` before `when()`.');
|
||||
}
|
||||
|
||||
$whenThen = (new WhenThenExpression($this->getTypeMap()))
|
||||
->when($this->whenBuffer['when'], $this->whenBuffer['type'])
|
||||
->then($result, $type);
|
||||
|
||||
$this->whenBuffer = null;
|
||||
|
||||
$this->when[] = $whenThen;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the `ELSE` result value.
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|object|scalar|null $result The result value.
|
||||
* @param string|null $type The result type. If no type is provided, the type will be tried to be inferred from the
|
||||
* value.
|
||||
* @return $this
|
||||
* @throws \LogicException In case a closing `then()` call is required before calling this method.
|
||||
* @throws \InvalidArgumentException In case the `$result` argument is neither a scalar value, nor an object, an
|
||||
* instance of `\Cake\Database\ExpressionInterface`, or `null`.
|
||||
*/
|
||||
public function else($result, ?string $type = null)
|
||||
{
|
||||
if ($this->whenBuffer !== null) {
|
||||
throw new LogicException('Cannot call `else()` between `when()` and `then()`.');
|
||||
}
|
||||
|
||||
if (
|
||||
$result !== null &&
|
||||
!is_scalar($result) &&
|
||||
!(is_object($result) && !($result instanceof Closure))
|
||||
) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'The `$result` argument must be either `null`, a scalar value, an object, ' .
|
||||
'or an instance of `\%s`, `%s` given.',
|
||||
ExpressionInterface::class,
|
||||
getTypeName($result)
|
||||
));
|
||||
}
|
||||
|
||||
if ($type === null) {
|
||||
$type = $this->inferType($result);
|
||||
}
|
||||
|
||||
$this->else = $result;
|
||||
$this->elseType = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the abstract type that this expression will return.
|
||||
*
|
||||
* If no type has been explicitly set via `setReturnType()`, this
|
||||
* method will try to obtain the type from the result types of the
|
||||
* `then()` and `else() `calls. All types must be identical in order
|
||||
* for this to work, otherwise the type will default to `string`.
|
||||
*
|
||||
* @return string
|
||||
* @see CaseStatementExpression::then()
|
||||
*/
|
||||
public function getReturnType(): string
|
||||
{
|
||||
if ($this->returnType !== null) {
|
||||
return $this->returnType;
|
||||
}
|
||||
|
||||
$types = [];
|
||||
foreach ($this->when as $when) {
|
||||
$type = $when->getResultType();
|
||||
if ($type !== null) {
|
||||
$types[] = $type;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->elseType !== null) {
|
||||
$types[] = $this->elseType;
|
||||
}
|
||||
|
||||
$types = array_unique($types);
|
||||
if (count($types) === 1) {
|
||||
return $types[0];
|
||||
}
|
||||
|
||||
return 'string';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the abstract type that this expression will return.
|
||||
*
|
||||
* If no type is being explicitly set via this method, then the
|
||||
* `getReturnType()` method will try to infer the type from the
|
||||
* result types of the `then()` and `else() `calls.
|
||||
*
|
||||
* @param string $type The type name to use.
|
||||
* @return $this
|
||||
*/
|
||||
public function setReturnType(string $type)
|
||||
{
|
||||
$this->returnType = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the available data for the given clause.
|
||||
*
|
||||
* ### Available clauses
|
||||
*
|
||||
* The following clause names are available:
|
||||
*
|
||||
* * `value`: The case value for a `CASE case_value WHEN ...` expression.
|
||||
* * `when`: An array of `WHEN ... THEN ...` expressions.
|
||||
* * `else`: The `ELSE` result value.
|
||||
*
|
||||
* @param string $clause The name of the clause to obtain.
|
||||
* @return \Cake\Database\ExpressionInterface|object|array<\Cake\Database\Expression\WhenThenExpression>|scalar|null
|
||||
* @throws \InvalidArgumentException In case the given clause name is invalid.
|
||||
*/
|
||||
public function clause(string $clause)
|
||||
{
|
||||
if (!in_array($clause, $this->validClauseNames, true)) {
|
||||
throw new InvalidArgumentException(
|
||||
sprintf(
|
||||
'The `$clause` argument must be one of `%s`, the given value `%s` is invalid.',
|
||||
implode('`, `', $this->validClauseNames),
|
||||
$clause
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $this->{$clause};
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function sql(ValueBinder $binder): string
|
||||
{
|
||||
if ($this->whenBuffer !== null) {
|
||||
throw new LogicException('Case expression has incomplete when clause. Missing `then()` after `when()`.');
|
||||
}
|
||||
|
||||
if (empty($this->when)) {
|
||||
throw new LogicException('Case expression must have at least one when statement.');
|
||||
}
|
||||
|
||||
$value = '';
|
||||
if ($this->isSimpleVariant) {
|
||||
$value = $this->compileNullableValue($binder, $this->value, $this->valueType) . ' ';
|
||||
}
|
||||
|
||||
$whenThenExpressions = [];
|
||||
foreach ($this->when as $whenThen) {
|
||||
$whenThenExpressions[] = $whenThen->sql($binder);
|
||||
}
|
||||
$whenThen = implode(' ', $whenThenExpressions);
|
||||
|
||||
$else = $this->compileNullableValue($binder, $this->else, $this->elseType);
|
||||
|
||||
return "CASE {$value}{$whenThen} ELSE $else END";
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function traverse(Closure $callback)
|
||||
{
|
||||
if ($this->whenBuffer !== null) {
|
||||
throw new LogicException('Case expression has incomplete when clause. Missing `then()` after `when()`.');
|
||||
}
|
||||
|
||||
if ($this->value instanceof ExpressionInterface) {
|
||||
$callback($this->value);
|
||||
$this->value->traverse($callback);
|
||||
}
|
||||
|
||||
foreach ($this->when as $when) {
|
||||
$callback($when);
|
||||
$when->traverse($callback);
|
||||
}
|
||||
|
||||
if ($this->else instanceof ExpressionInterface) {
|
||||
$callback($this->else);
|
||||
$this->else->traverse($callback);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones the inner expression objects.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
if ($this->whenBuffer !== null) {
|
||||
throw new LogicException('Case expression has incomplete when clause. Missing `then()` after `when()`.');
|
||||
}
|
||||
|
||||
if ($this->value instanceof ExpressionInterface) {
|
||||
$this->value = clone $this->value;
|
||||
}
|
||||
|
||||
foreach ($this->when as $key => $when) {
|
||||
$this->when[$key] = clone $this->when[$key];
|
||||
}
|
||||
|
||||
if ($this->else instanceof ExpressionInterface) {
|
||||
$this->else = clone $this->else;
|
||||
}
|
||||
}
|
||||
}
|
239
vendor/cakephp/database/Expression/CommonTableExpression.php
vendored
Normal file
239
vendor/cakephp/database/Expression/CommonTableExpression.php
vendored
Normal file
@ -0,0 +1,239 @@
|
||||
<?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.1.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Database\Expression;
|
||||
|
||||
use Cake\Database\ExpressionInterface;
|
||||
use Cake\Database\ValueBinder;
|
||||
use Closure;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* An expression that represents a common table expression definition.
|
||||
*/
|
||||
class CommonTableExpression implements ExpressionInterface
|
||||
{
|
||||
/**
|
||||
* The CTE name.
|
||||
*
|
||||
* @var \Cake\Database\Expression\IdentifierExpression
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* The field names to use for the CTE.
|
||||
*
|
||||
* @var array<\Cake\Database\Expression\IdentifierExpression>
|
||||
*/
|
||||
protected $fields = [];
|
||||
|
||||
/**
|
||||
* The CTE query definition.
|
||||
*
|
||||
* @var \Cake\Database\ExpressionInterface|null
|
||||
*/
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* Whether the CTE is materialized or not materialized.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $materialized = null;
|
||||
|
||||
/**
|
||||
* Whether the CTE is recursive.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $recursive = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $name The CTE name.
|
||||
* @param \Cake\Database\ExpressionInterface|\Closure $query CTE query
|
||||
*/
|
||||
public function __construct(string $name = '', $query = null)
|
||||
{
|
||||
$this->name = new IdentifierExpression($name);
|
||||
if ($query) {
|
||||
$this->query($query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of this CTE.
|
||||
*
|
||||
* This is the named you used to reference the expression
|
||||
* in select, insert, etc queries.
|
||||
*
|
||||
* @param string $name The CTE name.
|
||||
* @return $this
|
||||
*/
|
||||
public function name(string $name)
|
||||
{
|
||||
$this->name = new IdentifierExpression($name);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the query for this CTE.
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|\Closure $query CTE query
|
||||
* @return $this
|
||||
*/
|
||||
public function query($query)
|
||||
{
|
||||
if ($query instanceof Closure) {
|
||||
$query = $query();
|
||||
if (!($query instanceof ExpressionInterface)) {
|
||||
throw new RuntimeException(
|
||||
'You must return an `ExpressionInterface` from a Closure passed to `query()`.'
|
||||
);
|
||||
}
|
||||
}
|
||||
$this->query = $query;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds one or more fields (arguments) to the CTE.
|
||||
*
|
||||
* @param \Cake\Database\Expression\IdentifierExpression|array<\Cake\Database\Expression\IdentifierExpression>|array<string>|string $fields Field names
|
||||
* @return $this
|
||||
*/
|
||||
public function field($fields)
|
||||
{
|
||||
$fields = (array)$fields;
|
||||
foreach ($fields as &$field) {
|
||||
if (!($field instanceof IdentifierExpression)) {
|
||||
$field = new IdentifierExpression($field);
|
||||
}
|
||||
}
|
||||
$this->fields = array_merge($this->fields, $fields);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this CTE as materialized.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function materialized()
|
||||
{
|
||||
$this->materialized = 'MATERIALIZED';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this CTE as not materialized.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function notMaterialized()
|
||||
{
|
||||
$this->materialized = 'NOT MATERIALIZED';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this CTE is recursive.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isRecursive(): bool
|
||||
{
|
||||
return $this->recursive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this CTE as recursive.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function recursive()
|
||||
{
|
||||
$this->recursive = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function sql(ValueBinder $binder): string
|
||||
{
|
||||
$fields = '';
|
||||
if ($this->fields) {
|
||||
$expressions = array_map(function (IdentifierExpression $e) use ($binder) {
|
||||
return $e->sql($binder);
|
||||
}, $this->fields);
|
||||
$fields = sprintf('(%s)', implode(', ', $expressions));
|
||||
}
|
||||
|
||||
$suffix = $this->materialized ? $this->materialized . ' ' : '';
|
||||
|
||||
return sprintf(
|
||||
'%s%s AS %s(%s)',
|
||||
$this->name->sql($binder),
|
||||
$fields,
|
||||
$suffix,
|
||||
$this->query ? $this->query->sql($binder) : ''
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function traverse(Closure $callback)
|
||||
{
|
||||
$callback($this->name);
|
||||
foreach ($this->fields as $field) {
|
||||
$callback($field);
|
||||
$field->traverse($callback);
|
||||
}
|
||||
|
||||
if ($this->query) {
|
||||
$callback($this->query);
|
||||
$this->query->traverse($callback);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones the inner expression objects.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
$this->name = clone $this->name;
|
||||
if ($this->query) {
|
||||
$this->query = clone $this->query;
|
||||
}
|
||||
|
||||
foreach ($this->fields as $key => $field) {
|
||||
$this->fields[$key] = clone $field;
|
||||
}
|
||||
}
|
||||
}
|
7
vendor/cakephp/database/Expression/Comparison.php
vendored
Normal file
7
vendor/cakephp/database/Expression/Comparison.php
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use function Cake\Core\deprecationWarning;
|
||||
|
||||
deprecationWarning('Since 4.1.0: `Comparison` deprecated. Use `ComparisonExpression` instead.');
|
||||
class_exists('Cake\Database\Expression\ComparisonExpression');
|
324
vendor/cakephp/database/Expression/ComparisonExpression.php
vendored
Normal file
324
vendor/cakephp/database/Expression/ComparisonExpression.php
vendored
Normal file
@ -0,0 +1,324 @@
|
||||
<?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\Database\Expression;
|
||||
|
||||
use Cake\Database\Exception\DatabaseException;
|
||||
use Cake\Database\ExpressionInterface;
|
||||
use Cake\Database\Type\ExpressionTypeCasterTrait;
|
||||
use Cake\Database\ValueBinder;
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* A Comparison is a type of query expression that represents an operation
|
||||
* involving a field an operator and a value. In its most common form the
|
||||
* string representation of a comparison is `field = value`
|
||||
*/
|
||||
class ComparisonExpression implements ExpressionInterface, FieldInterface
|
||||
{
|
||||
use ExpressionTypeCasterTrait;
|
||||
use FieldTrait;
|
||||
|
||||
/**
|
||||
* The value to be used in the right hand side of the operation
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $_value;
|
||||
|
||||
/**
|
||||
* The type to be used for casting the value to a database representation
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $_type;
|
||||
|
||||
/**
|
||||
* The operator used for comparing field and value
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_operator = '=';
|
||||
|
||||
/**
|
||||
* Whether the value in this expression is a traversable
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $_isMultiple = false;
|
||||
|
||||
/**
|
||||
* A cached list of ExpressionInterface objects that were
|
||||
* found in the value for this expression.
|
||||
*
|
||||
* @var array<\Cake\Database\ExpressionInterface>
|
||||
*/
|
||||
protected $_valueExpressions = [];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|string $field the field name to compare to a value
|
||||
* @param mixed $value The value to be used in comparison
|
||||
* @param string|null $type the type name used to cast the value
|
||||
* @param string $operator the operator used for comparing field and value
|
||||
*/
|
||||
public function __construct($field, $value, ?string $type = null, string $operator = '=')
|
||||
{
|
||||
$this->_type = $type;
|
||||
$this->setField($field);
|
||||
$this->setValue($value);
|
||||
$this->_operator = $operator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value
|
||||
*
|
||||
* @param mixed $value The value to compare
|
||||
* @return void
|
||||
*/
|
||||
public function setValue($value): void
|
||||
{
|
||||
$value = $this->_castToExpression($value, $this->_type);
|
||||
|
||||
$isMultiple = $this->_type && strpos($this->_type, '[]') !== false;
|
||||
if ($isMultiple) {
|
||||
[$value, $this->_valueExpressions] = $this->_collectExpressions($value);
|
||||
}
|
||||
|
||||
$this->_isMultiple = $isMultiple;
|
||||
$this->_value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value used for comparison
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the operator to use for the comparison
|
||||
*
|
||||
* @param string $operator The operator to be used for the comparison.
|
||||
* @return void
|
||||
*/
|
||||
public function setOperator(string $operator): void
|
||||
{
|
||||
$this->_operator = $operator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the operator used for comparison
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOperator(): string
|
||||
{
|
||||
return $this->_operator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function sql(ValueBinder $binder): string
|
||||
{
|
||||
/** @var \Cake\Database\ExpressionInterface|string $field */
|
||||
$field = $this->_field;
|
||||
|
||||
if ($field instanceof ExpressionInterface) {
|
||||
$field = $field->sql($binder);
|
||||
}
|
||||
|
||||
if ($this->_value instanceof IdentifierExpression) {
|
||||
$template = '%s %s %s';
|
||||
$value = $this->_value->sql($binder);
|
||||
} elseif ($this->_value instanceof ExpressionInterface) {
|
||||
$template = '%s %s (%s)';
|
||||
$value = $this->_value->sql($binder);
|
||||
} else {
|
||||
[$template, $value] = $this->_stringExpression($binder);
|
||||
}
|
||||
|
||||
return sprintf($template, $field, $this->_operator, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function traverse(Closure $callback)
|
||||
{
|
||||
if ($this->_field instanceof ExpressionInterface) {
|
||||
$callback($this->_field);
|
||||
$this->_field->traverse($callback);
|
||||
}
|
||||
|
||||
if ($this->_value instanceof ExpressionInterface) {
|
||||
$callback($this->_value);
|
||||
$this->_value->traverse($callback);
|
||||
}
|
||||
|
||||
foreach ($this->_valueExpressions as $v) {
|
||||
$callback($v);
|
||||
$v->traverse($callback);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a deep clone.
|
||||
*
|
||||
* Clones the field and value if they are expression objects.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
foreach (['_value', '_field'] as $prop) {
|
||||
if ($this->{$prop} instanceof ExpressionInterface) {
|
||||
$this->{$prop} = clone $this->{$prop};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a template and a placeholder for the value after registering it
|
||||
* with the placeholder $binder
|
||||
*
|
||||
* @param \Cake\Database\ValueBinder $binder The value binder to use.
|
||||
* @return array First position containing the template and the second a placeholder
|
||||
*/
|
||||
protected function _stringExpression(ValueBinder $binder): array
|
||||
{
|
||||
$template = '%s ';
|
||||
|
||||
if ($this->_field instanceof ExpressionInterface && !$this->_field instanceof IdentifierExpression) {
|
||||
$template = '(%s) ';
|
||||
}
|
||||
|
||||
if ($this->_isMultiple) {
|
||||
$template .= '%s (%s)';
|
||||
$type = $this->_type;
|
||||
if ($type !== null) {
|
||||
$type = str_replace('[]', '', $type);
|
||||
}
|
||||
$value = $this->_flattenValue($this->_value, $binder, $type);
|
||||
|
||||
// To avoid SQL errors when comparing a field to a list of empty values,
|
||||
// better just throw an exception here
|
||||
if ($value === '') {
|
||||
$field = $this->_field instanceof ExpressionInterface ? $this->_field->sql($binder) : $this->_field;
|
||||
/** @psalm-suppress PossiblyInvalidCast */
|
||||
throw new DatabaseException(
|
||||
"Impossible to generate condition with empty list of values for field ($field)"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$template .= '%s %s';
|
||||
$value = $this->_bindValue($this->_value, $binder, $this->_type);
|
||||
}
|
||||
|
||||
return [$template, $value];
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a value in the placeholder generator and returns the generated placeholder
|
||||
*
|
||||
* @param mixed $value The value to bind
|
||||
* @param \Cake\Database\ValueBinder $binder The value binder to use
|
||||
* @param string|null $type The type of $value
|
||||
* @return string generated placeholder
|
||||
*/
|
||||
protected function _bindValue($value, ValueBinder $binder, ?string $type = null): string
|
||||
{
|
||||
$placeholder = $binder->placeholder('c');
|
||||
$binder->bind($placeholder, $value, $type);
|
||||
|
||||
return $placeholder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a traversable value into a set of placeholders generated by
|
||||
* $binder and separated by `,`
|
||||
*
|
||||
* @param iterable $value the value to flatten
|
||||
* @param \Cake\Database\ValueBinder $binder The value binder to use
|
||||
* @param string|null $type the type to cast values to
|
||||
* @return string
|
||||
*/
|
||||
protected function _flattenValue(iterable $value, ValueBinder $binder, ?string $type = null): string
|
||||
{
|
||||
$parts = [];
|
||||
if (is_array($value)) {
|
||||
foreach ($this->_valueExpressions as $k => $v) {
|
||||
$parts[$k] = $v->sql($binder);
|
||||
unset($value[$k]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($value)) {
|
||||
$parts += $binder->generateManyNamed($value, $type);
|
||||
}
|
||||
|
||||
return implode(',', $parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with the original $values in the first position
|
||||
* and all ExpressionInterface objects that could be found in the second
|
||||
* position.
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|iterable $values The rows to insert
|
||||
* @return array
|
||||
*/
|
||||
protected function _collectExpressions($values): array
|
||||
{
|
||||
if ($values instanceof ExpressionInterface) {
|
||||
return [$values, []];
|
||||
}
|
||||
|
||||
$expressions = $result = [];
|
||||
$isArray = is_array($values);
|
||||
|
||||
if ($isArray) {
|
||||
/** @var array $result */
|
||||
$result = $values;
|
||||
}
|
||||
|
||||
foreach ($values as $k => $v) {
|
||||
if ($v instanceof ExpressionInterface) {
|
||||
$expressions[$k] = $v;
|
||||
}
|
||||
|
||||
if ($isArray) {
|
||||
$result[$k] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
return [$result, $expressions];
|
||||
}
|
||||
}
|
||||
|
||||
// phpcs:disable
|
||||
class_alias(
|
||||
'Cake\Database\Expression\ComparisonExpression',
|
||||
'Cake\Database\Expression\Comparison'
|
||||
);
|
||||
// phpcs:enable
|
39
vendor/cakephp/database/Expression/FieldInterface.php
vendored
Normal file
39
vendor/cakephp/database/Expression/FieldInterface.php
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
<?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\Database\Expression;
|
||||
|
||||
/**
|
||||
* Describes a getter and a setter for the a field property. Useful for expressions
|
||||
* that contain an identifier to compare against.
|
||||
*/
|
||||
interface FieldInterface
|
||||
{
|
||||
/**
|
||||
* Sets the field name
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|array|string $field The field to compare with.
|
||||
* @return void
|
||||
*/
|
||||
public function setField($field): void;
|
||||
|
||||
/**
|
||||
* Returns the field name
|
||||
*
|
||||
* @return \Cake\Database\ExpressionInterface|array|string
|
||||
*/
|
||||
public function getField();
|
||||
}
|
51
vendor/cakephp/database/Expression/FieldTrait.php
vendored
Normal file
51
vendor/cakephp/database/Expression/FieldTrait.php
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
<?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\Database\Expression;
|
||||
|
||||
/**
|
||||
* Contains the field property with a getter and a setter for it
|
||||
*/
|
||||
trait FieldTrait
|
||||
{
|
||||
/**
|
||||
* The field name or expression to be used in the left hand side of the operator
|
||||
*
|
||||
* @var \Cake\Database\ExpressionInterface|array|string
|
||||
*/
|
||||
protected $_field;
|
||||
|
||||
/**
|
||||
* Sets the field name
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|array|string $field The field to compare with.
|
||||
* @return void
|
||||
*/
|
||||
public function setField($field): void
|
||||
{
|
||||
$this->_field = $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the field name
|
||||
*
|
||||
* @return \Cake\Database\ExpressionInterface|array|string
|
||||
*/
|
||||
public function getField()
|
||||
{
|
||||
return $this->_field;
|
||||
}
|
||||
}
|
178
vendor/cakephp/database/Expression/FunctionExpression.php
vendored
Normal file
178
vendor/cakephp/database/Expression/FunctionExpression.php
vendored
Normal file
@ -0,0 +1,178 @@
|
||||
<?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\Database\Expression;
|
||||
|
||||
use Cake\Database\ExpressionInterface;
|
||||
use Cake\Database\Query;
|
||||
use Cake\Database\Type\ExpressionTypeCasterTrait;
|
||||
use Cake\Database\TypedResultInterface;
|
||||
use Cake\Database\TypedResultTrait;
|
||||
use Cake\Database\ValueBinder;
|
||||
|
||||
/**
|
||||
* This class represents a function call string in a SQL statement. Calls can be
|
||||
* constructed by passing the name of the function and a list of params.
|
||||
* For security reasons, all params passed are quoted by default unless
|
||||
* explicitly told otherwise.
|
||||
*/
|
||||
class FunctionExpression extends QueryExpression implements TypedResultInterface
|
||||
{
|
||||
use ExpressionTypeCasterTrait;
|
||||
use TypedResultTrait;
|
||||
|
||||
/**
|
||||
* The name of the function to be constructed when generating the SQL string
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_name;
|
||||
|
||||
/**
|
||||
* Constructor. Takes a name for the function to be invoked and a list of params
|
||||
* to be passed into the function. Optionally you can pass a list of types to
|
||||
* be used for each bound param.
|
||||
*
|
||||
* By default, all params that are passed will be quoted. If you wish to use
|
||||
* literal arguments, you need to explicitly hint this function.
|
||||
*
|
||||
* ### Examples:
|
||||
*
|
||||
* `$f = new FunctionExpression('CONCAT', ['CakePHP', ' rules']);`
|
||||
*
|
||||
* Previous line will generate `CONCAT('CakePHP', ' rules')`
|
||||
*
|
||||
* `$f = new FunctionExpression('CONCAT', ['name' => 'literal', ' rules']);`
|
||||
*
|
||||
* Will produce `CONCAT(name, ' rules')`
|
||||
*
|
||||
* @param string $name the name of the function to be constructed
|
||||
* @param array $params list of arguments to be passed to the function
|
||||
* If associative the key would be used as argument when value is 'literal'
|
||||
* @param array<string, string>|array<string|null> $types Associative array of types to be associated with the
|
||||
* passed arguments
|
||||
* @param string $returnType The return type of this expression
|
||||
*/
|
||||
public function __construct(string $name, array $params = [], array $types = [], string $returnType = 'string')
|
||||
{
|
||||
$this->_name = $name;
|
||||
$this->_returnType = $returnType;
|
||||
parent::__construct($params, $types, ',');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the SQL function to be invoke in this expression.
|
||||
*
|
||||
* @param string $name The name of the function
|
||||
* @return $this
|
||||
*/
|
||||
public function setName(string $name)
|
||||
{
|
||||
$this->_name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the SQL function to be invoke in this expression.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds one or more arguments for the function call.
|
||||
*
|
||||
* @param array $conditions list of arguments to be passed to the function
|
||||
* If associative the key would be used as argument when value is 'literal'
|
||||
* @param array<string, string> $types Associative array of types to be associated with the
|
||||
* passed arguments
|
||||
* @param bool $prepend Whether to prepend or append to the list of arguments
|
||||
* @see \Cake\Database\Expression\FunctionExpression::__construct() for more details.
|
||||
* @return $this
|
||||
* @psalm-suppress MoreSpecificImplementedParamType
|
||||
*/
|
||||
public function add($conditions, array $types = [], bool $prepend = false)
|
||||
{
|
||||
$put = $prepend ? 'array_unshift' : 'array_push';
|
||||
$typeMap = $this->getTypeMap()->setTypes($types);
|
||||
foreach ($conditions as $k => $p) {
|
||||
if ($p === 'literal') {
|
||||
$put($this->_conditions, $k);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($p === 'identifier') {
|
||||
$put($this->_conditions, new IdentifierExpression($k));
|
||||
continue;
|
||||
}
|
||||
|
||||
$type = $typeMap->type($k);
|
||||
|
||||
if ($type !== null && !$p instanceof ExpressionInterface) {
|
||||
$p = $this->_castToExpression($p, $type);
|
||||
}
|
||||
|
||||
if ($p instanceof ExpressionInterface) {
|
||||
$put($this->_conditions, $p);
|
||||
continue;
|
||||
}
|
||||
|
||||
$put($this->_conditions, ['value' => $p, 'type' => $type]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function sql(ValueBinder $binder): string
|
||||
{
|
||||
$parts = [];
|
||||
foreach ($this->_conditions as $condition) {
|
||||
if ($condition instanceof Query) {
|
||||
$condition = sprintf('(%s)', $condition->sql($binder));
|
||||
} elseif ($condition instanceof ExpressionInterface) {
|
||||
$condition = $condition->sql($binder);
|
||||
} elseif (is_array($condition)) {
|
||||
$p = $binder->placeholder('param');
|
||||
$binder->bind($p, $condition['value'], $condition['type']);
|
||||
$condition = $p;
|
||||
}
|
||||
$parts[] = $condition;
|
||||
}
|
||||
|
||||
return $this->_name . sprintf('(%s)', implode(
|
||||
$this->_conjunction . ' ',
|
||||
$parts
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the function is in itself an expression to generate, thus
|
||||
* always adding 1 to the amount of expressions stored in this object.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return 1 + count($this->_conditions);
|
||||
}
|
||||
}
|
119
vendor/cakephp/database/Expression/IdentifierExpression.php
vendored
Normal file
119
vendor/cakephp/database/Expression/IdentifierExpression.php
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
<?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\Database\Expression;
|
||||
|
||||
use Cake\Database\ExpressionInterface;
|
||||
use Cake\Database\ValueBinder;
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* Represents a single identifier name in the database.
|
||||
*
|
||||
* Identifier values are unsafe with user supplied data.
|
||||
* Values will be quoted when identifier quoting is enabled.
|
||||
*
|
||||
* @see \Cake\Database\Query::identifier()
|
||||
*/
|
||||
class IdentifierExpression implements ExpressionInterface
|
||||
{
|
||||
/**
|
||||
* Holds the identifier string
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_identifier;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $collation;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $identifier The identifier this expression represents
|
||||
* @param string|null $collation The identifier collation
|
||||
*/
|
||||
public function __construct(string $identifier, ?string $collation = null)
|
||||
{
|
||||
$this->_identifier = $identifier;
|
||||
$this->collation = $collation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the identifier this expression represents
|
||||
*
|
||||
* @param string $identifier The identifier
|
||||
* @return void
|
||||
*/
|
||||
public function setIdentifier(string $identifier): void
|
||||
{
|
||||
$this->_identifier = $identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifier this expression represents
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIdentifier(): string
|
||||
{
|
||||
return $this->_identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the collation.
|
||||
*
|
||||
* @param string $collation Identifier collation
|
||||
* @return void
|
||||
*/
|
||||
public function setCollation(string $collation): void
|
||||
{
|
||||
$this->collation = $collation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the collation.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getCollation(): ?string
|
||||
{
|
||||
return $this->collation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function sql(ValueBinder $binder): string
|
||||
{
|
||||
$sql = $this->_identifier;
|
||||
if ($this->collation) {
|
||||
$sql .= ' COLLATE ' . $this->collation;
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function traverse(Closure $callback)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
}
|
88
vendor/cakephp/database/Expression/OrderByExpression.php
vendored
Normal file
88
vendor/cakephp/database/Expression/OrderByExpression.php
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
<?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\Database\Expression;
|
||||
|
||||
use Cake\Database\ExpressionInterface;
|
||||
use Cake\Database\ValueBinder;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* An expression object for ORDER BY clauses
|
||||
*/
|
||||
class OrderByExpression extends QueryExpression
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|array|string $conditions The sort columns
|
||||
* @param \Cake\Database\TypeMap|array<string, string> $types The types for each column.
|
||||
* @param string $conjunction The glue used to join conditions together.
|
||||
*/
|
||||
public function __construct($conditions = [], $types = [], $conjunction = '')
|
||||
{
|
||||
parent::__construct($conditions, $types, $conjunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function sql(ValueBinder $binder): string
|
||||
{
|
||||
$order = [];
|
||||
foreach ($this->_conditions as $k => $direction) {
|
||||
if ($direction instanceof ExpressionInterface) {
|
||||
$direction = $direction->sql($binder);
|
||||
}
|
||||
$order[] = is_numeric($k) ? $direction : sprintf('%s %s', $k, $direction);
|
||||
}
|
||||
|
||||
return sprintf('ORDER BY %s', implode(', ', $order));
|
||||
}
|
||||
|
||||
/**
|
||||
* Auxiliary function used for decomposing a nested array of conditions and
|
||||
* building a tree structure inside this object to represent the full SQL expression.
|
||||
*
|
||||
* New order by expressions are merged to existing ones
|
||||
*
|
||||
* @param array $conditions list of order by expressions
|
||||
* @param array $types list of types associated on fields referenced in $conditions
|
||||
* @return void
|
||||
*/
|
||||
protected function _addConditions(array $conditions, array $types): void
|
||||
{
|
||||
foreach ($conditions as $key => $val) {
|
||||
if (
|
||||
is_string($key) &&
|
||||
is_string($val) &&
|
||||
!in_array(strtoupper($val), ['ASC', 'DESC'], true)
|
||||
) {
|
||||
throw new RuntimeException(
|
||||
sprintf(
|
||||
'Passing extra expressions by associative array (`\'%s\' => \'%s\'`) ' .
|
||||
'is not allowed to avoid potential SQL injection. ' .
|
||||
'Use QueryExpression or numeric array instead.',
|
||||
$key,
|
||||
$val
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->_conditions = array_merge($this->_conditions, $conditions);
|
||||
}
|
||||
}
|
90
vendor/cakephp/database/Expression/OrderClauseExpression.php
vendored
Normal file
90
vendor/cakephp/database/Expression/OrderClauseExpression.php
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
<?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\Database\Expression;
|
||||
|
||||
use Cake\Database\ExpressionInterface;
|
||||
use Cake\Database\Query;
|
||||
use Cake\Database\ValueBinder;
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* An expression object for complex ORDER BY clauses
|
||||
*/
|
||||
class OrderClauseExpression implements ExpressionInterface, FieldInterface
|
||||
{
|
||||
use FieldTrait;
|
||||
|
||||
/**
|
||||
* The direction of sorting.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_direction;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|string $field The field to order on.
|
||||
* @param string $direction The direction to sort on.
|
||||
*/
|
||||
public function __construct($field, $direction)
|
||||
{
|
||||
$this->_field = $field;
|
||||
$this->_direction = strtolower($direction) === 'asc' ? 'ASC' : 'DESC';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function sql(ValueBinder $binder): string
|
||||
{
|
||||
/** @var \Cake\Database\ExpressionInterface|string $field */
|
||||
$field = $this->_field;
|
||||
if ($field instanceof Query) {
|
||||
$field = sprintf('(%s)', $field->sql($binder));
|
||||
} elseif ($field instanceof ExpressionInterface) {
|
||||
$field = $field->sql($binder);
|
||||
}
|
||||
|
||||
return sprintf('%s %s', $field, $this->_direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function traverse(Closure $callback)
|
||||
{
|
||||
if ($this->_field instanceof ExpressionInterface) {
|
||||
$callback($this->_field);
|
||||
$this->_field->traverse($callback);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a deep clone of the order clause.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
if ($this->_field instanceof ExpressionInterface) {
|
||||
$this->_field = clone $this->_field;
|
||||
}
|
||||
}
|
||||
}
|
876
vendor/cakephp/database/Expression/QueryExpression.php
vendored
Normal file
876
vendor/cakephp/database/Expression/QueryExpression.php
vendored
Normal file
@ -0,0 +1,876 @@
|
||||
<?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\Database\Expression;
|
||||
|
||||
use Cake\Database\ExpressionInterface;
|
||||
use Cake\Database\Query;
|
||||
use Cake\Database\TypeMapTrait;
|
||||
use Cake\Database\ValueBinder;
|
||||
use Closure;
|
||||
use Countable;
|
||||
use InvalidArgumentException;
|
||||
use function Cake\Core\deprecationWarning;
|
||||
|
||||
/**
|
||||
* Represents a SQL Query expression. Internally it stores a tree of
|
||||
* expressions that can be compiled by converting this object to string
|
||||
* and will contain a correctly parenthesized and nested expression.
|
||||
*/
|
||||
class QueryExpression implements ExpressionInterface, Countable
|
||||
{
|
||||
use TypeMapTrait;
|
||||
|
||||
/**
|
||||
* String to be used for joining each of the internal expressions
|
||||
* this object internally stores for example "AND", "OR", etc.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_conjunction;
|
||||
|
||||
/**
|
||||
* A list of strings or other expression objects that represent the "branches" of
|
||||
* the expression tree. For example one key of the array might look like "sum > :value"
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_conditions = [];
|
||||
|
||||
/**
|
||||
* Constructor. A new expression object can be created without any params and
|
||||
* be built dynamically. Otherwise, it is possible to pass an array of conditions
|
||||
* containing either a tree-like array structure to be parsed and/or other
|
||||
* expression objects. Optionally, you can set the conjunction keyword to be used
|
||||
* for joining each part of this level of the expression tree.
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|array|string $conditions Tree like array structure
|
||||
* containing all the conditions to be added or nested inside this expression object.
|
||||
* @param \Cake\Database\TypeMap|array $types Associative array of types to be associated with the values
|
||||
* passed in $conditions.
|
||||
* @param string $conjunction the glue that will join all the string conditions at this
|
||||
* level of the expression tree. For example "AND", "OR", "XOR"...
|
||||
* @see \Cake\Database\Expression\QueryExpression::add() for more details on $conditions and $types
|
||||
*/
|
||||
public function __construct($conditions = [], $types = [], $conjunction = 'AND')
|
||||
{
|
||||
$this->setTypeMap($types);
|
||||
$this->setConjunction(strtoupper($conjunction));
|
||||
if (!empty($conditions)) {
|
||||
$this->add($conditions, $this->getTypeMap()->getTypes());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the conjunction for the conditions at this level of the expression tree.
|
||||
*
|
||||
* @param string $conjunction Value to be used for joining conditions
|
||||
* @return $this
|
||||
*/
|
||||
public function setConjunction(string $conjunction)
|
||||
{
|
||||
$this->_conjunction = strtoupper($conjunction);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the currently configured conjunction for the conditions at this level of the expression tree.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getConjunction(): string
|
||||
{
|
||||
return $this->_conjunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds one or more conditions to this expression object. Conditions can be
|
||||
* expressed in a one dimensional array, that will cause all conditions to
|
||||
* be added directly at this level of the tree or they can be nested arbitrarily
|
||||
* making it create more expression objects that will be nested inside and
|
||||
* configured to use the specified conjunction.
|
||||
*
|
||||
* If the type passed for any of the fields is expressed "type[]" (note braces)
|
||||
* then it will cause the placeholder to be re-written dynamically so if the
|
||||
* value is an array, it will create as many placeholders as values are in it.
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|array|string $conditions single or multiple conditions to
|
||||
* be added. When using an array and the key is 'OR' or 'AND' a new expression
|
||||
* object will be created with that conjunction and internal array value passed
|
||||
* as conditions.
|
||||
* @param array<int|string, string> $types Associative array of fields pointing to the type of the
|
||||
* values that are being passed. Used for correctly binding values to statements.
|
||||
* @see \Cake\Database\Query::where() for examples on conditions
|
||||
* @return $this
|
||||
*/
|
||||
public function add($conditions, array $types = [])
|
||||
{
|
||||
if (is_string($conditions) || $conditions instanceof ExpressionInterface) {
|
||||
$this->_conditions[] = $conditions;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->_addConditions($conditions, $types);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new condition to the expression object in the form "field = value".
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|string $field Database field to be compared against value
|
||||
* @param mixed $value The value to be bound to $field for comparison
|
||||
* @param string|null $type the type name for $value as configured using the Type map.
|
||||
* If it is suffixed with "[]" and the value is an array then multiple placeholders
|
||||
* will be created, one per each value in the array.
|
||||
* @return $this
|
||||
*/
|
||||
public function eq($field, $value, ?string $type = null)
|
||||
{
|
||||
if ($type === null) {
|
||||
$type = $this->_calculateType($field);
|
||||
}
|
||||
|
||||
return $this->add(new ComparisonExpression($field, $value, $type, '='));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new condition to the expression object in the form "field != value".
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|string $field Database field to be compared against value
|
||||
* @param mixed $value The value to be bound to $field for comparison
|
||||
* @param string|null $type the type name for $value as configured using the Type map.
|
||||
* If it is suffixed with "[]" and the value is an array then multiple placeholders
|
||||
* will be created, one per each value in the array.
|
||||
* @return $this
|
||||
*/
|
||||
public function notEq($field, $value, $type = null)
|
||||
{
|
||||
if ($type === null) {
|
||||
$type = $this->_calculateType($field);
|
||||
}
|
||||
|
||||
return $this->add(new ComparisonExpression($field, $value, $type, '!='));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new condition to the expression object in the form "field > value".
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|string $field Database field to be compared against value
|
||||
* @param mixed $value The value to be bound to $field for comparison
|
||||
* @param string|null $type the type name for $value as configured using the Type map.
|
||||
* @return $this
|
||||
*/
|
||||
public function gt($field, $value, $type = null)
|
||||
{
|
||||
if ($type === null) {
|
||||
$type = $this->_calculateType($field);
|
||||
}
|
||||
|
||||
return $this->add(new ComparisonExpression($field, $value, $type, '>'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new condition to the expression object in the form "field < value".
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|string $field Database field to be compared against value
|
||||
* @param mixed $value The value to be bound to $field for comparison
|
||||
* @param string|null $type the type name for $value as configured using the Type map.
|
||||
* @return $this
|
||||
*/
|
||||
public function lt($field, $value, $type = null)
|
||||
{
|
||||
if ($type === null) {
|
||||
$type = $this->_calculateType($field);
|
||||
}
|
||||
|
||||
return $this->add(new ComparisonExpression($field, $value, $type, '<'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new condition to the expression object in the form "field >= value".
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|string $field Database field to be compared against value
|
||||
* @param mixed $value The value to be bound to $field for comparison
|
||||
* @param string|null $type the type name for $value as configured using the Type map.
|
||||
* @return $this
|
||||
*/
|
||||
public function gte($field, $value, $type = null)
|
||||
{
|
||||
if ($type === null) {
|
||||
$type = $this->_calculateType($field);
|
||||
}
|
||||
|
||||
return $this->add(new ComparisonExpression($field, $value, $type, '>='));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new condition to the expression object in the form "field <= value".
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|string $field Database field to be compared against value
|
||||
* @param mixed $value The value to be bound to $field for comparison
|
||||
* @param string|null $type the type name for $value as configured using the Type map.
|
||||
* @return $this
|
||||
*/
|
||||
public function lte($field, $value, $type = null)
|
||||
{
|
||||
if ($type === null) {
|
||||
$type = $this->_calculateType($field);
|
||||
}
|
||||
|
||||
return $this->add(new ComparisonExpression($field, $value, $type, '<='));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new condition to the expression object in the form "field IS NULL".
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|string $field database field to be
|
||||
* tested for null
|
||||
* @return $this
|
||||
*/
|
||||
public function isNull($field)
|
||||
{
|
||||
if (!($field instanceof ExpressionInterface)) {
|
||||
$field = new IdentifierExpression($field);
|
||||
}
|
||||
|
||||
return $this->add(new UnaryExpression('IS NULL', $field, UnaryExpression::POSTFIX));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new condition to the expression object in the form "field IS NOT NULL".
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|string $field database field to be
|
||||
* tested for not null
|
||||
* @return $this
|
||||
*/
|
||||
public function isNotNull($field)
|
||||
{
|
||||
if (!($field instanceof ExpressionInterface)) {
|
||||
$field = new IdentifierExpression($field);
|
||||
}
|
||||
|
||||
return $this->add(new UnaryExpression('IS NOT NULL', $field, UnaryExpression::POSTFIX));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new condition to the expression object in the form "field LIKE value".
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|string $field Database field to be compared against value
|
||||
* @param mixed $value The value to be bound to $field for comparison
|
||||
* @param string|null $type the type name for $value as configured using the Type map.
|
||||
* @return $this
|
||||
*/
|
||||
public function like($field, $value, $type = null)
|
||||
{
|
||||
if ($type === null) {
|
||||
$type = $this->_calculateType($field);
|
||||
}
|
||||
|
||||
return $this->add(new ComparisonExpression($field, $value, $type, 'LIKE'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new condition to the expression object in the form "field NOT LIKE value".
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|string $field Database field to be compared against value
|
||||
* @param mixed $value The value to be bound to $field for comparison
|
||||
* @param string|null $type the type name for $value as configured using the Type map.
|
||||
* @return $this
|
||||
*/
|
||||
public function notLike($field, $value, $type = null)
|
||||
{
|
||||
if ($type === null) {
|
||||
$type = $this->_calculateType($field);
|
||||
}
|
||||
|
||||
return $this->add(new ComparisonExpression($field, $value, $type, 'NOT LIKE'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new condition to the expression object in the form
|
||||
* "field IN (value1, value2)".
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|string $field Database field to be compared against value
|
||||
* @param \Cake\Database\ExpressionInterface|array|string $values the value to be bound to $field for comparison
|
||||
* @param string|null $type the type name for $value as configured using the Type map.
|
||||
* @return $this
|
||||
*/
|
||||
public function in($field, $values, $type = null)
|
||||
{
|
||||
if ($type === null) {
|
||||
$type = $this->_calculateType($field);
|
||||
}
|
||||
$type = $type ?: 'string';
|
||||
$type .= '[]';
|
||||
$values = $values instanceof ExpressionInterface ? $values : (array)$values;
|
||||
|
||||
return $this->add(new ComparisonExpression($field, $values, $type, 'IN'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new case expression to the expression object
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|array $conditions The conditions to test. Must be a ExpressionInterface
|
||||
* instance, or an array of ExpressionInterface instances.
|
||||
* @param \Cake\Database\ExpressionInterface|array $values Associative array of values to be associated with the
|
||||
* conditions passed in $conditions. If there are more $values than $conditions,
|
||||
* the last $value is used as the `ELSE` value.
|
||||
* @param array<string> $types Associative array of types to be associated with the values
|
||||
* passed in $values
|
||||
* @return $this
|
||||
* @deprecated 4.3.0 Use QueryExpression::case() or CaseStatementExpression instead
|
||||
*/
|
||||
public function addCase($conditions, $values = [], $types = [])
|
||||
{
|
||||
deprecationWarning('QueryExpression::addCase() is deprecated, use case() instead.');
|
||||
|
||||
return $this->add(new CaseExpression($conditions, $values, $types));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new case expression object.
|
||||
*
|
||||
* When a value is set, the syntax generated is
|
||||
* `CASE case_value WHEN when_value ... END` (simple case),
|
||||
* where the `when_value`'s are compared against the
|
||||
* `case_value`.
|
||||
*
|
||||
* When no value is set, the syntax generated is
|
||||
* `CASE WHEN when_conditions ... END` (searched case),
|
||||
* where the conditions hold the comparisons.
|
||||
*
|
||||
* Note that `null` is a valid case value, and thus should
|
||||
* only be passed if you actually want to create the simple
|
||||
* case expression variant!
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|object|scalar|null $value The case value.
|
||||
* @param string|null $type The case value type. If no type is provided, the type will be tried to be inferred
|
||||
* from the value.
|
||||
* @return \Cake\Database\Expression\CaseStatementExpression
|
||||
*/
|
||||
public function case($value = null, ?string $type = null): CaseStatementExpression
|
||||
{
|
||||
if (func_num_args() > 0) {
|
||||
$expression = new CaseStatementExpression($value, $type);
|
||||
} else {
|
||||
$expression = new CaseStatementExpression();
|
||||
}
|
||||
|
||||
return $expression->setTypeMap($this->getTypeMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new condition to the expression object in the form
|
||||
* "field NOT IN (value1, value2)".
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|string $field Database field to be compared against value
|
||||
* @param \Cake\Database\ExpressionInterface|array|string $values the value to be bound to $field for comparison
|
||||
* @param string|null $type the type name for $value as configured using the Type map.
|
||||
* @return $this
|
||||
*/
|
||||
public function notIn($field, $values, $type = null)
|
||||
{
|
||||
if ($type === null) {
|
||||
$type = $this->_calculateType($field);
|
||||
}
|
||||
$type = $type ?: 'string';
|
||||
$type .= '[]';
|
||||
$values = $values instanceof ExpressionInterface ? $values : (array)$values;
|
||||
|
||||
return $this->add(new ComparisonExpression($field, $values, $type, 'NOT IN'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new condition to the expression object in the form
|
||||
* "(field NOT IN (value1, value2) OR field IS NULL".
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|string $field Database field to be compared against value
|
||||
* @param \Cake\Database\ExpressionInterface|array|string $values the value to be bound to $field for comparison
|
||||
* @param string|null $type the type name for $value as configured using the Type map.
|
||||
* @return $this
|
||||
*/
|
||||
public function notInOrNull($field, $values, ?string $type = null)
|
||||
{
|
||||
$or = new static([], [], 'OR');
|
||||
$or
|
||||
->notIn($field, $values, $type)
|
||||
->isNull($field);
|
||||
|
||||
return $this->add($or);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new condition to the expression object in the form "EXISTS (...)".
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface $expression the inner query
|
||||
* @return $this
|
||||
*/
|
||||
public function exists(ExpressionInterface $expression)
|
||||
{
|
||||
return $this->add(new UnaryExpression('EXISTS', $expression, UnaryExpression::PREFIX));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new condition to the expression object in the form "NOT EXISTS (...)".
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface $expression the inner query
|
||||
* @return $this
|
||||
*/
|
||||
public function notExists(ExpressionInterface $expression)
|
||||
{
|
||||
return $this->add(new UnaryExpression('NOT EXISTS', $expression, UnaryExpression::PREFIX));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new condition to the expression object in the form
|
||||
* "field BETWEEN from AND to".
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|string $field The field name to compare for values inbetween the range.
|
||||
* @param mixed $from The initial value of the range.
|
||||
* @param mixed $to The ending value in the comparison range.
|
||||
* @param string|null $type the type name for $value as configured using the Type map.
|
||||
* @return $this
|
||||
*/
|
||||
public function between($field, $from, $to, $type = null)
|
||||
{
|
||||
if ($type === null) {
|
||||
$type = $this->_calculateType($field);
|
||||
}
|
||||
|
||||
return $this->add(new BetweenExpression($field, $from, $to, $type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new QueryExpression object containing all the conditions passed
|
||||
* and set up the conjunction to be "AND"
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|\Closure|array|string $conditions to be joined with AND
|
||||
* @param array<string, string> $types Associative array of fields pointing to the type of the
|
||||
* values that are being passed. Used for correctly binding values to statements.
|
||||
* @return \Cake\Database\Expression\QueryExpression
|
||||
*/
|
||||
public function and($conditions, $types = [])
|
||||
{
|
||||
if ($conditions instanceof Closure) {
|
||||
return $conditions(new static([], $this->getTypeMap()->setTypes($types)));
|
||||
}
|
||||
|
||||
return new static($conditions, $this->getTypeMap()->setTypes($types));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new QueryExpression object containing all the conditions passed
|
||||
* and set up the conjunction to be "OR"
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|\Closure|array|string $conditions to be joined with OR
|
||||
* @param array<string, string> $types Associative array of fields pointing to the type of the
|
||||
* values that are being passed. Used for correctly binding values to statements.
|
||||
* @return \Cake\Database\Expression\QueryExpression
|
||||
*/
|
||||
public function or($conditions, $types = [])
|
||||
{
|
||||
if ($conditions instanceof Closure) {
|
||||
return $conditions(new static([], $this->getTypeMap()->setTypes($types), 'OR'));
|
||||
}
|
||||
|
||||
return new static($conditions, $this->getTypeMap()->setTypes($types), 'OR');
|
||||
}
|
||||
|
||||
// phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
|
||||
|
||||
/**
|
||||
* Returns a new QueryExpression object containing all the conditions passed
|
||||
* and set up the conjunction to be "AND"
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|\Closure|array|string $conditions to be joined with AND
|
||||
* @param array<string, string> $types Associative array of fields pointing to the type of the
|
||||
* values that are being passed. Used for correctly binding values to statements.
|
||||
* @return \Cake\Database\Expression\QueryExpression
|
||||
* @deprecated 4.0.0 Use {@link and()} instead.
|
||||
*/
|
||||
public function and_($conditions, $types = [])
|
||||
{
|
||||
deprecationWarning('QueryExpression::and_() is deprecated use and() instead.');
|
||||
|
||||
return $this->and($conditions, $types);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new QueryExpression object containing all the conditions passed
|
||||
* and set up the conjunction to be "OR"
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|\Closure|array|string $conditions to be joined with OR
|
||||
* @param array<string, string> $types Associative array of fields pointing to the type of the
|
||||
* values that are being passed. Used for correctly binding values to statements.
|
||||
* @return \Cake\Database\Expression\QueryExpression
|
||||
* @deprecated 4.0.0 Use {@link or()} instead.
|
||||
*/
|
||||
public function or_($conditions, $types = [])
|
||||
{
|
||||
deprecationWarning('QueryExpression::or_() is deprecated use or() instead.');
|
||||
|
||||
return $this->or($conditions, $types);
|
||||
}
|
||||
|
||||
// phpcs:enable
|
||||
|
||||
/**
|
||||
* Adds a new set of conditions to this level of the tree and negates
|
||||
* the final result by prepending a NOT, it will look like
|
||||
* "NOT ( (condition1) AND (conditions2) )" conjunction depends on the one
|
||||
* currently configured for this object.
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|\Closure|array|string $conditions to be added and negated
|
||||
* @param array<string, string> $types Associative array of fields pointing to the type of the
|
||||
* values that are being passed. Used for correctly binding values to statements.
|
||||
* @return $this
|
||||
*/
|
||||
public function not($conditions, $types = [])
|
||||
{
|
||||
return $this->add(['NOT' => $conditions], $types);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of internal conditions that are stored in this expression.
|
||||
* Useful to determine if this expression object is void or it will generate
|
||||
* a non-empty string when compiled
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->_conditions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds equal condition or assignment with identifier wrapping.
|
||||
*
|
||||
* @param string $leftField Left join condition field name.
|
||||
* @param string $rightField Right join condition field name.
|
||||
* @return $this
|
||||
*/
|
||||
public function equalFields(string $leftField, string $rightField)
|
||||
{
|
||||
$wrapIdentifier = function ($field) {
|
||||
if ($field instanceof ExpressionInterface) {
|
||||
return $field;
|
||||
}
|
||||
|
||||
return new IdentifierExpression($field);
|
||||
};
|
||||
|
||||
return $this->eq($wrapIdentifier($leftField), $wrapIdentifier($rightField));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function sql(ValueBinder $binder): string
|
||||
{
|
||||
$len = $this->count();
|
||||
if ($len === 0) {
|
||||
return '';
|
||||
}
|
||||
$conjunction = $this->_conjunction;
|
||||
$template = $len === 1 ? '%s' : '(%s)';
|
||||
$parts = [];
|
||||
foreach ($this->_conditions as $part) {
|
||||
if ($part instanceof Query) {
|
||||
$part = '(' . $part->sql($binder) . ')';
|
||||
} elseif ($part instanceof ExpressionInterface) {
|
||||
$part = $part->sql($binder);
|
||||
}
|
||||
if ($part !== '') {
|
||||
$parts[] = $part;
|
||||
}
|
||||
}
|
||||
|
||||
return sprintf($template, implode(" $conjunction ", $parts));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function traverse(Closure $callback)
|
||||
{
|
||||
foreach ($this->_conditions as $c) {
|
||||
if ($c instanceof ExpressionInterface) {
|
||||
$callback($c);
|
||||
$c->traverse($callback);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a callable function for each of the parts that form this expression.
|
||||
*
|
||||
* The callable function is required to return a value with which the currently
|
||||
* visited part will be replaced. If the callable function returns null then
|
||||
* the part will be discarded completely from this expression.
|
||||
*
|
||||
* The callback function will receive each of the conditions as first param and
|
||||
* the key as second param. It is possible to declare the second parameter as
|
||||
* passed by reference, this will enable you to change the key under which the
|
||||
* modified part is stored.
|
||||
*
|
||||
* @param callable $callback The callable to apply to each part.
|
||||
* @return $this
|
||||
*/
|
||||
public function iterateParts(callable $callback)
|
||||
{
|
||||
$parts = [];
|
||||
foreach ($this->_conditions as $k => $c) {
|
||||
$key = &$k;
|
||||
$part = $callback($c, $key);
|
||||
if ($part !== null) {
|
||||
$parts[$key] = $part;
|
||||
}
|
||||
}
|
||||
$this->_conditions = $parts;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a callable is acceptable.
|
||||
*
|
||||
* We don't accept ['class', 'method'] style callbacks,
|
||||
* as they often contain user input and arrays of strings
|
||||
* are easy to sneak in.
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|callable|array|string $callable The callable to check.
|
||||
* @return bool Valid callable.
|
||||
* @deprecated 4.2.0 This method is unused.
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function isCallable($callable): bool
|
||||
{
|
||||
if (is_string($callable)) {
|
||||
return false;
|
||||
}
|
||||
if (is_object($callable) && is_callable($callable)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return is_array($callable) && isset($callable[0]) && is_object($callable[0]) && is_callable($callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this expression contains any other nested
|
||||
* ExpressionInterface objects
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasNestedExpression(): bool
|
||||
{
|
||||
foreach ($this->_conditions as $c) {
|
||||
if ($c instanceof ExpressionInterface) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auxiliary function used for decomposing a nested array of conditions and build
|
||||
* a tree structure inside this object to represent the full SQL expression.
|
||||
* String conditions are stored directly in the conditions, while any other
|
||||
* representation is wrapped around an adequate instance or of this class.
|
||||
*
|
||||
* @param array $conditions list of conditions to be stored in this object
|
||||
* @param array<int|string, string> $types list of types associated on fields referenced in $conditions
|
||||
* @return void
|
||||
*/
|
||||
protected function _addConditions(array $conditions, array $types): void
|
||||
{
|
||||
$operators = ['and', 'or', 'xor'];
|
||||
|
||||
$typeMap = $this->getTypeMap()->setTypes($types);
|
||||
|
||||
foreach ($conditions as $k => $c) {
|
||||
$numericKey = is_numeric($k);
|
||||
|
||||
if ($c instanceof Closure) {
|
||||
$expr = new static([], $typeMap);
|
||||
$c = $c($expr, $this);
|
||||
}
|
||||
|
||||
if ($numericKey && empty($c)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$isArray = is_array($c);
|
||||
$isOperator = $isNot = false;
|
||||
if (!$numericKey) {
|
||||
$normalizedKey = strtolower($k);
|
||||
$isOperator = in_array($normalizedKey, $operators);
|
||||
$isNot = $normalizedKey === 'not';
|
||||
}
|
||||
|
||||
if (($isOperator || $isNot) && ($isArray || $c instanceof Countable) && count($c) === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($numericKey && $c instanceof ExpressionInterface) {
|
||||
$this->_conditions[] = $c;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($numericKey && is_string($c)) {
|
||||
$this->_conditions[] = $c;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($numericKey && $isArray || $isOperator) {
|
||||
$this->_conditions[] = new static($c, $typeMap, $numericKey ? 'AND' : $k);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($isNot) {
|
||||
$this->_conditions[] = new UnaryExpression('NOT', new static($c, $typeMap));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$numericKey) {
|
||||
$this->_conditions[] = $this->_parseCondition($k, $c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string conditions by trying to extract the operator inside it if any
|
||||
* and finally returning either an adequate QueryExpression object or a plain
|
||||
* string representation of the condition. This function is responsible for
|
||||
* generating the placeholders and replacing the values by them, while storing
|
||||
* the value elsewhere for future binding.
|
||||
*
|
||||
* @param string $field The value from which the actual field and operator will
|
||||
* be extracted.
|
||||
* @param mixed $value The value to be bound to a placeholder for the field
|
||||
* @return \Cake\Database\ExpressionInterface
|
||||
* @throws \InvalidArgumentException If operator is invalid or missing on NULL usage.
|
||||
*/
|
||||
protected function _parseCondition(string $field, $value)
|
||||
{
|
||||
$field = trim($field);
|
||||
$operator = '=';
|
||||
$expression = $field;
|
||||
|
||||
$spaces = substr_count($field, ' ');
|
||||
// Handle field values that contain multiple spaces, such as
|
||||
// operators with a space in them like `field IS NOT` and
|
||||
// `field NOT LIKE`, or combinations with function expressions
|
||||
// like `CONCAT(first_name, ' ', last_name) IN`.
|
||||
if ($spaces > 1) {
|
||||
$parts = explode(' ', $field);
|
||||
if (preg_match('/(is not|not \w+)$/i', $field)) {
|
||||
$last = array_pop($parts);
|
||||
$second = array_pop($parts);
|
||||
$parts[] = "{$second} {$last}";
|
||||
}
|
||||
$operator = array_pop($parts);
|
||||
$expression = implode(' ', $parts);
|
||||
} elseif ($spaces == 1) {
|
||||
$parts = explode(' ', $field, 2);
|
||||
[$expression, $operator] = $parts;
|
||||
}
|
||||
$operator = strtolower(trim($operator));
|
||||
$type = $this->getTypeMap()->type($expression);
|
||||
|
||||
$typeMultiple = (is_string($type) && strpos($type, '[]') !== false);
|
||||
if (in_array($operator, ['in', 'not in']) || $typeMultiple) {
|
||||
$type = $type ?: 'string';
|
||||
if (!$typeMultiple) {
|
||||
$type .= '[]';
|
||||
}
|
||||
$operator = $operator === '=' ? 'IN' : $operator;
|
||||
$operator = $operator === '!=' ? 'NOT IN' : $operator;
|
||||
$typeMultiple = true;
|
||||
}
|
||||
|
||||
if ($typeMultiple) {
|
||||
$value = $value instanceof ExpressionInterface ? $value : (array)$value;
|
||||
}
|
||||
|
||||
if ($operator === 'is' && $value === null) {
|
||||
return new UnaryExpression(
|
||||
'IS NULL',
|
||||
new IdentifierExpression($expression),
|
||||
UnaryExpression::POSTFIX
|
||||
);
|
||||
}
|
||||
|
||||
if ($operator === 'is not' && $value === null) {
|
||||
return new UnaryExpression(
|
||||
'IS NOT NULL',
|
||||
new IdentifierExpression($expression),
|
||||
UnaryExpression::POSTFIX
|
||||
);
|
||||
}
|
||||
|
||||
if ($operator === 'is' && $value !== null) {
|
||||
$operator = '=';
|
||||
}
|
||||
|
||||
if ($operator === 'is not' && $value !== null) {
|
||||
$operator = '!=';
|
||||
}
|
||||
|
||||
if ($value === null && $this->_conjunction !== ',') {
|
||||
throw new InvalidArgumentException(
|
||||
sprintf('Expression `%s` is missing operator (IS, IS NOT) with `null` value.', $expression)
|
||||
);
|
||||
}
|
||||
|
||||
return new ComparisonExpression($expression, $value, $type, $operator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type name for the passed field if it was stored in the typeMap
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|string $field The field name to get a type for.
|
||||
* @return string|null The computed type or null, if the type is unknown.
|
||||
*/
|
||||
protected function _calculateType($field): ?string
|
||||
{
|
||||
$field = $field instanceof IdentifierExpression ? $field->getIdentifier() : $field;
|
||||
if (is_string($field)) {
|
||||
return $this->getTypeMap()->type($field);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone this object and its subtree of expressions.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
foreach ($this->_conditions as $i => $condition) {
|
||||
if ($condition instanceof ExpressionInterface) {
|
||||
$this->_conditions[$i] = clone $condition;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
87
vendor/cakephp/database/Expression/StringExpression.php
vendored
Normal file
87
vendor/cakephp/database/Expression/StringExpression.php
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
<?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\Database\Expression;
|
||||
|
||||
use Cake\Database\ExpressionInterface;
|
||||
use Cake\Database\ValueBinder;
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* String expression with collation.
|
||||
*/
|
||||
class StringExpression implements ExpressionInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $string;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $collation;
|
||||
|
||||
/**
|
||||
* @param string $string String value
|
||||
* @param string $collation String collation
|
||||
*/
|
||||
public function __construct(string $string, string $collation)
|
||||
{
|
||||
$this->string = $string;
|
||||
$this->collation = $collation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the string collation.
|
||||
*
|
||||
* @param string $collation String collation
|
||||
* @return void
|
||||
*/
|
||||
public function setCollation(string $collation): void
|
||||
{
|
||||
$this->collation = $collation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string collation.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCollation(): string
|
||||
{
|
||||
return $this->collation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function sql(ValueBinder $binder): string
|
||||
{
|
||||
$placeholder = $binder->placeholder('c');
|
||||
$binder->bind($placeholder, $this->string, 'string');
|
||||
|
||||
return $placeholder . ' COLLATE ' . $this->collation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function traverse(Closure $callback)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
}
|
231
vendor/cakephp/database/Expression/TupleComparison.php
vendored
Normal file
231
vendor/cakephp/database/Expression/TupleComparison.php
vendored
Normal file
@ -0,0 +1,231 @@
|
||||
<?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\Database\Expression;
|
||||
|
||||
use Cake\Database\ExpressionInterface;
|
||||
use Cake\Database\ValueBinder;
|
||||
use Closure;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* This expression represents SQL fragments that are used for comparing one tuple
|
||||
* to another, one tuple to a set of other tuples or one tuple to an expression
|
||||
*/
|
||||
class TupleComparison extends ComparisonExpression
|
||||
{
|
||||
/**
|
||||
* The type to be used for casting the value to a database representation
|
||||
*
|
||||
* @var array<string|null>
|
||||
* @psalm-suppress NonInvariantDocblockPropertyType
|
||||
*/
|
||||
protected $_type;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \Cake\Database\ExpressionInterface|array|string $fields the fields to use to form a tuple
|
||||
* @param \Cake\Database\ExpressionInterface|array $values the values to use to form a tuple
|
||||
* @param array<string|null> $types the types names to use for casting each of the values, only
|
||||
* one type per position in the value array in needed
|
||||
* @param string $conjunction the operator used for comparing field and value
|
||||
*/
|
||||
public function __construct($fields, $values, array $types = [], string $conjunction = '=')
|
||||
{
|
||||
$this->_type = $types;
|
||||
$this->setField($fields);
|
||||
$this->_operator = $conjunction;
|
||||
$this->setValue($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type to be used for casting the value to a database representation
|
||||
*
|
||||
* @return array<string|null>
|
||||
*/
|
||||
public function getType(): array
|
||||
{
|
||||
return $this->_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value
|
||||
*
|
||||
* @param mixed $value The value to compare
|
||||
* @return void
|
||||
*/
|
||||
public function setValue($value): void
|
||||
{
|
||||
if ($this->isMulti()) {
|
||||
if (is_array($value) && !is_array(current($value))) {
|
||||
throw new InvalidArgumentException(
|
||||
'Multi-tuple comparisons require a multi-tuple value, single-tuple given.'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (is_array($value) && is_array(current($value))) {
|
||||
throw new InvalidArgumentException(
|
||||
'Single-tuple comparisons require a single-tuple value, multi-tuple given.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->_value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function sql(ValueBinder $binder): string
|
||||
{
|
||||
$template = '(%s) %s (%s)';
|
||||
$fields = [];
|
||||
$originalFields = $this->getField();
|
||||
|
||||
if (!is_array($originalFields)) {
|
||||
$originalFields = [$originalFields];
|
||||
}
|
||||
|
||||
foreach ($originalFields as $field) {
|
||||
$fields[] = $field instanceof ExpressionInterface ? $field->sql($binder) : $field;
|
||||
}
|
||||
|
||||
$values = $this->_stringifyValues($binder);
|
||||
|
||||
$field = implode(', ', $fields);
|
||||
|
||||
return sprintf($template, $field, $this->_operator, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string with the values as placeholders in a string to be used
|
||||
* for the SQL version of this expression
|
||||
*
|
||||
* @param \Cake\Database\ValueBinder $binder The value binder to convert expressions with.
|
||||
* @return string
|
||||
*/
|
||||
protected function _stringifyValues(ValueBinder $binder): string
|
||||
{
|
||||
$values = [];
|
||||
$parts = $this->getValue();
|
||||
|
||||
if ($parts instanceof ExpressionInterface) {
|
||||
return $parts->sql($binder);
|
||||
}
|
||||
|
||||
foreach ($parts as $i => $value) {
|
||||
if ($value instanceof ExpressionInterface) {
|
||||
$values[] = $value->sql($binder);
|
||||
continue;
|
||||
}
|
||||
|
||||
$type = $this->_type;
|
||||
$isMultiOperation = $this->isMulti();
|
||||
if (empty($type)) {
|
||||
$type = null;
|
||||
}
|
||||
|
||||
if ($isMultiOperation) {
|
||||
$bound = [];
|
||||
foreach ($value as $k => $val) {
|
||||
/** @var string $valType */
|
||||
$valType = $type && isset($type[$k]) ? $type[$k] : $type;
|
||||
$bound[] = $this->_bindValue($val, $binder, $valType);
|
||||
}
|
||||
|
||||
$values[] = sprintf('(%s)', implode(',', $bound));
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var string $valType */
|
||||
$valType = $type && isset($type[$i]) ? $type[$i] : $type;
|
||||
$values[] = $this->_bindValue($value, $binder, $valType);
|
||||
}
|
||||
|
||||
return implode(', ', $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function _bindValue($value, ValueBinder $binder, ?string $type = null): string
|
||||
{
|
||||
$placeholder = $binder->placeholder('tuple');
|
||||
$binder->bind($placeholder, $value, $type);
|
||||
|
||||
return $placeholder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function traverse(Closure $callback)
|
||||
{
|
||||
/** @var array<string> $fields */
|
||||
$fields = $this->getField();
|
||||
foreach ($fields as $field) {
|
||||
$this->_traverseValue($field, $callback);
|
||||
}
|
||||
|
||||
$value = $this->getValue();
|
||||
if ($value instanceof ExpressionInterface) {
|
||||
$callback($value);
|
||||
$value->traverse($callback);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
foreach ($value as $val) {
|
||||
if ($this->isMulti()) {
|
||||
foreach ($val as $v) {
|
||||
$this->_traverseValue($v, $callback);
|
||||
}
|
||||
} else {
|
||||
$this->_traverseValue($val, $callback);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conditionally executes the callback for the passed value if
|
||||
* it is an ExpressionInterface
|
||||
*
|
||||
* @param mixed $value The value to traverse
|
||||
* @param \Closure $callback The callable to use when traversing
|
||||
* @return void
|
||||
*/
|
||||
protected function _traverseValue($value, Closure $callback): void
|
||||
{
|
||||
if ($value instanceof ExpressionInterface) {
|
||||
$callback($value);
|
||||
$value->traverse($callback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if each of the values in this expressions is a tuple in
|
||||
* itself
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isMulti(): bool
|
||||
{
|
||||
return in_array(strtolower($this->_operator), ['in', 'not in']);
|
||||
}
|
||||
}
|
118
vendor/cakephp/database/Expression/UnaryExpression.php
vendored
Normal file
118
vendor/cakephp/database/Expression/UnaryExpression.php
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
<?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\Database\Expression;
|
||||
|
||||
use Cake\Database\ExpressionInterface;
|
||||
use Cake\Database\ValueBinder;
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* An expression object that represents an expression with only a single operand.
|
||||
*/
|
||||
class UnaryExpression implements ExpressionInterface
|
||||
{
|
||||
/**
|
||||
* Indicates that the operation is in pre-order
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public const PREFIX = 0;
|
||||
|
||||
/**
|
||||
* Indicates that the operation is in post-order
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public const POSTFIX = 1;
|
||||
|
||||
/**
|
||||
* The operator this unary expression represents
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_operator;
|
||||
|
||||
/**
|
||||
* Holds the value which the unary expression operates
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $_value;
|
||||
|
||||
/**
|
||||
* Where to place the operator
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $position;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $operator The operator to used for the expression
|
||||
* @param mixed $value the value to use as the operand for the expression
|
||||
* @param int $position either UnaryExpression::PREFIX or UnaryExpression::POSTFIX
|
||||
*/
|
||||
public function __construct(string $operator, $value, $position = self::PREFIX)
|
||||
{
|
||||
$this->_operator = $operator;
|
||||
$this->_value = $value;
|
||||
$this->position = $position;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function sql(ValueBinder $binder): string
|
||||
{
|
||||
$operand = $this->_value;
|
||||
if ($operand instanceof ExpressionInterface) {
|
||||
$operand = $operand->sql($binder);
|
||||
}
|
||||
|
||||
if ($this->position === self::POSTFIX) {
|
||||
return '(' . $operand . ') ' . $this->_operator;
|
||||
}
|
||||
|
||||
return $this->_operator . ' (' . $operand . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function traverse(Closure $callback)
|
||||
{
|
||||
if ($this->_value instanceof ExpressionInterface) {
|
||||
$callback($this->_value);
|
||||
$this->_value->traverse($callback);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a deep clone of the inner expression.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
if ($this->_value instanceof ExpressionInterface) {
|
||||
$this->_value = clone $this->_value;
|
||||
}
|
||||
}
|
||||
}
|
325
vendor/cakephp/database/Expression/ValuesExpression.php
vendored
Normal file
325
vendor/cakephp/database/Expression/ValuesExpression.php
vendored
Normal file
@ -0,0 +1,325 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Database\Expression;
|
||||
|
||||
use Cake\Database\Exception\DatabaseException;
|
||||
use Cake\Database\ExpressionInterface;
|
||||
use Cake\Database\Query;
|
||||
use Cake\Database\Type\ExpressionTypeCasterTrait;
|
||||
use Cake\Database\TypeMap;
|
||||
use Cake\Database\TypeMapTrait;
|
||||
use Cake\Database\ValueBinder;
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* An expression object to contain values being inserted.
|
||||
*
|
||||
* Helps generate SQL with the correct number of placeholders and bind
|
||||
* values correctly into the statement.
|
||||
*/
|
||||
class ValuesExpression implements ExpressionInterface
|
||||
{
|
||||
use ExpressionTypeCasterTrait;
|
||||
use TypeMapTrait;
|
||||
|
||||
/**
|
||||
* Array of values to insert.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_values = [];
|
||||
|
||||
/**
|
||||
* List of columns to ensure are part of the insert.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_columns = [];
|
||||
|
||||
/**
|
||||
* The Query object to use as a values expression
|
||||
*
|
||||
* @var \Cake\Database\Query|null
|
||||
*/
|
||||
protected $_query;
|
||||
|
||||
/**
|
||||
* Whether values have been casted to expressions
|
||||
* already.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $_castedExpressions = false;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $columns The list of columns that are going to be part of the values.
|
||||
* @param \Cake\Database\TypeMap $typeMap A dictionary of column -> type names
|
||||
*/
|
||||
public function __construct(array $columns, TypeMap $typeMap)
|
||||
{
|
||||
$this->_columns = $columns;
|
||||
$this->setTypeMap($typeMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a row of data to be inserted.
|
||||
*
|
||||
* @param \Cake\Database\Query|array $values Array of data to append into the insert, or
|
||||
* a query for doing INSERT INTO .. SELECT style commands
|
||||
* @return void
|
||||
* @throws \Cake\Database\Exception\DatabaseException When mixing array + Query data types.
|
||||
*/
|
||||
public function add($values): void
|
||||
{
|
||||
if (
|
||||
(
|
||||
count($this->_values) &&
|
||||
$values instanceof Query
|
||||
) ||
|
||||
(
|
||||
$this->_query &&
|
||||
is_array($values)
|
||||
)
|
||||
) {
|
||||
throw new DatabaseException(
|
||||
'You cannot mix subqueries and array values in inserts.'
|
||||
);
|
||||
}
|
||||
if ($values instanceof Query) {
|
||||
$this->setQuery($values);
|
||||
|
||||
return;
|
||||
}
|
||||
$this->_values[] = $values;
|
||||
$this->_castedExpressions = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the columns to be inserted.
|
||||
*
|
||||
* @param array $columns Array with columns to be inserted.
|
||||
* @return $this
|
||||
*/
|
||||
public function setColumns(array $columns)
|
||||
{
|
||||
$this->_columns = $columns;
|
||||
$this->_castedExpressions = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the columns to be inserted.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getColumns(): array
|
||||
{
|
||||
return $this->_columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bare column names.
|
||||
*
|
||||
* Because column names could be identifier quoted, we
|
||||
* need to strip the identifiers off of the columns.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function _columnNames(): array
|
||||
{
|
||||
$columns = [];
|
||||
foreach ($this->_columns as $col) {
|
||||
if (is_string($col)) {
|
||||
$col = trim($col, '`[]"');
|
||||
}
|
||||
$columns[] = $col;
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the values to be inserted.
|
||||
*
|
||||
* @param array $values Array with values to be inserted.
|
||||
* @return $this
|
||||
*/
|
||||
public function setValues(array $values)
|
||||
{
|
||||
$this->_values = $values;
|
||||
$this->_castedExpressions = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the values to be inserted.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getValues(): array
|
||||
{
|
||||
if (!$this->_castedExpressions) {
|
||||
$this->_processExpressions();
|
||||
}
|
||||
|
||||
return $this->_values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the query object to be used as the values expression to be evaluated
|
||||
* to insert records in the table.
|
||||
*
|
||||
* @param \Cake\Database\Query $query The query to set
|
||||
* @return $this
|
||||
*/
|
||||
public function setQuery(Query $query)
|
||||
{
|
||||
$this->_query = $query;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the query object to be used as the values expression to be evaluated
|
||||
* to insert records in the table.
|
||||
*
|
||||
* @return \Cake\Database\Query|null
|
||||
*/
|
||||
public function getQuery(): ?Query
|
||||
{
|
||||
return $this->_query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function sql(ValueBinder $binder): string
|
||||
{
|
||||
if (empty($this->_values) && empty($this->_query)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!$this->_castedExpressions) {
|
||||
$this->_processExpressions();
|
||||
}
|
||||
|
||||
$columns = $this->_columnNames();
|
||||
$defaults = array_fill_keys($columns, null);
|
||||
$placeholders = [];
|
||||
|
||||
$types = [];
|
||||
$typeMap = $this->getTypeMap();
|
||||
foreach ($defaults as $col => $v) {
|
||||
$types[$col] = $typeMap->type($col);
|
||||
}
|
||||
|
||||
foreach ($this->_values as $row) {
|
||||
$row += $defaults;
|
||||
$rowPlaceholders = [];
|
||||
|
||||
foreach ($columns as $column) {
|
||||
$value = $row[$column];
|
||||
|
||||
if ($value instanceof ExpressionInterface) {
|
||||
$rowPlaceholders[] = '(' . $value->sql($binder) . ')';
|
||||
continue;
|
||||
}
|
||||
|
||||
$placeholder = $binder->placeholder('c');
|
||||
$rowPlaceholders[] = $placeholder;
|
||||
$binder->bind($placeholder, $value, $types[$column]);
|
||||
}
|
||||
|
||||
$placeholders[] = implode(', ', $rowPlaceholders);
|
||||
}
|
||||
|
||||
$query = $this->getQuery();
|
||||
if ($query) {
|
||||
return ' ' . $query->sql($binder);
|
||||
}
|
||||
|
||||
return sprintf(' VALUES (%s)', implode('), (', $placeholders));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function traverse(Closure $callback)
|
||||
{
|
||||
if ($this->_query) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (!$this->_castedExpressions) {
|
||||
$this->_processExpressions();
|
||||
}
|
||||
|
||||
foreach ($this->_values as $v) {
|
||||
if ($v instanceof ExpressionInterface) {
|
||||
$v->traverse($callback);
|
||||
}
|
||||
if (!is_array($v)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($v as $field) {
|
||||
if ($field instanceof ExpressionInterface) {
|
||||
$callback($field);
|
||||
$field->traverse($callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts values that need to be casted to expressions
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function _processExpressions(): void
|
||||
{
|
||||
$types = [];
|
||||
$typeMap = $this->getTypeMap();
|
||||
|
||||
$columns = $this->_columnNames();
|
||||
foreach ($columns as $c) {
|
||||
if (!is_string($c) && !is_int($c)) {
|
||||
continue;
|
||||
}
|
||||
$types[$c] = $typeMap->type($c);
|
||||
}
|
||||
|
||||
$types = $this->_requiresToExpressionCasting($types);
|
||||
|
||||
if (empty($types)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->_values as $row => $values) {
|
||||
foreach ($types as $col => $type) {
|
||||
/** @var \Cake\Database\Type\ExpressionTypeInterface $type */
|
||||
$this->_values[$row][$col] = $type->toExpression($values[$col]);
|
||||
}
|
||||
}
|
||||
$this->_castedExpressions = true;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user