updated streamline-setup v2

This commit is contained in:
2025-01-15 08:53:49 -08:00
committed by alec.turner
parent a2ce9248f0
commit 4b569f81b0
20228 changed files with 2932048 additions and 63204 deletions
@@ -139,8 +139,6 @@ Instantiating the class will give you an object with available methods
- `unit`: Mobile data unit. Can either be `MB` or `GB`. `REQUIRED`
- `validity`: How long the mobile data is valid for. Must be one of `Day`, `Week` and `Month`. `REQUIRED`
- `metadata`: Additional data to associate with the tranasction. `REQUIRED`
- `isPromoBundle`: This is an optional field that can be either `true` or `false`.
- **$options:** optional associative array with the following keys:
@@ -21,7 +21,11 @@
},
"autoload": {
"psr-4": {
"AfricasTalking\\SDK\\": "src/",
"AfricasTalking\\SDK\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"AfricasTalking\\SDK\\Tests\\": "tests"
}
}
@@ -12,13 +12,16 @@ class AfricasTalking
protected $apiKey;
protected $client;
protected $contentClient;
protected $voiceClient;
protected $tokenClient;
protected $contentClient;
protected $mobileDataClient;
protected $baseDomain;
public $baseUrl;
protected $voiceUrl;
protected $checkoutTokenUrl;
protected $contentUrl;
protected $mobileDataUrl;
@@ -68,6 +68,16 @@ class Content extends Service
'keyword' => $options['keyword']
];
/**
* checkoutToken Key was removed in commit:339f7057d8ff640ffa9802b4d3a812848b1072a9.
* To prevent breaking applications in production, we conditionally add it to
* the request otherwise previous behaviour persists.
**/
if(array_key_exists('checkoutToken',$options)){
$data['checkoutToken'] = $options['checkoutToken'];
}
$response = $this->client->post('subscription/create', ['form_params' => $data ] );
return $this->success($response);
@@ -63,10 +63,6 @@ class MobileData extends Service
'recipients' => $recipients,
];
if (isset($parameters['isPromoBundles'])) {
$requestData['isPromoBundle'] = $parameters['isPromoBundles'];
}
$requestOptions = [
'json' => $requestData,
];
@@ -4,6 +4,7 @@ namespace AfricasTalking\SDK\Tests;
use AfricasTalking\SDK\AfricasTalking;
use GuzzleHttp\Exception\GuzzleException;
#[\AllowDynamicProperties]
class AfricasTalkingTest extends \PHPUnit\Framework\TestCase
{
public function setUp(): void
@@ -4,6 +4,7 @@ namespace AfricasTalking\SDK\Tests;
use AfricasTalking\SDK\AfricasTalking;
use GuzzleHttp\Exception\GuzzleException;
#[\AllowDynamicProperties]
class AirtimeTest extends \PHPUnit\Framework\TestCase
{
public function setup(): void
@@ -4,6 +4,7 @@ namespace AfricasTalking\SDK\Tests;
use AfricasTalking\SDK\AfricasTalking;
use GuzzleHttp\Exception\GuzzleException;
#[\AllowDynamicProperties]
class ApplicationTest extends \PHPUnit\Framework\TestCase
{
public function setUp(): void
@@ -4,6 +4,7 @@ namespace AfricasTalking\SDK\Tests;
use AfricasTalking\SDK\AfricasTalking;
use GuzzleHttp\Exception\GuzzleException;
#[\AllowDynamicProperties]
class ContentTest extends \PHPUnit\Framework\TestCase
{
public function setUp(): void
@@ -4,6 +4,7 @@ namespace AfricasTalking\SDK\Tests;
use AfricasTalking\SDK\AfricasTalking;
use GuzzleHttp\Exception\GuzzleException;
#[\AllowDynamicProperties]
class SMSTest extends \PHPUnit\Framework\TestCase
{
public function setUp(): void
@@ -108,7 +109,9 @@ class SMSTest extends \PHPUnit\Framework\TestCase
]);
$this->assertArrayHasKey('status',$response);
$this->assertArrayHasKey('data',$response);
$this->assertEquals('success',$response['status']);
$this->assertEquals('Success',$response['data']->status);
}
public function testDeleteSubscription()
@@ -4,6 +4,7 @@ namespace AfricasTalking\SDK\Tests;
use AfricasTalking\SDK\AfricasTalking;
use GuzzleHttp\Exception\GuzzleException;
#[\AllowDynamicProperties]
class TokenTest extends \PHPUnit\Framework\TestCase
{
public function setUp(): void
@@ -4,6 +4,7 @@ namespace AfricasTalking\SDK\Tests;
use AfricasTalking\SDK\AfricasTalking;
use GuzzleHttp\Exception\GuzzleException;
#[\AllowDynamicProperties]
class VoiceTest extends \PHPUnit\Framework\TestCase
{
public function setUp(): void
+1 -1
View File
@@ -22,4 +22,4 @@ if (PHP_VERSION_ID < 50600) {
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit379a36e429894b61b55afc3b84fa116a::getLoader();
return ComposerAutoloaderInit69a41de4ce3c76c7865a7de0eec12ccc::getLoader();
@@ -0,0 +1,39 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2.0.0-beta3]
### Changed
- Remove the deprecated class 'Barryvdh\DomPDF\Facade' Facade in favor of Barryvdh\DomPDF\Facade\Pdf
- Set default Facade to Pdf instead of PDF
## [2.0.0-beta2]
### Added
- Upgraded to use dompdf/dompdf 2.x
- `setOption` to change only the specified option(s), instead of replace all options.
- Magic methods to allow calls to Dompdf methods easier. (#892)
- `default_paper_orientation` option has been added to the defaults.
- Add option to set public path (#890)
### Changed
- HTML5 parser option is deprecated, because this is always on.
- `orientation` option was never used. Removed in favor of `options.default_paper_orientation`
### Deprecated
- `setOptions` is now deprecated. Use `setOption` instead.
- Config `dompdf.defines` has been renamed to `dompdf.options`
## Dompdf 2.0.0, highlights since 1.2.x
> https://github.com/dompdf/dompdf/releases/tag/v2.0.0
> - Addresses multiple security vulnerabilities (see link)
> - Modifies callback and page_script/page_text handling (breaking change, see link)
> - Switches the HTML5 parser to Masterminds/HTML5
> - Improves CSS property parsing and representation
> - Improves border, outline, and background rendering for inline elements
> - Switches installed fonts and font metrics cache file format to JSON
> - Adds support for the inset CSS shorthand property and the legacy break-word keyword for word-break
> - Adds "end_document" callback event
@@ -1,11 +1,13 @@
Copyright (c) 2015-present Fabien Potencier
MIT License
Copyright (c) 2021 barryvdh
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:
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.
@@ -15,5 +17,5 @@ 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.
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,64 @@
{
"name": "barryvdh/laravel-dompdf",
"description": "A DOMPDF Wrapper for Laravel",
"license": "MIT",
"keywords": [
"laravel",
"dompdf",
"pdf"
],
"authors": [
{
"name": "Barry vd. Heuvel",
"email": "barryvdh@gmail.com"
}
],
"require": {
"php": "^7.2 || ^8.0",
"dompdf/dompdf": "^2.0.7",
"illuminate/support": "^6|^7|^8|^9|^10|^11"
},
"require-dev": {
"orchestra/testbench": "^4|^5|^6|^7|^8|^9",
"squizlabs/php_codesniffer": "^3.5",
"phpro/grumphp": "^1 || ^2.5",
"larastan/larastan": "^1.0|^2.7.0"
},
"autoload": {
"psr-4": {
"Barryvdh\\DomPDF\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Barryvdh\\DomPDF\\Tests\\": "tests"
}
},
"extra": {
"branch-alias": {
"dev-master": "2.0-dev"
},
"laravel": {
"providers": [
"Barryvdh\\DomPDF\\ServiceProvider"
],
"aliases": {
"Pdf": "Barryvdh\\DomPDF\\Facade\\Pdf",
"PDF": "Barryvdh\\DomPDF\\Facade\\Pdf"
}
}
},
"scripts": {
"test": "phpunit",
"check-style": "phpcs -p --standard=psr12 src/",
"fix-style": "phpcbf -p --standard=psr12 src/",
"phpstan": "phpstan analyze --memory-limit=-1"
},
"minimum-stability": "dev",
"prefer-stable": true,
"config": {
"allow-plugins": {
"phpro/grumphp": true
}
}
}
@@ -0,0 +1,284 @@
<?php
return array(
/*
|--------------------------------------------------------------------------
| Settings
|--------------------------------------------------------------------------
|
| Set some default values. It is possible to add all defines that can be set
| in dompdf_config.inc.php. You can also override the entire config file.
|
*/
'show_warnings' => false, // Throw an Exception on warnings from dompdf
'public_path' => null, // Override the public path if needed
/*
* Dejavu Sans font is missing glyphs for converted entities, turn it off if you need to show € and £.
*/
'convert_entities' => true,
'options' => array(
/**
* The location of the DOMPDF font directory
*
* The location of the directory where DOMPDF will store fonts and font metrics
* Note: This directory must exist and be writable by the webserver process.
* *Please note the trailing slash.*
*
* Notes regarding fonts:
* Additional .afm font metrics can be added by executing load_font.php from command line.
*
* Only the original "Base 14 fonts" are present on all pdf viewers. Additional fonts must
* be embedded in the pdf file or the PDF may not display correctly. This can significantly
* increase file size unless font subsetting is enabled. Before embedding a font please
* review your rights under the font license.
*
* Any font specification in the source HTML is translated to the closest font available
* in the font directory.
*
* The pdf standard "Base 14 fonts" are:
* Courier, Courier-Bold, Courier-BoldOblique, Courier-Oblique,
* Helvetica, Helvetica-Bold, Helvetica-BoldOblique, Helvetica-Oblique,
* Times-Roman, Times-Bold, Times-BoldItalic, Times-Italic,
* Symbol, ZapfDingbats.
*/
"font_dir" => storage_path('fonts'), // advised by dompdf (https://github.com/dompdf/dompdf/pull/782)
/**
* The location of the DOMPDF font cache directory
*
* This directory contains the cached font metrics for the fonts used by DOMPDF.
* This directory can be the same as DOMPDF_FONT_DIR
*
* Note: This directory must exist and be writable by the webserver process.
*/
"font_cache" => storage_path('fonts'),
/**
* The location of a temporary directory.
*
* The directory specified must be writeable by the webserver process.
* The temporary directory is required to download remote images and when
* using the PDFLib back end.
*/
"temp_dir" => sys_get_temp_dir(),
/**
* ==== IMPORTANT ====
*
* dompdf's "chroot": Prevents dompdf from accessing system files or other
* files on the webserver. All local files opened by dompdf must be in a
* subdirectory of this directory. DO NOT set it to '/' since this could
* allow an attacker to use dompdf to read any files on the server. This
* should be an absolute path.
* This is only checked on command line call by dompdf.php, but not by
* direct class use like:
* $dompdf = new DOMPDF(); $dompdf->load_html($htmldata); $dompdf->render(); $pdfdata = $dompdf->output();
*/
"chroot" => realpath(base_path()),
/**
* Protocol whitelist
*
* Protocols and PHP wrappers allowed in URIs, and the validation rules
* that determine if a resouce may be loaded. Full support is not guaranteed
* for the protocols/wrappers specified
* by this array.
*
* @var array
*/
'allowed_protocols' => [
"file://" => ["rules" => []],
"http://" => ["rules" => []],
"https://" => ["rules" => []]
],
/**
* @var string
*/
'log_output_file' => null,
/**
* Whether to enable font subsetting or not.
*/
"enable_font_subsetting" => false,
/**
* The PDF rendering backend to use
*
* Valid settings are 'PDFLib', 'CPDF' (the bundled R&OS PDF class), 'GD' and
* 'auto'. 'auto' will look for PDFLib and use it if found, or if not it will
* fall back on CPDF. 'GD' renders PDFs to graphic files. {@link
* Canvas_Factory} ultimately determines which rendering class to instantiate
* based on this setting.
*
* Both PDFLib & CPDF rendering backends provide sufficient rendering
* capabilities for dompdf, however additional features (e.g. object,
* image and font support, etc.) differ between backends. Please see
* {@link PDFLib_Adapter} for more information on the PDFLib backend
* and {@link CPDF_Adapter} and lib/class.pdf.php for more information
* on CPDF. Also see the documentation for each backend at the links
* below.
*
* The GD rendering backend is a little different than PDFLib and
* CPDF. Several features of CPDF and PDFLib are not supported or do
* not make any sense when creating image files. For example,
* multiple pages are not supported, nor are PDF 'objects'. Have a
* look at {@link GD_Adapter} for more information. GD support is
* experimental, so use it at your own risk.
*
* @link http://www.pdflib.com
* @link http://www.ros.co.nz/pdf
* @link http://www.php.net/image
*/
"pdf_backend" => "CPDF",
/**
* PDFlib license key
*
* If you are using a licensed, commercial version of PDFlib, specify
* your license key here. If you are using PDFlib-Lite or are evaluating
* the commercial version of PDFlib, comment out this setting.
*
* @link http://www.pdflib.com
*
* If pdflib present in web server and auto or selected explicitely above,
* a real license code must exist!
*/
//"DOMPDF_PDFLIB_LICENSE" => "your license key here",
/**
* html target media view which should be rendered into pdf.
* List of types and parsing rules for future extensions:
* http://www.w3.org/TR/REC-html40/types.html
* screen, tty, tv, projection, handheld, print, braille, aural, all
* Note: aural is deprecated in CSS 2.1 because it is replaced by speech in CSS 3.
* Note, even though the generated pdf file is intended for print output,
* the desired content might be different (e.g. screen or projection view of html file).
* Therefore allow specification of content here.
*/
"default_media_type" => "screen",
/**
* The default paper size.
*
* North America standard is "letter"; other countries generally "a4"
*
* @see CPDF_Adapter::PAPER_SIZES for valid sizes ('letter', 'legal', 'A4', etc.)
*/
"default_paper_size" => "a4",
/**
* The default paper orientation.
*
* The orientation of the page (portrait or landscape).
*
* @var string
*/
'default_paper_orientation' => "portrait",
/**
* The default font family
*
* Used if no suitable fonts can be found. This must exist in the font folder.
* @var string
*/
"default_font" => "serif",
/**
* Image DPI setting
*
* This setting determines the default DPI setting for images and fonts. The
* DPI may be overridden for inline images by explictly setting the
* image's width & height style attributes (i.e. if the image's native
* width is 600 pixels and you specify the image's width as 72 points,
* the image will have a DPI of 600 in the rendered PDF. The DPI of
* background images can not be overridden and is controlled entirely
* via this parameter.
*
* For the purposes of DOMPDF, pixels per inch (PPI) = dots per inch (DPI).
* If a size in html is given as px (or without unit as image size),
* this tells the corresponding size in pt.
* This adjusts the relative sizes to be similar to the rendering of the
* html page in a reference browser.
*
* In pdf, always 1 pt = 1/72 inch
*
* Rendering resolution of various browsers in px per inch:
* Windows Firefox and Internet Explorer:
* SystemControl->Display properties->FontResolution: Default:96, largefonts:120, custom:?
* Linux Firefox:
* about:config *resolution: Default:96
* (xorg screen dimension in mm and Desktop font dpi settings are ignored)
*
* Take care about extra font/image zoom factor of browser.
*
* In images, <img> size in pixel attribute, img css style, are overriding
* the real image dimension in px for rendering.
*
* @var int
*/
"dpi" => 96,
/**
* Enable inline PHP
*
* If this setting is set to true then DOMPDF will automatically evaluate
* inline PHP contained within <script type="text/php"> ... </script> tags.
*
* Enabling this for documents you do not trust (e.g. arbitrary remote html
* pages) is a security risk. Set this option to false if you wish to process
* untrusted documents.
*
* @var bool
*/
"enable_php" => false,
/**
* Enable inline Javascript
*
* If this setting is set to true then DOMPDF will automatically insert
* JavaScript code contained within <script type="text/javascript"> ... </script> tags.
*
* @var bool
*/
"enable_javascript" => true,
/**
* Enable remote file access
*
* If this setting is set to true, DOMPDF will access remote sites for
* images and CSS files as required.
* This is required for part of test case www/test/image_variants.html through www/examples.php
*
* Attention!
* This can be a security risk, in particular in combination with DOMPDF_ENABLE_PHP and
* allowing remote access to dompdf.php or on allowing remote html code to be passed to
* $dompdf = new DOMPDF(, $dompdf->load_html(...,
* This allows anonymous users to download legally doubtful internet content which on
* tracing back appears to being downloaded by your server, or allows malicious php code
* in remote html pages to be executed by your server with your account privileges.
*
* @var bool
*/
"enable_remote" => true,
/**
* A ratio applied to the fonts height to be more like browsers' line height
*/
"font_height_ratio" => 1.1,
/**
* Use the HTML5 Lib parser
*
* @deprecated This feature is now always on in dompdf 2.x
* @var bool
*/
"enable_html5_parser" => true,
),
);
@@ -0,0 +1,130 @@
## DOMPDF Wrapper for Laravel
### Laravel wrapper for [Dompdf HTML to PDF Converter](https://github.com/dompdf/dompdf)
[![Tests](https://github.com/barryvdh/laravel-dompdf/workflows/Tests/badge.svg)](https://github.com/barryvdh/laravel-dompdf/actions)
[![Packagist License](https://poser.pugx.org/barryvdh/laravel-dompdf/license.png)](http://choosealicense.com/licenses/mit/)
[![Latest Stable Version](https://poser.pugx.org/barryvdh/laravel-dompdf/version.png)](https://packagist.org/packages/barryvdh/laravel-dompdf)
[![Total Downloads](https://poser.pugx.org/barryvdh/laravel-dompdf/d/total.png)](https://packagist.org/packages/barryvdh/laravel-dompdf)
[![Fruitcake](https://img.shields.io/badge/Powered%20By-Fruitcake-b2bc35.svg)](https://fruitcake.nl/)
## Installation
### Laravel
Require this package in your composer.json and update composer. This will download the package and the dompdf + fontlib libraries also.
composer require barryvdh/laravel-dompdf
### Lumen
After updating composer add the following lines to register provider in `bootstrap/app.php`
```
$app->register(\Barryvdh\DomPDF\ServiceProvider::class);
```
To change the configuration, copy the config file to your config folder and enable it in `bootstrap/app.php`:
```
$app->configure('dompdf');
```
## Using
You can create a new DOMPDF instance and load a HTML string, file or view name. You can save it to a file, or stream (show in browser) or download.
```php
use Barryvdh\DomPDF\Facade\Pdf;
$pdf = Pdf::loadView('pdf.invoice', $data);
return $pdf->download('invoice.pdf');
```
or use the App container:
```php
$pdf = App::make('dompdf.wrapper');
$pdf->loadHTML('<h1>Test</h1>');
return $pdf->stream();
```
Or use the facade:
You can chain the methods:
```php
return Pdf::loadFile(public_path().'/myfile.html')->save('/path-to/my_stored_file.pdf')->stream('download.pdf');
```
You can change the orientation and paper size, and hide or show errors (by default, errors are shown when debug is on)
```php
Pdf::loadHTML($html)->setPaper('a4', 'landscape')->setWarnings(false)->save('myfile.pdf')
```
If you need the output as a string, you can get the rendered PDF with the output() function, so you can save/output it yourself.
Use `php artisan vendor:publish` to create a config file located at `config/dompdf.php` which will allow you to define local configurations to change some settings (default paper etc).
You can also use your ConfigProvider to set certain keys.
### Configuration
The defaults configuration settings are set in `config/dompdf.php`. Copy this file to your own config directory to modify the values. You can publish the config using this command:
```shell
php artisan vendor:publish --provider="Barryvdh\DomPDF\ServiceProvider"
```
You can still alter the dompdf options in your code before generating the pdf using this command:
```php
Pdf::setOption(['dpi' => 150, 'defaultFont' => 'sans-serif']);
```
Available options and their defaults:
* __rootDir__: "{app_directory}/vendor/dompdf/dompdf"
* __tempDir__: "/tmp" _(available in config/dompdf.php)_
* __fontDir__: "{app_directory}/storage/fonts" _(available in config/dompdf.php)_
* __fontCache__: "{app_directory}/storage/fonts" _(available in config/dompdf.php)_
* __chroot__: "{app_directory}" _(available in config/dompdf.php)_
* __logOutputFile__: "/tmp/log.htm"
* __defaultMediaType__: "screen" _(available in config/dompdf.php)_
* __defaultPaperSize__: "a4" _(available in config/dompdf.php)_
* __defaultFont__: "serif" _(available in config/dompdf.php)_
* __dpi__: 96 _(available in config/dompdf.php)_
* __fontHeightRatio__: 1.1 _(available in config/dompdf.php)_
* __isPhpEnabled__: false _(available in config/dompdf.php)_
* __isRemoteEnabled__: true _(available in config/dompdf.php)_
* __isJavascriptEnabled__: true _(available in config/dompdf.php)_
* __isHtml5ParserEnabled__: false _(available in config/dompdf.php)_
* __isFontSubsettingEnabled__: false _(available in config/dompdf.php)_
* __debugPng__: false
* __debugKeepTemp__: false
* __debugCss__: false
* __debugLayout__: false
* __debugLayoutLines__: true
* __debugLayoutBlocks__: true
* __debugLayoutInline__: true
* __debugLayoutPaddingBox__: true
* __pdfBackend__: "CPDF" _(available in config/dompdf.php)_
* __pdflibLicense__: ""
* __adminUsername__: "user"
* __adminPassword__: "password"
### Tip: UTF-8 support
In your templates, set the UTF-8 Metatag:
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
### Tip: Page breaks
You can use the CSS `page-break-before`/`page-break-after` properties to create a new page.
<style>
.page-break {
page-break-after: always;
}
</style>
<h1>Page 1</h1>
<div class="page-break"></div>
<h1>Page 2</h1>
### License
This DOMPDF Wrapper for Laravel is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT)
@@ -0,0 +1,68 @@
<?php
namespace Barryvdh\DomPDF\Facade;
use Barryvdh\DomPDF\PDF as BasePDF;
use Illuminate\Support\Facades\Facade as IlluminateFacade;
use RuntimeException;
/**
* @method static BasePDF setBaseHost(string $baseHost)
* @method static BasePDF setBasePath(string $basePath)
* @method static BasePDF setCanvas(\Dompdf\Canvas $canvas)
* @method static BasePDF setCallbacks(array $callbacks)
* @method static BasePDF setCss(\Dompdf\Css\Stylesheet $css)
* @method static BasePDF setDefaultView(string $defaultView, array $options)
* @method static BasePDF setDom(\DOMDocument $dom)
* @method static BasePDF setFontMetrics(\Dompdf\FontMetrics $fontMetrics)
* @method static BasePDF setHttpContext(resource|array $httpContext)
* @method static BasePDF setPaper(string|float[] $paper, string $orientation = 'portrait')
* @method static BasePDF setProtocol(string $protocol)
* @method static BasePDF setTree(\Dompdf\Frame\FrameTree $tree)
* @method static BasePDF setWarnings(bool $warnings)
* @method static BasePDF setOption(array|string $attribute, $value = null)
* @method static BasePDF setOptions(array $options)
* @method static BasePDF loadView(string $view, array $data = [], array $mergeData = [], ?string $encoding = null)
* @method static BasePDF loadHTML(string $string, ?string $encoding = null)
* @method static BasePDF loadFile(string $file)
* @method static BasePDF addInfo(array $info)
* @method static string output(array $options = [])
* @method static BasePDF save()
* @method static \Illuminate\Http\Response download(string $filename = 'document.pdf')
* @method static \Illuminate\Http\Response stream(string $filename = 'document.pdf')
*/
class Pdf extends IlluminateFacade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'dompdf.wrapper';
}
/**
* Handle dynamic, static calls to the object.
*
* @param string $method
* @param array<mixed> $args
* @return mixed
*
* @throws \RuntimeException
*/
public static function __callStatic($method, $args)
{
/** @var \Illuminate\Contracts\Foundation\Application|null */
$app = static::getFacadeApplication();
if (! $app) {
throw new RuntimeException('Facade application has not been set.');
}
// Resolve a new instance, avoid using a cached instance
$instance = $app->make(static::getFacadeAccessor());
return $instance->$method(...$args);
}
}
@@ -0,0 +1,304 @@
<?php
namespace Barryvdh\DomPDF;
use Dompdf\Adapter\CPDF;
use Dompdf\Dompdf;
use Dompdf\Options;
use Exception;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Contracts\View\Factory as ViewFactory;
use Illuminate\Contracts\Config\Repository as ConfigRepository;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Storage;
/**
* A Laravel wrapper for Dompdf
*
* @package laravel-dompdf
* @author Barry vd. Heuvel
*
* @method PDF setBaseHost(string $baseHost)
* @method PDF setBasePath(string $basePath)
* @method PDF setCanvas(\Dompdf\Canvas $canvas)
* @method PDF setCallbacks(array $callbacks)
* @method PDF setCss(\Dompdf\Css\Stylesheet $css)
* @method PDF setDefaultView(string $defaultView, array $options)
* @method PDF setDom(\DOMDocument $dom)
* @method PDF setFontMetrics(\Dompdf\FontMetrics $fontMetrics)
* @method PDF setHttpContext(resource|array $httpContext)
* @method PDF setPaper(string|float[] $paper, string $orientation = 'portrait')
* @method PDF setProtocol(string $protocol)
* @method PDF setTree(\Dompdf\Frame\FrameTree $tree)
* @method string getBaseHost()
* @method string getBasePath()
* @method \Dompdf\Canvas getCanvas()
* @method array getCallbacks()
* @method \Dompdf\Css\Stylesheet getCss()
* @method \DOMDocument getDom()
* @method \Dompdf\FontMetrics getFontMetrics()
* @method resource getHttpContext()
* @method Options getOptions()
* @method \Dompdf\Frame\FrameTree getTree()
* @method string getPaperOrientation()
* @method float[] getPaperSize()
* @method string getProtocol()
*/
class PDF
{
/** @var Dompdf */
protected $dompdf;
/** @var \Illuminate\Contracts\Config\Repository */
protected $config;
/** @var \Illuminate\Filesystem\Filesystem */
protected $files;
/** @var \Illuminate\Contracts\View\Factory */
protected $view;
/** @var bool */
protected $rendered = false;
/** @var bool */
protected $showWarnings;
/** @var string */
protected $public_path;
public function __construct(Dompdf $dompdf, ConfigRepository $config, Filesystem $files, ViewFactory $view)
{
$this->dompdf = $dompdf;
$this->config = $config;
$this->files = $files;
$this->view = $view;
$this->showWarnings = $this->config->get('dompdf.show_warnings', false);
}
/**
* Get the DomPDF instance
*/
public function getDomPDF(): Dompdf
{
return $this->dompdf;
}
/**
* Show or hide warnings
*/
public function setWarnings(bool $warnings): self
{
$this->showWarnings = $warnings;
return $this;
}
/**
* Load a HTML string
*
* @param string|null $encoding Not used yet
*/
public function loadHTML(string $string, ?string $encoding = null): self
{
$string = $this->convertEntities($string);
$this->dompdf->loadHtml($string, $encoding);
$this->rendered = false;
return $this;
}
/**
* Load a HTML file
*/
public function loadFile(string $file): self
{
$this->dompdf->loadHtmlFile($file);
$this->rendered = false;
return $this;
}
/**
* Add metadata info
* @param array<string, string> $info
*/
public function addInfo(array $info): self
{
foreach ($info as $name => $value) {
$this->dompdf->add_info($name, $value);
}
return $this;
}
/**
* Load a View and convert to HTML
* @param array<string, mixed> $data
* @param array<string, mixed> $mergeData
* @param string|null $encoding Not used yet
*/
public function loadView(string $view, array $data = [], array $mergeData = [], ?string $encoding = null): self
{
$html = $this->view->make($view, $data, $mergeData)->render();
return $this->loadHTML($html, $encoding);
}
/**
* Set/Change an option (or array of options) in Dompdf
*
* @param array<string, mixed>|string $attribute
* @param null|mixed $value
*/
public function setOption($attribute, $value = null): self
{
$this->dompdf->getOptions()->set($attribute, $value);
return $this;
}
/**
* Replace all the Options from DomPDF
*
* @param array<string, mixed> $options
*/
public function setOptions(array $options, bool $mergeWithDefaults = false): self
{
if ($mergeWithDefaults) {
$options = array_merge(app()->make('dompdf.options'), $options);
}
$this->dompdf->setOptions(new Options($options));
return $this;
}
/**
* Output the PDF as a string.
*
* The options parameter controls the output. Accepted options are:
*
* 'compress' = > 1 or 0 - apply content stream compression, this is
* on (1) by default
*
* @param array<string, int> $options
*
* @return string The rendered PDF as string
*/
public function output(array $options = []): string
{
if (!$this->rendered) {
$this->render();
}
return (string) $this->dompdf->output($options);
}
/**
* Save the PDF to a file
*/
public function save(string $filename, string $disk = null): self
{
$disk = $disk ?: $this->config->get('dompdf.disk');
if (! is_null($disk)) {
Storage::disk($disk)->put($filename, $this->output());
return $this;
}
$this->files->put($filename, $this->output());
return $this;
}
/**
* Make the PDF downloadable by the user
*/
public function download(string $filename = 'document.pdf'): Response
{
$output = $this->output();
return new Response($output, 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'attachment; filename="' . $filename . '"',
'Content-Length' => strlen($output),
]);
}
/**
* Return a response with the PDF to show in the browser
*/
public function stream(string $filename = 'document.pdf'): Response
{
$output = $this->output();
return new Response($output, 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'inline; filename="' . $filename . '"',
]);
}
/**
* Render the PDF
*/
public function render(): void
{
$this->dompdf->render();
if ($this->showWarnings) {
global $_dompdf_warnings;
if (!empty($_dompdf_warnings) && count($_dompdf_warnings)) {
$warnings = '';
foreach ($_dompdf_warnings as $msg) {
$warnings .= $msg . "\n";
}
// $warnings .= $this->dompdf->get_canvas()->get_cpdf()->messages;
if (!empty($warnings)) {
throw new Exception($warnings);
}
}
}
$this->rendered = true;
}
/** @param array<string> $pc */
public function setEncryption(string $password, string $ownerpassword = '', array $pc = []): void
{
$this->render();
$canvas = $this->dompdf->getCanvas();
if (! $canvas instanceof CPDF) {
throw new \RuntimeException('Encryption is only supported when using CPDF');
}
$canvas->get_cpdf()->setEncryption($password, $ownerpassword, $pc);
}
protected function convertEntities(string $subject): string
{
if (false === $this->config->get('dompdf.convert_entities', true)) {
return $subject;
}
$entities = [
'€' => '&euro;',
'£' => '&pound;',
];
foreach ($entities as $search => $replace) {
$subject = str_replace($search, $replace, $subject);
}
return $subject;
}
/**
* Dynamically handle calls into the dompdf instance.
*
* @param string $method
* @param array<mixed> $parameters
* @return $this|mixed
*/
public function __call($method, $parameters)
{
if (method_exists($this, $method)) {
return $this->$method(...$parameters);
}
if (method_exists($this->dompdf, $method)) {
$return = $this->dompdf->$method(...$parameters);
return $return == $this->dompdf ? $this : $return;
}
throw new \UnexpectedValueException("Method [{$method}] does not exist on PDF instance.");
}
}
@@ -0,0 +1,94 @@
<?php
namespace Barryvdh\DomPDF;
use Dompdf\Dompdf;
use Exception;
use Illuminate\Support\Str;
use Illuminate\Support\ServiceProvider as IlluminateServiceProvider;
class ServiceProvider extends IlluminateServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = false;
/**
* Register the service provider.
*
* @throws \Exception
* @return void
*/
public function register(): void
{
$configPath = __DIR__ . '/../config/dompdf.php';
$this->mergeConfigFrom($configPath, 'dompdf');
$this->app->bind('dompdf.options', function ($app) {
$defines = $app['config']->get('dompdf.defines');
if ($defines) {
$options = [];
/**
* @var string $key
* @var mixed $value
*/
foreach ($defines as $key => $value) {
$key = strtolower(str_replace('DOMPDF_', '', $key));
$options[$key] = $value;
}
} else {
$options = $app['config']->get('dompdf.options');
}
return $options;
});
$this->app->bind('dompdf', function ($app) {
$options = $app->make('dompdf.options');
$dompdf = new Dompdf($options);
$path = realpath($app['config']->get('dompdf.public_path') ?: base_path('public'));
if ($path === false) {
throw new \RuntimeException('Cannot resolve public path');
}
$dompdf->setBasePath($path);
return $dompdf;
});
$this->app->alias('dompdf', Dompdf::class);
$this->app->bind('dompdf.wrapper', function ($app) {
return new PDF($app['dompdf'], $app['config'], $app['files'], $app['view']);
});
}
/**
* Check if package is running under Lumen app
*/
protected function isLumen(): bool
{
return Str::contains($this->app->version(), 'Lumen') === true;
}
public function boot(): void
{
if (! $this->isLumen()) {
$configPath = __DIR__ . '/../config/dompdf.php';
$this->publishes([$configPath => config_path('dompdf.php')], 'config');
}
}
/**
* Get the services provided by the provider.
*
* @return array<string>
*/
public function provides(): array
{
return ['dompdf', 'dompdf.options', 'dompdf.wrapper'];
}
}
@@ -1,7 +1,14 @@
{
"name": "barryvdh/laravel-snappy",
"description": "Snappy PDF/Image for Laravel",
"keywords": ["laravel","snappy", "pdf", "image", "wkhtmltopdf", "wkhtmltoimage"],
"keywords": [
"laravel",
"snappy",
"pdf",
"image",
"wkhtmltopdf",
"wkhtmltoimage"
],
"license": "MIT",
"authors": [
{
@@ -11,12 +18,12 @@
],
"require": {
"php": ">=7.2",
"illuminate/support": "^9|^10",
"illuminate/filesystem": "^9|^10",
"knplabs/knp-snappy": "^1.4"
"illuminate/support": "^9|^10|^11.0",
"illuminate/filesystem": "^9|^10|^11.0",
"knplabs/knp-snappy": "^1.4.4"
},
"require-dev": {
"orchestra/testbench": "^7|^8"
"orchestra/testbench": "^7|^8|^9.0"
},
"autoload": {
"psr-4": {
@@ -17,7 +17,7 @@ return [
|
| The file path of the wkhtmltopdf / wkhtmltoimage executable.
|
| Timout:
| Timeout:
|
| The amount of time to wait (in seconds) before PDF / Image generation is stopped.
| Setting this to false disables the timeout (unlimited processing time).
@@ -4,6 +4,10 @@ use Knp\Snappy\Image;
use Illuminate\Filesystem\Filesystem;
class IlluminateSnappyImage extends Image {
/**
* @var \Illuminate\Filesystem\Filesystem
*/
protected $fs;
/**
* @param \Illuminate\Filesystem\Filesystem
@@ -23,6 +23,15 @@ class ImageWrapper {
*/
protected $options = array();
/**
* @var string
*/
protected $html;
/**
* @var string
*/
protected $file;
/**
* @param \Knp\Snappy\Image $snappy
+119
View File
@@ -0,0 +1,119 @@
#!/usr/bin/env php
<?php
/**
* Proxy PHP file generated by Composer
*
* This file includes the referenced bin path (../phpmyadmin/sql-parser/bin/highlight-query)
* using a stream wrapper to prevent the shebang from being output on PHP<8
*
* @generated
*/
namespace Composer;
$GLOBALS['_composer_bin_dir'] = __DIR__;
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
if (PHP_VERSION_ID < 80000) {
if (!class_exists('Composer\BinProxyWrapper')) {
/**
* @internal
*/
final class BinProxyWrapper
{
private $handle;
private $position;
private $realpath;
public function stream_open($path, $mode, $options, &$opened_path)
{
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
$opened_path = substr($path, 17);
$this->realpath = realpath($opened_path) ?: $opened_path;
$opened_path = $this->realpath;
$this->handle = fopen($this->realpath, $mode);
$this->position = 0;
return (bool) $this->handle;
}
public function stream_read($count)
{
$data = fread($this->handle, $count);
if ($this->position === 0) {
$data = preg_replace('{^#!.*\r?\n}', '', $data);
}
$this->position += strlen($data);
return $data;
}
public function stream_cast($castAs)
{
return $this->handle;
}
public function stream_close()
{
fclose($this->handle);
}
public function stream_lock($operation)
{
return $operation ? flock($this->handle, $operation) : true;
}
public function stream_seek($offset, $whence)
{
if (0 === fseek($this->handle, $offset, $whence)) {
$this->position = ftell($this->handle);
return true;
}
return false;
}
public function stream_tell()
{
return $this->position;
}
public function stream_eof()
{
return feof($this->handle);
}
public function stream_stat()
{
return array();
}
public function stream_set_option($option, $arg1, $arg2)
{
return true;
}
public function url_stat($path, $flags)
{
$path = substr($path, 17);
if (file_exists($path)) {
return stat($path);
}
return false;
}
}
}
if (
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
) {
return include("phpvfscomposer://" . __DIR__ . '/..'.'/phpmyadmin/sql-parser/bin/highlight-query');
}
}
return include __DIR__ . '/..'.'/phpmyadmin/sql-parser/bin/highlight-query';
+119
View File
@@ -0,0 +1,119 @@
#!/usr/bin/env php
<?php
/**
* Proxy PHP file generated by Composer
*
* This file includes the referenced bin path (../phpmyadmin/sql-parser/bin/lint-query)
* using a stream wrapper to prevent the shebang from being output on PHP<8
*
* @generated
*/
namespace Composer;
$GLOBALS['_composer_bin_dir'] = __DIR__;
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
if (PHP_VERSION_ID < 80000) {
if (!class_exists('Composer\BinProxyWrapper')) {
/**
* @internal
*/
final class BinProxyWrapper
{
private $handle;
private $position;
private $realpath;
public function stream_open($path, $mode, $options, &$opened_path)
{
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
$opened_path = substr($path, 17);
$this->realpath = realpath($opened_path) ?: $opened_path;
$opened_path = $this->realpath;
$this->handle = fopen($this->realpath, $mode);
$this->position = 0;
return (bool) $this->handle;
}
public function stream_read($count)
{
$data = fread($this->handle, $count);
if ($this->position === 0) {
$data = preg_replace('{^#!.*\r?\n}', '', $data);
}
$this->position += strlen($data);
return $data;
}
public function stream_cast($castAs)
{
return $this->handle;
}
public function stream_close()
{
fclose($this->handle);
}
public function stream_lock($operation)
{
return $operation ? flock($this->handle, $operation) : true;
}
public function stream_seek($offset, $whence)
{
if (0 === fseek($this->handle, $offset, $whence)) {
$this->position = ftell($this->handle);
return true;
}
return false;
}
public function stream_tell()
{
return $this->position;
}
public function stream_eof()
{
return feof($this->handle);
}
public function stream_stat()
{
return array();
}
public function stream_set_option($option, $arg1, $arg2)
{
return true;
}
public function url_stat($path, $flags)
{
$path = substr($path, 17);
if (file_exists($path)) {
return stat($path);
}
return false;
}
}
}
if (
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
) {
return include("phpvfscomposer://" . __DIR__ . '/..'.'/phpmyadmin/sql-parser/bin/lint-query');
}
}
return include __DIR__ . '/..'.'/phpmyadmin/sql-parser/bin/lint-query';
+119
View File
@@ -0,0 +1,119 @@
#!/usr/bin/env php
<?php
/**
* Proxy PHP file generated by Composer
*
* This file includes the referenced bin path (../phpmyadmin/sql-parser/bin/sql-parser)
* using a stream wrapper to prevent the shebang from being output on PHP<8
*
* @generated
*/
namespace Composer;
$GLOBALS['_composer_bin_dir'] = __DIR__;
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
if (PHP_VERSION_ID < 80000) {
if (!class_exists('Composer\BinProxyWrapper')) {
/**
* @internal
*/
final class BinProxyWrapper
{
private $handle;
private $position;
private $realpath;
public function stream_open($path, $mode, $options, &$opened_path)
{
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
$opened_path = substr($path, 17);
$this->realpath = realpath($opened_path) ?: $opened_path;
$opened_path = $this->realpath;
$this->handle = fopen($this->realpath, $mode);
$this->position = 0;
return (bool) $this->handle;
}
public function stream_read($count)
{
$data = fread($this->handle, $count);
if ($this->position === 0) {
$data = preg_replace('{^#!.*\r?\n}', '', $data);
}
$this->position += strlen($data);
return $data;
}
public function stream_cast($castAs)
{
return $this->handle;
}
public function stream_close()
{
fclose($this->handle);
}
public function stream_lock($operation)
{
return $operation ? flock($this->handle, $operation) : true;
}
public function stream_seek($offset, $whence)
{
if (0 === fseek($this->handle, $offset, $whence)) {
$this->position = ftell($this->handle);
return true;
}
return false;
}
public function stream_tell()
{
return $this->position;
}
public function stream_eof()
{
return feof($this->handle);
}
public function stream_stat()
{
return array();
}
public function stream_set_option($option, $arg1, $arg2)
{
return true;
}
public function url_stat($path, $flags)
{
$path = substr($path, 17);
if (file_exists($path)) {
return stat($path);
}
return false;
}
}
}
if (
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
) {
return include("phpvfscomposer://" . __DIR__ . '/..'.'/phpmyadmin/sql-parser/bin/sql-parser');
}
}
return include __DIR__ . '/..'.'/phpmyadmin/sql-parser/bin/sql-parser';
+119
View File
@@ -0,0 +1,119 @@
#!/usr/bin/env php
<?php
/**
* Proxy PHP file generated by Composer
*
* This file includes the referenced bin path (../phpmyadmin/sql-parser/bin/tokenize-query)
* using a stream wrapper to prevent the shebang from being output on PHP<8
*
* @generated
*/
namespace Composer;
$GLOBALS['_composer_bin_dir'] = __DIR__;
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
if (PHP_VERSION_ID < 80000) {
if (!class_exists('Composer\BinProxyWrapper')) {
/**
* @internal
*/
final class BinProxyWrapper
{
private $handle;
private $position;
private $realpath;
public function stream_open($path, $mode, $options, &$opened_path)
{
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
$opened_path = substr($path, 17);
$this->realpath = realpath($opened_path) ?: $opened_path;
$opened_path = $this->realpath;
$this->handle = fopen($this->realpath, $mode);
$this->position = 0;
return (bool) $this->handle;
}
public function stream_read($count)
{
$data = fread($this->handle, $count);
if ($this->position === 0) {
$data = preg_replace('{^#!.*\r?\n}', '', $data);
}
$this->position += strlen($data);
return $data;
}
public function stream_cast($castAs)
{
return $this->handle;
}
public function stream_close()
{
fclose($this->handle);
}
public function stream_lock($operation)
{
return $operation ? flock($this->handle, $operation) : true;
}
public function stream_seek($offset, $whence)
{
if (0 === fseek($this->handle, $offset, $whence)) {
$this->position = ftell($this->handle);
return true;
}
return false;
}
public function stream_tell()
{
return $this->position;
}
public function stream_eof()
{
return feof($this->handle);
}
public function stream_stat()
{
return array();
}
public function stream_set_option($option, $arg1, $arg2)
{
return true;
}
public function url_stat($path, $flags)
{
$path = substr($path, 17);
if (file_exists($path)) {
return stat($path);
}
return false;
}
}
}
if (
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
) {
return include("phpvfscomposer://" . __DIR__ . '/..'.'/phpmyadmin/sql-parser/bin/tokenize-query');
}
}
return include __DIR__ . '/..'.'/phpmyadmin/sql-parser/bin/tokenize-query';
+1 -1
View File
@@ -34,4 +34,4 @@ if [ -n "$bashSource" ]; then
fi
fi
"${dir}/wkhtmltoimage-amd64" "$@"
exec "${dir}/wkhtmltoimage-amd64" "$@"
+1 -1
View File
@@ -34,4 +34,4 @@ if [ -n "$bashSource" ]; then
fi
fi
"${dir}/wkhtmltopdf-amd64" "$@"
exec "${dir}/wkhtmltopdf-amd64" "$@"
+18
View File
@@ -2,6 +2,24 @@
All notable changes to this project will be documented in this file.
## [0.12.1](https://github.com/brick/math/releases/tag/0.12.1) - 2023-11-29
⚡️ **Performance improvements**
- `BigNumber::of()` is now faster, thanks to [@SebastienDug](https://github.com/SebastienDug) in [#77](https://github.com/brick/math/pull/77).
## [0.12.0](https://github.com/brick/math/releases/tag/0.12.0) - 2023-11-26
💥 **Breaking changes**
- Minimum PHP version is now 8.1
- `RoundingMode` is now an `enum`; if you're type-hinting rounding modes, you need to type-hint against `RoundingMode` instead of `int` now
- `BigNumber` classes do not implement the `Serializable` interface anymore (they use the [new custom object serialization mechanism](https://wiki.php.net/rfc/custom_object_serialization))
- The following breaking changes only affect you if you're creating your own `BigNumber` subclasses:
- the return type of `BigNumber::of()` is now `static`
- `BigNumber` has a new abstract method `from()`
- all `public` and `protected` functions of `BigNumber` are now `final`
## [0.11.0](https://github.com/brick/math/releases/tag/0.11.0) - 2023-01-16
💥 **Breaking changes**
+9 -4
View File
@@ -5,21 +5,26 @@
"keywords": [
"Brick",
"Math",
"Mathematics",
"Arbitrary-precision",
"Arithmetic",
"BigInteger",
"BigDecimal",
"BigRational",
"Bignum"
"BigNumber",
"Bignum",
"Decimal",
"Rational",
"Integer"
],
"license": "MIT",
"require": {
"php": "^8.0"
"php": "^8.1"
},
"require-dev": {
"phpunit/phpunit": "^9.0",
"phpunit/phpunit": "^10.1",
"php-coveralls/php-coveralls": "^2.2",
"vimeo/psalm": "5.0.0"
"vimeo/psalm": "5.16.0"
},
"autoload": {
"psr-4": {
+11 -43
View File
@@ -23,14 +23,14 @@ final class BigDecimal extends BigNumber
* No leading zero must be present.
* No leading minus sign must be present if the value is 0.
*/
private string $value;
private readonly string $value;
/**
* The scale (number of digits after the decimal point) of this decimal number.
*
* This must be zero or more.
*/
private int $scale;
private readonly int $scale;
/**
* Protected constructor. Use a factory method to obtain an instance.
@@ -45,15 +45,11 @@ final class BigDecimal extends BigNumber
}
/**
* 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
protected static function from(BigNumber $number): static
{
return parent::of($value)->toBigDecimal();
return $number->toBigDecimal();
}
/**
@@ -223,12 +219,12 @@ final class BigDecimal extends BigNumber
*
* @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.
* @param RoundingMode $roundingMode An optional rounding mode, defaults to UNNECESSARY.
*
* @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
public function dividedBy(BigNumber|int|float|string $that, ?int $scale = null, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
{
$that = BigDecimal::of($that);
@@ -324,7 +320,7 @@ final class BigDecimal extends BigNumber
}
/**
* Returns the quotient of the division of this number by this given one.
* Returns the quotient of the division of this number by the given one.
*
* The quotient has a scale of `0`.
*
@@ -349,7 +345,7 @@ final class BigDecimal extends BigNumber
}
/**
* Returns the remainder of the division of this number by this given one.
* Returns the remainder of the division of this number by the given one.
*
* The remainder has a scale of `max($this->scale, $that->scale)`.
*
@@ -384,6 +380,8 @@ final class BigDecimal extends BigNumber
*
* @return BigDecimal[] An array containing the quotient and the remainder.
*
* @psalm-return array{BigDecimal, BigDecimal}
*
* @throws MathException If the divisor is not a valid decimal number, or is zero.
*/
public function quotientAndRemainder(BigNumber|int|float|string $that) : array
@@ -631,7 +629,7 @@ final class BigDecimal extends BigNumber
return self::newBigRational($numerator, $denominator, false);
}
public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
{
if ($scale === $this->scale) {
return $this;
@@ -693,36 +691,6 @@ final class BigDecimal extends BigNumber
$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.
*
+10 -38
View File
@@ -27,7 +27,7 @@ final class BigInteger extends BigNumber
* No leading zeros must be present.
* No leading minus sign must be present if the number is zero.
*/
private string $value;
private readonly string $value;
/**
* Protected constructor. Use a factory method to obtain an instance.
@@ -40,15 +40,11 @@ final class BigInteger extends BigNumber
}
/**
* Creates a BigInteger of the given value.
*
* @throws MathException If the value cannot be converted to a BigInteger.
*
* @psalm-pure
*/
public static function of(BigNumber|int|float|string $value) : BigInteger
protected static function from(BigNumber $number): static
{
return parent::of($value)->toBigInteger();
return $number->toBigInteger();
}
/**
@@ -225,9 +221,10 @@ final class BigInteger extends BigNumber
}
if ($randomBytesGenerator === null) {
$randomBytesGenerator = 'random_bytes';
$randomBytesGenerator = random_bytes(...);
}
/** @var int<1, max> $byteLength */
$byteLength = \intdiv($numBits - 1, 8) + 1;
$extraBits = ($byteLength * 8 - $numBits);
@@ -429,12 +426,12 @@ final class BigInteger extends BigNumber
* Returns the result of the division of this number by the given one.
*
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigInteger.
* @param int $roundingMode An optional rounding mode.
* @param RoundingMode $roundingMode An optional rounding mode, defaults to UNNECESSARY.
*
* @throws MathException If the divisor is not a valid number, is not convertible to a BigInteger, is zero,
* or RoundingMode::UNNECESSARY is used and the remainder is not zero.
*/
public function dividedBy(BigNumber|int|float|string $that, int $roundingMode = RoundingMode::UNNECESSARY) : BigInteger
public function dividedBy(BigNumber|int|float|string $that, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigInteger
{
$that = BigInteger::of($that);
@@ -534,6 +531,8 @@ final class BigInteger extends BigNumber
*
* @return BigInteger[] An array containing the quotient and the remainder.
*
* @psalm-return array{BigInteger, BigInteger}
*
* @throws DivisionByZeroException If the divisor is zero.
*/
public function quotientAndRemainder(BigNumber|int|float|string $that) : array
@@ -888,7 +887,7 @@ final class BigInteger extends BigNumber
return self::newBigRational($this, BigInteger::one(), false);
}
public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
{
return $this->toBigDecimal()->toScale($scale, $roundingMode);
}
@@ -1049,31 +1048,4 @@ final class BigInteger extends BigNumber
$this->value = $data['value'];
}
/**
* This method is required by interface Serializable and SHOULD NOT be accessed directly.
*
* @internal
*/
public function serialize() : string
{
return $this->value;
}
/**
* 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.');
}
$this->value = $value;
}
}
+122 -125
View File
@@ -14,26 +14,29 @@ use Brick\Math\Exception\RoundingNecessaryException;
*
* @psalm-immutable
*/
abstract class BigNumber implements \Serializable, \JsonSerializable
abstract class BigNumber implements \JsonSerializable
{
/**
* The regular expression used to parse integer, decimal and rational numbers.
* The regular expression used to parse integer or decimal numbers.
*/
private const PARSE_REGEXP =
private const PARSE_REGEXP_NUMERICAL =
'/^' .
'(?<sign>[\-\+])?' .
'(?:' .
'(?:' .
'(?<integral>[0-9]+)?' .
'(?<point>\.)?' .
'(?<fractional>[0-9]+)?' .
'(?:[eE](?<exponent>[\-\+]?[0-9]+))?' .
')|(?:' .
'(?<numerator>[0-9]+)' .
'\/?' .
'(?<denominator>[0-9]+)' .
')' .
')' .
'(?<integral>[0-9]+)?' .
'(?<point>\.)?' .
'(?<fractional>[0-9]+)?' .
'(?:[eE](?<exponent>[\-\+]?[0-9]+))?' .
'$/';
/**
* The regular expression used to parse rational numbers.
*/
private const PARSE_REGEXP_RATIONAL =
'/^' .
'(?<sign>[\-\+])?' .
'(?<numerator>[0-9]+)' .
'\/?' .
'(?<denominator>[0-9]+)' .
'$/';
/**
@@ -53,7 +56,24 @@ abstract class BigNumber implements \Serializable, \JsonSerializable
*
* @psalm-pure
*/
public static function of(BigNumber|int|float|string $value) : BigNumber
final public static function of(BigNumber|int|float|string $value) : static
{
$value = self::_of($value);
if (static::class === BigNumber::class) {
// https://github.com/vimeo/psalm/issues/10309
assert($value instanceof static);
return $value;
}
return static::from($value);
}
/**
* @psalm-pure
*/
private static function _of(BigNumber|int|float|string $value) : BigNumber
{
if ($value instanceof BigNumber) {
return $value;
@@ -63,34 +83,25 @@ abstract class BigNumber implements \Serializable, \JsonSerializable
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();
if (is_float($value)) {
$value = (string) $value;
}
$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;
if (str_contains($value, '/')) {
// Rational number
if (\preg_match(self::PARSE_REGEXP_RATIONAL, $value, $matches, PREG_UNMATCHED_AS_NULL) !== 1) {
throw NumberFormatException::invalidFormat($value);
}
$numerator = self::cleanUp($numerator);
$denominator = self::cleanUp($denominator);
$sign = $matches['sign'];
$numerator = $matches['numerator'];
$denominator = $matches['denominator'];
assert($numerator !== null);
assert($denominator !== null);
$numerator = self::cleanUp($sign, $numerator);
$denominator = self::cleanUp(null, $denominator);
if ($denominator === '0') {
throw DivisionByZeroException::denominatorMustNotBeZero();
@@ -101,67 +112,62 @@ abstract class BigNumber implements \Serializable, \JsonSerializable
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.');
} else {
// Integer or decimal number
if (\preg_match(self::PARSE_REGEXP_NUMERICAL, $value, $matches, PREG_UNMATCHED_AS_NULL) !== 1) {
throw NumberFormatException::invalidFormat($value);
}
$unscaledValue = self::cleanUp(($sign ?? ''). $integral . $fractional);
$sign = $matches['sign'];
$point = $matches['point'];
$integral = $matches['integral'];
$fractional = $matches['fractional'];
$exponent = $matches['exponent'];
$scale = \strlen($fractional) - $exponent;
if ($integral === null && $fractional === null) {
throw NumberFormatException::invalidFormat($value);
}
if ($scale < 0) {
if ($unscaledValue !== '0') {
$unscaledValue .= \str_repeat('0', - $scale);
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.');
}
$scale = 0;
$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);
}
return new BigDecimal($unscaledValue, $scale);
$integral = self::cleanUp($sign, $integral);
return new BigInteger($integral);
}
$integral = self::cleanUp(($sign ?? '') . $integral);
return new BigInteger($integral);
}
/**
* Safely converts float to string, avoiding locale-dependent issues.
* Overridden by subclasses to convert a BigNumber to an instance of the subclass.
*
* @see https://github.com/brick/math/pull/20
* @throws MathException If the value cannot be converted.
*
* @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;
}
abstract protected static function from(BigNumber $number): static;
/**
* Proxy method to access BigInteger's protected constructor from sibling classes.
@@ -169,7 +175,7 @@ abstract class BigNumber implements \Serializable, \JsonSerializable
* @internal
* @psalm-pure
*/
protected function newBigInteger(string $value) : BigInteger
final protected function newBigInteger(string $value) : BigInteger
{
return new BigInteger($value);
}
@@ -180,7 +186,7 @@ abstract class BigNumber implements \Serializable, \JsonSerializable
* @internal
* @psalm-pure
*/
protected function newBigDecimal(string $value, int $scale = 0) : BigDecimal
final protected function newBigDecimal(string $value, int $scale = 0) : BigDecimal
{
return new BigDecimal($value, $scale);
}
@@ -191,7 +197,7 @@ abstract class BigNumber implements \Serializable, \JsonSerializable
* @internal
* @psalm-pure
*/
protected function newBigRational(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator) : BigRational
final protected function newBigRational(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator) : BigRational
{
return new BigRational($numerator, $denominator, $checkDenominator);
}
@@ -205,11 +211,9 @@ abstract class BigNumber implements \Serializable, \JsonSerializable
* @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
final public static function min(BigNumber|int|float|string ...$values) : static
{
$min = null;
@@ -237,11 +241,9 @@ abstract class BigNumber implements \Serializable, \JsonSerializable
* @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
final public static function max(BigNumber|int|float|string ...$values) : static
{
$max = null;
@@ -271,7 +273,7 @@ abstract class BigNumber implements \Serializable, \JsonSerializable
*
* @psalm-pure
*/
public static function sum(BigNumber|int|float|string ...$values) : static
final public static function sum(BigNumber|int|float|string ...$values) : static
{
/** @var static|null $sum */
$sum = null;
@@ -323,37 +325,28 @@ abstract class BigNumber implements \Serializable, \JsonSerializable
}
/**
* Removes optional leading zeros and + sign from the given number.
* Removes optional leading zeros and applies sign.
*
* @param string $number The number, validated as a non-empty string of digits with optional leading sign.
* @param string|null $sign The sign, '+' or '-', optional. Null is allowed for convenience and treated as '+'.
* @param string $number The number, validated as a non-empty string of digits.
*
* @psalm-pure
*/
private static function cleanUp(string $number) : string
private static function cleanUp(string|null $sign, 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;
return $sign === '-' ? '-' . $number : $number;
}
/**
* Checks if this number is equal to the given one.
*/
public function isEqualTo(BigNumber|int|float|string $that) : bool
final public function isEqualTo(BigNumber|int|float|string $that) : bool
{
return $this->compareTo($that) === 0;
}
@@ -361,7 +354,7 @@ abstract class BigNumber implements \Serializable, \JsonSerializable
/**
* Checks if this number is strictly lower than the given one.
*/
public function isLessThan(BigNumber|int|float|string $that) : bool
final public function isLessThan(BigNumber|int|float|string $that) : bool
{
return $this->compareTo($that) < 0;
}
@@ -369,7 +362,7 @@ abstract class BigNumber implements \Serializable, \JsonSerializable
/**
* Checks if this number is lower than or equal to the given one.
*/
public function isLessThanOrEqualTo(BigNumber|int|float|string $that) : bool
final public function isLessThanOrEqualTo(BigNumber|int|float|string $that) : bool
{
return $this->compareTo($that) <= 0;
}
@@ -377,7 +370,7 @@ abstract class BigNumber implements \Serializable, \JsonSerializable
/**
* Checks if this number is strictly greater than the given one.
*/
public function isGreaterThan(BigNumber|int|float|string $that) : bool
final public function isGreaterThan(BigNumber|int|float|string $that) : bool
{
return $this->compareTo($that) > 0;
}
@@ -385,7 +378,7 @@ abstract class BigNumber implements \Serializable, \JsonSerializable
/**
* Checks if this number is greater than or equal to the given one.
*/
public function isGreaterThanOrEqualTo(BigNumber|int|float|string $that) : bool
final public function isGreaterThanOrEqualTo(BigNumber|int|float|string $that) : bool
{
return $this->compareTo($that) >= 0;
}
@@ -393,7 +386,7 @@ abstract class BigNumber implements \Serializable, \JsonSerializable
/**
* Checks if this number equals zero.
*/
public function isZero() : bool
final public function isZero() : bool
{
return $this->getSign() === 0;
}
@@ -401,7 +394,7 @@ abstract class BigNumber implements \Serializable, \JsonSerializable
/**
* Checks if this number is strictly negative.
*/
public function isNegative() : bool
final public function isNegative() : bool
{
return $this->getSign() < 0;
}
@@ -409,7 +402,7 @@ abstract class BigNumber implements \Serializable, \JsonSerializable
/**
* Checks if this number is negative or zero.
*/
public function isNegativeOrZero() : bool
final public function isNegativeOrZero() : bool
{
return $this->getSign() <= 0;
}
@@ -417,7 +410,7 @@ abstract class BigNumber implements \Serializable, \JsonSerializable
/**
* Checks if this number is strictly positive.
*/
public function isPositive() : bool
final public function isPositive() : bool
{
return $this->getSign() > 0;
}
@@ -425,7 +418,7 @@ abstract class BigNumber implements \Serializable, \JsonSerializable
/**
* Checks if this number is positive or zero.
*/
public function isPositiveOrZero() : bool
final public function isPositiveOrZero() : bool
{
return $this->getSign() >= 0;
}
@@ -433,6 +426,8 @@ abstract class BigNumber implements \Serializable, \JsonSerializable
/**
* Returns the sign of this number.
*
* @psalm-return -1|0|1
*
* @return int -1 if the number is negative, 0 if zero, 1 if positive.
*/
abstract public function getSign() : int;
@@ -440,7 +435,9 @@ abstract class BigNumber implements \Serializable, \JsonSerializable
/**
* Compares this number to the given one.
*
* @return int [-1,0,1] If `$this` is lower than, equal to, or greater than `$that`.
* @psalm-return -1|0|1
*
* @return int -1 if `$this` is lower than, 0 if equal to, 1 if greater than `$that`.
*
* @throws MathException If the number is not valid.
*/
@@ -468,13 +465,13 @@ abstract class BigNumber implements \Serializable, \JsonSerializable
/**
* 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.
* @param int $scale The scale of the resulting `BigDecimal`.
* @param RoundingMode $roundingMode An optional rounding mode, defaults to UNNECESSARY.
*
* @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;
abstract public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal;
/**
* Returns the exact value of this number as a native integer.
@@ -505,7 +502,7 @@ abstract class BigNumber implements \Serializable, \JsonSerializable
*/
abstract public function __toString() : string;
public function jsonSerialize() : string
final public function jsonSerialize() : string
{
return $this->__toString();
}
+7 -39
View File
@@ -21,12 +21,12 @@ final class BigRational extends BigNumber
/**
* The numerator.
*/
private BigInteger $numerator;
private readonly BigInteger $numerator;
/**
* The denominator. Always strictly positive.
*/
private BigInteger $denominator;
private readonly BigInteger $denominator;
/**
* Protected constructor. Use a factory method to obtain an instance.
@@ -55,15 +55,11 @@ final class BigRational extends BigNumber
}
/**
* 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
protected static function from(BigNumber $number): static
{
return parent::of($value)->toBigRational();
return $number->toBigRational();
}
/**
@@ -181,6 +177,8 @@ final class BigRational extends BigNumber
* Returns the quotient and remainder of the division of the numerator by the denominator.
*
* @return BigInteger[]
*
* @psalm-return array{BigInteger, BigInteger}
*/
public function quotientAndRemainder() : array
{
@@ -353,7 +351,7 @@ final class BigRational extends BigNumber
return $this;
}
public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
{
return $this->numerator->toBigDecimal()->dividedBy($this->denominator, $scale, $roundingMode);
}
@@ -412,34 +410,4 @@ final class BigRational extends BigNumber
$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);
}
}
@@ -9,6 +9,14 @@ namespace Brick\Math\Exception;
*/
class NumberFormatException extends MathException
{
public static function invalidFormat(string $value) : self
{
return new self(\sprintf(
'The given value "%s" does not represent a valid number.',
$value,
));
}
/**
* @param string $char The failing character.
*
@@ -28,6 +36,6 @@ class NumberFormatException extends MathException
$char = '"' . $char . '"';
}
return new self(sprintf('Char %s is not a valid character in the given alphabet.', $char));
return new self(\sprintf('Char %s is not a valid character in the given alphabet.', $char));
}
}
@@ -25,7 +25,7 @@ abstract class Calculator
/**
* The maximum exponent value allowed for the pow() method.
*/
public const MAX_POWER = 1000000;
public const MAX_POWER = 1_000_000;
/**
* The alphabet for converting from and to base 2 to 36, lowercase.
@@ -128,7 +128,9 @@ abstract class Calculator
/**
* Compares two numbers.
*
* @return int [-1, 0, 1] If the first number is less than, equal to, or greater than the second number.
* @psalm-return -1|0|1
*
* @return int -1 if the first number is less than, 0 if equal to, 1 if greater than the second number.
*/
final public function cmp(string $a, string $b) : int
{
@@ -428,16 +430,16 @@ abstract class Calculator
*
* 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.
* @param string $a The dividend.
* @param string $b The divisor, must not be zero.
* @param RoundingMode $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
final public function divRound(string $a, string $b, RoundingMode $roundingMode) : string
{
[$quotient, $remainder] = $this->divQR($a, $b);
@@ -571,27 +573,17 @@ abstract class Calculator
$bBin = $this->twosComplement($bBin);
}
switch ($operator) {
case 'and':
$value = $aBin & $bBin;
$negative = ($aNeg and $bNeg);
break;
$value = match ($operator) {
'and' => $aBin & $bBin,
'or' => $aBin | $bBin,
'xor' => $aBin ^ $bBin,
};
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
}
$negative = match ($operator) {
'and' => $aNeg and $bNeg,
'or' => $aNeg or $bNeg,
'xor' => $aNeg xor $bNeg,
};
if ($negative) {
$value = $this->twosComplement($value);
@@ -35,10 +35,6 @@ class BcMathCalculator extends Calculator
return \bcdiv($a, $b, 0);
}
/**
* @psalm-suppress InvalidNullableReturnType
* @psalm-suppress NullableReturnStatement
*/
public function divR(string $a, string $b) : string
{
return \bcmod($a, $b, 0);
@@ -49,8 +45,6 @@ class BcMathCalculator extends Calculator
$q = \bcdiv($a, $b, 0);
$r = \bcmod($a, $b, 0);
assert($r !== null);
return [$q, $r];
}
@@ -64,10 +58,6 @@ class BcMathCalculator extends Calculator
return \bcpowmod($base, $exp, $mod, 0);
}
/**
* @psalm-suppress InvalidNullableReturnType
* @psalm-suppress NullableReturnStatement
*/
public function sqrt(string $n) : string
{
return \bcsqrt($n, 0);
@@ -23,25 +23,18 @@ class NativeCalculator extends Calculator
* 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;
private readonly 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.');
}
$this->maxDigits = match (PHP_INT_SIZE) {
4 => 9,
8 => 18,
default => throw new \RuntimeException('The platform is not 32-bit or 64-bit as expected.')
};
}
public function add(string $a, string $b) : string
@@ -161,10 +154,8 @@ class NativeCalculator extends Calculator
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.
$q = intdiv($na, $nb);
$r = $na % $nb;
$q = ($na - $r) / $nb;
assert(is_int($q));
return [
(string) $q,
@@ -536,7 +527,7 @@ class NativeCalculator extends Calculator
/**
* Compares two non-signed large numbers.
*
* @return int [-1, 0, 1]
* @psalm-return -1|0|1
*/
private function doCmp(string $a, string $b) : int
{
@@ -549,7 +540,7 @@ class NativeCalculator extends Calculator
return $cmp;
}
return \strcmp($a, $b) <=> 0; // enforce [-1, 0, 1]
return \strcmp($a, $b) <=> 0; // enforce -1|0|1
}
/**
+11 -20
View File
@@ -13,24 +13,15 @@ namespace Brick\Math;
* 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
enum 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;
case UNNECESSARY;
/**
* Rounds away from zero.
@@ -38,7 +29,7 @@ final class RoundingMode
* 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;
case UP;
/**
* Rounds towards zero.
@@ -46,7 +37,7 @@ final class RoundingMode
* 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;
case DOWN;
/**
* Rounds towards positive infinity.
@@ -54,7 +45,7 @@ final class RoundingMode
* 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;
case CEILING;
/**
* Rounds towards negative infinity.
@@ -62,7 +53,7 @@ final class RoundingMode
* 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;
case FLOOR;
/**
* Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round up.
@@ -70,28 +61,28 @@ final class RoundingMode
* 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;
case HALF_UP;
/**
* 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;
case HALF_DOWN;
/**
* 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;
case HALF_CEILING;
/**
* 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;
case HALF_FLOOR;
/**
* Rounds towards the "nearest neighbor" unless both neighbors are equidistant, in which case rounds towards the even neighbor.
@@ -103,5 +94,5 @@ final class RoundingMode
* 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;
case HALF_EVEN;
}
@@ -322,6 +322,7 @@ class InstalledVersions
}
$installed = array();
$copiedLocalDir = false;
if (self::$canGetVendors) {
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
@@ -330,9 +331,11 @@ class InstalledVersions
} elseif (is_file($vendorDir.'/composer/installed.php')) {
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require $vendorDir.'/composer/installed.php';
$installed[] = self::$installedByVendor[$vendorDir] = $required;
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $installed[count($installed) - 1];
self::$installedByVendor[$vendorDir] = $required;
$installed[] = $required;
if (strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $required;
$copiedLocalDir = true;
}
}
}
@@ -350,7 +353,7 @@ class InstalledVersions
}
}
if (self::$installed !== array()) {
if (self::$installed !== array() && !$copiedLocalDir) {
$installed[] = self::$installed;
}
File diff suppressed because it is too large Load Diff
+4 -5
View File
@@ -6,15 +6,14 @@ $vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
'8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php',
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php',
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
'662a729f963d39afe703c9d9b7ab4a8c' => $vendorDir . '/symfony/polyfill-php83/bootstrap.php',
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
@@ -24,6 +23,7 @@ return array(
'47e1160838b5e5a10346ac4084b58c23' => $vendorDir . '/laravel/prompts/src/helpers.php',
'6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
'35a6ad97d21e794e7e22a17d806652e4' => $vendorDir . '/nunomaduro/termwind/src/Functions.php',
'9b38cf48e83f5d8f60375221cd213eee' => $vendorDir . '/phpstan/phpstan/bootstrap.php',
'801c31d8ed748cfa537fa45402288c95' => $vendorDir . '/psy/psysh/src/functions.php',
'e39a8b23c42d4e1452234d762b03835a' => $vendorDir . '/ramsey/uuid/src/functions.php',
'e23faeee409e941dc9b4c80386209c39' => $vendorDir . '/laracasts/flash/src/Laracasts/Flash/functions.php',
@@ -37,7 +37,6 @@ return array(
'c72349b1fe8d0deeedd3a52e8aa814d8' => $vendorDir . '/mockery/mockery/library/helpers.php',
'ce9671a430e4846b44e1c68c7611f9f5' => $vendorDir . '/mockery/mockery/library/Mockery.php',
'9f394da3192a168c4633675768d80428' => $vendorDir . '/nwidart/laravel-modules/src/helpers.php',
'9b38cf48e83f5d8f60375221cd213eee' => $vendorDir . '/phpstan/phpstan/bootstrap.php',
'ec07570ca5a812141189b1fa81503674' => $vendorDir . '/phpunit/phpunit/src/Framework/Assert/Functions.php',
'377b22b161c09ed6e5152de788ca020a' => $vendorDir . '/spatie/laravel-permission/src/helpers.php',
'646961a8eab48144f6c03fc7c3185753' => $baseDir . '/app/Http/Helpers/Functions.php',
+9 -3
View File
@@ -16,7 +16,6 @@ return array(
'Symfony\\Polyfill\\Uuid\\' => array($vendorDir . '/symfony/polyfill-uuid'),
'Symfony\\Polyfill\\Php83\\' => array($vendorDir . '/symfony/polyfill-php83'),
'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'),
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
'Symfony\\Polyfill\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'),
'Symfony\\Polyfill\\Intl\\Idn\\' => array($vendorDir . '/symfony/polyfill-intl-idn'),
@@ -40,8 +39,10 @@ return array(
'Symfony\\Component\\ErrorHandler\\' => array($vendorDir . '/symfony/error-handler'),
'Symfony\\Component\\CssSelector\\' => array($vendorDir . '/symfony/css-selector'),
'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'),
'Svg\\' => array($vendorDir . '/phenx/php-svg-lib/src/Svg'),
'Streamline\\' => array($baseDir . '/app'),
'Spatie\\Permission\\' => array($vendorDir . '/spatie/laravel-permission/src'),
'Sabberworm\\CSS\\' => array($vendorDir . '/sabberworm/php-css-parser/src'),
'Ramsey\\Uuid\\' => array($vendorDir . '/ramsey/uuid/src'),
'Ramsey\\Collection\\' => array($vendorDir . '/ramsey/collection/src'),
'Psy\\' => array($vendorDir . '/psy/psysh/src'),
@@ -55,11 +56,13 @@ return array(
'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'),
'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
'PhpOption\\' => array($vendorDir . '/phpoption/phpoption/src/PhpOption'),
'PhpMyAdmin\\SqlParser\\' => array($vendorDir . '/phpmyadmin/sql-parser/src'),
'OwenIt\\Auditing\\' => array($vendorDir . '/owen-it/laravel-auditing/src'),
'Nwidart\\Modules\\' => array($vendorDir . '/nwidart/laravel-modules/src'),
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
'Modules\\' => array($baseDir . '/Modules'),
'Mockery\\' => array($vendorDir . '/mockery/mockery/library/Mockery'),
'Masterminds\\' => array($vendorDir . '/masterminds/html5/src'),
'League\\MimeTypeDetection\\' => array($vendorDir . '/league/mime-type-detection/src'),
'League\\Flysystem\\Local\\' => array($vendorDir . '/league/flysystem-local'),
'League\\Flysystem\\' => array($vendorDir . '/league/flysystem/src'),
@@ -69,6 +72,7 @@ return array(
'Laravel\\Tinker\\' => array($vendorDir . '/laravel/tinker/src'),
'Laravel\\SerializableClosure\\' => array($vendorDir . '/laravel/serializable-closure/src'),
'Laravel\\Prompts\\' => array($vendorDir . '/laravel/prompts/src'),
'Larastan\\Larastan\\' => array($vendorDir . '/larastan/larastan/src'),
'Knp\\Snappy\\' => array($vendorDir . '/knplabs/knp-snappy/src/Knp/Snappy'),
'Illuminate\\Support\\' => array($vendorDir . '/laravel/framework/src/Illuminate/Macroable', $vendorDir . '/laravel/framework/src/Illuminate/Collections', $vendorDir . '/laravel/framework/src/Illuminate/Conditionable'),
'Illuminate\\Foundation\\Auth\\' => array($vendorDir . '/laravel/ui/auth-backend'),
@@ -80,11 +84,13 @@ return array(
'GrahamCampbell\\ResultType\\' => array($vendorDir . '/graham-campbell/result-type/src'),
'Fx3costa\\LaravelChartJs\\' => array($vendorDir . '/fx3costa/laravelchartjs/src'),
'Fruitcake\\Cors\\' => array($vendorDir . '/fruitcake/php-cors/src'),
'FontLib\\' => array($vendorDir . '/phenx/php-font-lib/src/FontLib'),
'Faker\\' => array($vendorDir . '/fakerphp/faker/src/Faker'),
'Egulias\\EmailValidator\\' => array($vendorDir . '/egulias/email-validator/src'),
'Dotenv\\' => array($vendorDir . '/vlucas/phpdotenv/src'),
'Dompdf\\' => array($vendorDir . '/dompdf/dompdf/src'),
'Doctrine\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib/Doctrine/Inflector'),
'Doctrine\\Deprecations\\' => array($vendorDir . '/doctrine/deprecations/lib/Doctrine/Deprecations'),
'Doctrine\\Deprecations\\' => array($vendorDir . '/doctrine/deprecations/src'),
'Doctrine\\DBAL\\' => array($vendorDir . '/doctrine/dbal/src'),
'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/src'),
'Doctrine\\Common\\Cache\\' => array($vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache'),
@@ -97,6 +103,6 @@ return array(
'Carbon\\' => array($vendorDir . '/nesbot/carbon/src/Carbon'),
'Brick\\Math\\' => array($vendorDir . '/brick/math/src'),
'Barryvdh\\Snappy\\' => array($vendorDir . '/barryvdh/laravel-snappy/src'),
'AfricasTalking\\SDK\\Tests\\' => array($vendorDir . '/africastalking/africastalking/tests'),
'Barryvdh\\DomPDF\\' => array($vendorDir . '/barryvdh/laravel-dompdf/src'),
'AfricasTalking\\SDK\\' => array($vendorDir . '/africastalking/africastalking/src'),
);
+5 -5
View File
@@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit379a36e429894b61b55afc3b84fa116a
class ComposerAutoloaderInit69a41de4ce3c76c7865a7de0eec12ccc
{
private static $loader;
@@ -24,16 +24,16 @@ class ComposerAutoloaderInit379a36e429894b61b55afc3b84fa116a
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInit379a36e429894b61b55afc3b84fa116a', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInit69a41de4ce3c76c7865a7de0eec12ccc', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInit379a36e429894b61b55afc3b84fa116a', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInit69a41de4ce3c76c7865a7de0eec12ccc', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit379a36e429894b61b55afc3b84fa116a::getInitializer($loader));
call_user_func(\Composer\Autoload\ComposerStaticInit69a41de4ce3c76c7865a7de0eec12ccc::getInitializer($loader));
$loader->register(true);
$filesToLoad = \Composer\Autoload\ComposerStaticInit379a36e429894b61b55afc3b84fa116a::$files;
$filesToLoad = \Composer\Autoload\ComposerStaticInit69a41de4ce3c76c7865a7de0eec12ccc::$files;
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [3.0.3] - 2024-07-08
### Fixed
- Fixed PHP 8.4 deprecation notices (#47)
## [3.0.2] - 2022-10-27
### Fixed
@@ -57,7 +63,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
**Initial release!**
[Unreleased]: https://github.com/dflydev/dflydev-dot-access-data/compare/v3.0.2...main
[Unreleased]: https://github.com/dflydev/dflydev-dot-access-data/compare/v3.0.3...main
[3.0.3]: https://github.com/dflydev/dflydev-dot-access-data/compare/v3.0.2...v3.0.3
[3.0.2]: https://github.com/dflydev/dflydev-dot-access-data/compare/v3.0.1...v3.0.2
[3.0.1]: https://github.com/dflydev/dflydev-dot-access-data/compare/v3.0.0...v3.0.1
[3.0.0]: https://github.com/dflydev/dflydev-dot-access-data/compare/v2.0.0...v3.0.0
@@ -23,7 +23,7 @@ class MissingPathException extends DataException
/** @var string */
protected $path;
public function __construct(string $path, string $message = '', int $code = 0, Throwable $previous = null)
public function __construct(string $path, string $message = '', int $code = 0, ?Throwable $previous = null)
{
$this->path = $path;
+20 -28
View File
@@ -1,11 +1,11 @@
# Doctrine DBAL
| [5.0-dev][5.0] | [4.1-dev][4.1] | [4.0][4.0] | [3.9-dev][3.9] | [3.8][3.8] |
|:---------------------------------------------------:|:---------------------------------------------------:|:---------------------------------------------------:|:---------------------------------------------------:|:---------------------------------------------------:|
| [![GitHub Actions][GA 5.0 image]][GA 5.0] | [![GitHub Actions][GA 4.1 image]][GA 4.1] | [![GitHub Actions][GA 4.0 image]][GA 4.0] | [![GitHub Actions][GA 3.9 image]][GA 3.9] | [![GitHub Actions][GA 3.8 image]][GA 3.8] |
| [![AppVeyor][AppVeyor 5.0 image]][AppVeyor 5.0] | [![AppVeyor][AppVeyor 4.1 image]][AppVeyor 4.1] | [![AppVeyor][AppVeyor 4.0 image]][AppVeyor 4.0] | [![AppVeyor][AppVeyor 3.9 image]][AppVeyor 3.9] | [![AppVeyor][AppVeyor 3.8 image]][AppVeyor 3.8] |
| [![Code Coverage][Coverage 5.0 image]][CodeCov 5.0] | [![Code Coverage][Coverage 4.1 image]][CodeCov 4.1] | [![Code Coverage][Coverage 4.0 image]][CodeCov 4.0] | [![Code Coverage][Coverage 3.9 image]][CodeCov 3.9] | [![Code Coverage][Coverage 3.8 image]][CodeCov 3.8] |
| N/A | N/A | [![Type Coverage][TypeCov image]][TypeCov] | N/A | |
| [5.0-dev][5.0] | [4.3-dev][4.3] | [4.2][4.2] | [3.9][3.9] |
|:---------------------------------------------------:|:---------------------------------------------------:|:---------------------------------------------------:|:---------------------------------------------------:|
| [![GitHub Actions][GA 5.0 image]][GA 5.0] | [![GitHub Actions][GA 4.3 image]][GA 4.3] | [![GitHub Actions][GA 4.2 image]][GA 4.2] | [![GitHub Actions][GA 3.9 image]][GA 3.9] |
| [![AppVeyor][AppVeyor 5.0 image]][AppVeyor 5.0] | [![AppVeyor][AppVeyor 4.3 image]][AppVeyor 4.3] | [![AppVeyor][AppVeyor 4.2 image]][AppVeyor 4.2] | [![AppVeyor][AppVeyor 3.9 image]][AppVeyor 3.9] |
| [![Code Coverage][Coverage 5.0 image]][CodeCov 5.0] | [![Code Coverage][Coverage 4.3 image]][CodeCov 4.3] | [![Code Coverage][Coverage 4.2 image]][CodeCov 4.2] | [![Code Coverage][Coverage 3.9 image]][CodeCov 3.9] |
| N/A | N/A | [![Type Coverage][TypeCov image]][TypeCov] | N/A |
Powerful ***D***ata***B***ase ***A***bstraction ***L***ayer with many features for database schema introspection and schema management.
@@ -23,21 +23,21 @@ Powerful ***D***ata***B***ase ***A***bstraction ***L***ayer with many features f
[GA 5.0]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A5.0.x
[GA 5.0 image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg?branch=5.0.x
[Coverage 4.1 image]: https://codecov.io/gh/doctrine/dbal/branch/4.1.x/graph/badge.svg
[4.1]: https://github.com/doctrine/dbal/tree/4.1.x
[CodeCov 4.1]: https://codecov.io/gh/doctrine/dbal/branch/4.1.x
[AppVeyor 4.1]: https://ci.appveyor.com/project/doctrine/dbal/branch/4.1.x
[AppVeyor 4.1 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/4.1.x?svg=true
[GA 4.1]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A4.1.x
[GA 4.1 image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg?branch=4.1.x
[Coverage 4.3 image]: https://codecov.io/gh/doctrine/dbal/branch/4.3.x/graph/badge.svg
[4.3]: https://github.com/doctrine/dbal/tree/4.3.x
[CodeCov 4.3]: https://codecov.io/gh/doctrine/dbal/branch/4.3.x
[AppVeyor 4.3]: https://ci.appveyor.com/project/doctrine/dbal/branch/4.3.x
[AppVeyor 4.3 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/4.3.x?svg=true
[GA 4.3]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A4.3.x
[GA 4.3 image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg?branch=4.3.x
[Coverage 4.0 image]: https://codecov.io/gh/doctrine/dbal/branch/4.0.x/graph/badge.svg
[4.0]: https://github.com/doctrine/dbal/tree/4.0.x
[CodeCov 4.0]: https://codecov.io/gh/doctrine/dbal/branch/4.0.x
[AppVeyor 4.0]: https://ci.appveyor.com/project/doctrine/dbal/branch/4.0.x
[AppVeyor 4.0 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/4.0.x?svg=true
[GA 4.0]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A4.0.x
[GA 4.0 image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg?branch=4.0.x
[Coverage 4.2 image]: https://codecov.io/gh/doctrine/dbal/branch/4.2.x/graph/badge.svg
[4.2]: https://github.com/doctrine/dbal/tree/4.2.x
[CodeCov 4.2]: https://codecov.io/gh/doctrine/dbal/branch/4.2.x
[AppVeyor 4.2]: https://ci.appveyor.com/project/doctrine/dbal/branch/4.2.x
[AppVeyor 4.2 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/4.2.x?svg=true
[GA 4.2]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A4.2.x
[GA 4.2 image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg?branch=4.2.x
[TypeCov]: https://shepherd.dev/github/doctrine/dbal
[TypeCov image]: https://shepherd.dev/github/doctrine/dbal/coverage.svg
@@ -48,11 +48,3 @@ Powerful ***D***ata***B***ase ***A***bstraction ***L***ayer with many features f
[AppVeyor 3.9 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/3.9.x?svg=true
[GA 3.9]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A3.9.x
[GA 3.9 image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg?branch=3.9.x
[Coverage 3.8 image]: https://codecov.io/gh/doctrine/dbal/branch/3.8.x/graph/badge.svg
[3.8]: https://github.com/doctrine/dbal/tree/3.8.x
[CodeCov 3.8]: https://codecov.io/gh/doctrine/dbal/branch/3.8.x
[AppVeyor 3.8]: https://ci.appveyor.com/project/doctrine/dbal/branch/3.8.x
[AppVeyor 3.8 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/3.8.x?svg=true
[GA 3.8]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A3.8.x
[GA 3.8 image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg?branch=3.8.x
+4 -4
View File
@@ -43,12 +43,12 @@
"doctrine/coding-standard": "12.0.0",
"fig/log-test": "^1",
"jetbrains/phpstorm-stubs": "2023.1",
"phpstan/phpstan": "1.10.57",
"phpstan/phpstan-strict-rules": "^1.5",
"phpunit/phpunit": "9.6.16",
"phpstan/phpstan": "1.12.6",
"phpstan/phpstan-strict-rules": "^1.6",
"phpunit/phpunit": "9.6.20",
"psalm/plugin-phpunit": "0.18.4",
"slevomat/coding-standard": "8.13.1",
"squizlabs/php_codesniffer": "3.8.1",
"squizlabs/php_codesniffer": "3.10.2",
"symfony/cache": "^5.4|^6.0|^7.0",
"symfony/console": "^4.4|^5.4|^6.0|^7.0",
"vimeo/psalm": "4.30.0"
@@ -1317,10 +1317,6 @@ class Connection
throw ConnectionException::mayNotAlterNestedTransactionWithSavepointsInTransaction();
}
if (! $this->getDatabasePlatform()->supportsSavepoints()) {
throw ConnectionException::savepointsNotSupported();
}
$this->nestTransactionsWithSavepoints = (bool) $nestTransactionsWithSavepoints;
}
@@ -101,6 +101,7 @@ final class ExceptionConverter implements ExceptionConverterInterface
return new ConnectionException($exception, $query);
case 2006:
case 4031:
return new ConnectionLost($exception, $query);
case 1048:
@@ -87,7 +87,7 @@ abstract class AbstractDB2Driver implements VersionAwarePlatformDriver
'/^(?:[^\s]+\s)?(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)/i',
$versionString,
$versionParts,
) === 0
) !== 1
) {
throw DBALException::invalidPlatformVersionSpecified(
$versionString,
@@ -8,12 +8,14 @@ use Doctrine\DBAL\Driver\API\MySQL;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Platforms\AbstractMySQLPlatform;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\MariaDb1010Platform;
use Doctrine\DBAL\Platforms\MariaDb1027Platform;
use Doctrine\DBAL\Platforms\MariaDb1043Platform;
use Doctrine\DBAL\Platforms\MariaDb1052Platform;
use Doctrine\DBAL\Platforms\MariaDb1060Platform;
use Doctrine\DBAL\Platforms\MySQL57Platform;
use Doctrine\DBAL\Platforms\MySQL80Platform;
use Doctrine\DBAL\Platforms\MySQL84Platform;
use Doctrine\DBAL\Platforms\MySQLPlatform;
use Doctrine\DBAL\Schema\MySQLSchemaManager;
use Doctrine\DBAL\VersionAwarePlatformDriver;
@@ -40,6 +42,10 @@ abstract class AbstractMySQLDriver implements VersionAwarePlatformDriver
if ($mariadb) {
$mariaDbVersion = $this->getMariaDbMysqlVersionNumber($version);
if (version_compare($mariaDbVersion, '10.10.0', '>=')) {
return new MariaDb1010Platform();
}
if (version_compare($mariaDbVersion, '10.6.0', '>=')) {
return new MariaDb1060Platform();
}
@@ -64,6 +70,20 @@ abstract class AbstractMySQLDriver implements VersionAwarePlatformDriver
}
} else {
$oracleMysqlVersion = $this->getOracleMysqlVersionNumber($version);
if (version_compare($oracleMysqlVersion, '8.4.0', '>=')) {
if (! version_compare($version, '8.4.0', '>=')) {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/dbal/pull/5779',
'Version detection logic for MySQL will change in DBAL 4. '
. 'Please specify the version as the server reports it, e.g. "8.4.0" instead of "8.4".',
);
}
return new MySQL84Platform();
}
if (version_compare($oracleMysqlVersion, '8', '>=')) {
if (! version_compare($version, '8.0.0', '>=')) {
Deprecation::trigger(
@@ -116,7 +136,7 @@ abstract class AbstractMySQLDriver implements VersionAwarePlatformDriver
'/^(?P<major>\d+)(?:\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?)?/',
$versionString,
$versionParts,
) === 0
) !== 1
) {
throw Exception::invalidPlatformVersionSpecified(
$versionString,
@@ -130,6 +150,8 @@ abstract class AbstractMySQLDriver implements VersionAwarePlatformDriver
if ($majorVersion === '5' && $minorVersion === '7') {
$patchVersion ??= '9';
} else {
$patchVersion ??= '0';
}
return $majorVersion . '.' . $minorVersion . '.' . $patchVersion;
@@ -160,7 +182,7 @@ abstract class AbstractMySQLDriver implements VersionAwarePlatformDriver
'/^(?:5\.5\.5-)?(mariadb-)?(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)/i',
$versionString,
$versionParts,
) === 0
) !== 1
) {
throw Exception::invalidPlatformVersionSpecified(
$versionString,
@@ -8,6 +8,7 @@ use Doctrine\DBAL\Driver\API\PostgreSQL;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\PostgreSQL100Platform;
use Doctrine\DBAL\Platforms\PostgreSQL120Platform;
use Doctrine\DBAL\Platforms\PostgreSQL94Platform;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\DBAL\Schema\PostgreSQLSchemaManager;
@@ -28,7 +29,7 @@ abstract class AbstractPostgreSQLDriver implements VersionAwarePlatformDriver
*/
public function createDatabasePlatformForVersion($version)
{
if (preg_match('/^(?P<major>\d+)(?:\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?)?/', $version, $versionParts) === 0) {
if (preg_match('/^(?P<major>\d+)(?:\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?)?/', $version, $versionParts) !== 1) {
throw Exception::invalidPlatformVersionSpecified(
$version,
'<major_version>.<minor_version>.<patch_version>',
@@ -40,6 +41,10 @@ abstract class AbstractPostgreSQLDriver implements VersionAwarePlatformDriver
$patchVersion = $versionParts['patch'] ?? 0;
$version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion;
if (version_compare($version, '12.0', '>=')) {
return new PostgreSQL120Platform();
}
if (version_compare($version, '10.0', '>=')) {
return new PostgreSQL100Platform();
}
@@ -26,7 +26,6 @@ class InitializeSession implements Middleware
'ALTER SESSION SET'
. " NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS'"
. " NLS_TIME_FORMAT = 'HH24:MI:SS'"
. " NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS'"
. " NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS'"
. " NLS_TIMESTAMP_TZ_FORMAT = 'YYYY-MM-DD HH24:MI:SS TZH:TZM'"
. " NLS_NUMERIC_CHARACTERS = '.,'",
@@ -126,6 +126,10 @@ final class Driver extends AbstractPostgreSQLDriver
$dsn .= 'application_name=' . $params['application_name'] . ';';
}
if (isset($params['gssencmode'])) {
$dsn .= 'gssencmode=' . $params['gssencmode'] . ';';
}
return $dsn;
}
}
@@ -72,6 +72,7 @@ final class Driver extends AbstractPostgreSQLDriver
'user' => $params['user'] ?? null,
'password' => $params['password'] ?? null,
'sslmode' => $params['sslmode'] ?? null,
'gssencmode' => $params['gssencmode'] ?? null,
],
static fn ($value) => $value !== '' && $value !== null,
);
@@ -1468,4 +1468,30 @@ SQL
return $result;
}
public function fetchTableOptionsByTable(bool $includeTableName): string
{
$sql = <<<'SQL'
SELECT t.TABLE_NAME,
t.ENGINE,
t.AUTO_INCREMENT,
t.TABLE_COMMENT,
t.CREATE_OPTIONS,
t.TABLE_COLLATION,
ccsa.CHARACTER_SET_NAME
FROM information_schema.TABLES t
INNER JOIN information_schema.COLLATION_CHARACTER_SET_APPLICABILITY ccsa
ON ccsa.COLLATION_NAME = t.TABLE_COLLATION
SQL;
$conditions = ['t.TABLE_SCHEMA = ?'];
if ($includeTableName) {
$conditions[] = 't.TABLE_NAME = ?';
}
$conditions[] = "t.TABLE_TYPE = 'BASE TABLE'";
return $sql . ' WHERE ' . implode(' AND ', $conditions);
}
}
@@ -213,6 +213,7 @@ abstract class AbstractPlatform
foreach (Type::getTypesMap() as $typeName => $className) {
foreach (Type::getType($typeName)->getMappedDatabaseTypes($this) as $dbType) {
$dbType = strtolower($dbType);
$this->doctrineTypeMapping[$dbType] = $typeName;
}
}
@@ -4692,6 +4693,11 @@ abstract class AbstractPlatform
return false;
}
// If disableTypeComments is true, we do not need to check types, all comparison is already done above
if ($this->disableTypeComments) {
return true;
}
return $column1->getType() === $column2->getType();
}
@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Platforms\Keywords;
use Doctrine\Deprecations\Deprecation;
use function array_diff;
use function array_merge;
/**
* MySQL 8.4 reserved keywords list.
*/
class MySQL84Keywords extends MySQL80Keywords
{
/**
* {@inheritDoc}
*
* @deprecated
*/
public function getName()
{
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/5433',
'MySQL84Keywords::getName() is deprecated.',
);
return 'MySQL84';
}
/**
* {@inheritDoc}
*
* @link https://dev.mysql.com/doc/refman/8.4/en/keywords.html
*/
protected function getKeywords()
{
$keywords = parent::getKeywords();
// Removed Keywords and Reserved Words
$keywords = array_diff($keywords, [
'MASTER_BIND',
'MASTER_SSL_VERIFY_SERVER_CERT',
]);
// New Keywords and Reserved Words
$keywords = array_merge($keywords, [
'AUTO',
'BERNOULLI',
'GTIDS',
'LOG',
'MANUAL',
'PARALLEL',
'PARSE_TREE',
'QUALIFY',
'S3',
'TABLESAMPLE',
]);
return $keywords;
}
}
@@ -0,0 +1,47 @@
<?php
namespace Doctrine\DBAL\Platforms;
use Doctrine\DBAL\SQL\Builder\SelectSQLBuilder;
use function implode;
/**
* Provides the behavior, features and SQL dialect of the MariaDB 10.10 database platform.
*/
class MariaDb1010Platform extends MariaDb1060Platform
{
public function createSelectSQLBuilder(): SelectSQLBuilder
{
return AbstractPlatform::createSelectSQLBuilder();
}
public function fetchTableOptionsByTable(bool $includeTableName): string
{
// MariaDB-10.10.1 added FULL_COLLATION_NAME to the information_schema.COLLATION_CHARACTER_SET_APPLICABILITY.
// A base collation like uca1400_ai_ci can refer to multiple character sets. The value in the
// information_schema.TABLES.TABLE_COLLATION corresponds to the full collation name.
$sql = <<<'SQL'
SELECT t.TABLE_NAME,
t.ENGINE,
t.AUTO_INCREMENT,
t.TABLE_COMMENT,
t.CREATE_OPTIONS,
t.TABLE_COLLATION,
ccsa.CHARACTER_SET_NAME
FROM information_schema.TABLES t
INNER JOIN information_schema.COLLATION_CHARACTER_SET_APPLICABILITY ccsa
ON ccsa.FULL_COLLATION_NAME = t.TABLE_COLLATION
SQL;
$conditions = ['t.TABLE_SCHEMA = ?'];
if ($includeTableName) {
$conditions[] = 't.TABLE_NAME = ?';
}
$conditions[] = "t.TABLE_TYPE = 'BASE TABLE'";
return $sql . ' WHERE ' . implode(' AND ', $conditions);
}
}
@@ -3,9 +3,7 @@
namespace Doctrine\DBAL\Platforms;
/**
* Provides the behavior, features and SQL dialect of the MariaDB 10.2 (10.2.7 GA) database platform.
*
* Note: Should not be used with versions prior to 10.2.7.
* Provides the behavior, features and SQL dialect of the MariaDB 10.2 database platform.
*
* @deprecated This class will be merged with {@see MariaDBPlatform} in 4.0 because support for MariaDB
* releases prior to 10.4.3 will be dropped.
@@ -8,7 +8,7 @@ use Doctrine\Deprecations\Deprecation;
use function sprintf;
/**
* Provides the behavior, features and SQL dialect of the MariaDB 10.4 (10.4.6 GA) database platform.
* Provides the behavior, features and SQL dialect of the MariaDB 10.4 database platform.
*
* Extend deprecated MariaDb1027Platform to ensure correct functions used in MySQLSchemaManager which
* tests for MariaDb1027Platform not MariaDBPlatform.
@@ -6,9 +6,7 @@ use Doctrine\DBAL\Schema\Index;
use Doctrine\DBAL\Schema\TableDiff;
/**
* Provides the behavior, features and SQL dialect of the MariaDB 10.5 (10.5.2 GA) database platform.
*
* Note: Should not be used with versions prior to 10.5.2.
* Provides the behavior, features and SQL dialect of the MariaDB 10.5 database platform.
*/
class MariaDb1052Platform extends MariaDb1043Platform
{
@@ -5,7 +5,7 @@ namespace Doctrine\DBAL\Platforms;
use Doctrine\DBAL\SQL\Builder\SelectSQLBuilder;
/**
* Provides the behavior, features and SQL dialect of the MariaDB 10.6 (10.6.0 GA) database platform.
* Provides the behavior, features and SQL dialect of the MariaDB 10.6 database platform.
*/
class MariaDb1060Platform extends MariaDb1052Platform
{
@@ -9,7 +9,7 @@ use Doctrine\DBAL\Types\Types;
use Doctrine\Deprecations\Deprecation;
/**
* Provides the behavior, features and SQL dialect of the MySQL 5.7 (5.7.9 GA) database platform.
* Provides the behavior, features and SQL dialect of the MySQL 5.7 database platform.
*
* @deprecated This class will be merged with {@see MySQLPlatform} in 4.0 because support for MySQL
* releases prior to 5.7 will be dropped.
@@ -6,7 +6,7 @@ use Doctrine\DBAL\SQL\Builder\SelectSQLBuilder;
use Doctrine\Deprecations\Deprecation;
/**
* Provides the behavior, features and SQL dialect of the MySQL 8.0 (8.0 GA) database platform.
* Provides the behavior, features and SQL dialect of the MySQL 8.0 database platform.
*/
class MySQL80Platform extends MySQL57Platform
{
@@ -0,0 +1,28 @@
<?php
namespace Doctrine\DBAL\Platforms;
use Doctrine\Deprecations\Deprecation;
/**
* Provides the behavior, features and SQL dialect of the MySQL 8.4 database platform.
*/
class MySQL84Platform extends MySQL80Platform
{
/**
* {@inheritDoc}
*
* @deprecated Implement {@see createReservedKeywordsList()} instead.
*/
protected function getReservedKeywordsClass()
{
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/issues/4510',
'MySQL84Platform::getReservedKeywordsClass() is deprecated,'
. ' use MySQL84Platform::createReservedKeywordsList() instead.',
);
return Keywords\MySQL84Keywords::class;
}
}
@@ -473,8 +473,6 @@ class OraclePlatform extends AbstractPlatform
* @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon.
*
* {@inheritDoc}
*
* @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaOracleReader.html
*/
public function getListTableIndexesSQL($table, $database = null)
{
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Platforms;
/**
* Provides the behavior, features and SQL dialect of the PostgreSQL 12.0 database platform.
*/
class PostgreSQL120Platform extends PostgreSQL100Platform
{
public function getDefaultColumnValueSQLSnippet(): string
{
// in case of GENERATED ALWAYS AS (foobar) STORED column (added in PostgreSQL 12.0)
// PostgreSQL's pg_get_expr(adbin, adrelid) will return the 'foobar' part
// which is not the 'default' value of the column but its 'definition'
// so in that case we force it to NULL as DBAL will use that column only for the
// 'default' value
return <<<'SQL'
SELECT
CASE
WHEN a.attgenerated = 's' THEN NULL
ELSE pg_get_expr(adbin, adrelid)
END
FROM pg_attrdef
WHERE c.oid = pg_attrdef.adrelid
AND pg_attrdef.adnum=a.attnum
SQL;
}
}
@@ -405,8 +405,6 @@ SQL
* @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon.
*
* {@inheritDoc}
*
* @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html
*/
public function getListTableIndexesSQL($table, $database = null)
{
@@ -1193,6 +1191,19 @@ SQL
return $sql;
}
/**
* Get the snippet used to retrieve the default value for a given column
*/
public function getDefaultColumnValueSQLSnippet(): string
{
return <<<'SQL'
SELECT pg_get_expr(adbin, adrelid)
FROM pg_attrdef
WHERE c.oid = pg_attrdef.adrelid
AND pg_attrdef.adnum=a.attnum
SQL;
}
/**
* {@inheritDoc}
*/
@@ -38,9 +38,12 @@ use function is_string;
use function preg_match;
use function preg_match_all;
use function sprintf;
use function str_ends_with;
use function str_replace;
use function str_starts_with;
use function strpos;
use function strtoupper;
use function substr;
use function substr_count;
use const PREG_OFFSET_CAPTURE;
@@ -399,6 +402,13 @@ class SQLServerPlatform extends AbstractPlatform
return $sql . ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')';
}
private function unquoteSingleIdentifier(string $possiblyQuotedName): string
{
return str_starts_with($possiblyQuotedName, '[') && str_ends_with($possiblyQuotedName, ']')
? substr($possiblyQuotedName, 1, -1)
: $possiblyQuotedName;
}
/**
* Returns the SQL statement for creating a column comment.
*
@@ -419,23 +429,20 @@ class SQLServerPlatform extends AbstractPlatform
protected function getCreateColumnCommentSQL($tableName, $columnName, $comment)
{
if (strpos($tableName, '.') !== false) {
[$schemaSQL, $tableSQL] = explode('.', $tableName);
$schemaSQL = $this->quoteStringLiteral($schemaSQL);
$tableSQL = $this->quoteStringLiteral($tableSQL);
[$schemaName, $tableName] = explode('.', $tableName);
} else {
$schemaSQL = "'dbo'";
$tableSQL = $this->quoteStringLiteral($tableName);
$schemaName = 'dbo';
}
return $this->getAddExtendedPropertySQL(
'MS_Description',
$comment,
'SCHEMA',
$schemaSQL,
$this->quoteStringLiteral($this->unquoteSingleIdentifier($schemaName)),
'TABLE',
$tableSQL,
$this->quoteStringLiteral($this->unquoteSingleIdentifier($tableName)),
'COLUMN',
$columnName,
$this->quoteStringLiteral($this->unquoteSingleIdentifier($columnName)),
);
}
@@ -806,23 +813,20 @@ class SQLServerPlatform extends AbstractPlatform
protected function getAlterColumnCommentSQL($tableName, $columnName, $comment)
{
if (strpos($tableName, '.') !== false) {
[$schemaSQL, $tableSQL] = explode('.', $tableName);
$schemaSQL = $this->quoteStringLiteral($schemaSQL);
$tableSQL = $this->quoteStringLiteral($tableSQL);
[$schemaName, $tableName] = explode('.', $tableName);
} else {
$schemaSQL = "'dbo'";
$tableSQL = $this->quoteStringLiteral($tableName);
$schemaName = 'dbo';
}
return $this->getUpdateExtendedPropertySQL(
'MS_Description',
$comment,
'SCHEMA',
$schemaSQL,
$this->quoteStringLiteral($this->unquoteSingleIdentifier($schemaName)),
'TABLE',
$tableSQL,
$this->quoteStringLiteral($this->unquoteSingleIdentifier($tableName)),
'COLUMN',
$columnName,
$this->quoteStringLiteral($this->unquoteSingleIdentifier($columnName)),
);
}
@@ -845,22 +849,19 @@ class SQLServerPlatform extends AbstractPlatform
protected function getDropColumnCommentSQL($tableName, $columnName)
{
if (strpos($tableName, '.') !== false) {
[$schemaSQL, $tableSQL] = explode('.', $tableName);
$schemaSQL = $this->quoteStringLiteral($schemaSQL);
$tableSQL = $this->quoteStringLiteral($tableSQL);
[$schemaName, $tableName] = explode('.', $tableName);
} else {
$schemaSQL = "'dbo'";
$tableSQL = $this->quoteStringLiteral($tableName);
$schemaName = 'dbo';
}
return $this->getDropExtendedPropertySQL(
'MS_Description',
'SCHEMA',
$schemaSQL,
$this->quoteStringLiteral($this->unquoteSingleIdentifier($schemaName)),
'TABLE',
$tableSQL,
$this->quoteStringLiteral($this->unquoteSingleIdentifier($tableName)),
'COLUMN',
$columnName,
$this->quoteStringLiteral($this->unquoteSingleIdentifier($columnName)),
);
}
@@ -907,10 +908,13 @@ class SQLServerPlatform extends AbstractPlatform
$level2Name = null
) {
return 'EXEC sp_addextendedproperty ' .
'N' . $this->quoteStringLiteral($name) . ', N' . $this->quoteStringLiteral((string) $value) . ', ' .
'N' . $this->quoteStringLiteral((string) $level0Type) . ', ' . $level0Name . ', ' .
'N' . $this->quoteStringLiteral((string) $level1Type) . ', ' . $level1Name . ', ' .
'N' . $this->quoteStringLiteral((string) $level2Type) . ', ' . $level2Name;
'N' . $this->quoteStringLiteral($name) . ', N' . $this->quoteStringLiteral($value ?? '') . ', ' .
'N' . $this->quoteStringLiteral($level0Type ?? '') . ', ' . $level0Name . ', ' .
'N' . $this->quoteStringLiteral($level1Type ?? '') . ', ' . $level1Name .
($level2Type !== null || $level2Name !== null
? ', N' . $this->quoteStringLiteral($level2Type ?? '') . ', ' . $level2Name
: ''
);
}
/**
@@ -941,9 +945,12 @@ class SQLServerPlatform extends AbstractPlatform
) {
return 'EXEC sp_dropextendedproperty ' .
'N' . $this->quoteStringLiteral($name) . ', ' .
'N' . $this->quoteStringLiteral((string) $level0Type) . ', ' . $level0Name . ', ' .
'N' . $this->quoteStringLiteral((string) $level1Type) . ', ' . $level1Name . ', ' .
'N' . $this->quoteStringLiteral((string) $level2Type) . ', ' . $level2Name;
'N' . $this->quoteStringLiteral($level0Type ?? '') . ', ' . $level0Name . ', ' .
'N' . $this->quoteStringLiteral($level1Type ?? '') . ', ' . $level1Name .
($level2Type !== null || $level2Name !== null
? ', N' . $this->quoteStringLiteral($level2Type ?? '') . ', ' . $level2Name
: ''
);
}
/**
@@ -975,10 +982,13 @@ class SQLServerPlatform extends AbstractPlatform
$level2Name = null
) {
return 'EXEC sp_updateextendedproperty ' .
'N' . $this->quoteStringLiteral($name) . ', N' . $this->quoteStringLiteral((string) $value) . ', ' .
'N' . $this->quoteStringLiteral((string) $level0Type) . ', ' . $level0Name . ', ' .
'N' . $this->quoteStringLiteral((string) $level1Type) . ', ' . $level1Name . ', ' .
'N' . $this->quoteStringLiteral((string) $level2Type) . ', ' . $level2Name;
'N' . $this->quoteStringLiteral($name) . ', N' . $this->quoteStringLiteral($value ?? '') . ', ' .
'N' . $this->quoteStringLiteral($level0Type ?? '') . ', ' . $level0Name . ', ' .
'N' . $this->quoteStringLiteral($level1Type ?? '') . ', ' . $level1Name .
($level2Type !== null || $level2Name !== null
? ', N' . $this->quoteStringLiteral($level2Type ?? '') . ', ' . $level2Name
: ''
);
}
/**
@@ -1296,7 +1306,7 @@ class SQLServerPlatform extends AbstractPlatform
{
$length = $column['length'] ?? null;
if (! isset($column['fixed'])) {
if (empty($column['fixed'])) {
return sprintf('VARCHAR(%d)', $length ?? 255);
}
@@ -1549,12 +1559,14 @@ class SQLServerPlatform extends AbstractPlatform
'smalldatetime' => Types::DATETIME_MUTABLE,
'smallint' => Types::SMALLINT,
'smallmoney' => Types::INTEGER,
'sysname' => Types::STRING,
'text' => Types::TEXT,
'time' => Types::TIME_MUTABLE,
'tinyint' => Types::SMALLINT,
'uniqueidentifier' => Types::GUID,
'varbinary' => Types::BINARY,
'varchar' => Types::STRING,
'xml' => Types::TEXT,
];
}
@@ -1765,15 +1777,13 @@ class SQLServerPlatform extends AbstractPlatform
protected function getCommentOnTableSQL(string $tableName, ?string $comment): string
{
return sprintf(
<<<'SQL'
EXEC sys.sp_addextendedproperty @name=N'MS_Description',
@value=N%s, @level0type=N'SCHEMA', @level0name=N'dbo',
@level1type=N'TABLE', @level1name=N%s
SQL
,
$this->quoteStringLiteral((string) $comment),
$this->quoteStringLiteral($tableName),
return $this->getAddExtendedPropertySQL(
'MS_Description',
$comment,
'SCHEMA',
$this->quoteStringLiteral('dbo'),
'TABLE',
$this->quoteStringLiteral($this->unquoteSingleIdentifier($tableName)),
);
}
@@ -950,11 +950,6 @@ class SqlitePlatform extends AbstractPlatform
$name = $index->getQuotedName($this);
$columns = $index->getColumns();
if (strpos($table, '.') !== false) {
[$schema, $table] = explode('.', $table);
$name = $schema . '.' . $name;
}
if (count($columns) === 0) {
throw new InvalidArgumentException(sprintf(
'Incomplete or invalid index definition %s on table %s',
@@ -967,6 +962,11 @@ class SqlitePlatform extends AbstractPlatform
return $this->getCreatePrimaryKeySQL($index, $table);
}
if (strpos($table, '.') !== false) {
[$schema, $table] = explode('.', $table, 2);
$name = $schema . '.' . $name;
}
$query = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table;
$query .= ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')' . $this->getPartialIndexSQL($index);
@@ -1150,7 +1150,12 @@ class SqlitePlatform extends AbstractPlatform
$sql = [];
$tableSql = [];
if (! $this->onSchemaAlterTable($diff, $tableSql)) {
$dataTable = new Table('__temp__' . $table->getName());
$tableName = $table->getName();
if (strpos($tableName, '.') !== false) {
[, $tableName] = explode('.', $tableName, 2);
}
$dataTable = new Table('__temp__' . $tableName);
$newTable = new Table(
$table->getQuotedName($this),
@@ -1734,7 +1734,7 @@ class QueryBuilder
/**
* Enables caching of the results of this query, for given amount of seconds
* and optionally specified witch key to use for the cache entry.
* and optionally specified which key to use for the cache entry.
*
* @return $this
*/
+1 -1
View File
@@ -184,7 +184,7 @@ class Result
}
/**
* {@inheritDoc}
* @return Traversable<mixed, mixed>
*
* @throws Exception
*/
@@ -20,7 +20,8 @@ use function substr;
* The abstract asset allows to reset the name of all assets without publishing this to the public userland.
*
* This encapsulation hack is necessary to keep a consistent state of the database schema. Say we have a list of tables
* array($tableName => Table($tableName)); if you want to rename the table, you have to make sure
* array($tableName => Table($tableName)); if you want to rename the table, you have to make sure this does not get
* recreated during schema migration.
*/
abstract class AbstractAsset
{
@@ -31,7 +31,7 @@ use function strtolower;
* Base class for schema managers. Schema managers are used to inspect and/or
* modify the database schema/structure.
*
* @template T of AbstractPlatform
* @template-covariant T of AbstractPlatform
*/
abstract class AbstractSchemaManager
{
@@ -1740,6 +1740,10 @@ abstract class AbstractSchemaManager
*/
public function extractDoctrineTypeFromComment($comment, $currentType)
{
if ($this->_conn->getConfiguration()->getDisableTypeComments()) {
return $currentType;
}
if ($comment !== null && preg_match('(\(DC2Type:(((?!\)).)+)\))', $comment, $match) === 1) {
return $match[1];
}
@@ -1757,6 +1761,10 @@ abstract class AbstractSchemaManager
*/
public function removeDoctrineTypeFromComment($comment, $type)
{
if ($this->_conn->getConfiguration()->getDisableTypeComments()) {
return $comment;
}
if ($comment === null) {
return null;
}
@@ -557,31 +557,13 @@ SQL;
*/
protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array
{
$sql = <<<'SQL'
SELECT t.TABLE_NAME,
t.ENGINE,
t.AUTO_INCREMENT,
t.TABLE_COMMENT,
t.CREATE_OPTIONS,
t.TABLE_COLLATION,
ccsa.CHARACTER_SET_NAME
FROM information_schema.TABLES t
INNER JOIN information_schema.COLLATION_CHARACTER_SET_APPLICABILITY ccsa
ON ccsa.COLLATION_NAME = t.TABLE_COLLATION
SQL;
$conditions = ['t.TABLE_SCHEMA = ?'];
$params = [$databaseName];
$sql = $this->_platform->fetchTableOptionsByTable($tableName !== null);
$params = [$databaseName];
if ($tableName !== null) {
$conditions[] = 't.TABLE_NAME = ?';
$params[] = $tableName;
$params[] = $tableName;
}
$conditions[] = "t.TABLE_TYPE = 'BASE TABLE'";
$sql .= ' WHERE ' . implode(' AND ', $conditions);
/** @var array<string,array<string,mixed>> $metadata */
$metadata = $this->_conn->executeQuery($sql, $params)
->fetchAllAssociativeIndexed();
@@ -107,8 +107,6 @@ class OracleSchemaManager extends AbstractSchemaManager
/**
* {@inheritDoc}
*
* @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html
*/
protected function _getPortableTableIndexesList($tableIndexes, $tableName = null)
{
@@ -287,8 +287,6 @@ SQL,
/**
* {@inheritDoc}
*
* @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html
*/
protected function _getPortableTableIndexesList($tableIndexes, $tableName = null)
{
@@ -463,6 +461,7 @@ SQL,
$length = null;
break;
case 'json':
case 'text':
case '_varchar':
case 'varchar':
@@ -616,7 +615,7 @@ SQL;
$sql .= ' c.relname AS table_name, n.nspname AS schema_name,';
}
$sql .= <<<'SQL'
$sql .= sprintf(<<<'SQL'
a.attnum,
quote_ident(a.attname) AS field,
t.typname AS type,
@@ -632,11 +631,7 @@ SQL;
AND pg_index.indkey[0] = a.attnum
AND pg_index.indisprimary = 't'
) AS pri,
(SELECT pg_get_expr(adbin, adrelid)
FROM pg_attrdef
WHERE c.oid = pg_attrdef.adrelid
AND pg_attrdef.adnum=a.attnum
) AS default,
(%s) AS default,
(SELECT pg_description.description
FROM pg_description WHERE pg_description.objoid = c.oid AND a.attnum = pg_description.objsubid
) AS comment
@@ -651,7 +646,7 @@ SQL;
ON d.objid = c.oid
AND d.deptype = 'e'
AND d.classid = (SELECT oid FROM pg_class WHERE relname = 'pg_class')
SQL;
SQL, $this->_platform->getDefaultColumnValueSQLSnippet());
$conditions = array_merge([
'a.attnum > 0',
@@ -202,7 +202,7 @@ class SqliteSchemaManager extends AbstractSchemaManager
{
$table = $this->normalizeName($table);
$columns = $this->selectForeignKeyColumns('', $table)
$columns = $this->selectForeignKeyColumns($database ?? 'main', $table)
->fetchAllAssociative();
if (count($columns) > 0) {
@@ -222,8 +222,6 @@ class SqliteSchemaManager extends AbstractSchemaManager
/**
* {@inheritDoc}
*
* @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html
*/
protected function _getPortableTableIndexesList($tableIndexes, $tableName = null)
{
@@ -744,7 +742,7 @@ SQL;
p.*
FROM sqlite_master t
JOIN pragma_foreign_key_list(t.name) p
ON p."seq" != "-1"
ON p."seq" != '-1'
SQL;
$conditions = [
@@ -9,6 +9,7 @@ use Doctrine\DBAL\Platforms\Keywords\KeywordList;
use Doctrine\DBAL\Platforms\Keywords\MariaDb102Keywords;
use Doctrine\DBAL\Platforms\Keywords\MySQL57Keywords;
use Doctrine\DBAL\Platforms\Keywords\MySQL80Keywords;
use Doctrine\DBAL\Platforms\Keywords\MySQL84Keywords;
use Doctrine\DBAL\Platforms\Keywords\MySQLKeywords;
use Doctrine\DBAL\Platforms\Keywords\OracleKeywords;
use Doctrine\DBAL\Platforms\Keywords\PostgreSQL100Keywords;
@@ -59,6 +60,7 @@ class ReservedWordsCommand extends Command
'mysql' => new MySQLKeywords(),
'mysql57' => new MySQL57Keywords(),
'mysql80' => new MySQL80Keywords(),
'mysql84' => new MySQL84Keywords(),
'oracle' => new OracleKeywords(),
'pgsql' => new PostgreSQL94Keywords(),
'pgsql100' => new PostgreSQL100Keywords(),
@@ -130,6 +132,7 @@ The following keyword lists are currently shipped with Doctrine:
* mysql
* mysql57
* mysql80
* mysql84
* oracle
* pgsql
* pgsql100
@@ -11,18 +11,19 @@ use Doctrine\Deprecations\Deprecation;
use function get_class;
/**
* DateTime type saving additional timezone information.
* DateTime type accepting additional information about timezone offsets.
*
* Caution: Databases are not necessarily experts at storing timezone related
* data of dates. First, of all the supported vendors only PostgreSQL and Oracle
* support storing Timezone data. But those two don't save the actual timezone
* attached to a DateTime instance (for example "Europe/Berlin" or "America/Montreal")
* but the current offset of them related to UTC. That means depending on daylight saving times
* or not you may get different offsets.
* data of dates. First, of not all the supported vendors support storing Timezone data, and some of
* them only use the offset to calculate the timestamp in its default timezone (usually UTC) and persist
* the value without the offset information. They even don't save the actual timezone names attached
* to a DateTime instance (for example "Europe/Berlin" or "America/Montreal") but the current offset
* of them related to UTC. That means, depending on daylight saving times or not, you may get different
* offsets.
*
* This datatype makes only sense to use, if your application works with an offset, not
* with an actual timezone that uses transitions. Otherwise your DateTime instance
* attached with a timezone such as Europe/Berlin gets saved into the database with
* This datatype makes only sense to use, if your application only needs to accept the timezone offset,
* not the actual timezone that uses transitions. Otherwise your DateTime instance
* attached with a timezone such as "Europe/Berlin" gets saved into the database with
* the offset and re-created from persistence with only the offset, not the original timezone
* attached.
*/
@@ -150,6 +150,67 @@ class MyTest extends TestCase
}
```
## Displaying deprecations after running a PHPUnit test suite
It is possible to integrate this library with PHPUnit to display all
deprecations triggered during the test suite execution.
```xml
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
colors="true"
bootstrap="vendor/autoload.php"
displayDetailsOnTestsThatTriggerDeprecations="true"
failOnDeprecation="true"
>
<!-- one attribute to display the deprecations, the other to fail the test suite -->
<php>
<!-- ensures native PHP deprecations are used -->
<server name="DOCTRINE_DEPRECATIONS" value="trigger"/>
</php>
<!-- ensures the @ operator in @trigger_error is ignored -->
<source ignoreSuppressionOfDeprecations="true">
<include>
<directory>src</directory>
</include>
</source>
</phpunit>
```
Note that you can still trigger Deprecations in your code, provided you use the
`#[WithoutErrorHandler]` attribute to disable PHPUnit's error handler for tests
that call it. Be wary that this will disable all error handling, meaning it
will mask any warnings or errors that would otherwise be caught by PHPUnit.
At the moment, it is not possible to disable deduplication with an environment
variable, but you can use a bootstrap file to achieve that:
```php
// tests/bootstrap.php
<?php
declare(strict_types=1);
require dirname(__DIR__) . '/vendor/autoload.php';
use Doctrine\Deprecations\Deprecation;
Deprecation::withoutDeduplication();
```
Then, reference that file in your PHPUnit configuration:
```xml
<phpunit …
bootstrap="tests/bootstrap.php"
>
</phpunit>
```
## What is a deprecation identifier?
An identifier for deprecations is just a link to any resource, most often a
@@ -8,20 +8,18 @@
"php": "^7.1 || ^8.0"
},
"require-dev": {
"doctrine/coding-standard": "^9",
"phpstan/phpstan": "1.4.10 || 1.10.15",
"phpstan/phpstan-phpunit": "^1.0",
"doctrine/coding-standard": "^9 || ^12",
"phpstan/phpstan": "1.4.10 || 2.0.3",
"phpstan/phpstan-phpunit": "^1.0 || ^2",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
"psalm/plugin-phpunit": "0.18.4",
"psr/log": "^1 || ^2 || ^3",
"vimeo/psalm": "4.30.0 || 5.12.0"
"psr/log": "^1 || ^2 || ^3"
},
"suggest": {
"psr/log": "Allows logging deprecations via PSR-3 logger implementation"
},
"autoload": {
"psr-4": {
"Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations"
"Doctrine\\Deprecations\\": "src"
}
},
"autoload-dev": {
@@ -173,9 +173,7 @@ class Deprecation
self::delegateTriggerToBackend($message, $backtrace, $link, $package);
}
/**
* @param list<array{function: string, line?: int, file?: string, class?: class-string, type?: string, args?: mixed[], object?: object}> $backtrace
*/
/** @param list<array{function: string, line?: int, file?: string, class?: class-string, type?: string, args?: mixed[], object?: object}> $backtrace */
private static function delegateTriggerToBackend(string $message, array $backtrace, string $link, string $package): void
{
$type = self::$type ?? self::getTypeFromEnv();
@@ -226,19 +224,19 @@ class Deprecation
public static function enableTrackingDeprecations(): void
{
self::$type = self::$type ?? 0;
self::$type = self::$type ?? self::getTypeFromEnv();
self::$type |= self::TYPE_TRACK_DEPRECATIONS;
}
public static function enableWithTriggerError(): void
{
self::$type = self::$type ?? 0;
self::$type = self::$type ?? self::getTypeFromEnv();
self::$type |= self::TYPE_TRIGGER_ERROR;
}
public static function enableWithPsrLogger(LoggerInterface $logger): void
{
self::$type = self::$type ?? 0;
self::$type = self::$type ?? self::getTypeFromEnv();
self::$type |= self::TYPE_PSR_LOGGER;
self::$logger = $logger;
}
@@ -289,9 +287,7 @@ class Deprecation
return self::$triggeredDeprecations;
}
/**
* @return int-mask-of<self::TYPE_*>
*/
/** @return int-mask-of<self::TYPE_*> */
private static function getTypeFromEnv(): int
{
switch ($_SERVER['DOCTRINE_DEPRECATIONS'] ?? $_ENV['DOCTRINE_DEPRECATIONS'] ?? null) {
@@ -26,17 +26,13 @@ trait VerifyDeprecations
$this->doctrineNoDeprecationsExpectations[$identifier] = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0;
}
/**
* @before
*/
/** @before */
public function enableDeprecationTracking(): void
{
Deprecation::enableTrackingDeprecations();
}
/**
* @after
*/
/** @after */
public function verifyDeprecationsAreTriggered(): void
{
foreach ($this->doctrineDeprecationsExpectations as $identifier => $expectation) {
@@ -41,10 +41,10 @@
"php": "^8.1"
},
"require-dev": {
"doctrine/coding-standard": "^10",
"doctrine/coding-standard": "^12",
"phpstan/phpstan": "^1.8.8",
"phpunit/phpunit": "^9.5",
"vimeo/psalm": "^4.28"
"phpunit/phpunit": "^10.5",
"vimeo/psalm": "^5.24"
},
"conflict": {
"doctrine/common": "<2.9"
@@ -1,5 +0,0 @@
parameters:
level: 9
paths:
- src/
- tests/
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="5.24.0@462c80e31c34e58cc4f750c656be3927e80e550e">
<file src="src/EventManager.php">
<RiskyTruthyFalsyComparison>
<code><![CDATA[empty($this->listeners[$event])]]></code>
</RiskyTruthyFalsyComparison>
</file>
</files>
@@ -1,15 +0,0 @@
<?xml version="1.0"?>
<psalm
errorLevel="2"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
<projectFiles>
<directory name="src" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
</psalm>
+24
View File
@@ -0,0 +1,24 @@
Dompdf was designed and developed by Benj Carson.
### Current Team
* **Brian Sweeney** (maintainer)
* **Till Berger**
### Alumni
* **Benj Carson** (creator)
* **Fabien Ménager**
* **Simon Berger**
* **Orion Richardson**
### Contributors
* **Gabriel Bull**
* **Barry vd. Heuvel**
* **Ryan H. Masten**
* **Helmut Tischer**
* [and many more...](https://github.com/dompdf/dompdf/graphs/contributors)
### Thanks
Dompdf would not have been possible without strong community support.
+456
View File
@@ -0,0 +1,456 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
+232
View File
@@ -0,0 +1,232 @@
Dompdf
======
[![Build Status](https://github.com/dompdf/dompdf/actions/workflows/test.yml/badge.svg)](https://github.com/dompdf/dompdf/actions/workflows/test.yml)
[![Latest Release](https://poser.pugx.org/dompdf/dompdf/v/stable.png)](https://packagist.org/packages/dompdf/dompdf)
[![Total Downloads](https://poser.pugx.org/dompdf/dompdf/downloads.png)](https://packagist.org/packages/dompdf/dompdf)
[![License](https://poser.pugx.org/dompdf/dompdf/license.png)](https://packagist.org/packages/dompdf/dompdf)
**Dompdf is an HTML to PDF converter**
At its heart, dompdf is (mostly) a [CSS 2.1](http://www.w3.org/TR/CSS2/) compliant
HTML layout and rendering engine written in PHP. It is a style-driven renderer:
it will download and read external stylesheets, inline style tags, and the style
attributes of individual HTML elements. It also supports most presentational
HTML attributes.
*This document applies to the latest stable code which may not reflect the current
release. For released code please
[navigate to the appropriate tag](https://github.com/dompdf/dompdf/tags).*
----
**Check out the [demo](http://eclecticgeek.com/dompdf/debug.php) and ask any
question on [StackOverflow](https://stackoverflow.com/questions/tagged/dompdf) or
in [Discussions](https://github.com/dompdf/dompdf/discussions).**
Follow us on [![Twitter](http://twitter-badges.s3.amazonaws.com/twitter-a.png)](http://www.twitter.com/dompdf).
---
## Features
* Handles most CSS 2.1 and a few CSS3 properties, including @import, @media &
@page rules
* Supports most presentational HTML 4.0 attributes
* Supports external stylesheets, either local or through http/ftp (via
fopen-wrappers)
* Supports complex tables, including row & column spans, separate & collapsed
border models, individual cell styling
* Image support (gif, png (8, 24 and 32 bit with alpha channel), bmp & jpeg)
* No dependencies on external PDF libraries, thanks to the R&OS PDF class
* Inline PHP support
* Basic SVG support (see "Limitations" below)
## Requirements
* PHP version 7.1 or higher
* DOM extension
* MBString extension
* php-font-lib
* php-svg-lib
Note that some required dependencies may have further dependencies
(notably php-svg-lib requires sabberworm/php-css-parser).
### Recommendations
* OPcache (OPcache, XCache, APC, etc.): improves performance
* GD (for image processing)
* IMagick or GMagick extension: improves image processing performance
Visit the wiki for more information:
https://github.com/dompdf/dompdf/wiki/Requirements
## About Fonts & Character Encoding
PDF documents internally support the following fonts: Helvetica, Times-Roman,
Courier, Zapf-Dingbats, & Symbol. These fonts only support Windows ANSI
encoding. In order for a PDF to display characters that are not available in
Windows ANSI, you must supply an external font. Dompdf will embed any referenced
font in the PDF so long as it has been pre-loaded or is accessible to dompdf and
reference in CSS @font-face rules. See the
[font overview](https://github.com/dompdf/dompdf/wiki/About-Fonts-and-Character-Encoding)
for more information on how to use fonts.
The [DejaVu TrueType fonts](https://dejavu-fonts.github.io/) have been pre-installed
to give dompdf decent Unicode character coverage by default. To use the DejaVu
fonts reference the font in your stylesheet, e.g. `body { font-family: DejaVu
Sans; }` (for DejaVu Sans). The following DejaVu 2.34 fonts are available:
DejaVu Sans, DejaVu Serif, and DejaVu Sans Mono.
## Easy Installation
### Install with composer
To install with [Composer](https://getcomposer.org/), simply require the
latest version of this package.
```bash
composer require dompdf/dompdf
```
Make sure that the autoload file from Composer is loaded.
```php
// somewhere early in your project's loading, require the Composer autoloader
// see: http://getcomposer.org/doc/00-intro.md
require 'vendor/autoload.php';
```
### Download and install
Download a packaged archive of dompdf and extract it into the
directory where dompdf will reside
* You can download stable copies of dompdf from
https://github.com/dompdf/dompdf/releases
* Or download a nightly (the latest, unreleased code) from
http://eclecticgeek.com/dompdf
Use the packaged release autoloader to load dompdf, libraries,
and helper functions in your PHP:
```php
// include autoloader
require_once 'dompdf/autoload.inc.php';
```
Note: packaged releases are named according using semantic
versioning (_dompdf_MAJOR-MINOR-PATCH.zip_). So the 1.0.0
release would be dompdf_1-0-0.zip. This is the only download
that includes the autoloader for Dompdf and all its dependencies.
### Install with git
From the command line, switch to the directory where dompdf will
reside and run the following commands:
```sh
git clone https://github.com/dompdf/dompdf.git
cd dompdf/lib
git clone https://github.com/PhenX/php-font-lib.git php-font-lib
cd php-font-lib
git checkout 0.5.1
cd ..
git clone https://github.com/PhenX/php-svg-lib.git php-svg-lib
cd php-svg-lib
git checkout v0.3.2
cd ..
git clone https://github.com/sabberworm/PHP-CSS-Parser.git php-css-parser
cd php-css-parser
git checkout 8.1.0
```
Require dompdf and it's dependencies in your PHP.
For details see the [autoloader in the utils project](https://github.com/dompdf/utils/blob/master/autoload.inc.php).
## Quick Start
Just pass your HTML in to dompdf and stream the output:
```php
// reference the Dompdf namespace
use Dompdf\Dompdf;
// instantiate and use the dompdf class
$dompdf = new Dompdf();
$dompdf->loadHtml('hello world');
// (Optional) Setup the paper size and orientation
$dompdf->setPaper('A4', 'landscape');
// Render the HTML as PDF
$dompdf->render();
// Output the generated PDF to Browser
$dompdf->stream();
```
### Setting Options
Set options during dompdf instantiation:
```php
use Dompdf\Dompdf;
use Dompdf\Options;
$options = new Options();
$options->set('defaultFont', 'Courier');
$dompdf = new Dompdf($options);
```
or at run time
```php
use Dompdf\Dompdf;
$dompdf = new Dompdf();
$options = $dompdf->getOptions();
$options->setDefaultFont('Courier');
$dompdf->setOptions($options);
```
See [Dompdf\Options](src/Options.php) for a list of available options.
### Resource Reference Requirements
In order to protect potentially sensitive information Dompdf imposes
restrictions on files referenced from the local file system or the web.
Files accessed through web-based protocols have the following requirements:
* The Dompdf option "isRemoteEnabled" must be set to "true"
* PHP must either have the curl extension enabled or the
allow_url_fopen setting set to true
Files accessed through the local file system have the following requirement:
* The file must fall within the path(s) specified for the Dompdf "chroot" option
## Limitations (Known Issues)
* Table cells are not pageable, meaning a table row must fit on a single page.
* Elements are rendered on the active page when they are parsed.
* Embedding "raw" SVG's (`<svg><path...></svg>`) isn't working yet, you need to
either link to an external SVG file, or use a DataURI like this:
```php
$html = '<img src="data:image/svg+xml;base64,' . base64_encode($svg) . '" ...>';
```
Watch https://github.com/dompdf/dompdf/issues/320 for progress
* Does not support CSS flexbox.
* Does not support CSS Grid.
---
[![Donate button](https://www.paypal.com/en_US/i/btn/btn_donate_SM.gif)](http://goo.gl/DSvWf)
*If you find this project useful, please consider making a donation.
Any funds donated will be used to help further development on this project.)*
+1
View File
@@ -0,0 +1 @@
2.0.8

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