# Technical Documentation: lizhipay/acg-faka

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/lizhipay/acg-faka)

### Overview (`README.md`)

<div align="center">

<a href="https://faka.wiki/zh-cn/"><img src="https://raw.githubusercontent.com/lizhipay/acg-faka/refs/heads/main/favicon.ico" width="104" height="104" alt="异次元店铺系统"></a>

# 异次元店铺系统

基于 PHP 8 的开源自动发卡与个人店铺系统。<br>
卡密自动发货、插件化支付、分站、店铺对接、应用商店，开箱即用。

[简体中文](README.md) | [English](README.en.md) | [繁體中文](README.zh-TW.md) | [日本語](README.ja.md)






[文档](https://faka.wiki/zh-cn/) · [在线演示](#在线演示) · [安装](#安装) · [问题反馈](https://github.com/lizhipay/acg-faka/issues)

</div>

## 法律声明

> 本商城程序基于 MIT 协议开源，并且完全免费。该程序的初衷是为开发者提供学习和研究的机会。未取得合法资质，严禁将本程序用于任何商业用途，尤其是禁止利用本程序搭建平台进行商品销售。
>
> 用户在使用或学习本程序时，必须严格遵守法律法规。我们提倡依法行事，尊重法律，坚守法律，避免对社会产生不良影响。
>
> 使用本程序即表示您已充分理解并同意本法律声明的所有内容。

## 广告

🚀 **ARM AI** — GPT / Claude / Gemini / Grok Token 服务平台，超低倍率、余额长期有效并支持无理由退款。立即体验：<https://ai.arm.moe/>

## 在线演示

| | 地址 | 账号 | 密码 |
| --- | --- | --- | --- |
| 前台 | <https://demo.faka.wiki> | 为了明天美好而战斗 | 123456 |
| 后台 | <https://demo.faka.wiki/admin> | demo@demo.com | 123456 |

## 功能一览

- **商品与发货**：卡密自动发货、手动发货（可开启「付款即发货」）、卡密预选（买家自选卡号，可单独加价）、种类与 SKU 规格、批发价、限时秒杀、优惠券、限购、强制登录购买、自定义下单控件、邮件通知。
- **价格与会员**：游客价与会员价分开设置，会员等级、商户等级完全自定义，每个商品都能按会员等级单独定价。
- **支付**：支付网关全部插件化，几乎任何支付渠道都能接入；内置余额支付与在线充值。
- **分站**：会员可以开通自己的分站并绑定独立域名，自行上架商品，也可以代售主站商品。
- **推广分成**：会员等级即代理等级，推广人赚取买家实付金额与自身等级价之间的差价。
- **店铺对接**：在后台直接对接其他店铺的商品，用余额自动进货、自动发货。
- **应用商店**：在后台安装插件与模板；核心程序可一键在线升级。
- **多语言与多币种**：内置简体中文、繁体中文、English、日本語，可自定义站点货币与汇率。
- **安全**：两步验证与通行密钥（Passkey）登录、后台安全入口、内容安全策略（CSP）等多层防护。
- **界面**：PC 与手机端完整适配，应用商店里有多套风格各异的模板。
- **二次开发**：80 多个 Hook 埋点，插件可以接管下单、发货、支付等流程。比如：游戏道具购买后即时发到玩家背包、商业软件自动授权与余额充值、论坛 / 社区 VIP 自动开通。

## 安装

### 方式一：Docker（推荐）

```bash
docker run -d --name faka -p 80:80 -v acg_data:/data --restart unless-stopped ghcr.io/lizhipay/acg-faka:latest
```

镜像自带 MariaDB 与 Redis，启动后打开 `http://服务器IP` 即可进入安装向导，数据库信息已经自动填好。站点的全部数据都在 `/data` 卷里，务必挂载。需要使用独立的 MySQL 时，请参考 [Docker 安装文档](https://faka.wiki/zh-cn/install/docker)。

### 方式二：宝塔面板 / 手动安装

| 项目 | 要求 |
| --- | --- |
| PHP | 8.0 及以上（推荐 8.2） |
| MySQL | 5.7 及以上（MariaDB 10.3+ 亦可） |
| PHP 扩展 | `gd`、`curl`、`pdo`、`pdo_mysql`、`date`、`json`、`session`、`zip`、`bcmath` |
| 伪静态 | 必须配置，见下方 |

1. 获取源码：`composer create-project lizhipay/acg-faka`，或从 [GitHub](https://github.com/lizhipay/acg-faka)、[faka.wiki/download.php](https://faka.wiki/download.php) 下载压缩包。
2. 配置伪静态：Apache 无需配置，根目录已自带 `.htaccess`；Nginx 请把下面整段粘进站点配置（宝塔：网站 → 伪静态）。

   ```nginx
   if ($uri ~* "^/(?!(?-i:index\.php)(/|$)).*\.php(/|$)")             { return 404; }

   location ~* ^/(runtime|kernel|config|vendor)/                { return 404; }
   location ~  (^|/)(runtime|\.acgcache)(/|$)                   { return 404; }
   location ~  /\.(?!well-known)                                { return 404; }
   location ~* \.(log|sql|sqlite|db|db-wal|db-shm|bak|old|save|orig|swp|swo|tmp|ini|lock|key|pem|crt|cer|der|p12|pfx|p8|keystore|jks|asc|gpg|ppk|env|secret)$  { return 404; }
   location ~* (~|composer\.(json|lock)|package(-lock)?\.json)$ { return 404; }
   location / {
       try_files $uri $uri/ /index.php?s=$uri&$args;
   }
   ```

   请整段复制，不要只抄最后的 `location /`：前面几条负责挡住日志、配置、密钥等不该被访问的文件。IIS 规则见[伪静态文档](https://faka.wiki/zh-cn/install/rewrite)。
3. 确认 `config`、`runtime`、`kernel/Install`、`assets/cache` 目录可写，然后访问网站首页，按安装向导完成安装。
4. 后台地址：`https://你的域名/admin`

更详细的步骤（含 Windows）见[安装文档](https://faka.wiki/zh-cn/install/)。

## 升级

后台左下角显示当前版本号，点击即可检查新版本并一键在线升级，Docker 部署同样适用。升级前请先备份数据库；手动升级的方法见[升级文档](https://faka.wiki/zh-cn/install/upgrade)。

## 二次开发

插件、支付插件、Hook 与 API 对接的说明见[开发者文档](https://faka.wiki/zh-cn/dev/)。

## 交流与反馈

- Telegram：<https://t.me/mcyofficial>
- QQ 群：868709021
- 问题反馈：[GitHub Issues](https://github.com/lizhipay/acg-faka/issues)

## 开源协议

[MIT](LICENSE)

---

### Overview (`docker/README.md`)

# Docker 部署

## 最简单：一个容器

```bash
docker run -d --name acg-faka -p 80:80 -v acg_data:/data --restart unless-stopped ghcr.io/lizhipay/acg-faka:latest
```

**就这一条。** 镜像自带 MariaDB 和 Redis，数据库密码首次启动随机生成，
打开 <http://服务器IP> 直接进安装向导 —— 数据库那一步已经替你填好，只需要设个管理员账号。

宝塔 / 1Panel 的「创建容器」里也是同样三件事：镜像填 `ghcr.io/lizhipay/acg-faka:latest`、
端口映射 **主机端口 → 容器 80**、目录映射一个卷到 **`/data`**。

> ⚠️ 容器内部端口是 **80**，不是 8080。映射错了浏览器打不开。
>
> ⚠️ `/data` 一定要挂出来。站点的数据库、配置、上传文件、插件、模板全在里面，
> 不挂的话容器一删数据就没了。

## 要独立的 MySQL / Redis：用 compose

```bash
docker compose up -d
```

同样不需要 `.env`、不需要改文件。这种方式下内置的 MariaDB/Redis 不会启动，
app 容器只跑 nginx + php-fpm，数据库走独立的 `mysql` 服务。

改端口：`ACG_HTTP_PORT=8081 docker compose up -d`

## 这套编排有什么

| 服务 | 镜像 | 说明 |
|---|---|---|
| `app` | 本仓库构建 | nginx + php-fpm 8.2（+ 单容器模式下的 MariaDB/Redis），supervisord 托管，对外只开 80 |
| `mysql` | `mysql:5.7` | 只在内网可达，不往宿主机发布端口 |
| `redis` | `redis:7.2-alpine` | PHP 会话存储（`acg_sess:` 前缀，db 1） |
| `secrets-init` | 一次性任务 | 首次启动生成随机数据库密码，之后退出 |

## 密码是怎么来的

镜像里**不带任何默认密码**。首次 `up` 时 `secrets-init` 会往 `acg_secrets` 卷里写两串
32 位随机密码（root 一串、应用一串，权限 600），MySQL 通过 `MYSQL_*_PASSWORD_FILE`
读它，安装向导则由服务端读出来直接用。

密码**不会渲染进安装页面的 HTML** —— 页面把密码栏留空、提交时带一个
`use_builtin=1` 标记，服务端自己去环境变量里取。

要自己看一眼的话：

```bash
docker compose exec mysql cat /secrets/mysql_root
```

## 想用外部数据库

安装向导第二步点「改用其它数据库」，手填即可，内置的那套就不会被用到。
也可以直接把 compose 里的 `mysql` 服务和 `ACG_DB_*` 环境变量删掉。

## 为什么是 nginx 而不是 Apache

整个程序只有 `index.php` 一个 web 入口，其余请求都靠 `?s=` 路由。所以 nginx 配置里
只放行了它一个，**其它 `.php` 一律返回 404** —— 上传目录里就算真被塞进一个 `.php`
也执行不了。`config/`、`kernel/`、`runtime/`、`vendor/` 和点开头的文件同样直接 404。

伪静态规则随镜像发布（等价于仓库里的 `.htaccess`），用户不需要配置任何东西。

反向代理（Caddy / Traefik / CDN / 宝塔）放在前面时，`X-Forwarded-Proto` 和
`X-Forwarded-For` 会被正确转成 `$_SERVER['HTTPS']` 与真实来访 IP —— 只信任私有网段，
公网伪造的头不生效。

## 线程管理器插件

在应用商店装好插件后，进容器执行一次（只需一次，二进制会落在数据卷里）：

```bash
docker compose exec app ./app/Plugin/ThreadManager/service.sh install
# 单容器：docker exec -it acg-faka ./app/Plugin/ThreadManager/service.sh install
```

之后**不用再管** —— 容器每次启动都会自动把它的守护进程交给 supervisord 拉起，
不需要 `service.sh start`。

> GitHub 直连不稳导致下载不完整时，脚本会自动换镜像源重试；还是不行就手动指定：
> `TM_MIRRORS="https://ghfast.top/" ./app/Plugin/ThreadManager/service.sh install`

## 线程管理器（如果你装了）

镜像会自动接管它：容器启动时若检测到 `app/Plugin/ThreadManager/swoole-cli`，
就把守护进程注册成 supervisord 的程序，和 nginx / php-fpm 一个待遇 ——
**容器起来自动拉起、崩溃自动重启、`docker stop` 时按 `stop_timeout` 优雅收尾**。

注意：托管是在**容器启动时**登记的，所以插件装好、二进制下完之后要
**重启一次容器**才会生效（`docker restart <容器名>`）。在那之前 `service.sh start`
起的进程照常能用，只是活不过容器重建。

### el9 系宿主机的 xz 坑

在 RHEL 9 / AlmaLinux 9 / Rocky 9（内核 `5.14.x.el9`）上，容器里任何 `xz` 操作都会报：

```
xz: Failed to enable the sandbox
```

原因是这类内核回移 Landlock 时**版本号谎报**（`landlock_create_ruleset` 查询返回 ABI 6，
实际建规则集却 `EINVAL`），而 xz 5.6+ 把沙箱失败当致命错误。**跟下载的包无关**，
包是好的。线程管理器 1.0.7 起会在这种情况下自动改用 python3 的 `lzma` 解包，无需干预。

## 已知限制

- **默认 PHP 8.2**（Debian bookworm，仍在支持期）。需要贴合旧线上环境时可以降版本：

  ```bash
  docker compose build --build-arg PHP_VERSION=8.0 app
  ```

  ⚠ 但 **PHP 8.0 / 8.1 已停止安全维护**，它们的 Debian bullseye 底座也整体进了归档 ——
  构建时会自动改用 `archive.debian.org`，代价是镜像里的系统包（**包括 nginx、openssl**）
  拿不到任何新的安全更新。挂公网的站点不建议这么用。

- **MySQL 5.7 官方只发 amd64 镜像**。ARM 机器（Apple Silicon / Ampere / Graviton）
  会通过 qemu 模拟运行 —— 能跑，但导入数据那一步明显偏慢。想要原生性能就把
  compose 里的 `mysql` 服务换成 `mariadb:10.6` 并删掉 `platform:` 那行。

## 持久化

站点里所有会被写入的目录（配置、运行时、上传、插件、支付、模板、安装锁）在镜像里
都软链到 `/data`，**挂这一个卷就全保住了**。单容器模式下数据库和 Redis 的数据也在
`/data/mysql`、`/data/redis`。

compose 方式下是 `acg_data`（站点）+ `acg_mysql` + `acg_redis` + `acg_secrets`（密码）。

## 常用命令

```bash
docker compose logs -f app          # 看日志（访问日志只由 nginx 出，不重复）
docker compose restart app          # 重启
docker compose down                 # 停止（数据保留）
docker compose down -v              # 停止并清空所有数据，回到全新状态
```

---

### Overview (`vendor/doctrine/inflector/README.md`)

# Doctrine Inflector

Doctrine Inflector is a small library that can perform string manipulations
with regard to uppercase/lowercase and singular/plural forms of words.

[](https://github.com/doctrine/inflector/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A4.0.x)
[](https://codecov.io/gh/doctrine/inflector/branch/2.0.x)

---

### Overview (`vendor/ezyang/htmlpurifier/README.md`)

HTML Purifier [](https://github.com/ezyang/htmlpurifier/actions/workflows/ci.yml)
=============

HTML Purifier is an HTML filtering solution that uses a unique combination
of robust whitelists and aggressive parsing to ensure that not only are
XSS attacks thwarted, but the resulting HTML is standards compliant.

HTML Purifier is oriented towards richly formatted documents from
untrusted sources that require CSS and a full tag-set.  This library can
be configured to accept a more restrictive set of tags, but it won't be
as efficient as more bare-bones parsers. It will, however, do the job
right, which may be more important.

Places to go:

* See INSTALL for a quick installation guide
* See docs/ for developer-oriented documentation, code examples and
  an in-depth installation guide.
* See WYSIWYG for information on editors like TinyMCE and FCKeditor

HTML Purifier can be found on the web at: [http://htmlpurifier.org/](http://htmlpurifier.org/)

## Installation

Package available on [Composer](https://packagist.org/packages/ezyang/htmlpurifier).

If you're using Composer to manage dependencies, you can use

    $ composer require ezyang/htmlpurifier

---

### Overview (`vendor/firebase/php-jwt/README.md`)

[](https://packagist.org/packages/firebase/php-jwt)
[](https://packagist.org/packages/firebase/php-jwt)
[](https://packagist.org/packages/firebase/php-jwt)

PHP-JWT
=======
A simple library to encode and decode JSON Web Tokens (JWT) in PHP, conforming to [RFC 7519](https://tools.ietf.org/html/rfc7519).

Installation
------------

Use composer to manage your dependencies and download PHP-JWT:

```bash
composer require firebase/php-jwt
```

Optionally, install the `paragonie/sodium_compat` package from composer if your
php env does not have libsodium installed:

```bash
composer require paragonie/sodium_compat
```

Example
-------
```php
use Firebase\JWT\JWT;
use Firebase\JWT\Key;

$key = 'example_key';
$payload = [
    'iss' => 'http://example.org',
    'aud' => 'http://example.com',
    'iat' => 1356999524,
    'nbf' => 1357000000
];

/**
 * IMPORTANT:
 * You must specify supported algorithms for your application. See
 * https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40
 * for a list of spec-compliant algorithms.
 */
$jwt = JWT::encode($payload, $key, 'HS256');
$decoded = JWT::decode($jwt, new Key($key, 'HS256'));
print_r($decoded);

// Pass a stdClass in as the third parameter to get the decoded header values
$headers = new stdClass();
$decoded = JWT::decode($jwt, new Key($key, 'HS256'), $headers);
print_r($headers);

/*
 NOTE: This will now be an object instead of an associative array. To get
 an associative array, you will need to cast it as such:
*/

$decoded_array = (array) $decoded;

/**
 * You can add a leeway to account for when there is a clock skew times between
 * the signing and verifying servers. It is recommended that this leeway should
 * not be bigger than a few minutes.
 *
 * Source: http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#nbfDef
 */
JWT::$leeway = 60; // $leeway in seconds
$decoded = JWT::decode($jwt, new Key($key, 'HS256'));
```
Example encode/decode headers
-------
Decoding the JWT headers without verifying the JWT first is NOT recommended, and is not supported by
this library. This is because without verifying the JWT, the header values could have been tampered with.
Any value pulled from an unverified header should be treated as if it could be any string sent in from an
attacker.  If this is something you still want to do in your application for whatever reason, it's possible to
decode the header values manually simply by calling `json_decode` and `base64_decode` on the JWT
header part:
```php
use Firebase\JWT\JWT;

$key = 'example_key';
$payload = [
    'iss' => 'http://example.org',
    'aud' => 'http://example.com',
    'iat' => 1356999524,
    'nbf' => 1357000000
];

$headers = [
    'x-forwarded-for' => 'www.google.com'
];

// Encode headers in the JWT string
$jwt = JWT::encode($payload, $key, 'HS256', null, $headers);

// Decode headers from the JWT string WITHOUT validation
// **IMPORTANT**: This operation is vulnerable to attacks, as the JWT has not yet been verified.
// These headers could be any value sent by an attacker.
list($headersB64, $payloadB64, $sig) = explode('.', $jwt);
$decoded = json_decode(base64_decode($headersB64), true);

print_r($decoded);
```
Example with RS256 (openssl)
----------------------------
```
/* Detailed source-code truncated for AI context efficiency. */
```

Example with a passphrase
-------------------------

```php
use Firebase\JWT\JWT;
use Firebase\JWT\Key;

// Your passphrase
$passphrase = '[YOUR_PASSPHRASE]';

// Your private key file with passphrase
// Can be generated with "ssh-keygen -t rsa -m pem"
$privateKeyFile = '/path/to/key-with-passphrase.pem';

// Create a private key of type "resource"
$privateKey = openssl_pkey_get_private(
    file_get_contents($privateKeyFile),
    $passphrase
);

$payload = [
    'iss' => 'example.org',
    'aud' => 'example.com',
    'iat' => 1356999524,
    'nbf' => 1357000000
];

$jwt = JWT::encode($payload, $privateKey, 'RS256');
echo "Encode:\n" . print_r($jwt, true) . "\n";

// Get public key from the private key, or pull from from a file.
$publicKey = openssl_pkey_get_details($privateKey)['key'];

$decoded = JWT::decode($jwt, new Key($publicKey, 'RS256'));
echo "Decode:\n" . print_r((array) $decoded, true) . "\n";
```

Example with EdDSA (libsodium and Ed25519 signature)
----------------------------
```php
use Firebase\JWT\JWT;
use Firebase\JWT\Key;

// Public and private keys are expected to be Base64 encoded. The last
// non-empty line is used so that keys can be generated with
// sodium_crypto_sign_keypair(). The secret keys generated by other tools may
// need to be adjusted to match the input expected by libsodium.

$keyPair = sodium_crypto_sign_keypair();

$privateKey = base64_encode(sodium_crypto_sign_secretkey($keyPair));

$publicKey = base64_encode(sodium_crypto_sign_publickey($keyPair));

$payload = [
    'iss' => 'example.org',
    'aud' => 'example.com',
    'iat' => 1356999524,
    'nbf' => 1357000000
];

$jwt = JWT::encode($payload, $privateKey, 'EdDSA');
echo "Encode:\n" . print_r($jwt, true) . "\n";

$decoded = JWT::decode($jwt, new Key($publicKey, 'EdDSA'));
echo "Decode:\n" . print_r((array) $decoded, true) . "\n";
````

Example with multiple keys
--------------------------
```php
use Firebase\JWT\JWT;
use Firebase\JWT\Key;

// Example RSA keys from previous example
// $privateKey1 = '...';
// $publicKey1 = '...';

// Example EdDSA keys from previous example
// $privateKey2 = '...';
// $publicKey2 = '...';

$payload = [
    'iss' => 'example.org',
    'aud' => 'example.com',
    'iat' => 1356999524,
    'nbf' => 1357000000
];

$jwt1 = JWT::encode($payload, $privateKey1, 'RS256', 'kid1');
$jwt2 = JWT::encode($payload, $privateKey2, 'EdDSA', 'kid2');
echo "Encode 1:\n" . print_r($jwt1, true) . "\n";
echo "Encode 2:\n" . print_r($jwt2, true) . "\n";

$keys = [
    'kid1' => new Key($publicKey1, 'RS256'),
    'kid2' => new Key($publicKey2, 'EdDSA'),
];

$decoded1 = JWT::decode($jwt1, $keys);
$decoded2 = JWT::decode($jwt2, $keys);

echo "Decode 1:\n" . print_r((array) $decoded1, true) . "\n";
echo "Decode 2:\n" . print_r((array) $decoded2, true) . "\n";
```

Using JWKs
----------

```php
use Firebase\JWT\JWK;
use Firebase\JWT\JWT;

// Set of keys. The "keys" key is required. For example, the JSON response to
// this endpoint: https://www.gstatic.com/iap/verify/public_key-jwk
$jwks = ['keys' => []];

// JWK::parseKeySet($jwks) returns an associative array of **kid** to Firebase\JWT\Key
// objects. Pass this as the second parameter to JWT::decode.
JWT::decode($jwt, JWK::parseKeySet($jwks));
```

Using Cached Key Sets
---------------------

The `CachedKeySet` class can be used to fetch and cache JWKS (JSON Web Key Sets) from a public URI.
This has the following advantages:

1. The results are cached for performance.
2. If an unrecognized key is requested, the cache is refreshed, to accomodate for key rotation.
3. If rate limiting is enabled, the JWKS URI will not make more than 10 requests a second.

```php
use Firebase\JWT\CachedKeySet;
use Firebase\JWT\JWT;

// The URI for the JWKS you wish to cache the results from
$jwksUri = 'https://www.gstatic.com/iap/verify/public_key-jwk';

// Create an HTTP client (can be any PSR-7 compatible HTTP client)
$httpClient = new GuzzleHttp\Client();

// Create an HTTP request factory (can be any PSR-17 compatible HTTP request factory)
$httpFactory = new GuzzleHttp\Psr\HttpFactory();

// Create a cache item pool (can be any PSR-6 compatible cache item pool)
$cacheItemPool = Phpfastcache\CacheManager::getInstance('files');

$keySet = new CachedKeySet(
    $jwksUri,
    $httpClient,
    $httpFactory,
    $cacheItemPool,
    null, // $expiresAfter int seconds to set the JWKS to expire
    true  // $rateLimit    true to enable rate limit of 10 RPS on lookup of invalid keys
);

$jwt = 'eyJhbGci...'; // Some JWT signed by a key from the $jwkUri above
$decoded = JWT::decode($jwt, $keySet);
```

Miscellaneous
-------------

#### Exception Handling

When a call to `JWT::decode` is invalid, it will throw one of the following exceptions:

```php
use Firebase\JWT\JWT;
use Firebase\JWT\SignatureInvalidException;
use Firebase\JWT\BeforeValidException;
use Firebase\JWT\ExpiredException;
use DomainException;
use InvalidArgumentException;
use UnexpectedValueException;

try {
    $decoded = JWT::decode($jwt, $keys);
} catch (InvalidArgumentException $e) {
    // provided key/key-array is empty or malformed.
} catch (DomainException $e) {
    // provided algorithm is unsupported OR
    // provided key is invalid OR
    // unknown error thrown in openSSL or libsodium OR
    // libsodium is required but not available.
} catch (SignatureInvalidException $e) {
    // provided JWT signature verification failed.
} catch (BeforeValidException $e) {
    // provided JWT is trying to be used before "nbf" claim OR
    // provided JWT is trying to be used before "iat" claim.
} catch (ExpiredException $e) {
    // provided JWT is trying to be used after "exp" claim.
} catch (UnexpectedValueException $e) {
    // provided JWT is malformed OR
    // provided JWT is missing an algorithm / using an unsupported algorithm OR
    // provided JWT algorithm does not match provided key OR
    // provided key ID in key/key-array is empty or invalid.
}
```

All exceptions in the `Firebase\JWT` namespace extend `UnexpectedValueException`, and can be simplified
like this:

```php
use Firebase\JWT\JWT;
use UnexpectedValueException;
try {
    $decoded = JWT::decode($jwt, $keys);
} catch (LogicException $e) {
    // errors having to do with environmental setup or malformed JWT Keys
} catch (UnexpectedValueException $e) {
    // errors having to do with JWT signature and claims
}
```

#### Casting to array

The return value of `JWT::decode` is the generic PHP object `stdClass`. If you'd like to handle with arrays
instead, you can do the following:

```php
// return type is stdClass
$decoded = JWT::decode($jwt, $keys);

// cast to array
$decoded = json_decode(json_encode($decoded), true);
```

Tests
-----
Run the tests using phpunit:

```bash
$ pear install PHPUnit
$ phpunit --configuration phpunit.xml.dist
PHPUnit 3.7.10 by Sebastian Bergmann.
.....
Time: 0 seconds, Memory: 2.50Mb
OK (5 tests, 5 assertions)
```

New Lines in private keys
-----

If your private key contains `\n` characters, be sure to wrap it in double quotes `""`
and not single quotes `''` in order to properly interpret the escaped characters.

License
-------
[3-Clause BSD](http://opensource.org/licenses/BSD-3-Clause).

---

### Overview (`vendor/guzzlehttp/guzzle/README.md`)

# Guzzle, PHP HTTP client

[](https://github.com/guzzle/guzzle/releases)
[](https://github.com/guzzle/guzzle/actions?query=workflow%3ACI)
[](https://packagist.org/packages/guzzlehttp/guzzle)

Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and
trivial to integrate with web services.

- Simple interface for building query strings, POST requests, streaming large
  uploads, streaming large downloads, using HTTP cookies, uploading JSON data,
  etc...
- Can send both synchronous and asynchronous requests using the same interface.
- Uses PSR-7 interfaces for requests, responses, and streams. This allows you
  to utilize other PSR-7 compatible libraries with Guzzle.
- Supports PSR-18 allowing interoperability between other PSR-18 HTTP Clients.
- Abstracts away the underlying HTTP transport, allowing you to write
  environment and transport agnostic code; i.e., no hard dependency on cURL,
  PHP streams, sockets, or non-blocking event loops.
- Middleware system allows you to augment and compose client behavior.

```php
$client = new \GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.github.com/repos/guzzle/guzzle');

echo $response->getStatusCode(); // 200
echo $response->getHeaderLine('content-type'); // 'application/json; charset=utf8'
echo $response->getBody(); // '{"id": 1420053, "name": "guzzle", ...}'

// Send an asynchronous request.
$request = new \GuzzleHttp\Psr7\Request('GET', 'http://httpbin.org');
$promise = $client->sendAsync($request)->then(function ($response) {
    echo 'I completed! ' . $response->getBody();
});

$promise->wait();
```

## Help and docs

We use GitHub issues only to discuss bugs and new features. For support please refer to:

- [Documentation](https://docs.guzzlephp.org)
- [Stack Overflow](https://stackoverflow.com/questions/tagged/guzzle)
- [#guzzle](https://app.slack.com/client/T0D2S9JCT/CE6UAAKL4) channel on [PHP-HTTP Slack](https://slack.httplug.io/)
- [Gitter](https://gitter.im/guzzle/guzzle)


## Installing Guzzle

The recommended way to install Guzzle is through
[Composer](https://getcomposer.org/).

```bash
composer require guzzlehttp/guzzle
```


## Version Guidance

| Version | Status         | Packagist           | Namespace    | Repo                | Docs                | PSR-7 | PHP Version  |
|---------|----------------|---------------------|--------------|---------------------|---------------------|-------|--------------|
| 3.x     | EOL            | `guzzle/guzzle`     | `Guzzle`     | [v3][guzzle-3-repo] | [v3][guzzle-3-docs] | No    | >=5.3.3,<7.0 |
| 4.x     | EOL            | `guzzlehttp/guzzle` | `GuzzleHttp` | [v4][guzzle-4-repo] | N/A                 | No    | >=5.4,<7.0   |
| 5.x     | EOL            | `guzzlehttp/guzzle` | `GuzzleHttp` | [v5][guzzle-5-repo] | [v5][guzzle-5-docs] | No    | >=5.4,<7.4   |
| 6.x     | Security fixes | `guzzlehttp/guzzle` | `GuzzleHttp` | [v6][guzzle-6-repo] | [v6][guzzle-6-docs] | Yes   | >=5.5,<8.0   |
| 7.x     | Latest         | `guzzlehttp/guzzle` | `GuzzleHttp` | [v7][guzzle-7-repo] | [v7][guzzle-7-docs] | Yes   | >=7.2.5,<8.2 |

[guzzle-3-repo]: https://github.com/guzzle/guzzle3
[guzzle-4-repo]: https://github.com/guzzle/guzzle/tree/4.x
[guzzle-5-repo]: https://github.com/guzzle/guzzle/tree/5.3
[guzzle-6-repo]: https://github.com/guzzle/guzzle/tree/6.5
[guzzle-7-repo]: https://github.com/guzzle/guzzle
[guzzle-3-docs]: https://guzzle3.readthedocs.io/
[guzzle-5-docs]: https://docs.guzzlephp.org/en/5.3/
[guzzle-6-docs]: https://docs.guzzlephp.org/en/6.5/
[guzzle-7-docs]: https://docs.guzzlephp.org/en/latest/


## Security

If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see [Security Policy](https://github.com/guzzle/guzzle/security/policy) for more information.

## License

Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information.

## For Enterprise

Available as part of the Tidelift Subscription

The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-guzzlehttp-guzzle?utm_source=packagist-guzzlehttp-guzzle&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)

---

### Overview (`vendor/guzzlehttp/promises/README.md`)

# Guzzle Promises

[Promises/A+](https://promisesaplus.com/) implementation that handles promise
chaining and resolution iteratively, allowing for "infinite" promise chaining
while keeping the stack size constant. Read [this blog post](https://blog.domenic.me/youre-missing-the-point-of-promises/)
for a general introduction to promises.

- [Features](#features)
- [Quick start](#quick-start)
- [Synchronous wait](#synchronous-wait)
- [Cancellation](#cancellation)
- [API](#api)
  - [Promise](#promise)
  - [FulfilledPromise](#fulfilledpromise)
  - [RejectedPromise](#rejectedpromise)
- [Promise interop](#promise-interop)
- [Implementation notes](#implementation-notes)


## Features

- [Promises/A+](https://promisesaplus.com/) implementation.
- Promise resolution and chaining is handled iteratively, allowing for
  "infinite" promise chaining.
- Promises have a synchronous `wait` method.
- Promises can be cancelled.
- Works with any object that has a `then` function.
- C# style async/await coroutine promises using
  `GuzzleHttp\Promise\Coroutine::of()`.


## Quick Start

A *promise* represents the eventual result of an asynchronous operation. The
primary way of interacting with a promise is through its `then` method, which
registers callbacks to receive either a promise's eventual value or the reason
why the promise cannot be fulfilled.

### Callbacks

Callbacks are registered with the `then` method by providing an optional 
`$onFulfilled` followed by an optional `$onRejected` function.


```php
use GuzzleHttp\Promise\Promise;

$promise = new Promise();
$promise->then(
    // $onFulfilled
    function ($value) {
        echo 'The promise was fulfilled.';
    },
    // $onRejected
    function ($reason) {
        echo 'The promise was rejected.';
    }
);
```

*Resolving* a promise means that you either fulfill a promise with a *value* or
reject a promise with a *reason*. Resolving a promise triggers callbacks
registered with the promise's `then` method. These callbacks are triggered
only once and in the order in which they were added.

### Resolving a Promise

Promises are fulfilled using the `resolve($value)` method. Resolving a promise
with any value other than a `GuzzleHttp\Promise\RejectedPromise` will trigger
all of the onFulfilled callbacks (resolving a promise with a rejected promise
will reject the promise and trigger the `$onRejected` callbacks).

```php
use GuzzleHttp\Promise\Promise;

$promise = new Promise();
$promise
    ->then(function ($value) {
        // Return a value and don't break the chain
        return "Hello, " . $value;
    })
    // This then is executed after the first then and receives the value
    // returned from the first then.
    ->then(function ($value) {
        echo $value;
    });

// Resolving the promise triggers the $onFulfilled callbacks and outputs
// "Hello, reader."
$promise->resolve('reader.');
```

### Promise Forwarding

Promises can be chained one after the other. Each then in the chain is a new
promise. The return value of a promise is what's forwarded to the next
promise in the chain. Returning a promise in a `then` callback will cause the
subsequent promises in the chain to only be fulfilled when the returned promise
has been fulfilled. The next promise in the chain will be invoked with the
resolved value of the promise.

```php
use GuzzleHttp\Promise\Promise;

$promise = new Promise();
$nextPromise = new Promise();

$promise
    ->then(function ($value) use ($nextPromise) {
        echo $value;
        return $nextPromise;
    })
    ->then(function ($value) {
        echo $value;
    });

// Triggers the first callback and outputs "A"
$promise->resolve('A');
// Triggers the second callback and outputs "B"
$nextPromise->resolve('B');
```

### Promise Rejection

When a promise is rejected, the `$onRejected` callbacks are invoked with the
rejection reason.

```php
use GuzzleHttp\Promise\Promise;

$promise = new Promise();
$promise->then(null, function ($reason) {
    echo $reason;
});

$promise->reject('Error!');
// Outputs "Error!"
```

### Rejection Forwarding

If an exception is thrown in an `$onRejected` callback, subsequent
`$onRejected` callbacks are invoked with the thrown exception as the reason.

```php
use GuzzleHttp\Promise\Promise;

$promise = new Promise();
$promise->then(null, function ($reason) {
    throw new Exception($reason);
})->then(null, function ($reason) {
    assert($reason->getMessage() === 'Error!');
});

$promise->reject('Error!');
```

You can also forward a rejection down the promise chain by returning a
`GuzzleHttp\Promise\RejectedPromise` in either an `$onFulfilled` or
`$onRejected` callback.

```php
use GuzzleHttp\Promise\Promise;
use GuzzleHttp\Promise\RejectedPromise;

$promise = new Promise();
$promise->then(null, function ($reason) {
    return new RejectedPromise($reason);
})->then(null, function ($reason) {
    assert($reason === 'Error!');
});

$promise->reject('Error!');
```

If an exception is not thrown in a `$onRejected` callback and the callback
does not return a rejected promise, downstream `$onFulfilled` callbacks are
invoked using the value returned from the `$onRejected` callback.

```php
use GuzzleHttp\Promise\Promise;

$promise = new Promise();
$promise
    ->then(null, function ($reason) {
        return "It's ok";
    })
    ->then(function ($value) {
        assert($value === "It's ok");
    });

$promise->reject('Error!');
```


## Synchronous Wait

You can synchronously force promises to complete using a promise's `wait`
method. When creating a promise, you can provide a wait function that is used
to synchronously force a promise to complete. When a wait function is invoked
it is expected to deliver a value to the promise or reject the promise. If the
wait function does not deliver a value, then an exception is thrown. The wait
function provided to a promise constructor is invoked when the `wait` function
of the promise is called.

```php
$promise = new Promise(function () use (&$promise) {
    $promise->resolve('foo');
});

// Calling wait will return the value of the promise.
echo $promise->wait(); // outputs "foo"
```

If an exception is encountered while invoking the wait function of a promise,
the promise is rejected with the exception and the exception is thrown.

```php
$promise = new Promise(function () use (&$promise) {
    throw new Exception('foo');
});

$promise->wait(); // throws the exception.
```

Calling `wait` on a promise that has been fulfilled will not trigger the wait
function. It will simply return the previously resolved value.

```php
$promise = new Promise(function () { die('this is not called!'); });
$promise->resolve('foo');
echo $promise->wait(); // outputs "foo"
```

Calling `wait` on a promise that has been rejected will throw an exception. If
the rejection reason is an instance of `\Exception` the reason is thrown.
Otherwise, a `GuzzleHttp\Promise\RejectionException` is thrown and the reason
can be obtained by calling the `getReason` method of the exception.

```php
$promise = new Promise();
$promise->reject('foo');
$promise->wait();
```

> PHP Fatal error:  Uncaught exception 'GuzzleHttp\Promise\RejectionException' with message 'The promise was rejected with value: foo'

### Unwrapping a Promise

When synchronously waiting on a promise, you are joining the state of the
promise into the current state of execution (i.e., return the value of the
promise if it was fulfilled or throw an exception if it was rejected). This is
called "unwrapping" the promise. Waiting on a promise will by default unwrap
the promise state.

You can force a promise to resolve and *not* unwrap the state of the promise
by passing `false` to the first argument of the `wait` function:

```php
$promise = new Promise();
$promise->reject('foo');
// This will not throw an exception. It simply ensures the promise has
// been resolved.
$promise->wait(false);
```

When unwrapping a promise, the resolved value of the promise will be waited
upon until the unwrapped value is not a promise. This means that if you resolve
promise A with a promise B and unwrap promise A, the value returned by the
wait function will be the value delivered to promise B.

**Note**: when you do not unwrap the promise, no value is returned.


## Cancellation

You can cancel a promise that has not yet been fulfilled using the `cancel()`
method of a promise. When creating a promise you can provide an optional
cancel function that when invoked cancels the action of computing a resolution
of the promise.


## API

### Promise

When creating a promise object, you can provide an optional `$waitFn` and
`$cancelFn`. `$waitFn` is a function that is invoked with no arguments and is
expected to resolve the promise. `$cancelFn` is a function with no arguments
that is expected to cancel the computation of a promise. It is invoked when the
`cancel()` method of a promise is called.

```php
use GuzzleHttp\Promise\Promise;

$promise = new Promise(
    function () use (&$promise) {
        $promise->resolve('waited');
    },
    function () {
        // do something that will cancel the promise computation (e.g., close
        // a socket, cancel a database query, etc...)
    }
);

assert('waited' === $promise->wait());
```

A promise has the following methods:

- `then(callable $onFulfilled, callable $onRejected) : PromiseInterface`
  
  Appends fulfillment and rejection handlers to the promise, and returns a new promise resolving to the return value of the called handler.

- `otherwise(callable $onRejected) : PromiseInterface`
  
  Appends a rejection handler callback to the promise, and returns a new promise resolving to the return value of the callback if it is called, or to its original fulfillment value if the promise is instead fulfilled.

- `wait($unwrap = true) : mixed`

  Synchronously waits on the promise to complete.
  
  `$unwrap` controls whether or not the value of the promise is returned for a
  fulfilled promise or if an exception is thrown if the promise is rejected.
  This is set to `true` by default.

- `cancel()`

  Attempts to cancel the promise if possible. The promise being cancelled and
  the parent most ancestor that has not yet been resolved will also be
  cancelled. Any promises waiting on the cancelled promise to resolve will also
  be cancelled.

- `getState() : string`

  Returns the state of the promise. One of `pending`, `fulfilled`, or
  `rejected`.

- `resolve($value)`

  Fulfills the promise with the given `$value`.

- `reject($reason)`

  Rejects the promise with the given `$reason`.


### FulfilledPromise

A fulfilled promise can be created to represent a promise that has been
fulfilled.

```php
use GuzzleHttp\Promise\FulfilledPromise;

$promise = new FulfilledPromise('value');

// Fulfilled callbacks are immediately invoked.
$promise->then(function ($value) {
    echo $value;
});
```


### RejectedPromise

A rejected promise can be created to represent a promise that has been
rejected.

```php
use GuzzleHttp\Promise\RejectedPromise;

$promise = new RejectedPromise('Error');

// Rejected callbacks are immediately invoked.
$promise->then(null, function ($reason) {
    echo $reason;
});
```


## Promise Interoperability

This library works with foreign promises that have a `then` method. This means
you can use Guzzle promises with [React promises](https://github.com/reactphp/promise)
for example. When a foreign promise is returned inside of a then method
callback, promise resolution will occur recursively.

```php
// Create a React promise
$deferred = new React\Promise\Deferred();
$reactPromise = $deferred->promise();

// Create a Guzzle promise that is fulfilled with a React promise.
$guzzlePromise = new GuzzleHttp\Promise\Promise();
$guzzlePromise->then(function ($value) use ($reactPromise) {
    // Do something something with the value...
    // Return the React promise
    return $reactPromise;
});
```

Please note that wait and cancel chaining is no longer possible when forwarding
a foreign promise. You will need to wrap a third-party promise with a Guzzle
promise in order to utilize wait and cancel functions with foreign promises.


### Event Loop Integration

In order to keep the stack size constant, Guzzle promises are resolved
asynchronously using a task queue. When waiting on promises synchronously, the
task queue will be automatically run to ensure that the blocking promise and
any forwarded promises are resolved. When using promises asynchronously in an
event loop, you will need to run the task queue on each tick of the loop. If
you do not run the task queue, then promises will not be resolved.

You can run the task queue using the `run()` method of the global task queue
instance.

```php
// Get the global task queue
$queue = GuzzleHttp\Promise\Utils::queue();
$queue->run();
```

For example, you could use Guzzle promises with React using a periodic timer:

```php
$loop = React\EventLoop\Factory::create();
$loop->addPeriodicTimer(0, [$queue, 'run']);
```

*TODO*: Perhaps adding a `futureTick()` on each tick would be faster?


## Implementation Notes

### Promise Resolution and Chaining is Handled Iteratively

By shuffling pending handlers from one owner to another, promises are
resolved iteratively, allowing for "infinite" then chaining.

```php
<?php
require 'vendor/autoload.php';

use GuzzleHttp\Promise\Promise;

$parent = new Promise();
$p = $parent;

for ($i = 0; $i < 1000; $i++) {
    $p = $p->then(function ($v) {
        // The stack size remains constant (a good thing)
        echo xdebug_get_stack_depth() . ', ';
        return $v + 1;
    });
}

$parent->resolve(0);
var_dump($p->wait()); // int(1000)

```

When a promise is fulfilled or rejected with a non-promise value, the promise
then takes ownership of the handlers of each child promise and delivers values
down the chain without using recursion.

When a promise is resolved with another promise, the original promise transfers
all of its pending handlers to the new promise. When the new promise is
eventually resolved, all of the pending handlers are delivered the forwarded
value.

### A Promise is the Deferred

Some promise libraries implement promises using a deferred object to represent
a computation and a promise object to represent the delivery of the result of
the computation. This is a nice separation of computation and delivery because
consumers of the promise cannot modify the value that will be eventually
delivered.

One side effect of being able to implement promise resolution and chaining
iteratively is that you need to be able for one promise to reach into the state
of another promise to shuffle around ownership of handlers. In order to achieve
this without making the handlers of a promise publicly mutable, a promise is
also the deferred value, allowing promises of the same parent class to reach
into and modify the private properties of promises of the same type. While this
does allow consumers of the value to modify the resolution or rejection of the
deferred, it is a small price to pay for keeping the stack size constant.

```php
$promise = new Promise();
$promise->then(function ($value) { echo $value; });
// The promise is the deferred value, so you can deliver a value to it.
$promise->resolve('foo');
// prints "foo"
```


## Upgrading from Function API

A static API was first introduced in 1.4.0, in order to mitigate problems with
functions conflicting between global and local copies of the package. The
function API will be removed in 2.0.0. A migration table has been provided here
for your convenience:

| Original Function | Replacement Method |
|----------------|----------------|
| `queue` | `Utils::queue` |
| `task` | `Utils::task` |
| `promise_for` | `Create::promiseFor` |
| `rejection_for` | `Create::rejectionFor` |
| `exception_for` | `Create::exceptionFor` |
| `iter_for` | `Create::iterFor` |
| `inspect` | `Utils::inspect` |
| `inspect_all` | `Utils::inspectAll` |
| `unwrap` | `Utils::unwrap` |
| `all` | `Utils::all` |
| `some` | `Utils::some` |
| `any` | `Utils::any` |
| `settle` | `Utils::settle` |
| `each` | `Each::of` |
| `each_limit` | `Each::ofLimit` |
| `each_limit_all` | `Each::ofLimitAll` |
| `!is_fulfilled` | `Is::pending` |
| `is_fulfilled` | `Is::fulfilled` |
| `is_rejected` | `Is::rejected` |
| `is_settled` | `Is::settled` |
| `coroutine` | `Coroutine::of` |


## Security

If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see [Security Policy](https://github.com/guzzle/promises/security/policy) for more information.


## License

Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information.


## For Enterprise

Available as part of the Tidelift Subscription

The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-guzzlehttp-promises?utm_source=packagist-guzzlehttp-promises&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)

---

### Overview (`vendor/guzzlehttp/psr7/README.md`)

# PSR-7 Message Implementation

This repository contains a full [PSR-7](https://www.php-fig.org/psr/psr-7/)
message implementation, several stream decorators, and some helpful
functionality like query string parsing.





# Stream implementation

This package comes with a number of stream implementations and stream
decorators.


## AppendStream

`GuzzleHttp\Psr7\AppendStream`

Reads from multiple streams, one after the other.

```php
use GuzzleHttp\Psr7;

$a = Psr7\Utils::streamFor('abc, ');
$b = Psr7\Utils::streamFor('123.');
$composed = new Psr7\AppendStream([$a, $b]);

$composed->addStream(Psr7\Utils::streamFor(' Above all listen to me'));

echo $composed; // abc, 123. Above all listen to me.
```


## BufferStream

`GuzzleHttp\Psr7\BufferStream`

Provides a buffer stream that can be written to fill a buffer, and read
from to remove bytes from the buffer.

This stream returns a "hwm" metadata value that tells upstream consumers
what the configured high water mark of the stream is, or the maximum
preferred size of the buffer.

```php
use GuzzleHttp\Psr7;

// When more than 1024 bytes are in the buffer, it will begin returning
// false to writes. This is an indication that writers should slow down.
$buffer = new Psr7\BufferStream(1024);
```


## CachingStream

The CachingStream is used to allow seeking over previously read bytes on
non-seekable streams. This can be useful when transferring a non-seekable
entity body fails due to needing to rewind the stream (for example, resulting
from a redirect). Data that is read from the remote stream will be buffered in
a PHP temp stream so that previously read bytes are cached first in memory,
then on disk.

```php
use GuzzleHttp\Psr7;

$original = Psr7\Utils::streamFor(fopen('http://www.google.com', 'r'));
$stream = new Psr7\CachingStream($original);

$stream->read(1024);
echo $stream->tell();
// 1024

$stream->seek(0);
echo $stream->tell();
// 0
```


## DroppingStream

`GuzzleHttp\Psr7\DroppingStream`

Stream decorator that begins dropping data once the size of the underlying
stream becomes too full.

```php
use GuzzleHttp\Psr7;

// Create an empty stream
$stream = Psr7\Utils::streamFor();

// Start dropping data when the stream has more than 10 bytes
$dropping = new Psr7\DroppingStream($stream, 10);

$dropping->write('01234567890123456789');
echo $stream; // 0123456789
```


## FnStream

`GuzzleHttp\Psr7\FnStream`

Compose stream implementations based on a hash of functions.

Allows for easy testing and extension of a provided stream without needing
to create a concrete class for a simple extension point.

```php

use GuzzleHttp\Psr7;

$stream = Psr7\Utils::streamFor('hi');
$fnStream = Psr7\FnStream::decorate($stream, [
    'rewind' => function () use ($stream) {
        echo 'About to rewind - ';
        $stream->rewind();
        echo 'rewound!';
    }
]);

$fnStream->rewind();
// Outputs: About to rewind - rewound!
```


## InflateStream

`GuzzleHttp\Psr7\InflateStream`

Uses PHP's zlib.inflate filter to inflate zlib (HTTP deflate, RFC1950) or gzipped (RFC1952) content.

This stream decorator converts the provided stream to a PHP stream resource,
then appends the zlib.inflate filter. The stream is then converted back
to a Guzzle stream resource to be used as a Guzzle stream.


## LazyOpenStream

`GuzzleHttp\Psr7\LazyOpenStream`

Lazily reads or writes to a file that is opened only after an IO operation
take place on the stream.

```php
use GuzzleHttp\Psr7;

$stream = new Psr7\LazyOpenStream('/path/to/file', 'r');
// The file has not yet been opened...

echo $stream->read(10);
// The file is opened and read from only when needed.
```


## LimitStream

`GuzzleHttp\Psr7\LimitStream`

LimitStream can be used to read a subset or slice of an existing stream object.
This can be useful for breaking a large file into smaller pieces to be sent in
chunks (e.g. Amazon S3's multipart upload API).

```php
use GuzzleHttp\Psr7;

$original = Psr7\Utils::streamFor(fopen('/tmp/test.txt', 'r+'));
echo $original->getSize();
// >>> 1048576

// Limit the size of the body to 1024 bytes and start reading from byte 2048
$stream = new Psr7\LimitStream($original, 1024, 2048);
echo $stream->getSize();
// >>> 1024
echo $stream->tell();
// >>> 0
```


## MultipartStream

`GuzzleHttp\Psr7\MultipartStream`

Stream that when read returns bytes for a streaming multipart or
multipart/form-data stream.


## NoSeekStream

`GuzzleHttp\Psr7\NoSeekStream`

NoSeekStream wraps a stream and does not allow seeking.

```php
use GuzzleHttp\Psr7;

$original = Psr7\Utils::streamFor('foo');
$noSeek = new Psr7\NoSeekStream($original);

echo $noSeek->read(3);
// foo
var_export($noSeek->isSeekable());
// false
$noSeek->seek(0);
var_export($noSeek->read(3));
// NULL
```


## PumpStream

`GuzzleHttp\Psr7\PumpStream`

Provides a read only stream that pumps data from a PHP callable.

When invoking the provided callable, the PumpStream will pass the amount of
data requested to read to the callable. The callable can choose to ignore
this value and return fewer or more bytes than requested. Any extra data
returned by the provided callable is buffered internally until drained using
the read() function of the PumpStream. The provided callable MUST return
false when there is no more data to read.


## Implementing stream decorators

Creating a stream decorator is very easy thanks to the
`GuzzleHttp\Psr7\StreamDecoratorTrait`. This trait provides methods that
implement `Psr\Http\Message\StreamInterface` by proxying to an underlying
stream. Just `use` the `StreamDecoratorTrait` and implement your custom
methods.

For example, let's say we wanted to call a specific function each time the last
byte is read from a stream. This could be implemented by overriding the
`read()` method.

```php
use Psr\Http\Message\StreamInterface;
use GuzzleHttp\Psr7\StreamDecoratorTrait;

class EofCallbackStream implements StreamInterface
{
    use StreamDecoratorTrait;

    private $callback;

    public function __construct(StreamInterface $stream, callable $cb)
    {
        $this->stream = $stream;
        $this->callback = $cb;
    }

    public function read($length)
    {
        $result = $this->stream->read($length);

        // Invoke the callback when EOF is hit.
        if ($this->eof()) {
            call_user_func($this->callback);
        }

        return $result;
    }
}
```

This decorator could be added to any existing stream and used like so:

```php
use GuzzleHttp\Psr7;

$original = Psr7\Utils::streamFor('foo');

$eofStream = new EofCallbackStream($original, function () {
    echo 'EOF!';
});

$eofStream->read(2);
$eofStream->read(1);
// echoes "EOF!"
$eofStream->seek(0);
$eofStream->read(3);
// echoes "EOF!"
```


## PHP StreamWrapper

You can use the `GuzzleHttp\Psr7\StreamWrapper` class if you need to use a
PSR-7 stream as a PHP stream resource.

Use the `GuzzleHttp\Psr7\StreamWrapper::getResource()` method to create a PHP
stream from a PSR-7 stream.

```php
use GuzzleHttp\Psr7\StreamWrapper;

$stream = GuzzleHttp\Psr7\Utils::streamFor('hello!');
$resource = StreamWrapper::getResource($stream);
echo fread($resource, 6); // outputs hello!
```


# Static API

There are various static methods available under the `GuzzleHttp\Psr7` namespace.


## `GuzzleHttp\Psr7\Message::toString`

`public static function toString(MessageInterface $message): string`

Returns the string representation of an HTTP message.

```php
$request = new GuzzleHttp\Psr7\Request('GET', 'http://example.com');
echo GuzzleHttp\Psr7\Message::toString($request);
```


## `GuzzleHttp\Psr7\Message::bodySummary`

`public static function bodySummary(MessageInterface $message, int $truncateAt = 120): string|null`

Get a short summary of the message body.

Will return `null` if the response is not printable.


## `GuzzleHttp\Psr7\Message::rewindBody`

`public static function rewindBody(MessageInterface $message): void`

Attempts to rewind a message body and throws an exception on failure.

The body of the message will only be rewound if a call to `tell()`
returns a value other than `0`.


## `GuzzleHttp\Psr7\Message::parseMessage`

`public static function parseMessage(string $message): array`

Parses an HTTP message into an associative array.

The array contains the "start-line" key containing the start line of
the message, "headers" key containing an associative array of header
array values, and a "body" key containing the body of the message.


## `GuzzleHttp\Psr7\Message::parseRequestUri`

`public static function parseRequestUri(string $path, array $headers): string`

Constructs a URI for an HTTP request message.


## `GuzzleHttp\Psr7\Message::parseRequest`

`public static function parseRequest(string $message): Request`

Parses a request message string into a request object.


## `GuzzleHttp\Psr7\Message::parseResponse`

`public static function parseResponse(string $message): Response`

Parses a response message string into a response object.


## `GuzzleHttp\Psr7\Header::parse`

`public static function parse(string|array $header): array`

Parse an array of header values containing ";" separated data into an
array of associative arrays representing the header key value pair data
of the header. When a parameter does not contain a value, but just
contains a key, this function will inject a key with a '' string value.


## `GuzzleHttp\Psr7\Header::normalize`

`public static function normalize(string|array $header): array`

Converts an array of header values that may contain comma separated
headers into an array of headers with no comma separated values.


## `GuzzleHttp\Psr7\Query::parse`

`public static function parse(string $str, int|bool $urlEncoding = true): array`

Parse a query string into an associative array.

If multiple values are found for the same key, the value of that key
value pair will become an array. This function does not parse nested
PHP style arrays into an associative array (e.g., `foo[a]=1&foo[b]=2`
will be parsed into `['foo[a]' => '1', 'foo[b]' => '2'])`.


## `GuzzleHttp\Psr7\Query::build`

`public static function build(array $params, int|false $encoding = PHP_QUERY_RFC3986): string`

Build a query string from an array of key value pairs.

This function can use the return value of `parse()` to build a query
string. This function does not modify the provided keys when an array is
encountered (like `http_build_query()` would).


## `GuzzleHttp\Psr7\Utils::caselessRemove`

`public static function caselessRemove(iterable<string> $keys, $keys, array $data): array`

Remove the items given by the keys, case insensitively from the data.


## `GuzzleHttp\Psr7\Utils::copyToStream`

`public static function copyToStream(StreamInterface $source, StreamInterface $dest, int $maxLen = -1): void`

Copy the contents of a stream into another stream until the given number
of bytes have been read.


## `GuzzleHttp\Psr7\Utils::copyToString`

`public static function copyToString(StreamInterface $stream, int $maxLen = -1): string`

Copy the contents of a stream into a string until the given number of
bytes have been read.


## `GuzzleHttp\Psr7\Utils::hash`

`public static function hash(StreamInterface $stream, string $algo, bool $rawOutput = false): string`

Calculate a hash of a stream.

This method reads the entire stream to calculate a rolling hash, based on
PHP's `hash_init` functions.


## `GuzzleHttp\Psr7\Utils::modifyRequest`

`public static function modifyRequest(RequestInterface $request, array $changes): RequestInterface`

Clone and modify a request with the given changes.

This method is useful for reducing the number of clones needed to mutate
a message.

- method: (string) Changes the HTTP method.
- set_headers: (array) Sets the given headers.
- remove_headers: (array) Remove the given headers.
- body: (mixed) Sets the given body.
- uri: (UriInterface) Set the URI.
- query: (string) Set the query string value of the URI.
- version: (string) Set the protocol version.


## `GuzzleHttp\Psr7\Utils::readLine`

`public static function readLine(StreamInterface $stream, int $maxLength = null): string`

Read a line from the stream up to the maximum allowed buffer length.


## `GuzzleHttp\Psr7\Utils::streamFor`

`public static function streamFor(resource|string|null|int|float|bool|StreamInterface|callable|\Iterator $resource = '', array $options = []): StreamInterface`

Create a new stream based on the input type.

Options is an associative array that can contain the following keys:

- metadata: Array of custom metadata.
- size: Size of the stream.

This method accepts the following `$resource` types:

- `Psr\Http\Message\StreamInterface`: Returns the value as-is.
- `string`: Creates a stream object that uses the given string as the contents.
- `resource`: Creates a stream object that wraps the given PHP stream resource.
- `Iterator`: If the provided value implements `Iterator`, then a read-only
  stream object will be created that wraps the given iterable. Each time the
  stream is read from, data from the iterator will fill a buffer and will be
  continuously called until the buffer is equal to the requested read size.
  Subsequent read calls will first read from the buffer and then call `next`
  on the underlying iterator until it is exhausted.
- `object` with `__toString()`: If the object has the `__toString()` method,
  the object will be cast to a string and then a stream will be returned that
  uses the string value.
- `NULL`: When `null` is passed, an empty stream object is returned.
- `callable` When a callable is passed, a read-only stream object will be
  created that invokes the given callable. The callable is invoked with the
  number of suggested bytes to read. The callable can return any number of
  bytes, but MUST return `false` when there is no more data to return. The
  stream object that wraps the callable will invoke the callable until the
  number of requested bytes are available. Any additional bytes will be
  buffered and used in subsequent reads.

```php
$stream = GuzzleHttp\Psr7\Utils::streamFor('foo');
$stream = GuzzleHttp\Psr7\Utils::streamFor(fopen('/path/to/file', 'r'));

$generator = function ($bytes) {
    for ($i = 0; $i < $bytes; $i++) {
        yield ' ';
    }
}

$stream = GuzzleHttp\Psr7\Utils::streamFor($generator(100));
```


## `GuzzleHttp\Psr7\Utils::tryFopen`

`public static function tryFopen(string $filename, string $mode): resource`

Safely opens a PHP stream resource using a filename.

When fopen fails, PHP normally raises a warning. This function adds an
error handler that checks for errors and throws an exception instead.


## `GuzzleHttp\Psr7\Utils::tryGetContents`

`public static function tryGetContents(resource $stream): string`

Safely gets the contents of a given stream.

When stream_get_contents fails, PHP normally raises a warning. This
function adds an error handler that checks for errors and throws an
exception instead.


## `GuzzleHttp\Psr7\Utils::uriFor`

`public static function uriFor(string|UriInterface $uri): UriInterface`

Returns a UriInterface for the given value.

This function accepts a string or UriInterface and returns a
UriInterface for the given value. If the value is already a
UriInterface, it is returned as-is.


## `GuzzleHttp\Psr7\MimeType::fromFilename`

`public static function fromFilename(string $filename): string|null`

Determines the mimetype of a file by looking at its extension.


## `GuzzleHttp\Psr7\MimeType::fromExtension`

`public static function fromExtension(string $extension): string|null`

Maps a file extensions to a mimetype.


## Upgrading from Function API

The static API was first introduced in 1.7.0, in order to mitigate problems with functions conflicting between global and local copies of the package. The function API was removed in 2.0.0. A migration table has been provided here for your convenience:

| Original Function | Replacement Method |
|----------------|----------------|
| `str` | `Message::toString` |
| `uri_for` | `Utils::uriFor` |
| `stream_for` | `Utils::streamFor` |
| `parse_header` | `Header::parse` |
| `normalize_header` | `Header::normalize` |
| `modify_request` | `Utils::modifyRequest` |
| `rewind_body` | `Message::rewindBody` |
| `try_fopen` | `Utils::tryFopen` |
| `copy_to_string` | `Utils::copyToString` |
| `copy_to_stream` | `Utils::copyToStream` |
| `hash` | `Utils::hash` |
| `readline` | `Utils::readLine` |
| `parse_request` | `Message::parseRequest` |
| `parse_response` | `Message::parseResponse` |
| `parse_query` | `Query::parse` |
| `build_query` | `Query::build` |
| `mimetype_from_filename` | `MimeType::fromFilename` |
| `mimetype_from_extension` | `MimeType::fromExtension` |
| `_parse_message` | `Message::parseMessage` |
| `_parse_request_uri` | `Message::parseRequestUri` |
| `get_message_body_summary` | `Message::bodySummary` |
| `_caseless_remove` | `Utils::caselessRemove` |


# Additional URI Methods

Aside from the standard `Psr\Http\Message\UriInterface` implementation in form of the `GuzzleHttp\Psr7\Uri` class,
this library also provides additional functionality when working with URIs as static methods.

## URI Types

An instance of `Psr\Http\Message\UriInterface` can either be an absolute URI or a relative reference.
An absolute URI has a scheme. A relative reference is used to express a URI relative to another URI,
the base URI. Relative references can be divided into several forms according to
[RFC 3986 Section 4.2](https://tools.ietf.org/html/rfc3986#section-4.2):

- network-path references, e.g. `//example.com/path`
- absolute-path references, e.g. `/path`
- relative-path references, e.g. `subpath`

The following methods can be used to identify the type of the URI.

### `GuzzleHttp\Psr7\Uri::isAbsolute`

`public static function isAbsolute(UriInterface $uri): bool`

Whether the URI is absolute, i.e. it has a scheme.

### `GuzzleHttp\Psr7\Uri::isNetworkPathReference`

`public static function isNetworkPathReference(UriInterface $uri): bool`

Whether the URI is a network-path reference. A relative reference that begins with two slash characters is
termed an network-path reference.

### `GuzzleHttp\Psr7\Uri::isAbsolutePathReference`

`public static function isAbsolutePathReference(UriInterface $uri): bool`

Whether the URI is a absolute-path reference. A relative reference that begins with a single slash character is
termed an absolute-path reference.

### `GuzzleHttp\Psr7\Uri::isRelativePathReference`

`public static function isRelativePathReference(UriInterface $uri): bool`

Whether the URI is a relative-path reference. A relative reference that does not begin with a slash character is
termed a relative-path reference.

### `GuzzleHttp\Psr7\Uri::isSameDocumentReference`

`public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null): bool`

Whether the URI is a same-document reference. A same-document reference refers to a URI that is, aside from its
fragment component, identical to the base URI. When no base URI is given, only an empty URI reference
(apart from its fragment) is considered a same-document reference.

## URI Components

Additional methods to work with URI components.

### `GuzzleHttp\Psr7\Uri::isDefaultPort`

`public static function isDefaultPort(UriInterface $uri): bool`

Whether the URI has the default port of the current scheme. `Psr\Http\Message\UriInterface::getPort` may return null
or the standard port. This method can be used independently of the implementation.

### `GuzzleHttp\Psr7\Uri::composeComponents`

`public static function composeComponents($scheme, $authority, $path, $query, $fragment): string`

Composes a URI reference string from its various components according to
[RFC 3986 Section 5.3](https://tools.ietf.org/html/rfc3986#section-5.3). Usually this method does not need to be called
manually but instead is used indirectly via `Psr\Http\Message\UriInterface::__toString`.

### `GuzzleHttp\Psr7\Uri::fromParts`

`public static function fromParts(array $parts): UriInterface`

Creates a URI from a hash of [`parse_url`](https://www.php.net/manual/en/function.parse-url.php) components.


### `GuzzleHttp\Psr7\Uri::withQueryValue`

`public static function withQueryValue(UriInterface $uri, $key, $value): UriInterface`

Creates a new URI with a specific query string value. Any existing query string values that exactly match the
provided key are removed and replaced with the given key value pair. A value of null will set the query string
key without a value, e.g. "key" instead of "key=value".

### `GuzzleHttp\Psr7\Uri::withQueryValues`

`public static function withQueryValues(UriInterface $uri, array $keyValueArray): UriInterface`

Creates a new URI with multiple query string values. It has the same behavior as `withQueryValue()` but for an
associative array of key => value.

### `GuzzleHttp\Psr7\Uri::withoutQueryValue`

`public static function withoutQueryValue(UriInterface $uri, $key): UriInterface`

Creates a new URI with a specific query string value removed. Any existing query string values that exactly match the
provided key are removed.

## Cross-Origin Detection

`GuzzleHttp\Psr7\UriComparator` provides methods to determine if a modified URL should be considered cross-origin.

### `GuzzleHttp\Psr7\UriComparator::isCrossOrigin`

`public static function isCrossOrigin(UriInterface $original, UriInterface $modified): bool`

Determines if a modified URL should be considered cross-origin with respect to an original URL.

## Reference Resolution

`GuzzleHttp\Psr7\UriResolver` provides methods to resolve a URI reference in the context of a base URI according
to [RFC 3986 Section 5](https://tools.ietf.org/html/rfc3986#section-5). This is for example also what web browsers
do when resolving a link in a website based on the current request URI.

### `GuzzleHttp\Psr7\UriResolver::resolve`

`public static function resolve(UriInterface $base, UriInterface $rel): UriInterface`

Converts the relative URI into a new URI that is resolved against the base URI.

### `GuzzleHttp\Psr7\UriResolver::removeDotSegments`

`public static function removeDotSegments(string $path): string`

Removes dot segments from a path and returns the new path according to
[RFC 3986 Section 5.2.4](https://tools.ietf.org/html/rfc3986#section-5.2.4).

### `GuzzleHttp\Psr7\UriResolver::relativize`

`public static function relativize(UriInterface $base, UriInterface $target): UriInterface`

Returns the target URI as a relative reference from the base URI. This method is the counterpart to resolve():

```php
(string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))
```

One use-case is to use the current request URI as base URI and then generate relative links in your documents
to reduce the document size or offer self-contained downloadable document archives.

```php
$base = new Uri('http://example.com/a/b/');
echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c'));  // prints 'c'.
echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y'));  // prints '../x/y'.
echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
echo UriResolver::relativize($base, new Uri('http://example.org/a/b/'));   // prints '//example.org/a/b/'.
```

## Normalization and Comparison

`GuzzleHttp\Psr7\UriNormalizer` provides methods to normalize and compare URIs according to
[RFC 3986 Section 6](https://tools.ietf.org/html/rfc3986#section-6).

### `GuzzleHttp\Psr7\UriNormalizer::normalize`

`public static function normalize(UriInterface $uri, $flags = self::PRESERVING_NORMALIZATIONS): UriInterface`

Returns a normalized URI. The scheme and host component are already normalized to lowercase per PSR-7 UriInterface.
This methods adds additional normalizations that can be configured with the `$flags` parameter which is a bitmask
of normalizations to apply. The following normalizations are available:

- `UriNormalizer::PRESERVING_NORMALIZATIONS`

    Default normalizations which only include the ones that preserve semantics.

- `UriNormalizer::CAPITALIZE_PERCENT_ENCODING`

    All letters within a percent-encoding triplet (e.g., "%3A") are case-insensitive, and should be capitalized.

    Example: `http://example.org/a%c2%b1b` → `http://example.org/a%C2%B1b`

- `UriNormalizer::DECODE_UNRESERVED_CHARACTERS`

    Decodes percent-encoded octets of unreserved characters. For consistency, percent-encoded octets in the ranges of
    ALPHA (%41–%5A and %61–%7A), DIGIT (%30–%39), hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should
    not be created by URI producers and, when found in a URI, should be decoded to their corresponding unreserved
    characters by URI normalizers.

    Example: `http://example.org/%7Eusern%61me/` → `http://example.org/~username/`

- `UriNormalizer::CONVERT_EMPTY_PATH`

    Converts the empty path to "/" for http and https URIs.

    Example: `http://example.org` → `http://example.org/`

- `UriNormalizer::REMOVE_DEFAULT_HOST`

    Removes the default host of the given URI scheme from the URI. Only the "file" scheme defines the default host
    "localhost". All of `file:/myfile`, `file:///myfile`, and `file://localhost/myfile` are equivalent according to
    RFC 3986.

    Example: `file://localhost/myfile` → `file:///myfile`

- `UriNormalizer::REMOVE_DEFAULT_PORT`

    Removes the default port of the given URI scheme from the URI.

    Example: `http://example.org:80/` → `http://example.org/`

- `UriNormalizer::REMOVE_DOT_SEGMENTS`

    Removes unnecessary dot-segments. Dot-segments in relative-path references are not removed as it would
    change the semantics of the URI reference.

    Example: `http://example.org/../a/b/../c/./d.html` → `http://example.org/a/c/d.html`

- `UriNormalizer::REMOVE_DUPLICATE_SLASHES`

    Paths which include two or more adjacent slashes are converted to one. Webservers usually ignore duplicate slashes
    and treat those URIs equivalent. But in theory those URIs do not need to be equivalent. So this normalization
    may change the semantics. Encoded slashes (%2F) are not removed.

    Example: `http://example.org//foo///bar.html` → `http://example.org/foo/bar.html`

- `UriNormalizer::SORT_QUERY_PARAMETERS`

    Sort query parameters with their values in alphabetical order. However, the order of parameters in a URI may be
    significant (this is not defined by the standard). So this normalization is not safe and may change the semantics
    of the URI.

    Example: `?lang=en&article=fred` → `?article=fred&lang=en`

### `GuzzleHttp\Psr7\UriNormalizer::isEquivalent`

`public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS): bool`

Whether two URIs can be considered equivalent. Both URIs are normalized automatically before comparison with the given
`$normalizations` bitmask. The method also accepts relative URI references and returns true when they are equivalent.
This of course assumes they will be resolved against the same base URI. If this is not the case, determination of
equivalence or difference of relative references does not mean anything.


## Version Guidance

| Version | Status         | PHP Version      |
|---------|----------------|------------------|
| 1.x     | Security fixes | >=5.4,<8.1       |
| 2.x     | Latest         | ^7.2.5 \|\| ^8.0 |


## Security

If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see [Security Policy](https://github.com/guzzle/psr7/security/policy) for more information.


## License

Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information.


## For Enterprise

Available as part of the Tidelift Subscription

The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-guzzlehttp-psr7?utm_source=packagist-guzzlehttp-psr7&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)

---

### Overview (`vendor/illuminate/database/README.md`)

## Illuminate Database

The Illuminate Database component is a full database toolkit for PHP, providing an expressive query builder, ActiveRecord style ORM, and schema builder. It currently supports MySQL, Postgres, SQL Server, and SQLite. It also serves as the database layer of the Laravel PHP framework.

### Usage Instructions

First, create a new "Capsule" manager instance. Capsule aims to make configuring the library for usage outside of the Laravel framework as easy as possible.

```PHP
use Illuminate\Database\Capsule\Manager as Capsule;

$capsule = new Capsule;

$capsule->addConnection([
    'driver'    => 'mysql',
    'host'      => 'localhost',
    'database'  => 'database',
    'username'  => 'root',
    'password'  => 'password',
    'charset'   => 'utf8',
    'collation' => 'utf8_unicode_ci',
    'prefix'    => '',
]);

// Set the event dispatcher used by Eloquent models... (optional)
use Illuminate\Events\Dispatcher;
use Illuminate\Container\Container;
$capsule->setEventDispatcher(new Dispatcher(new Container));

// Make this Capsule instance available globally via static methods... (optional)
$capsule->setAsGlobal();

// Setup the Eloquent ORM... (optional; unless you've used setEventDispatcher())
$capsule->bootEloquent();
```

> `composer require "illuminate/events"` required when you need to use observers with Eloquent.

Once the Capsule instance has been registered. You may use it like so:

**Using The Query Builder**

```PHP
$users = Capsule::table('users')->where('votes', '>', 100)->get();
```
Other core methods may be accessed directly from the Capsule in the same manner as from the DB facade:
```PHP
$results = Capsule::select('select * from users where id = ?', [1]);
```

**Using The Schema Builder**

```PHP
Capsule::schema()->create('users', function ($table) {
    $table->increments('id');
    $table->string('email')->unique();
    $table->timestamps();
});
```

**Using The Eloquent ORM**

```PHP
class User extends Illuminate\Database\Eloquent\Model {}

$users = User::where('votes', '>', 1)->get();
```

For further documentation on using the various database facilities this library provides, consult the [Laravel framework documentation](https://laravel.com/docs).

---

### Overview (`vendor/khanamiryan/qrcode-detector-decoder/README.md`)

# QR code decoder / reader for PHP
This is a PHP library to detect and decode QR-codes.<br />This is first and only QR code reader that works without extensions.<br />
Ported from [ZXing library](https://github.com/zxing/zxing)


## Installation
The recommended method of installing this library is via [Composer](https://getcomposer.org/).

Run the following command from your project root:

```bash
$ composer require khanamiryan/qrcode-detector-decoder
```


## Usage 
```php
require __DIR__ . "/vendor/autoload.php";
$qrcode = new QrReader('path/to_image');
$text = $qrcode->text(); //return decoded text from QR Code
```

## Requirements 
* PHP >= 5.6
* GD Library


## Contributing

You can help the project by adding features, cleaning the code, adding composer and other.

 
1. Fork it
2. Create your feature branch: `git checkout -b my-new-feature`
3. Commit your changes: `git commit -am 'Add some feature'`
4. Push to the branch: `git push origin my-new-feature`
5. Submit a pull request

---

### Overview (`vendor/mrgoon/aliyun-sms-sdk/README.md`)

# 阿里云短信服务核心代码
* 网上一些开源项目的更新时间用的相对较老的版本，使用的request文件是2016-09-27
* 本项目更新于2017-05-25

---

### Overview (`vendor/mrgoon/aliyun-sms/README.md`)

# 介绍
阿里大于关闭了新用户渠道，迁移到阿里云短信服务上，然而阿里云短信服务在5月底更新了api。
在github上找了一下，轮子都是基于2016年的接口，非常难受。
自己造了一个轮子，亲测可用。

* 安装
` composer require mrgoon/aliyun-sms `

## 基于laravel框架的使用方法

* 加载
在config/app的providers中添加
` Mrgoon\AliSms\ServiceProvider::class `

同时，可以选择性添加aliases

控制台运行:
` php artisan vendor:publish `

根据新增的` aliyunsms.php ` 文件，在.env文件中添加环境变量：
``` 
ALIYUN_SMS_AK=your access key
ALIYUN_SMS_AS=your secret key
ALIYUN_SMS_SIGN_NAME=sign name
```

* 使用
```
$aliSms = new AliSms();
$response = $aliSms->sendSms('phone number', 'SMS_code', ['name'=> 'value in your template']);
//dump($response);
```

## 非laravel框架的使用方法

* 加载方式通过composer，不变
* 使用样例代码如下：

```
$config = [
        'access_key' => 'your access key',
        'access_secret' => 'your access secret',
        'sign_name' => 'your sign name',
    ];

    $aliSms = new Mrgoon\AliSms\AliSms();
    $response = $sms->sendSms('phone number', 'tempplate code', ['name'=> 'value in your template'], $config);
```

---

### Overview (`vendor/nesbot/carbon/readme.md`)

# Carbon

[](https://packagist.org/packages/nesbot/carbon)
[](https://packagist.org/packages/nesbot/carbon)
[](https://actions-badge.atrox.dev/briannesbitt/Carbon/goto)
[](https://codecov.io/github/briannesbitt/Carbon?branch=master)
[](https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme)

An international PHP extension for DateTime. [https://carbon.nesbot.com](https://carbon.nesbot.com)

```
/* Detailed source-code truncated for AI context efficiency. */
```

[Get supported nesbot/carbon with the Tidelift Subscription](https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme)

## Installation

### With Composer

```
$ composer require nesbot/carbon
```

```json
{
    "require": {
        "nesbot/carbon": "^2.16"
    }
}
```

```php
<?php
require 'vendor/autoload.php';

use Carbon\Carbon;

printf("Now: %s", Carbon::now());
```

### Without Composer

Why are you not using [composer](https://getcomposer.org/)? Download the Carbon [latest release](https://github.com/briannesbitt/Carbon/releases) and put the contents of the ZIP archive into a directory in your project. Then require the file `autoload.php` to get all classes and dependencies loaded on need.

```php
<?php
require 'path-to-Carbon-directory/autoload.php';

use Carbon\Carbon;

printf("Now: %s", Carbon::now());
```

## Docs

[https://carbon.nesbot.com/docs](https://carbon.nesbot.com/docs)

## Security contact information

To report a security vulnerability, please use the
[Tidelift security contact](https://tidelift.com/security).
Tidelift will coordinate the fix and disclosure.

## Credits

### Contributors

This project exists thanks to all the people who contribute. 

<a href="https://github.com/briannesbitt/Carbon/graphs/contributors" target="_blank"><img src="https://opencollective.com/Carbon/contributors.svg?width=890&button=false" /></a>

### Translators

[Thanks to people helping us to translate Carbon in so many languages](https://carbon.nesbot.com/contribute/translators/)

### Sponsors

Support this project by becoming a sponsor. Your logo will show up here with a link to your website.

<a href="https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme" target="_blank"><img src="https://carbon.nesbot.com/tidelift-brand.png" width="256" height="64"></a>
<a href="https://onlinecasinohex.ca/?utm_source=opencollective&amp;utm_medium=github&amp;utm_campaign=Carbon" target="_blank"><img src="https://images.opencollective.com/hexcasinoca/2da3af2/logo/256.png" width="85" height="64"></a>
<a href="https://opencollective.com/Carbon/sponsor/0/website" target="_blank"><img src="https://opencollective.com/Carbon/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/Carbon/sponsor/1/website" target="_blank"><img src="https://opencollective.com/Carbon/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/Carbon/sponsor/2/website" target="_blank"><img src="https://opencollective.com/Carbon/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/Carbon/sponsor/3/website" target="_blank"><img src="https://opencollective.com/Carbon/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/Carbon/sponsor/4/website" target="_blank"><img src="https://opencollective.com/Carbon/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/Carbon/sponsor/5/website" target="_blank"><img src="https://opencollective.com/Carbon/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/Carbon/sponsor/6/website" target="_blank"><img src="https://opencollective.com/Carbon/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/Carbon/sponsor/7/website" target="_blank"><img src="https://opencollective.com/Carbon/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/Carbon/sponsor/8/website" target="_blank"><img src="https://opencollective.com/Carbon/sponsor/8/avatar.svg"></a>

[[Become a sponsor](https://opencollective.com/Carbon#sponsor)]

### Backers

Thank you to all our backers! 🙏

<a href="https://opencollective.com/Carbon#backers" target="_blank"><img src="https://opencollective.com/Carbon/backers.svg?width=890"></a>

[[Become a backer](https://opencollective.com/Carbon#backer)]

## Carbon for enterprise

Available as part of the Tidelift Subscription.

The maintainers of ``Carbon`` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)

---

### Overview (`vendor/phpmailer/phpmailer/README.md`)

[](https://supportukrainenow.org/)



# PHPMailer – A full-featured email creation and transfer class for PHP

[](https://github.com/PHPMailer/PHPMailer/actions)
[](https://codecov.io/gh/PHPMailer/PHPMailer)
[](https://packagist.org/packages/phpmailer/phpmailer)
[](https://packagist.org/packages/phpmailer/phpmailer)
[](https://packagist.org/packages/phpmailer/phpmailer)
[](https://phpmailer.github.io/PHPMailer/)
[](https://api.securityscorecards.dev/projects/github.com/PHPMailer/PHPMailer)

## Features
- Probably the world's most popular code for sending email from PHP!
- Used by many open-source projects: WordPress, Drupal, 1CRM, SugarCRM, Yii, Joomla! and many more
- Integrated SMTP support – send without a local mail server
- Send emails with multiple To, CC, BCC and Reply-to addresses
- Multipart/alternative emails for mail clients that do not read HTML email
- Add attachments, including inline
- Support for UTF-8 content and 8bit, base64, binary, and quoted-printable encodings
- SMTP authentication with LOGIN, PLAIN, CRAM-MD5, and XOAUTH2 mechanisms over SMTPS and SMTP+STARTTLS transports
- Validates email addresses automatically
- Protects against header injection attacks
- Error messages in over 50 languages!
- DKIM and S/MIME signing support
- Compatible with PHP 5.5 and later, including PHP 8.1
- Namespaced to prevent name clashes
- Much more!

## Why you might need it
Many PHP developers need to send email from their code. The only PHP function that supports this directly is [`mail()`](https://www.php.net/manual/en/function.mail.php). However, it does not provide any assistance for making use of popular features such as encryption, authentication, HTML messages, and attachments.

Formatting email correctly is surprisingly difficult. There are myriad overlapping (and conflicting) standards, requiring tight adherence to horribly complicated formatting and encoding rules – the vast majority of code that you'll find online that uses the `mail()` function directly is just plain wrong, if not unsafe!

The PHP `mail()` function usually sends via a local mail server, typically fronted by a `sendmail` binary on Linux, BSD, and macOS platforms, however, Windows usually doesn't include a local mail server; PHPMailer's integrated SMTP client allows email sending on all platforms without needing a local mail server. Be aware though, that the `mail()` function should be avoided when possible; it's both faster and [safer](https://exploitbox.io/paper/Pwning-PHP-Mail-Function-For-Fun-And-RCE.html) to use SMTP to localhost.

*Please* don't be tempted to do it yourself – if you don't use PHPMailer, there are many other excellent libraries that
you should look at before rolling your own. Try [SwiftMailer](https://swiftmailer.symfony.com/)
, [Laminas/Mail](https://docs.laminas.dev/laminas-mail/), [ZetaComponents](https://github.com/zetacomponents/Mail) etc.

## License
This software is distributed under the [LGPL 2.1](http://www.gnu.org/licenses/lgpl-2.1.html) license, along with the [GPL Cooperation Commitment](https://gplcc.github.io/gplcc/). Please read [LICENSE](https://github.com/PHPMailer/PHPMailer/blob/master/LICENSE) for information on the software availability and distribution.

## Installation & loading
PHPMailer is available on [Packagist](https://packagist.org/packages/phpmailer/phpmailer) (using semantic versioning), and installation via [Composer](https://getcomposer.org) is the recommended way to install PHPMailer. Just add this line to your `composer.json` file:

```json
"phpmailer/phpmailer": "^6.5"
```

or run

```sh
composer require phpmailer/phpmailer
```

Note that the `vendor` folder and the `vendor/autoload.php` script are generated by Composer; they are not part of PHPMailer.

If you want to use the Gmail XOAUTH2 authentication class, you will also need to add a dependency on the `league/oauth2-client` package in your `composer.json`.

Alternatively, if you're not using Composer, you
can [download PHPMailer as a zip file](https://github.com/PHPMailer/PHPMailer/archive/master.zip), (note that docs and examples are not included in the zip file), then copy the contents of the PHPMailer folder into one of the `include_path` directories specified in your PHP configuration and load each class file manually:

```php
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

require 'path/to/PHPMailer/src/Exception.php';
require 'path/to/PHPMailer/src/PHPMailer.php';
require 'path/to/PHPMailer/src/SMTP.php';
```

If you're not using the `SMTP` class explicitly (you're probably not), you don't need a `use` line for the SMTP class. Even if you're not using exceptions, you do still need to load the `Exception` class as it is used internally.

## Legacy versions
PHPMailer 5.2 (which is compatible with PHP 5.0 — 7.0) is no longer supported, even for security updates. You will find the latest version of 5.2 in the [5.2-stable branch](https://github.com/PHPMailer/PHPMailer/tree/5.2-stable). If you're using PHP 5.5 or later (which you should be), switch to the 6.x releases.

### Upgrading from 5.2
The biggest changes are that source files are now in the `src/` folder, and PHPMailer now declares the namespace `PHPMailer\PHPMailer`. This has several important effects – [read the upgrade guide](https://github.com/PHPMailer/PHPMailer/tree/master/UPGRADING.md) for more details.

### Minimal installation
While installing the entire package manually or with Composer is simple, convenient, and reliable, you may want to include only vital files in your project. At the very least you will need [src/PHPMailer.php](https://github.com/PHPMailer/PHPMailer/tree/master/src/PHPMailer.php). If you're using SMTP, you'll need [src/SMTP.php](https://github.com/PHPMailer/PHPMailer/tree/master/src/SMTP.php), and if you're using POP-before SMTP (*very* unlikely!), you'll need [src/POP3.php](https://github.com/PHPMailer/PHPMailer/tree/master/src/POP3.php). You can skip the [language](https://github.com/PHPMailer/PHPMailer/tree/master/language/) folder if you're not showing errors to users and can make do with English-only errors. If you're using XOAUTH2 you will need [src/OAuth.php](https://github.com/PHPMailer/PHPMailer/tree/master/src/OAuth.php) as well as the Composer dependencies for the services you wish to authenticate with. Really, it's much easier to use Composer!

## A Simple Example

```
/* Detailed source-code truncated for AI context efficiency. */
```

You'll find plenty to play with in the [examples](https://github.com/PHPMailer/PHPMailer/tree/master/examples) folder, which covers many common scenarios including sending through gmail, building contact forms, sending to mailing lists, and more.

If you are re-using the instance (e.g. when sending to a mailing list), you may need to clear the recipient list to avoid sending duplicate messages. See [the mailing list example](https://github.com/PHPMailer/PHPMailer/blob/master/examples/mailing_list.phps) for further guidance.

That's it. You should now be ready to use PHPMailer!

## Localization
PHPMailer defaults to English, but in the [language](https://github.com/PHPMailer/PHPMailer/tree/master/language/) folder you'll find many translations for PHPMailer error messages that you may encounter. Their filenames contain [ISO 639-1](http://en.wikipedia.org/wiki/ISO_639-1) language code for the translations, for example `fr` for French. To specify a language, you need to tell PHPMailer which one to use, like this:

```php
//To load the French version
$mail->setLanguage('fr', '/optional/path/to/language/directory/');
```

We welcome corrections and new languages – if you're looking for corrections, run the [PHPMailerLangTest.php](https://github.com/PHPMailer/PHPMailer/tree/master/test/PHPMailerLangTest.php) script in the tests folder and it will show any missing translations.

## Documentation
Start reading at the [GitHub wiki](https://github.com/PHPMailer/PHPMailer/wiki). If you're having trouble, head for [the troubleshooting guide](https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting) as it's frequently updated.

Examples of how to use PHPMailer for common scenarios can be found in the [examples](https://github.com/PHPMailer/PHPMailer/tree/master/examples) folder. If you're looking for a good starting point, we recommend you start with [the Gmail example](https://github.com/PHPMailer/PHPMailer/tree/master/examples/gmail.phps).

To reduce PHPMailer's deployed code footprint, examples are not included if you load PHPMailer via Composer or via [GitHub's zip file download](https://github.com/PHPMailer/PHPMailer/archive/master.zip), so you'll need to either clone the git repository or use the above links to get to the examples directly.

Complete generated API documentation is [available online](https://phpmailer.github.io/PHPMailer/).

You can generate complete API-level documentation by running `phpdoc` in the top-level folder, and documentation will appear in the `docs` folder, though you'll need to have [PHPDocumentor](http://www.phpdoc.org) installed. You may find [the unit tests](https://github.com/PHPMailer/PHPMailer/blob/master/test/PHPMailerTest.php) a good reference for how to do various operations such as encryption.

If the documentation doesn't cover what you need, search the [many questions on Stack Overflow](http://stackoverflow.com/questions/tagged/phpmailer), and before you ask a question about "SMTP Error: Could not connect to SMTP host.", [read the troubleshooting guide](https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting).

## Tests
[PHPMailer tests](https://github.com/PHPMailer/PHPMailer/tree/master/test/) use PHPUnit 9, with [a polyfill](https://github.com/Yoast/PHPUnit-Polyfills) to let 9-style tests run on older PHPUnit and PHP versions.

[](https://github.com/PHPMailer/PHPMailer/actions)

If this isn't passing, is there something you can do to help?

## Security
Please disclose any vulnerabilities found responsibly – report security issues to the maintainers privately.

See [SECURITY](https://github.com/PHPMailer/PHPMailer/tree/master/SECURITY.md) and [PHPMailer's security advisories on GitHub](https://github.com/PHPMailer/PHPMailer/security). 

## Contributing
Please submit bug reports, suggestions and pull requests to the [GitHub issue tracker](https://github.com/PHPMailer/PHPMailer/issues).

We're particularly interested in fixing edge-cases, expanding test coverage and updating translations.

If you found a mistake in the docs, or want to add something, go ahead and amend the wiki – anyone can edit it.

If you have git clones from prior to the move to the PHPMailer GitHub organisation, you'll need to update any remote URLs referencing the old GitHub location with a command like this from within your clone:

```sh
git remote set-url upstream https://github.com/PHPMailer/PHPMailer.git
```

Please *don't* use the SourceForge or Google Code projects any more; they are obsolete and no longer maintained.

## Sponsorship
Development time and resources for PHPMailer are provided by [Smartmessages.net](https://info.smartmessages.net/), the world's only privacy-first email marketing system.

<a href="https://info.smartmessages.net/"><img src="https://www.smartmessages.net/img/smartmessages-logo.svg" width="550" alt="Smartmessages.net privacy-first email marketing logo"></a>

Donations are very welcome, whether in beer 🍺, T-shirts 👕, or cold, hard cash 💰. Sponsorship through GitHub is a simple and convenient way to say "thank you" to PHPMailer's maintainers and contributors – just click the "Sponsor" button [on the project page](https://github.com/PHPMailer/PHPMailer). If your company uses PHPMailer, consider taking part in Tidelift's enterprise support programme.

## PHPMailer For Enterprise

Available as part of the Tidelift Subscription.

The maintainers of PHPMailer and thousands of other packages are working with Tidelift to deliver commercial
support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and
improve code health, while paying the maintainers of the exact packages you
use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-phpmailer-phpmailer?utm_source=packagist-phpmailer-phpmailer&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)

## Changelog
See [changelog](changelog.md).

## History
- PHPMailer was originally written in 2001 by Brent R. Matzelle as a [SourceForge project](http://sourceforge.net/projects/phpmailer/).
- [Marcus Bointon](https://github.com/Synchro) (`coolbru` on SF) and Andy Prevost (`codeworxtech`) took over the project in 2004.
- Became an Apache incubator project on Google Code in 2010, managed by Jim Jagielski.
- Marcus created [his fork on GitHub](https://github.com/Synchro/PHPMailer) in 2008.
- Jim and Marcus decide to join forces and use GitHub as the canonical and official repo for PHPMailer in 2013.
- PHPMailer moves to [the PHPMailer organisation](https://github.com/PHPMailer) on GitHub in 2013.

### What's changed since moving from SourceForge?
- Official successor to the SourceForge and Google Code projects.
- Test suite.
- Continuous integration with Github Actions.
- Composer support.
- Public development.
- Additional languages and language strings.
- CRAM-MD5 authentication support.
- Preserves full repo history of authors, commits and branches from the original SourceForge project.

---

### Overview (`vendor/psr/container/README.md`)

Container interface
==============

This repository holds all interfaces related to [PSR-11 (Container Interface)][psr-url].

Note that this is not a Container implementation of its own. It is merely abstractions that describe the components of a Dependency Injection Container.

The installable [package][package-url] and [implementations][implementation-url] are listed on Packagist.

[psr-url]: https://www.php-fig.org/psr/psr-11/
[package-url]: https://packagist.org/packages/psr/container
[implementation-url]: https://packagist.org/providers/psr/container-implementation

---

### Overview (`vendor/psr/http-client/README.md`)

HTTP Client
===========

This repository holds all the common code related to [PSR-18 (HTTP Client)][psr-url].

Note that this is not a HTTP Client implementation of its own. It is merely abstractions that describe the components of a HTTP Client.

The installable [package][package-url] and [implementations][implementation-url] are listed on Packagist.

[psr-url]: http://www.php-fig.org/psr/psr-18
[package-url]: https://packagist.org/packages/psr/http-client
[implementation-url]: https://packagist.org/providers/psr/http-client-implementation

---

### Overview (`vendor/psr/http-factory/README.md`)

HTTP Factories
==============

This repository holds all interfaces related to [PSR-17 (HTTP Message Factories)][psr-17]. 
Please refer to the specification for a description.

You can find implementations of the specification by looking for packages providing the 
[psr/http-factory-implementation](https://packagist.org/providers/psr/http-factory-implementation) virtual package.

[psr-17]: https://www.php-fig.org/psr/psr-17/

---

### Overview (`vendor/psr/http-message/README.md`)

PSR Http Message
================

This repository holds all interfaces/classes/traits related to
[PSR-7](http://www.php-fig.org/psr/psr-7/).

Note that this is not a HTTP message implementation of its own. It is merely an
interface that describes a HTTP message. See the specification for more details.

Usage
-----

We'll certainly need some stuff in here.

---

### Overview (`vendor/psr/simple-cache/README.md`)

PHP FIG Simple Cache PSR
========================

This repository holds all interfaces related to PSR-16.

Note that this is not a cache implementation of its own. It is merely an interface that describes a cache implementation. See [the specification](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-16-simple-cache.md) for more details.

You can find implementations of the specification by looking for packages providing the [psr/simple-cache-implementation](https://packagist.org/providers/psr/simple-cache-implementation) virtual package.

---

### Overview (`vendor/rah/danpu/README.md`)

Danpu - MySQL dump tool for PHP
=========

[Packagist](https://packagist.org/packages/rah/danpu) | [Downloads](https://github.com/gocom/danpu/releases) | [Issues](https://github.com/gocom/danpu/issues) | [Donate](https://rahforum.biz/donate/danpu)

[](https://travis-ci.org/gocom/danpu) [](https://coveralls.io/r/gocom/danpu)

Danpu is a dependency-free, cross-platform, portable PHP library for backing up MySQL databases. It has no hard dependencies, and is fit for restricted environments where security is key and access is limited. Danpu requires nothing more than access to your database, PDO and a directory it can write the backup to. The script is optimized and has low memory-footprint, allowing it to handle even larger databases.

Danpu supports backing up table structures, the data itself, views and triggers. Created dump files can optionally be compressed to save space, and generated SQL output is optimized for compatibility.

Requirements
---------

Minimum:

* PHP 5.3.0 or newer
* MySQL 4.1.0 or newer
* [PDO](https://php.net/pdo)

Recommended, but optional:

* PHP 5.4.0 or newer
* MySQL 5.0.11 or newer
* [zlib](https://www.php.net/manual/en/book.zlib.php)

Backing up views and triggers requires MySQL 5.0.11 or newer.

Install
---------

Using [Composer](https://getcomposer.org):

    $ composer require rah/danpu

Usage
---------

To create a new backup or import one, configure a new ```Dump``` instance and pass it to one of the worker classes. To begin, first make sure you have included Composer's autoloader file in your project:

```php
require './vendor/autoload.php';
```

If you are already using other Composer packages, or a modern Composer-managed framework, this should be taken care of already. If not, merely add the autoloader to your base bootstrap includes. See [Composer's documentation](https://getcomposer.org) for more information.

### Take a backup

Backups can be created with the ```Export``` class. The class exports the database to a SQL file, or throws exceptions on errors. The file is compressed if the target filename ends to a .gz extension.

```php
use Rah\Danpu\Dump;
use Rah\Danpu\Export;

try {
    $dump = new Dump;
    $dump
        ->file('/path/to/target/dump/file.sql')
        ->dsn('mysql:dbname=database;host=localhost')
        ->user('username')
        ->pass('password')
        ->tmp('/tmp');

    new Export($dump);
} catch (\Exception $e) {
    echo 'Export failed with message: ' . $e->getMessage();
}
```

The database is dumped in chunks, one row at the time without buffering huge amount of data to the memory. This makes the script very memory efficient, and can allow Danpu to handle databases of any size, given the system limitations of course. You physically won't be able backup rows that take more memory than can be allocated to PHP, nor write backups if there isn't enough space for the files.

### Import a backup

Danpu can also import its own backups using the Import class. While the importer works with backups it has made, it doesn't accept freely formatted SQL. The importer is pretty strict about formatting, and expects exactly the same format as generated by Danpu. It expects that values in statements are escaped properly, including newlines, queries have to end to a semicolon and statements preferably should not wrap to multiple lines.

To import a backup, create a new instance of the Import class. It uncompresses any .gz files before importing.

```php
use Rah\Danpu\Dump;
use Rah\Danpu\Import;

try {
    $dump = new Dump;
    $dump
        ->file('/path/to/imported/file.sql')
        ->dsn('mysql:dbname=database;host=localhost')
        ->user('username')
        ->pass('password')
        ->tmp('/tmp');

    new Import($dump);
} catch (\Exception $e) {
    echo 'Import failed with message: ' . $e->getMessage();
}
```

### Options

In addition to the mandatory connection and file location, Danpu accepts various optional configuration options. These include filtering tables and views by prefix, ignoring tables and creating dumps without row data. See the [src/Rah/Danpu/Config.php](https://github.com/gocom/danpu/blob/master/src/Rah/Danpu/Config.php) for full list of options. The source file contains detailed documentation blocks, outlining each option.

Troubleshooting
---------

### Running out of memory, backup taking too long

As with any PHP script, Danpu is restricted by the safe limits you have set for PHP. When dealing with larger databases, taking a backup will take longer and require more memory. If you find yourself hitting the maximum execution time or running out of memory, its time to increase the limits enough to let the script to work.

PHP lets you to change these limits with its [configuration options](https://php.net/manual/en/ini.core.php), which can be modified [temporarily during script execution](https://php.net/manual/en/function.ini-set.php), or in global configuration files. The configuration options you will be the most interested in, are [memory_limit](https://www.php.net/manual/en/ini.core.php#ini.memory-limit) and [max_execution_time](https://www.php.net/manual/en/info.configuration.php#ini.max-execution-time), and possibly [ignore_user_abort](https://php.net/manual/en/function.ignore-user-abort.php). You can change these values in your script before running a backup with Danpu:

```php
ini_set('memory_limit', '256M');
set_time_limit(0);
ignore_user_abort(true);
```

This of course requires that you have access to these values and you actually have more memory to give. Keep in mind that PHP may be affected by other limitations, such as your web server.

---

### Overview (`vendor/ralouphie/getallheaders/README.md`)

getallheaders
=============

PHP `getallheaders()` polyfill. Compatible with PHP >= 5.3.

[](https://travis-ci.org/ralouphie/getallheaders)
[](https://coveralls.io/r/ralouphie/getallheaders?branch=master)
[](https://packagist.org/packages/ralouphie/getallheaders)
[](https://packagist.org/packages/ralouphie/getallheaders)
[](https://packagist.org/packages/ralouphie/getallheaders)


This is a simple polyfill for [`getallheaders()`](http://www.php.net/manual/en/function.getallheaders.php).

## Install

For PHP version **`>= 5.6`**:

```
composer require ralouphie/getallheaders
```

For PHP version **`< 5.6`**:

```
composer require ralouphie/getallheaders "^2"
```

---

### Overview (`vendor/smarty/smarty/README.md`)

# Smarty 3 template engine
[smarty.net](https://www.smarty.net/) 

[](https://travis-ci.org/smarty-php/smarty)

## Documentation

For documentation see 
[www.smarty.net/docs/en/](https://www.smarty.net/docs/en/) 

## Requirements

Smarty can be run with PHP 5.2 to PHP 7.4.

## Distribution repository

> Smarty 3.1.28 introduces run time template inheritance

> Read the NEW_FEATURES and INHERITANCE_RELEASE_NOTES file for recent extensions to Smarty 3.1 functionality

Smarty versions 3.1.11 or later are now on GitHub and can be installed with Composer.


The "smarty/smarty" package will start at libs/....   subfolder.

To get the latest stable version of Smarty 3.1 use:

```json
"require": {
    "smarty/smarty": "~3.1"
}
```

in your composer.json file.

To get the trunk version use:

```json
"require": {
    "smarty/smarty": "~3.1@dev"
}
```

For a specific version use something like:

```json
"require": {
    "smarty/smarty": "3.1.19"
}
```

PHPUnit test can be installed by corresponding composer entries like:

```json
"require": {
    "smarty/smarty-phpunit": "3.1.19"
}
```

Similar applies for the lexer/parser generator.

```json
"require": {
    "smarty/smarty-lexer": "3.1.19"
}
```

Or you could use:

```json
"require": {
    "smarty/smarty-dev": "3.1.19"
}
```

Which is a wrapper to install all 3 packages.

Composer can also be used for Smarty2 versions 2.6.24 to 2.6.30.

---

### Overview (`vendor/symfony/console/README.md`)

Console Component
=================

The Console component eases the creation of beautiful and testable command line
interfaces.

Sponsor
-------

The Console component for Symfony 5.4/6.0 is [backed][1] by [Les-Tilleuls.coop][2].

Les-Tilleuls.coop is a team of 50+ Symfony experts who can help you design, develop and
fix your projects. We provide a wide range of professional services including development,
consulting, coaching, training and audits. We also are highly skilled in JS, Go and DevOps.
We are a worker cooperative!

Help Symfony by [sponsoring][3] its development!

Resources
---------

 * [Documentation](https://symfony.com/doc/current/components/console.html)
 * [Contributing](https://symfony.com/doc/current/contributing/index.html)
 * [Report issues](https://github.com/symfony/symfony/issues) and
   [send Pull Requests](https://github.com/symfony/symfony/pulls)
   in the [main Symfony repository](https://github.com/symfony/symfony)

Credits
-------

`Resources/bin/hiddeninput.exe` is a third party binary provided within this
component. Find sources and license at https://github.com/Seldaek/hidden-input.

[1]: https://symfony.com/backers
[2]: https://les-tilleuls.coop
[3]: https://symfony.com/sponsor

---

### Overview (`vendor/symfony/deprecation-contracts/README.md`)

Symfony Deprecation Contracts
=============================

A generic function and convention to trigger deprecation notices.

This package provides a single global function named `trigger_deprecation()` that triggers silenced deprecation notices.

By using a custom PHP error handler such as the one provided by the Symfony ErrorHandler component,
the triggered deprecations can be caught and logged for later discovery, both on dev and prod environments.

The function requires at least 3 arguments:
 - the name of the Composer package that is triggering the deprecation
 - the version of the package that introduced the deprecation
 - the message of the deprecation
 - more arguments can be provided: they will be inserted in the message using `printf()` formatting

Example:
```php
trigger_deprecation('symfony/blockchain', '8.9', 'Using "%s" is deprecated, use "%s" instead.', 'bitcoin', 'fabcoin');
```

This will generate the following message:
`Since symfony/blockchain 8.9: Using "bitcoin" is deprecated, use "fabcoin" instead.`

While not necessarily recommended, the deprecation notices can be completely ignored by declaring an empty
`function trigger_deprecation() {}` in your application.

---

### Overview (`vendor/symfony/polyfill-ctype/README.md`)

Symfony Polyfill / Ctype
========================

This component provides `ctype_*` functions to users who run php versions without the ctype extension.

More information can be found in the
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).

License
=======

This library is released under the [MIT license](LICENSE).

---

### Overview (`vendor/symfony/polyfill-intl-grapheme/README.md`)

Symfony Polyfill / Intl: Grapheme
=================================

This component provides a partial, native PHP implementation of the
[Grapheme functions](https://php.net/intl.grapheme) from the
[Intl](https://php.net/intl) extension.

- [`grapheme_extract`](https://php.net/grapheme_extract): Extract a sequence of grapheme
  clusters from a text buffer, which must be encoded in UTF-8
- [`grapheme_stripos`](https://php.net/grapheme_stripos): Find position (in grapheme units)
  of first occurrence of a case-insensitive string
- [`grapheme_stristr`](https://php.net/grapheme_stristr): Returns part of haystack string
  from the first occurrence of case-insensitive needle to the end of haystack
- [`grapheme_strlen`](https://php.net/grapheme_strlen): Get string length in grapheme units
- [`grapheme_strpos`](https://php.net/grapheme_strpos): Find position (in grapheme units)
  of first occurrence of a string
- [`grapheme_strripos`](https://php.net/grapheme_strripos): Find position (in grapheme units)
  of last occurrence of a case-insensitive string
- [`grapheme_strrpos`](https://php.net/grapheme_strrpos): Find position (in grapheme units)
  of last occurrence of a string
- [`grapheme_strstr`](https://php.net/grapheme_strstr): Returns part of haystack string from
  the first occurrence of needle to the end of haystack
- [`grapheme_substr`](https://php.net/grapheme_substr): Return part of a string

More information can be found in the
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).

License
=======

This library is released under the [MIT license](LICENSE).

---

### Overview (`vendor/symfony/polyfill-intl-normalizer/README.md`)

Symfony Polyfill / Intl: Normalizer
===================================

This component provides a fallback implementation for the
[`Normalizer`](https://php.net/Normalizer) class provided
by the [Intl](https://php.net/intl) extension.

More information can be found in the
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).

License
=======

This library is released under the [MIT license](LICENSE).

---

### Overview (`vendor/symfony/polyfill-mbstring/README.md`)

Symfony Polyfill / Mbstring
===========================

This component provides a partial, native PHP implementation for the
[Mbstring](https://php.net/mbstring) extension.

More information can be found in the
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).

License
=======

This library is released under the [MIT license](LICENSE).

---

### Overview (`vendor/symfony/polyfill-php73/README.md`)

Symfony Polyfill / Php73
========================

This component provides functions added to PHP 7.3 core:

- [`array_key_first`](https://php.net/array_key_first)
- [`array_key_last`](https://php.net/array_key_last)
- [`hrtime`](https://php.net/function.hrtime)
- [`is_countable`](https://php.net/is_countable)
- [`JsonException`](https://php.net/JsonException)

More information can be found in the
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).

License
=======

This library is released under the [MIT license](LICENSE).

---

### Overview (`vendor/symfony/polyfill-php80/README.md`)

Symfony Polyfill / Php80
========================

This component provides features added to PHP 8.0 core:

- [`Stringable`](https://php.net/stringable) interface
- [`fdiv`](https://php.net/fdiv)
- [`ValueError`](https://php.net/valueerror) class
- [`UnhandledMatchError`](https://php.net/unhandledmatcherror) class
- `FILTER_VALIDATE_BOOL` constant
- [`get_debug_type`](https://php.net/get_debug_type)
- [`PhpToken`](https://php.net/phptoken) class
- [`preg_last_error_msg`](https://php.net/preg_last_error_msg)
- [`str_contains`](https://php.net/str_contains)
- [`str_starts_with`](https://php.net/str_starts_with)
- [`str_ends_with`](https://php.net/str_ends_with)
- [`get_resource_id`](https://php.net/get_resource_id)

More information can be found in the
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).

License
=======

This library is released under the [MIT license](LICENSE).

---

### Overview (`vendor/symfony/service-contracts/README.md`)

Symfony Service Contracts
=========================

A set of abstractions extracted out of the Symfony components.

Can be used to build on semantics that the Symfony components proved useful - and
that already have battle tested implementations.

See https://github.com/symfony/contracts/blob/main/README.md for more information.

---

### Overview (`vendor/symfony/string/README.md`)

String Component
================

The String component provides an object-oriented API to strings and deals
with bytes, UTF-8 code points and grapheme clusters in a unified way.

Resources
---------

 * [Documentation](https://symfony.com/doc/current/components/string.html)
 * [Contributing](https://symfony.com/doc/current/contributing/index.html)
 * [Report issues](https://github.com/symfony/symfony/issues) and
   [send Pull Requests](https://github.com/symfony/symfony/pulls)
   in the [main Symfony repository](https://github.com/symfony/symfony)

---

### Overview (`vendor/symfony/translation-contracts/README.md`)

Symfony Translation Contracts
=============================

A set of abstractions extracted out of the Symfony components.

Can be used to build on semantics that the Symfony components proved useful - and
that already have battle tested implementations.

See https://github.com/symfony/contracts/blob/main/README.md for more information.

---

### Overview (`vendor/symfony/translation/README.md`)

Translation Component
=====================

The Translation component provides tools to internationalize your application.

Getting Started
---------------

```
$ composer require symfony/translation
```

```php
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\Loader\ArrayLoader;

$translator = new Translator('fr_FR');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', [
    'Hello World!' => 'Bonjour !',
], 'fr_FR');

echo $translator->trans('Hello World!'); // outputs « Bonjour ! »
```

Sponsor
-------

The Translation component for Symfony 5.4/6.0 is [backed][1] by:

 * [Crowdin][2], a cloud-based localization management software helping teams to go global and stay agile.
 * [Lokalise][3], a continuous localization and translation management platform that integrates into your development workflow so you can ship localized products, faster.

Help Symfony by [sponsoring][4] its development!

Resources
---------

 * [Documentation](https://symfony.com/doc/current/translation.html)
 * [Contributing](https://symfony.com/doc/current/contributing/index.html)
 * [Report issues](https://github.com/symfony/symfony/issues) and
   [send Pull Requests](https://github.com/symfony/symfony/pulls)
   in the [main Symfony repository](https://github.com/symfony/symfony)

[1]: https://symfony.com/backers
[2]: https://crowdin.com
[3]: https://lokalise.com
[4]: https://symfony.com/sponsor

---

### Overview (`vendor/symfony/var-dumper/README.md`)

VarDumper Component
===================

The VarDumper component provides mechanisms for walking through any arbitrary
PHP variable. It provides a better `dump()` function that you can use instead
of `var_dump()`.

Resources
---------

 * [Documentation](https://symfony.com/doc/current/components/var_dumper/introduction.html)
 * [Contributing](https://symfony.com/doc/current/contributing/index.html)
 * [Report issues](https://github.com/symfony/symfony/issues) and
   [send Pull Requests](https://github.com/symfony/symfony/pulls)
   in the [main Symfony repository](https://github.com/symfony/symfony)

---

### Overview (`vendor/voku/portable-ascii/README.md`)

[//]: # (AUTO-GENERATED BY "PHP README Helper": base file -> docs/base.md)
[](https://github.com/voku/portable-ascii/actions)
[](https://ci.appveyor.com/project/voku/portable-ascii/branch/master)
[](https://codecov.io/github/voku/portable-ascii?branch=master)
[](https://www.codacy.com/app/voku/portable-ascii)
[](https://packagist.org/packages/voku/portable-ascii) 
[](https://packagist.org/packages/voku/portable-ascii)
[](https://packagist.org/packages/voku/portable-ascii)
[](https://www.paypal.me/moelleken)
[](https://www.patreon.com/voku)

# 🔡 Portable ASCII

## Description

It is written in PHP (PHP 7+) and can work without "mbstring", "iconv" or any other extra encoding php-extension on your server. 

The benefit of Portable ASCII is that it is easy to use, easy to bundle.

The project based on ...
+ Sean M. Burke's work (https://metacpan.org/pod/Text::Unidecode)
+ Tomaz Solc's work (https://pypi.org/project/Unidecode/)
+ Portable UTF-8 work (https://github.com/voku/portable-utf8) 
+ Daniel St. Jules's work (https://github.com/danielstjules/Stringy) 
+ Johnny Broadway's work (https://github.com/jbroadway/urlify)
+ and many cherry-picks from "github"-gists and "Stack Overflow"-snippets ...

## Index

* [Alternative](#alternative)
* [Install](#install-portable-ascii-via-composer-require)
* [Why Portable ASCII?](#why-portable-ascii)
* [Requirements and Recommendations](#requirements-and-recommendations)
* [Usage](#usage)
* [Class methods](#class-methods)
* [Unit Test](#unit-test)
* [License and Copyright](#license-and-copyright)

## Alternative

If you like a more Object Oriented Way to edit strings, then you can take a look at [voku/Stringy](https://github.com/voku/Stringy), it's a fork of "danielstjules/Stringy" but it used the "Portable ASCII"-Class and some extra methods. 

```php
// Portable ASCII
use voku\helper\ASCII;
ASCII::to_transliterate('déjà σσς iıii'); // 'deja sss iiii'

// voku/Stringy
use Stringy\Stringy as S;
$stringy = S::create('déjà σσς iıii');
$stringy->toTransliterate();              // 'deja sss iiii'
```

## Install "Portable ASCII" via "composer require"
```shell
composer require voku/portable-ascii
```

##  Why Portable ASCII?[]()
I need ASCII char handling in different classes and before I added this functions into "Portable UTF-8",
but this repo is more modular and portable, because it has no dependencies.

## Requirements and Recommendations

*   No extensions are required to run this library. Portable ASCII only needs PCRE library that is available by default since PHP 4.2.0 and cannot be disabled since PHP 5.3.0. "\u" modifier support in PCRE for ASCII handling is not a must.
*   PHP 7.0 is the minimum requirement
*   PHP 8.0 is also supported

## Usage

Example: ASCII::to_ascii()
```php
  echo ASCII::to_ascii('�Düsseldorf�', 'de');
  
  // will output
  // Duesseldorf

  echo ASCII::to_ascii('�Düsseldorf�', 'en');
  
  // will output
  // Dusseldorf
```

# Portable ASCII | API

The API from the "ASCII"-Class is written as small static methods.


## Class methods

<p id="voku-php-readme-class-methods"></p><table><tr><td><a href="#charsarraybool-replace_extra_symbols-array">charsArray</a>
</td><td><a href="#charsarraywithmultilanguagevaluesbool-replace_extra_symbols-array">charsArrayWithMultiLanguageValues</a>
</td><td><a href="#charsarraywithonelanguagestring-language-bool-replace_extra_symbols-bool-asorigreplacearray-array">charsArrayWithOneLanguage</a>
</td><td><a href="#charsarraywithsinglelanguagevaluesbool-replace_extra_symbols-bool-asorigreplacearray-array">charsArrayWithSingleLanguageValues</a>
</td></tr><tr><td><a href="#cleanstring-str-bool-normalize_whitespace-bool-keep_non_breaking_space-bool-normalize_msword-bool-remove_invisible_characters-string">clean</a>
</td><td><a href="#getalllanguages-string">getAllLanguages</a>
</td><td><a href="#is_asciistring-str-bool">is_ascii</a>
</td><td><a href="#normalize_mswordstring-str-string">normalize_msword</a>
</td></tr><tr><td><a href="#normalize_whitespacestring-str-bool-keepnonbreakingspace-bool-keepbidiunicodecontrols-bool-normalize_control_characters-string">normalize_whitespace</a>
</td><td><a href="#remove_invisible_charactersstring-str-bool-url_encoded-string-replacement-bool-keep_basic_control_characters-string">remove_invisible_characters</a>
</td><td><a href="#to_asciistring-str-string-language-bool-remove_unsupported_chars-bool-replace_extra_symbols-bool-use_transliterate-boolnull-replace_single_chars_only-string">to_ascii</a>
</td><td><a href="#to_filenamestring-str-bool-use_transliterate-string-fallback_char-string">to_filename</a>
</td></tr><tr><td><a href="#to_slugifystring-str-string-separator-string-language-string-replacements-bool-replace_extra_symbols-bool-use_str_to_lower-bool-use_transliterate-string">to_slugify</a>
</td><td><a href="#to_transliteratestring-str-stringnull-unknown-bool-strict-string">to_transliterate</a>
</td></tr></table>

#### charsArray(bool $replace_extra_symbols): array
<a href="#voku-php-readme-class-methods">↑</a>
Returns an replacement array for ASCII methods.

EXAMPLE: <code>
$array = ASCII::charsArray();
var_dump($array['ru']['б']); // 'b'
</code>

**Parameters:**
- `bool $replace_extra_symbols [optional] <p>Add some more replacements e.g. "£" with " pound ".</p>`

**Return:**
- `array`

--------

#### charsArrayWithMultiLanguageValues(bool $replace_extra_symbols): array
<a href="#voku-php-readme-class-methods">↑</a>
Returns an replacement array for ASCII methods with a mix of multiple languages.

EXAMPLE: <code>
$array = ASCII::charsArrayWithMultiLanguageValues();
var_dump($array['b']); // ['β', 'б', 'ဗ', 'ბ', 'ب']
</code>

**Parameters:**
- `bool $replace_extra_symbols [optional] <p>Add some more replacements e.g. "£" with " pound ".</p>`

**Return:**
- `array <p>An array of replacements.</p>`

--------

#### charsArrayWithOneLanguage(string $language, bool $replace_extra_symbols, bool $asOrigReplaceArray): array
<a href="#voku-php-readme-class-methods">↑</a>
Returns an replacement array for ASCII methods with one language.

For example, German will map 'ä' to 'ae', while other languages
will simply return e.g. 'a'.

EXAMPLE: <code>
$array = ASCII::charsArrayWithOneLanguage('ru');
$tmpKey = \array_search('yo', $array['replace']);
echo $array['orig'][$tmpKey]; // 'ё'
</code>

**Parameters:**
- `string $language [optional] <p>Language of the source string e.g.: en, de_at, or de-ch.
(default is 'en') | ASCII::*_LANGUAGE_CODE</p>`
- `bool $replace_extra_symbols [optional] <p>Add some more replacements e.g. "£" with " pound ".</p>`
- `bool $asOrigReplaceArray [optional] <p>TRUE === return {orig: string[], replace: string[]}
array</p>`

**Return:**
- `array <p>An array of replacements.</p>`

--------

#### charsArrayWithSingleLanguageValues(bool $replace_extra_symbols, bool $asOrigReplaceArray): array
<a href="#voku-php-readme-class-methods">↑</a>
Returns an replacement array for ASCII methods with multiple languages.

EXAMPLE: <code>
$array = ASCII::charsArrayWithSingleLanguageValues();
$tmpKey = \array_search('hnaik', $array['replace']);
echo $array['orig'][$tmpKey]; // '၌'
</code>

**Parameters:**
- `bool $replace_extra_symbols [optional] <p>Add some more replacements e.g. "£" with " pound ".</p>`
- `bool $asOrigReplaceArray [optional] <p>TRUE === return {orig: string[], replace: string[]}
array</p>`

**Return:**
- `array <p>An array of replacements.</p>`

--------

#### clean(string $str, bool $normalize_whitespace, bool $keep_non_breaking_space, bool $normalize_msword, bool $remove_invisible_characters): string
<a href="#voku-php-readme-class-methods">↑</a>
Accepts a string and removes all non-UTF-8 characters from it + extras if needed.

**Parameters:**
- `string $str <p>The string to be sanitized.</p>`
- `bool $normalize_whitespace [optional] <p>Set to true, if you need to normalize the
whitespace.</p>`
- `bool $keep_non_breaking_space [optional] <p>Set to true, to keep non-breaking-spaces, in
combination with
$normalize_whitespace</p>`
- `bool $normalize_msword [optional] <p>Set to true, if you need to normalize MS Word chars
e.g.: "…"
=> "..."</p>`
- `bool $remove_invisible_characters [optional] <p>Set to false, if you not want to remove invisible
characters e.g.: "\0"</p>`

**Return:**
- `string <p>A clean UTF-8 string.</p>`

--------

#### getAllLanguages(): string[]
<a href="#voku-php-readme-class-methods">↑</a>
Get all languages from the constants "ASCII::.*LANGUAGE_CODE".

**Parameters:**
__nothing__

**Return:**
- `string[]`

--------

#### is_ascii(string $str): bool
<a href="#voku-php-readme-class-methods">↑</a>
Checks if a string is 7 bit ASCII.

EXAMPLE: <code>
ASCII::is_ascii('白'); // false
</code>

**Parameters:**
- `string $str <p>The string to check.</p>`

**Return:**
- `bool <p>
<strong>true</strong> if it is ASCII<br>
<strong>false</strong> otherwise
</p>`

--------

#### normalize_msword(string $str): string
<a href="#voku-php-readme-class-methods">↑</a>
Returns a string with smart quotes, ellipsis characters, and dashes from
Windows-1252 (commonly used in Word documents) replaced by their ASCII
equivalents.

EXAMPLE: <code>
ASCII::normalize_msword('„Abcdef…”'); // '"Abcdef..."'
</code>

**Parameters:**
- `string $str <p>The string to be normalized.</p>`

**Return:**
- `string <p>A string with normalized characters for commonly used chars in Word documents.</p>`

--------

#### normalize_whitespace(string $str, bool $keepNonBreakingSpace, bool $keepBidiUnicodeControls, bool $normalize_control_characters): string
<a href="#voku-php-readme-class-methods">↑</a>
Normalize the whitespace.

EXAMPLE: <code>
ASCII::normalize_whitespace("abc-\xc2\xa0-öäü-\xe2\x80\xaf-\xE2\x80\xAC", true); // "abc-\xc2\xa0-öäü- -"
</code>

**Parameters:**
- `string $str <p>The string to be normalized.</p>`
- `bool $keepNonBreakingSpace [optional] <p>Set to true, to keep non-breaking-spaces.</p>`
- `bool $keepBidiUnicodeControls [optional] <p>Set to true, to keep non-printable (for the web)
bidirectional text chars.</p>`
- `bool $normalize_control_characters [optional] <p>Set to true, to convert e.g. LINE-, PARAGRAPH-SEPARATOR with "\n" and LINE TABULATION with "\t".</p>`

**Return:**
- `string <p>A string with normalized whitespace.</p>`

--------

#### remove_invisible_characters(string $str, bool $url_encoded, string $replacement, bool $keep_basic_control_characters): string
<a href="#voku-php-readme-class-methods">↑</a>
Remove invisible characters from a string.

e.g.: This prevents sandwiching null characters between ascii characters, like Java\0script.

copy&past from https://github.com/bcit-ci/CodeIgniter/blob/develop/system/core/Common.php

**Parameters:**
- `string $str`
- `bool $url_encoded`
- `string $replacement`
- `bool $keep_basic_control_characters`

**Return:**
- `string`

--------

#### to_ascii(string $str, string $language, bool $remove_unsupported_chars, bool $replace_extra_symbols, bool $use_transliterate, bool|null $replace_single_chars_only): string
<a href="#voku-php-readme-class-methods">↑</a>
Returns an ASCII version of the string. A set of non-ASCII characters are
replaced with their closest ASCII counterparts, and the rest are removed
by default. The language or locale of the source string can be supplied
for language-specific transliteration in any of the following formats:
en, en_GB, or en-GB. For example, passing "de" results in "äöü" mapping
to "aeoeue" rather than "aou" as in other languages.

EXAMPLE: <code>
ASCII::to_ascii('�Düsseldorf�', 'en'); // Dusseldorf
</code>

**Parameters:**
- `string $str <p>The input string.</p>`
- `string $language [optional] <p>Language of the source string.
(default is 'en') | ASCII::*_LANGUAGE_CODE</p>`
- `bool $remove_unsupported_chars [optional] <p>Whether or not to remove the
unsupported characters.</p>`
- `bool $replace_extra_symbols [optional]  <p>Add some more replacements e.g. "£" with " pound
".</p>`
- `bool $use_transliterate [optional]  <p>Use ASCII::to_transliterate() for unknown chars.</p>`
- `bool|null $replace_single_chars_only [optional]  <p>Single char replacement is better for the
performance, but some languages need to replace more then one char
at the same time. | NULL === auto-setting, depended on the
language</p>`

**Return:**
- `string <p>A string that contains only ASCII characters.</p>`

--------

#### to_filename(string $str, bool $use_transliterate, string $fallback_char): string
<a href="#voku-php-readme-class-methods">↑</a>
Convert given string to safe filename (and keep string case).

EXAMPLE: <code>
ASCII::to_filename('שדגשדג.png', true)); // 'shdgshdg.png'
</code>

**Parameters:**
- `string $str`
- `bool $use_transliterate <p>ASCII::to_transliterate() is used by default - unsafe characters are
simply replaced with hyphen otherwise.</p>`
- `string $fallback_char`

**Return:**
- `string <p>A string that contains only safe characters for a filename.</p>`

--------

#### to_slugify(string $str, string $separator, string $language, string[] $replacements, bool $replace_extra_symbols, bool $use_str_to_lower, bool $use_transliterate): string
<a href="#voku-php-readme-class-methods">↑</a>
Converts the string into an URL slug. This includes replacing non-ASCII
characters with their closest ASCII equivalents, removing remaining
non-ASCII and non-alphanumeric characters, and replacing whitespace with
$separator. The separator defaults to a single dash, and the string
is also converted to lowercase. The language of the source string can
also be supplied for language-specific transliteration.

**Parameters:**
- `string $str`
- `string $separator [optional] <p>The string used to replace whitespace.</p>`
- `string $language [optional] <p>Language of the source string.
(default is 'en') | ASCII::*_LANGUAGE_CODE</p>`
- `array<string, string> $replacements [optional] <p>A map of replaceable strings.</p>`
- `bool $replace_extra_symbols [optional]  <p>Add some more replacements e.g. "£" with "
pound ".</p>`
- `bool $use_str_to_lower [optional] <p>Use "string to lower" for the input.</p>`
- `bool $use_transliterate [optional]  <p>Use ASCII::to_transliterate() for unknown
chars.</p>`

**Return:**
- `string <p>A string that has been converted to an URL slug.</p>`

--------

#### to_transliterate(string $str, string|null $unknown, bool $strict): string
<a href="#voku-php-readme-class-methods">↑</a>
Returns an ASCII version of the string. A set of non-ASCII characters are
replaced with their closest ASCII counterparts, and the rest are removed
unless instructed otherwise.

EXAMPLE: <code>
ASCII::to_transliterate('déjà σσς iıii'); // 'deja sss iiii'
</code>

**Parameters:**
- `string $str <p>The input string.</p>`
- `string|null $unknown [optional] <p>Character use if character unknown. (default is '?')
But you can also use NULL to keep the unknown chars.</p>`
- `bool $strict [optional] <p>Use "transliterator_transliterate()" from PHP-Intl`

**Return:**
- `string <p>A String that contains only ASCII characters.</p>`

--------



## Unit Test

1) [Composer](https://getcomposer.org) is a prerequisite for running the tests.

```
composer install
```

2) The tests can be executed by running this command from the root directory:

```bash
./vendor/bin/phpunit
```

### Support

For support and donations please visit [Github](https://github.com/voku/portable-ascii/) | [Issues](https://github.com/voku/portable-ascii/issues) | [PayPal](https://paypal.me/moelleken) | [Patreon](https://www.patreon.com/voku).

For status updates and release announcements please visit [Releases](https://github.com/voku/portable-ascii/releases) | [Twitter](https://twitter.com/suckup_de) | [Patreon](https://www.patreon.com/voku/posts).

For professional support please contact [me](https://about.me/voku).

### Thanks

- Thanks to [GitHub](https://github.com) (Microsoft) for hosting the code and a good infrastructure including Issues-Managment, etc.
- Thanks to [IntelliJ](https://www.jetbrains.com) as they make the best IDEs for PHP and they gave me an open source license for PhpStorm!
- Thanks to [Travis CI](https://travis-ci.com/) for being the most awesome, easiest continous integration tool out there!
- Thanks to [StyleCI](https://styleci.io/) for the simple but powerful code style check.
- Thanks to [PHPStan](https://github.com/phpstan/phpstan) && [Psalm](https://github.com/vimeo/psalm) for really great Static analysis tools and for discover bugs in the code!

### License and Copyright

Released under the MIT License - see `LICENSE.txt` for details.

---

### Vendor Doctrine Inflector Docs En Index (`vendor/doctrine/inflector/docs/en/index.rst`)

Introduction
============

The Doctrine Inflector has methods for inflecting text. The features include pluralization,
singularization, converting between camelCase and under_score and capitalizing
words.

Installation
============

You can install the Inflector with composer:

.. code-block:: console

    $ composer require doctrine/inflector

Usage
=====

Using the inflector is easy, you can create a new ``Doctrine\Inflector\Inflector`` instance by using
the ``Doctrine\Inflector\InflectorFactory`` class:

.. code-block:: php

    use Doctrine\Inflector\InflectorFactory;

    $inflector = InflectorFactory::create()->build();

By default it will create an English inflector. If you want to use another language, just pass the language
you want to create an inflector for to the ``createForLanguage()`` method:

.. code-block:: php

    use Doctrine\Inflector\InflectorFactory;
    use Doctrine\Inflector\Language;

    $inflector = InflectorFactory::createForLanguage(Language::SPANISH)->build();

The supported languages are as follows:

- ``Language::ENGLISH``
- ``Language::FRENCH``
- ``Language::NORWEGIAN_BOKMAL``
- ``Language::PORTUGUESE``
- ``Language::SPANISH``
- ``Language::TURKISH``

If you want to manually construct the inflector instead of using a factory, you can do so like this:

.. code-block:: php

    use Doctrine\Inflector\CachedWordInflector;
    use Doctrine\Inflector\RulesetInflector;
    use Doctrine\Inflector\Rules\English;

    $inflector = new Inflector(
        new CachedWordInflector(new RulesetInflector(
            English\Rules::getSingularRuleset()
        )),
        new CachedWordInflector(new RulesetInflector(
            English\Rules::getPluralRuleset()
        ))
    );

Adding Languages
----------------

If you are interested in adding support for your language, take a look at the other languages defined in the
``Doctrine\Inflector\Rules`` namespace and the tests located in ``Doctrine\Tests\Inflector\Rules``. You can copy
one of the languages and update the rules for your language.

Once you have done this, send a pull request to the ``doctrine/inflector`` repository with the additions.

Custom Setup
============

If you want to setup custom singular and plural rules, you can configure these in the factory:

.. code-block:: php

    use Doctrine\Inflector\InflectorFactory;
    use Doctrine\Inflector\Rules\Pattern;
    use Doctrine\Inflector\Rules\Patterns;
    use Doctrine\Inflector\Rules\Ruleset;
    use Doctrine\Inflector\Rules\Substitution;
    use Doctrine\Inflector\Rules\Substitutions;
    use Doctrine\Inflector\Rules\Transformation;
    use Doctrine\Inflector\Rules\Transformations;
    use Doctrine\Inflector\Rules\Word;

    $inflector = InflectorFactory::create()
        ->withSingularRules(
            new Ruleset(
                new Transformations(
                    new Transformation(new Pattern('/^(bil)er$/i'), '\1'),
                    new Transformation(new Pattern('/^(inflec|contribu)tors$/i'), '\1ta')
                ),
                new Patterns(new Pattern('singulars')),
                new Substitutions(new Substitution(new Word('spins'), new Word('spinor')))
            )
        )
        ->withPluralRules(
            new Ruleset(
                new Transformations(
                    new Transformation(new Pattern('^(bil)er$'), '\1'),
                    new Transformation(new Pattern('^(inflec|contribu)tors$'), '\1ta')
                ),
                new Patterns(new Pattern('noflect'), new Pattern('abtuse')),
                new Substitutions(
                    new Substitution(new Word('amaze'), new Word('amazable')),
                    new Substitution(new Word('phone'), new Word('phonezes'))
                )
            )
        )
        ->build();

No operation inflector
----------------------

The ``Doctrine\Inflector\NoopWordInflector`` may be used to configure an inflector that doesn't perform any operation for
pluralization and/or singularization. If will simply return the input as output.

This is an implementation of the `Null Object design pattern <https://sourcemaking.com/design_patterns/null_object>`_.

.. code-block:: php

    use Doctrine\Inflector\Inflector;
    use Doctrine\Inflector\NoopWordInflector;

    $inflector = new Inflector(new NoopWordInflector(), new NoopWordInflector());

Tableize
========

Converts ``ModelName`` to ``model_name``:

.. code-block:: php

    echo $inflector->tableize('ModelName'); // model_name

Classify
========

Converts ``model_name`` to ``ModelName``:

.. code-block:: php

    echo $inflector->classify('model_name'); // ModelName

Camelize
========

This method uses `Classify`_ and then converts the first character to lowercase:

.. code-block:: php

    echo $inflector->camelize('model_name'); // modelName

Capitalize
==========

Takes a string and capitalizes all of the words, like PHP's built-in
``ucwords`` function. This extends that behavior, however, by allowing the
word delimiters to be configured, rather than only separating on
whitespace.

Here is an example:

.. code-block:: php

    $string = 'top-o-the-morning to all_of_you!';

    echo $inflector->capitalize($string); // Top-O-The-Morning To All_of_you!

    echo $inflector->capitalize($string, '-_ '); // Top-O-The-Morning To All_Of_You!

Pluralize
=========

Returns a word in plural form.

.. code-block:: php

    echo $inflector->pluralize('browser'); // browsers

Singularize
===========

Returns a word in singular form.

.. code-block:: php

    echo $inflector->singularize('browsers'); // browser

Urlize
======

Generate a URL friendly string from a string of text:

.. code-block:: php

    echo $inflector->urlize('My first blog post'); // my-first-blog-post

Unaccent
========

You can unaccent a string of text using the ``unaccent()`` method:

.. code-block:: php

    echo $inflector->unaccent('año'); // ano

Legacy API
==========

The API present in Inflector 1.x is still available, but will be deprecated in a future release and dropped for 3.0.
Support for languages other than English is available in the 2.0 API only.

Acknowledgements
================

The language rules in this library have been adapted from several different sources, including but not limited to:

- `Ruby On Rails Inflector <http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html>`_
- `ICanBoogie Inflector <https://github.com/ICanBoogie/Inflector>`_
- `CakePHP Inflector <https://book.cakephp.org/3.0/en/core-libraries/inflector.html>`_

---

--- METRICS ---
- Files Extracted: 37
- Estimated Token Budget: ~34516 tokens
- Canonical Reference: https://codewiki.google/github.com/lizhipay/acg-faka
