diff --git a/common/composer.json b/common/composer.json index d9ccf189..5e2f2348 100644 --- a/common/composer.json +++ b/common/composer.json @@ -2,6 +2,7 @@ "require": { "gregwar/image": "^2.1", "tencentcloud/ocr": "^3.0", - "wechatpay/wechatpay-guzzle-middleware": "^0.2.2" + "wechatpay/wechatpay-guzzle-middleware": "^0.2.2", + "mikehaertl/php-pdftk": "^0.11.0" } } diff --git a/common/composer.lock b/common/composer.lock index 6d64f719..50480203 100644 --- a/common/composer.lock +++ b/common/composer.lock @@ -4,28 +4,17 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4f7bbe710bfa187154b75afba695ea66", + "content-hash": "9f27737ffbf35aa1f124c7f62201bb1c", "packages": [ { "name": "gregwar/cache", "version": "v1.0.13", "target-dir": "Gregwar/Cache", - "source": { - "type": "git", - "url": "https://github.com/Gregwar/Cache.git", - "reference": "184cc341c25298ff7d584f86b55b6ca26626da4f" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/Gregwar/Cache/zipball/184cc341c25298ff7d584f86b55b6ca26626da4f", "reference": "184cc341c25298ff7d584f86b55b6ca26626da4f", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=5.3" @@ -59,22 +48,11 @@ "name": "gregwar/image", "version": "v2.1.0", "target-dir": "Gregwar/Image", - "source": { - "type": "git", - "url": "https://github.com/Gregwar/Image.git", - "reference": "1cf64c34cbb22933b36727c16b15ed4d925b6fc6" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/Gregwar/Image/zipball/1cf64c34cbb22933b36727c16b15ed4d925b6fc6", "reference": "1cf64c34cbb22933b36727c16b15ed4d925b6fc6", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "ext-gd": "*", @@ -116,22 +94,11 @@ { "name": "guzzlehttp/guzzle", "version": "6.5.5", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/guzzle/guzzle/zipball/9d4290de1cfd701f38099ef7e183b64b4b7b0c5e", "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "ext-json": "*", @@ -189,22 +156,11 @@ { "name": "guzzlehttp/promises", "version": "1.4.1", - "source": { - "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d", "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=5.5" @@ -246,22 +202,11 @@ { "name": "guzzlehttp/psr7", "version": "1.8.2", - "source": { - "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "dc960a912984efb74d0a90222870c72c87f10c91" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/guzzle/psr7/zipball/dc960a912984efb74d0a90222870c72c87f10c91", "reference": "dc960a912984efb74d0a90222870c72c87f10c91", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=5.4.0", @@ -320,25 +265,123 @@ ], "time": "2021-04-26T09:17:50+00:00" }, + { + "name": "mikehaertl/php-pdftk", + "version": "0.11.0", + "dist": { + "type": "zip", + "url": "https://mirrors.tencent.com/repository/composer/mikehaertl/php-pdftk/0.11.0/mikehaertl-php-pdftk-0.11.0.zip", + "reference": "61ba14c08491c1bb7d479c87087f236389b4babf", + "shasum": "" + }, + "require": { + "mikehaertl/php-shellcommand": "^1.6.3", + "mikehaertl/php-tmpfile": "^1.1.0", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": ">4.0 <9.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "mikehaertl\\pdftk\\": "src/" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Haertl", + "email": "haertl.mike@gmail.com" + } + ], + "description": "A PDF conversion and form utility based on pdftk.", + "keywords": [ + "pdf", + "pdftk" + ], + "time": "2021-11-23T07:15:01+00:00" + }, + { + "name": "mikehaertl/php-shellcommand", + "version": "1.6.4", + "dist": { + "type": "zip", + "url": "https://mirrors.tencent.com/repository/composer/mikehaertl/php-shellcommand/1.6.4/mikehaertl-php-shellcommand-1.6.4.zip", + "reference": "3488d7803df1e8f1a343d3d0ca452d527ad8d5e5", + "shasum": "" + }, + "require": { + "php": ">= 5.3.0" + }, + "require-dev": { + "phpunit/phpunit": ">4.0 <=9.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "mikehaertl\\shellcommand\\": "src/" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Härtl", + "email": "haertl.mike@gmail.com" + } + ], + "description": "An object oriented interface to shell commands", + "keywords": [ + "shell" + ], + "time": "2021-03-17T06:54:33+00:00" + }, + { + "name": "mikehaertl/php-tmpfile", + "version": "1.2.1", + "dist": { + "type": "zip", + "url": "https://mirrors.tencent.com/repository/composer/mikehaertl/php-tmpfile/1.2.1/mikehaertl-php-tmpfile-1.2.1.zip", + "reference": "70a5b70b17bc0d9666388e6a551ecc93d0b40a10", + "shasum": "" + }, + "require-dev": { + "php": ">=5.3.0", + "phpunit/phpunit": ">4.0 <=9.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "mikehaertl\\tmp\\": "src/" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Härtl", + "email": "haertl.mike@gmail.com" + } + ], + "description": "A convenience class for temporary files", + "keywords": [ + "files" + ], + "time": "2021-03-01T18:26:25+00:00" + }, { "name": "paragonie/random_compat", "version": "v2.0.20", - "source": { - "type": "git", - "url": "https://github.com/paragonie/random_compat.git", - "reference": "0f1f60250fccffeaf5dda91eea1c018aed1adc2a" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/paragonie/random_compat/zipball/0f1f60250fccffeaf5dda91eea1c018aed1adc2a", "reference": "0f1f60250fccffeaf5dda91eea1c018aed1adc2a", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=5.2.0" @@ -378,22 +421,11 @@ { "name": "psr/http-message", "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=5.3.0" @@ -434,22 +466,11 @@ { "name": "ralouphie/getallheaders", "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", "reference": "120b605dfeb996808c31b6477290a714d356e822", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=5.6" @@ -480,22 +501,11 @@ { "name": "symfony/polyfill-intl-idn", "version": "v1.19.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "4ad5115c0f5d5172a9fe8147675ec6de266d8826" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/4ad5115c0f5d5172a9fe8147675ec6de266d8826", "reference": "4ad5115c0f5d5172a9fe8147675ec6de266d8826", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=5.3.3", @@ -552,41 +562,16 @@ "portable", "shim" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-10-21T09:57:48+00:00" }, { "name": "symfony/polyfill-intl-normalizer", "version": "v1.19.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "8db0ae7936b42feb370840cf24de1a144fb0ef27" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8db0ae7936b42feb370840cf24de1a144fb0ef27", "reference": "8db0ae7936b42feb370840cf24de1a144fb0ef27", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=5.3.3" @@ -639,41 +624,16 @@ "portable", "shim" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-10-23T09:01:57+00:00" }, { "name": "symfony/polyfill-php70", "version": "v1.19.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "3fe414077251a81a1b15b1c709faf5c2fbae3d4e" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/3fe414077251a81a1b15b1c709faf5c2fbae3d4e", "reference": "3fe414077251a81a1b15b1c709faf5c2fbae3d4e", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "paragonie/random_compat": "~1.0|~2.0|~9.99", @@ -722,41 +682,16 @@ "portable", "shim" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-10-23T09:01:57+00:00" }, { "name": "symfony/polyfill-php72", "version": "v1.19.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "beecef6b463b06954638f02378f52496cb84bacc" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/beecef6b463b06954638f02378f52496cb84bacc", "reference": "beecef6b463b06954638f02378f52496cb84bacc", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=5.3.3" @@ -801,41 +736,16 @@ "portable", "shim" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-10-23T09:01:57+00:00" }, { "name": "tencentcloud/common", "version": "3.0.436", - "source": { - "type": "git", - "url": "https://github.com/tencentcloud-sdk-php/common.git", - "reference": "8e1742f06287accfd173141a919d3460d097ac03" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/tencentcloud-sdk-php/common/zipball/8e1742f06287accfd173141a919d3460d097ac03", "reference": "8e1742f06287accfd173141a919d3460d097ac03", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "guzzlehttp/guzzle": "^6.3||^7.0", @@ -867,22 +777,11 @@ { "name": "tencentcloud/ocr", "version": "3.0.436", - "source": { - "type": "git", - "url": "https://github.com/tencentcloud-sdk-php/ocr.git", - "reference": "3158bf1b0d4c89ca0623d0c4eda76a4a8eba656b" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/tencentcloud-sdk-php/ocr/zipball/3158bf1b0d4c89ca0623d0c4eda76a4a8eba656b", "reference": "3158bf1b0d4c89ca0623d0c4eda76a4a8eba656b", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "tencentcloud/common": "3.0.436" @@ -912,22 +811,11 @@ { "name": "wechatpay/wechatpay-guzzle-middleware", "version": "0.2.2", - "source": { - "type": "git", - "url": "https://github.com/wechatpay-apiv3/wechatpay-guzzle-middleware.git", - "reference": "6782ac33ed8cf97628609a71cdc5e84a6a40677a" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/wechatpay-apiv3/wechatpay-guzzle-middleware/zipball/6782ac33ed8cf97628609a71cdc5e84a6a40677a", "reference": "6782ac33ed8cf97628609a71cdc5e84a6a40677a", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "ext-openssl": "*", @@ -968,6 +856,5 @@ "prefer-stable": false, "prefer-lowest": false, "platform": [], - "platform-dev": [], - "plugin-api-version": "1.1.0" + "platform-dev": [] } diff --git a/common/vendor/composer/ClassLoader.php b/common/vendor/composer/ClassLoader.php index 03b9bb9c..fce8549f 100644 --- a/common/vendor/composer/ClassLoader.php +++ b/common/vendor/composer/ClassLoader.php @@ -60,7 +60,7 @@ class ClassLoader public function getPrefixes() { if (!empty($this->prefixesPsr0)) { - return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); + return call_user_func_array('array_merge', $this->prefixesPsr0); } return array(); diff --git a/common/vendor/composer/autoload_psr4.php b/common/vendor/composer/autoload_psr4.php index b216aa6b..c945418e 100644 --- a/common/vendor/composer/autoload_psr4.php +++ b/common/vendor/composer/autoload_psr4.php @@ -6,6 +6,9 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( + 'mikehaertl\\tmp\\' => array($vendorDir . '/mikehaertl/php-tmpfile/src'), + 'mikehaertl\\shellcommand\\' => array($vendorDir . '/mikehaertl/php-shellcommand/src'), + 'mikehaertl\\pdftk\\' => array($vendorDir . '/mikehaertl/php-pdftk/src'), 'WechatPay\\GuzzleMiddleware\\' => array($vendorDir . '/wechatpay/wechatpay-guzzle-middleware/src'), 'TencentCloud\\' => array($vendorDir . '/tencentcloud/common/src/TencentCloud', $vendorDir . '/tencentcloud/ocr/src/TencentCloud'), 'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'), diff --git a/common/vendor/composer/autoload_real.php b/common/vendor/composer/autoload_real.php index f6babce6..72880593 100644 --- a/common/vendor/composer/autoload_real.php +++ b/common/vendor/composer/autoload_real.php @@ -13,9 +13,6 @@ class ComposerAutoloaderInitd0872984a1db7aa104ae1184a3170d3e } } - /** - * @return \Composer\Autoload\ClassLoader - */ public static function getLoader() { if (null !== self::$loader) { diff --git a/common/vendor/composer/autoload_static.php b/common/vendor/composer/autoload_static.php index f6396a8e..e5a88f61 100644 --- a/common/vendor/composer/autoload_static.php +++ b/common/vendor/composer/autoload_static.php @@ -19,6 +19,12 @@ class ComposerStaticInitd0872984a1db7aa104ae1184a3170d3e ); public static $prefixLengthsPsr4 = array ( + 'm' => + array ( + 'mikehaertl\\tmp\\' => 15, + 'mikehaertl\\shellcommand\\' => 24, + 'mikehaertl\\pdftk\\' => 17, + ), 'W' => array ( 'WechatPay\\GuzzleMiddleware\\' => 27, @@ -47,6 +53,18 @@ class ComposerStaticInitd0872984a1db7aa104ae1184a3170d3e ); public static $prefixDirsPsr4 = array ( + 'mikehaertl\\tmp\\' => + array ( + 0 => __DIR__ . '/..' . '/mikehaertl/php-tmpfile/src', + ), + 'mikehaertl\\shellcommand\\' => + array ( + 0 => __DIR__ . '/..' . '/mikehaertl/php-shellcommand/src', + ), + 'mikehaertl\\pdftk\\' => + array ( + 0 => __DIR__ . '/..' . '/mikehaertl/php-pdftk/src', + ), 'WechatPay\\GuzzleMiddleware\\' => array ( 0 => __DIR__ . '/..' . '/wechatpay/wechatpay-guzzle-middleware/src', diff --git a/common/vendor/composer/installed.json b/common/vendor/composer/installed.json index f589d125..982dc8d8 100644 --- a/common/vendor/composer/installed.json +++ b/common/vendor/composer/installed.json @@ -4,22 +4,11 @@ "version": "v1.0.13", "version_normalized": "1.0.13.0", "target-dir": "Gregwar/Cache", - "source": { - "type": "git", - "url": "https://github.com/Gregwar/Cache.git", - "reference": "184cc341c25298ff7d584f86b55b6ca26626da4f" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/Gregwar/Cache/zipball/184cc341c25298ff7d584f86b55b6ca26626da4f", "reference": "184cc341c25298ff7d584f86b55b6ca26626da4f", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=5.3" @@ -55,22 +44,11 @@ "version": "v2.1.0", "version_normalized": "2.1.0.0", "target-dir": "Gregwar/Image", - "source": { - "type": "git", - "url": "https://github.com/Gregwar/Image.git", - "reference": "1cf64c34cbb22933b36727c16b15ed4d925b6fc6" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/Gregwar/Image/zipball/1cf64c34cbb22933b36727c16b15ed4d925b6fc6", "reference": "1cf64c34cbb22933b36727c16b15ed4d925b6fc6", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "ext-gd": "*", @@ -114,22 +92,11 @@ "name": "guzzlehttp/guzzle", "version": "6.5.5", "version_normalized": "6.5.5.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/guzzle/guzzle/zipball/9d4290de1cfd701f38099ef7e183b64b4b7b0c5e", "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "ext-json": "*", @@ -189,22 +156,11 @@ "name": "guzzlehttp/promises", "version": "1.4.1", "version_normalized": "1.4.1.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/promises.git", - "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d", "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=5.5" @@ -248,22 +204,11 @@ "name": "guzzlehttp/psr7", "version": "1.8.2", "version_normalized": "1.8.2.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "dc960a912984efb74d0a90222870c72c87f10c91" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/guzzle/psr7/zipball/dc960a912984efb74d0a90222870c72c87f10c91", "reference": "dc960a912984efb74d0a90222870c72c87f10c91", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=5.4.0", @@ -323,26 +268,130 @@ "url" ] }, + { + "name": "mikehaertl/php-pdftk", + "version": "0.11.0", + "version_normalized": "0.11.0.0", + "dist": { + "type": "zip", + "url": "https://mirrors.tencent.com/repository/composer/mikehaertl/php-pdftk/0.11.0/mikehaertl-php-pdftk-0.11.0.zip", + "reference": "61ba14c08491c1bb7d479c87087f236389b4babf", + "shasum": "" + }, + "require": { + "mikehaertl/php-shellcommand": "^1.6.3", + "mikehaertl/php-tmpfile": "^1.1.0", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": ">4.0 <9.4" + }, + "time": "2021-11-23T07:15:01+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "mikehaertl\\pdftk\\": "src/" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Haertl", + "email": "haertl.mike@gmail.com" + } + ], + "description": "A PDF conversion and form utility based on pdftk.", + "keywords": [ + "pdf", + "pdftk" + ] + }, + { + "name": "mikehaertl/php-shellcommand", + "version": "1.6.4", + "version_normalized": "1.6.4.0", + "dist": { + "type": "zip", + "url": "https://mirrors.tencent.com/repository/composer/mikehaertl/php-shellcommand/1.6.4/mikehaertl-php-shellcommand-1.6.4.zip", + "reference": "3488d7803df1e8f1a343d3d0ca452d527ad8d5e5", + "shasum": "" + }, + "require": { + "php": ">= 5.3.0" + }, + "require-dev": { + "phpunit/phpunit": ">4.0 <=9.4" + }, + "time": "2021-03-17T06:54:33+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "mikehaertl\\shellcommand\\": "src/" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Härtl", + "email": "haertl.mike@gmail.com" + } + ], + "description": "An object oriented interface to shell commands", + "keywords": [ + "shell" + ] + }, + { + "name": "mikehaertl/php-tmpfile", + "version": "1.2.1", + "version_normalized": "1.2.1.0", + "dist": { + "type": "zip", + "url": "https://mirrors.tencent.com/repository/composer/mikehaertl/php-tmpfile/1.2.1/mikehaertl-php-tmpfile-1.2.1.zip", + "reference": "70a5b70b17bc0d9666388e6a551ecc93d0b40a10", + "shasum": "" + }, + "require-dev": { + "php": ">=5.3.0", + "phpunit/phpunit": ">4.0 <=9.4" + }, + "time": "2021-03-01T18:26:25+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "mikehaertl\\tmp\\": "src/" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Härtl", + "email": "haertl.mike@gmail.com" + } + ], + "description": "A convenience class for temporary files", + "keywords": [ + "files" + ] + }, { "name": "paragonie/random_compat", "version": "v2.0.20", "version_normalized": "2.0.20.0", - "source": { - "type": "git", - "url": "https://github.com/paragonie/random_compat.git", - "reference": "0f1f60250fccffeaf5dda91eea1c018aed1adc2a" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/paragonie/random_compat/zipball/0f1f60250fccffeaf5dda91eea1c018aed1adc2a", "reference": "0f1f60250fccffeaf5dda91eea1c018aed1adc2a", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=5.2.0" @@ -384,22 +433,11 @@ "name": "psr/http-message", "version": "1.0.1", "version_normalized": "1.0.1.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=5.3.0" @@ -442,22 +480,11 @@ "name": "ralouphie/getallheaders", "version": "3.0.3", "version_normalized": "3.0.3.0", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", "reference": "120b605dfeb996808c31b6477290a714d356e822", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=5.6" @@ -490,22 +517,11 @@ "name": "symfony/polyfill-intl-idn", "version": "v1.19.0", "version_normalized": "1.19.0.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "4ad5115c0f5d5172a9fe8147675ec6de266d8826" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/4ad5115c0f5d5172a9fe8147675ec6de266d8826", "reference": "4ad5115c0f5d5172a9fe8147675ec6de266d8826", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=5.3.3", @@ -563,42 +579,17 @@ "polyfill", "portable", "shim" - ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } ] }, { "name": "symfony/polyfill-intl-normalizer", "version": "v1.19.0", "version_normalized": "1.19.0.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "8db0ae7936b42feb370840cf24de1a144fb0ef27" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8db0ae7936b42feb370840cf24de1a144fb0ef27", "reference": "8db0ae7936b42feb370840cf24de1a144fb0ef27", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=5.3.3" @@ -652,42 +643,17 @@ "polyfill", "portable", "shim" - ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } ] }, { "name": "symfony/polyfill-php70", "version": "v1.19.0", "version_normalized": "1.19.0.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "3fe414077251a81a1b15b1c709faf5c2fbae3d4e" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/3fe414077251a81a1b15b1c709faf5c2fbae3d4e", "reference": "3fe414077251a81a1b15b1c709faf5c2fbae3d4e", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "paragonie/random_compat": "~1.0|~2.0|~9.99", @@ -737,42 +703,17 @@ "polyfill", "portable", "shim" - ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } ] }, { "name": "symfony/polyfill-php72", "version": "v1.19.0", "version_normalized": "1.19.0.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "beecef6b463b06954638f02378f52496cb84bacc" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/beecef6b463b06954638f02378f52496cb84bacc", "reference": "beecef6b463b06954638f02378f52496cb84bacc", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "php": ">=5.3.3" @@ -818,42 +759,17 @@ "polyfill", "portable", "shim" - ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } ] }, { "name": "tencentcloud/common", "version": "3.0.436", "version_normalized": "3.0.436.0", - "source": { - "type": "git", - "url": "https://github.com/tencentcloud-sdk-php/common.git", - "reference": "8e1742f06287accfd173141a919d3460d097ac03" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/tencentcloud-sdk-php/common/zipball/8e1742f06287accfd173141a919d3460d097ac03", "reference": "8e1742f06287accfd173141a919d3460d097ac03", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "guzzlehttp/guzzle": "^6.3||^7.0", @@ -887,22 +803,11 @@ "name": "tencentcloud/ocr", "version": "3.0.436", "version_normalized": "3.0.436.0", - "source": { - "type": "git", - "url": "https://github.com/tencentcloud-sdk-php/ocr.git", - "reference": "3158bf1b0d4c89ca0623d0c4eda76a4a8eba656b" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/tencentcloud-sdk-php/ocr/zipball/3158bf1b0d4c89ca0623d0c4eda76a4a8eba656b", "reference": "3158bf1b0d4c89ca0623d0c4eda76a4a8eba656b", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "tencentcloud/common": "3.0.436" @@ -934,22 +839,11 @@ "name": "wechatpay/wechatpay-guzzle-middleware", "version": "0.2.2", "version_normalized": "0.2.2.0", - "source": { - "type": "git", - "url": "https://github.com/wechatpay-apiv3/wechatpay-guzzle-middleware.git", - "reference": "6782ac33ed8cf97628609a71cdc5e84a6a40677a" - }, "dist": { "type": "zip", "url": "https://api.github.com/repos/wechatpay-apiv3/wechatpay-guzzle-middleware/zipball/6782ac33ed8cf97628609a71cdc5e84a6a40677a", "reference": "6782ac33ed8cf97628609a71cdc5e84a6a40677a", - "shasum": "", - "mirrors": [ - { - "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", - "preferred": true - } - ] + "shasum": "" }, "require": { "ext-openssl": "*", diff --git a/common/vendor/mikehaertl/php-pdftk/.github/workflows/tests.yml b/common/vendor/mikehaertl/php-pdftk/.github/workflows/tests.yml new file mode 100644 index 00000000..b17b83cf --- /dev/null +++ b/common/vendor/mikehaertl/php-pdftk/.github/workflows/tests.yml @@ -0,0 +1,55 @@ +name: Tests +on: pull_request +jobs: + phpunit: + name: PHP ${{ matrix.php }} + runs-on: ubuntu-latest + strategy: + matrix: + php: + - "5.3" + - "5.4" + - "5.5" + - "5.6" + - "7.0" + - "7.1" + - "7.2" + - "7.3" + - "7.4" + - "8.0" + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install pdftk + run: | + cd /tmp + sudo wget http://mirrors.kernel.org/ubuntu/pool/universe/p/pdftk-java/pdftk-java_3.0.9-1_all.deb + sudo apt install -y -q ./pdftk-java_3.0.9-1_all.deb + pdftk --version + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + tools: composer:v2 + + - name: Update composer + run: composer self-update + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer cache + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install composer packages + run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi + + - name: Run phpunit + run: vendor/bin/phpunit --color=always diff --git a/common/vendor/mikehaertl/php-pdftk/LICENSE b/common/vendor/mikehaertl/php-pdftk/LICENSE new file mode 100644 index 00000000..28dbb2a2 --- /dev/null +++ b/common/vendor/mikehaertl/php-pdftk/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Michael Härtl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/common/vendor/mikehaertl/php-pdftk/README.md b/common/vendor/mikehaertl/php-pdftk/README.md new file mode 100644 index 00000000..6776b8cc --- /dev/null +++ b/common/vendor/mikehaertl/php-pdftk/README.md @@ -0,0 +1,466 @@ +php-pdftk +========= + +[![GitHub Tests](https://github.com/mikehaertl/php-pdftk/workflows/Tests/badge.svg)](https://github.com/mikehaertl/php-pdftk/actions) +[![Packagist Version](https://img.shields.io/packagist/v/mikehaertl/php-pdftk?label=version)](https://packagist.org/packages/mikehaertl/php-pdftk) +[![Packagist Downloads](https://img.shields.io/packagist/dt/mikehaertl/php-pdftk)](https://packagist.org/packages/mikehaertl/php-pdftk) +[![GitHub license](https://img.shields.io/github/license/mikehaertl/php-pdftk)](https://github.com/mikehaertl/php-pdftk/blob/master/LICENSE) +[![Packagist PHP Version Support](https://img.shields.io/packagist/php-v/mikehaertl/php-pdftk)](https://packagist.org/packages/mikehaertl/php-pdftk) + +A PDF conversion and form utility based on pdftk. + +## Features + +*php-pdftk* brings the full power of `pdftk` to PHP - and more. + + * Fill forms, either from a XFDF/FDF file or from a data array (UTF-8 safe for unflattened forms, requires pdftk 2.x !) + * Create XFDF or FDF files from PHP arrays (UTF-8 safe!) + * Create FDF files from filled PDF forms + * Combine pages from several PDF files into a new PDF file + * Split a PDF into one file per page + * Add background or overlay PDFs + * Read out meta data about PDF and form fields + * Set passwords and permissions + * Remove passwords + +## Requirements + + * The `pdftk` command must be installed and working on your system + * This library is written for pdftk 2.x versions. You should be able to use it with pdftk 1.x but not all methods will work there. + For details consult the man page of pdftk on your system. + * There is a [known issue](https://github.com/mikehaertl/php-pdftk/issues/150) + on Ubuntu if you installed the `pdftk` package from snap. This version has + no permission to write to the `/tmp` directory. You can either set another + temporay directory as described below or use another package. For Ubuntu + 18.10 there's also a `pdftk-java` package available via apt which should work + fine. You can also install this package on Ubuntu 18.04 if you download it + manually. Also check [this answer](https://askubuntu.com/a/1028983/175814) + on askubuntu. + +> **Note:** The pdftk version from the alternative PPA `ppa:malteworld/ppa` is +> no longer available. The author instead now points to his answer on askubuntu +> linked above. + +## Installation + +You should use [composer](https://getcomposer.org/) to install this library. + +``` +composer require mikehaertl/php-pdftk +``` + +## Examples + +### Create instance for PDF files + +There are several ways to tell the `Pdf` instance which file(s) it should use. +Some files may also require a password or need an alias to be used as a handle +in some operations (e.g. cat or shuffle). + +> **Note:** In version 2.x of pdftk a handle can be one or more upper case letters. + +```php +// Create an instance for a single file +$pdf = new Pdf('/path/to/form.pdf'); + +// Alternatively add files later. Handles are autogenerated in this case. +$pdf = new Pdf(); +$pdf->addFile('/path/to/file1.pdf'); +$pdf->addFile('/path/to/file2.pdf'); + +// Add files with own handle +$pdf = new Pdf(); +$pdf->addFile('/path/to/file1.pdf', 'A'); +$pdf->addFile('/path/to/file2.pdf', 'B'); +// Add file with handle and password +$pdf->addFile('/path/to/file3.pdf', 'C', 'secret*password'); + +// Shortcut to pass all files to the constructor +$pdf = new Pdf([ + 'A' => ['/path/to/file1.pdf', 'secret*password1'], + 'B' => ['/path/to/file2.pdf', 'secret*password2'], +]); +``` + +### Operations + +Please consult the `pdftk` man page for each operation to find out how each operation works +in detail and which options are available. + +For all operations you can either save the PDF locally through `saveAs($name)` or send it to the +browser with `send()`. If you pass a filename to `send($name)` the client browser will open a download +dialogue whereas without a filename it will usually display the PDF inline. + +**IMPORTANT: You can always only perform *one* of the following operations on a single PDF instance. +Below you can find a workaround if you need multiple operations.** + +#### Fill Form + +Fill a PDF form with data from a PHP array or an XFDF/FDF file. + +```php +use mikehaertl\pdftk\Pdf; + +// Fill form with data array +$pdf = new Pdf('/full/path/to/form.pdf'); +$result = $pdf->fillForm([ + 'name'=>'ÄÜÖ äüö мирано čárka', + 'nested.name' => 'valX', + ]) + ->needAppearances() + ->saveAs('filled.pdf'); + +// Always check for errors +if ($result === false) { + $error = $pdf->getError(); +} + +// Fill form from FDF +$pdf = new Pdf('form.pdf'); +$result = $pdf->fillForm('data.xfdf') + ->saveAs('filled.pdf'); +if ($result === false) { + $error = $pdf->getError(); +} +``` + +**Note:** When filling in UTF-8 data, you should always add the `needAppearances()` option. +This will make sure, that the PDF reader takes care of using the right fonts for rendering, +something that pdftk can't do for you. Also note that `flatten()` doesn't really work well +if you have special characters in your data. + +#### Create a XFDF/FDF file from a PHP array + +This is a bonus feature that is not available from `pdftk`. + +```php +use mikehaertl\pdftk\XfdfFile; +use mikehaertl\pdftk\FdfFile; + +$xfdf = new XfdfFile(['name' => 'Jürgen мирано']); +$xfdf->saveAs('/path/to/data.xfdf'); + +$fdf = new FdfFile(['name' => 'Jürgen мирано']); +$fdf->saveAs('/path/to/data.fdf'); +``` + +#### Cat + +Assemble a PDF from pages from one or more PDF files. + +```php +use mikehaertl\pdftk\Pdf; + +// Extract pages 1-5 and 7,4,9 into a new file +$pdf = new Pdf('/path/to/my.pdf'); +$result = $pdf->cat(1, 5) + ->cat([7, 4, 9]) + ->saveAs('/path/to/new.pdf'); +if ($result === false) { + $error = $pdf->getError(); +} + +// Combine pages from several files +$pdf = new Pdf([ + 'A' => '/path/file1.pdf', // A is alias for file1.pdf + 'B' => ['/path/file2.pdf','pass**word'], // B is alias for file2.pdf + 'C' => ['/path/file3.pdf','secret**pw'], // C is alias for file3.pdf +]); +$result = $pdf->cat(1, 5, 'A') // pages 1-5 from A + ->cat(3, null, 'B') // page 3 from B + ->cat(7, 'end', 'B', null, 'east') // pages 7-end from B, rotated East + ->cat('end',3,'A','even') // even pages 3-end in reverse order from A + ->cat([2,3,7], 'C') // pages 2,3 and 7 from C + ->saveAs('/path/new.pdf'); +if ($result === false) { + $error = $pdf->getError(); +} +``` + +#### Shuffle + +Like `cat()` but create "*streams*" and fill the new PDF with one page from each +stream at a time. + +```php +use mikehaertl\pdftk\Pdf; + +$pdf = new Pdf([ + 'A' => '/path/file1.pdf', // A is alias for file1.pdf + 'B' => '/path/file2.pdf', // B is alias for file2.pdf +]); + +// new.pdf will have pages A1, B3, A2, B4, A3, B5, ... +$result = $pdf->shuffle(1, 5, 'A') // pages 1-5 from A + ->shuffle(3, 8, 'B') // pages 3-8 from B + ->saveAs('/path/new.pdf'); +if ($result === false) { + $error = $pdf->getError(); +} +``` + +#### Burst + +Split a PDF file into one file per page. + +```php +use mikehaertl\pdftk\Pdf; + +$pdf = new Pdf('/path/my.pdf'); +$result = $pdf->burst('/path/page_%d.pdf'); // Supply a printf() pattern +if ($result === false) { + $error = $pdf->getError(); +} +``` + +#### Add background PDF + +Add another PDF file as background. + +```php +use mikehaertl\pdftk\Pdf; + +// Set background from another PDF (first page repeated) +$pdf = new Pdf('/path/my.pdf'); +$result = $pdf->background('/path/back.pdf') + ->saveAs('/path/watermarked.pdf'); +if ($result === false) { + $error = $pdf->getError(); +} + +// Set background from another PDF (one page each) +$pdf = new Pdf('/path/my.pdf'); +$result = $pdf->multiBackground('/path/back_pages.pdf') + ->saveAs('/path/watermarked.pdf'); +if ($result === false) { + $error = $pdf->getError(); +} +``` + +#### Add overlay PDF + +Add another PDF file as overlay. + +```php +use mikehaertl\pdftk\Pdf; + +// Stamp with another PDF (first page repeated) +$pdf = new Pdf('/path/my.pdf'); +$result = $pdf->stamp('/path/overlay.pdf') + ->saveAs('/path/stamped.pdf'); +if ($result === false) { + $error = $pdf->getError(); +} + +// Stamp with another PDF (one page each) +$pdf = new Pdf('/path/my.pdf'); +$result = $pdf->multiStamp('/path/overlay_pages.pdf') + ->saveAs('/path/stamped.pdf'); +if ($result === false) { + $error = $pdf->getError(); +} +``` + +#### Attach Files + +Add file attachments to the document or to a specific page. + +```php +use mikehaertl\pdftk\Pdf; + +$files = [ + '/path/to/file1', + '/path/to/file2', +] + +// Add files at the document level +$pdf = new Pdf('/path/my.pdf'); +$result = $pdf->attachFiles($files) + ->saveAs('/path/withfiles.pdf'); +if ($result === false) { + $error = $pdf->getError(); +} + +// Add files to a specific page +$pdf = new Pdf('/path/my.pdf'); +$page = 7; +$result = $pdf->attachFiles($files, $page) + ->saveAs('/path/withfiles.pdf'); +if ($result === false) { + $error = $pdf->getError(); +} +``` +#### Unpack Files + +Copy file attachments from a PDF to the given directory. + +```php +use mikehaertl\pdftk\Pdf; + +$pdf = new Pdf('/path/my.pdf'); +$result = $pdf->unpackFiles('/path/to/dir'); +if ($result === false) { + $error = $pdf->getError(); +} +``` + +#### Generate FDF + +Create a FDF file from a given filled PDF form. + +```php +use mikehaertl\pdftk\Pdf; + +// Create FDF from PDF +$pdf = new Pdf('/path/form.pdf'); +$result = $pdf->generateFdfFile('/path/data.fdf'); +if ($result === false) { + $error = $pdf->getError(); +} +``` + +#### Get PDF data + +Read out metadata or form field information from a PDF file. + +```php +use mikehaertl\pdftk\Pdf; + +// Get data +$pdf = new Pdf('/path/my.pdf'); +$data = $pdf->getData(); +if ($data === false) { + $error = $pdf->getError(); +} + +// Get form data fields +$pdf = new Pdf('/path/my.pdf'); +$data = $pdf->getDataFields(); +if ($data === false) { + $error = $pdf->getError(); +} + +// Get data as string +echo $data; +$txt = (string) $data; +$txt = $data->__toString(); + +// Get data as array +$arr = (array) $data; +$arr = $data->__toArray(); +$field1 = $data[0]['Field1']; +``` + +#### How to perform more than one operation on a PDF + +As stated above, you can only perform one of the preceeding operations on a single PDF instance. +If you need more than one operation you can feed one `Pdf` instance into another: + +```php +use mikehaertl\pdftk\Pdf; + +// Extract pages 1-5 and 7,4,9 into a new file +$pdf = new Pdf('/path/my.pdf'); +$pdf->cat(1, 5) + ->cat([7, 4, 9]); + +// We now use the above PDF as source file for a new PDF +$pdf2 = new Pdf($pdf); +$result = $pdf2->fillForm(['name' => 'ÄÜÖ äüö мирано čárka']) + ->needAppearances() + ->saveAs('/path/filled.pdf'); +if ($result === false) { + $error = $pdf->getError(); +} +``` + +### Options + +You can combine the above operations with one or more of the following options. + +```php +use mikehaertl\pdftk\Pdf; + +$pdf = new Pdf('/path/my.pdf'); + +$result = $pdf->allow('AllFeatures') // Change permissions + ->flatten() // Merge form data into document (doesn't work well with UTF-8!) + ->compress($value) // Compress/Uncompress + ->keepId('first') // Keep first/last Id of combined files + ->dropXfa() // Drop newer XFA form from PDF + ->dropXmp() // Drop newer XMP data from PDF + ->needAppearances() // Make clients create appearance for form fields + ->setPassword($pw) // Set owner password + ->setUserPassword($pw) // Set user password + ->passwordEncryption(128) // Set password encryption strength + ->saveAs('new.pdf'); +if ($result === false) { + $error = $pdf->getError(); +} + +// Example: Fill PDF form and merge form data into PDF +// Fill form with data array +$result = $pdf = new Pdf('/path/form.pdf'); +$pdf->fillForm(['name' => 'My Name']) + ->flatten() + ->saveAs('/path/filled.pdf'); +if ($result === false) { + $error = $pdf->getError(); +} + +// Example: Remove password from a PDF +$pdf = new Pdf; +$result = $pdf->addFile('/path/my.pdf', null, 'some**password') + ->saveAs('/path/new.pdf'); +if ($result === false) { + $error = $pdf->getError(); +} +``` + +### Shell Command + +The class uses [php-shellcommand](https://github.com/mikehaertl/php-shellcommand) to execute +`pdftk`. You can pass `$options` for its `Command` class as second argument to the constructor: + +```php +use mikehaertl\pdftk\Pdf; + +$pdf = new Pdf('/path/my.pdf', [ + 'command' => '/some/other/path/to/pdftk', + // or on most Windows systems: + // 'command' => 'C:\Program Files (x86)\PDFtk\bin\pdftk.exe', + 'useExec' => true, // May help on Windows systems if execution fails +]); +``` + +### Temporary File + +Internally a temporary file is created via [php-tmpfile](https://github.com/mikehaertl/php-tmpfile). +You can also access that file directly, e.g. if you neither want to send or save the +file but only need the binary PDF content: + +```php +use mikehaertl\pdftk\Pdf; + +$pdf = new Pdf('/path/my.pdf'); +$result = $pdf->fillForm(['name' => 'My Name']) + ->execute(); +if ($result === false) { + $error = $pdf->getError(); +} +$content = file_get_contents( (string) $pdf->getTmpFile() ); +``` + +If you have permission issues you may have to set a directory where your +`pdftk` command can write to: + +```php +use mikehaertl\pdftk\Pdf; + +$pdf = new Pdf('/path/my.pdf'); +$pdf->tempDir = '/home/john/temp'; +``` + +## API + +Please consult the source files for a full documentation of each method. diff --git a/common/vendor/mikehaertl/php-pdftk/composer.json b/common/vendor/mikehaertl/php-pdftk/composer.json new file mode 100644 index 00000000..155ba172 --- /dev/null +++ b/common/vendor/mikehaertl/php-pdftk/composer.json @@ -0,0 +1,31 @@ +{ + "name": "mikehaertl/php-pdftk", + "description": "A PDF conversion and form utility based on pdftk.", + "keywords": ["pdf", "pdftk"], + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Michael Haertl", + "email": "haertl.mike@gmail.com" + } + ], + "require": { + "php": ">=5.3.0", + "mikehaertl/php-shellcommand": "^1.6.3", + "mikehaertl/php-tmpfile": "^1.1.0" + }, + "require-dev": { + "phpunit/phpunit": ">4.0 <9.4" + }, + "autoload": { + "psr-4": { + "mikehaertl\\pdftk\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "tests\\": "tests" + } + } +} diff --git a/common/vendor/mikehaertl/php-pdftk/src/Command.php b/common/vendor/mikehaertl/php-pdftk/src/Command.php new file mode 100644 index 00000000..0919e6b8 --- /dev/null +++ b/common/vendor/mikehaertl/php-pdftk/src/Command.php @@ -0,0 +1,249 @@ + + * @license http://www.opensource.org/licenses/MIT + */ +class Command extends BaseCommand +{ + /** + * @var string the pdftk binary + */ + protected $_command = 'pdftk'; + + /** + * @var array list of input files to process as array('name' => $filename, + * 'password' => $pw) indexed by handle + */ + protected $_files = array(); + + /** + * @var array list of command options, either strings or array with + * arguments to addArg() + */ + protected $_options = array(); + + /** + * @var string the operation to perform + */ + protected $_operation; + + /** + * @var string|array operation arguments, e.g. a list of page ranges or a + * filename or tmp file instance + */ + protected $_operationArgument = array(); + + /** + * @var bool whether to force escaping of the operation argument e.g. for + * filenames + */ + protected $_escapeOperationArgument = false; + + /** + * @param string $name the PDF file to add for processing + * @param string $handle one or more uppercase letters A..Z to reference + * this file later. + * @param string|null $password the owner (or user) password if any + * @return Command the command instance for method chaining + * @throws \Exception + */ + public function addFile($name, $handle, $password = null) + { + $this->checkExecutionStatus(); + $file = array( + 'name' => $name, + 'password' => $password, + ); + $this->_files[$handle] = $file; + return $this; + } + + /** + * @param string $option the pdftk option to add + * @param string|File|null $argument the argument to add, either string, + * File instance or null if none + * @param null|bool whether to escape the option. Default is null meaning + * use Command default setting. + * @return Command the command instance for method chaining + */ + public function addOption($option, $argument = null, $escape = null) + { + $this->_options[] = $argument === null ? $option : array($option, $argument, $escape); + return $this; + } + + /** + * @param string $operation the operation to perform + * @return Command the command instance for method chaining + */ + public function setOperation($operation) + { + $this->checkExecutionStatus(); + $this->_operation = $operation; + return $this; + } + + /** + * @return string|null the current operation or null if none set + */ + public function getOperation() + { + return $this->_operation; + } + + /** + * @param string $value the operation argument + * @param bool $escape whether to escape the operation argument + * @return Command the command instance for method chaining + */ + public function setOperationArgument($value, $escape = false) + { + $this->checkExecutionStatus(); + $this->_operationArgument = $value; + $this->_escapeOperationArgument = $escape; + return $this; + } + + /** + * @return string|array|null the current operation argument as string or + * array or null if none set + */ + public function getOperationArgument() + { + // Typecast to string in case we have a File instance as argument + return is_array($this->_operationArgument) ? $this->_operationArgument : (string) $this->_operationArgument; + } + + /** + * @return int the number of files added to the command + */ + public function getFileCount() + { + return count($this->_files); + } + + /** + * Add a page range as used by some operations + * + * @param int|string|array $start the start page number or an array of page + * numbers. If an array, the other arguments will be ignored. $start can + * also be bigger than $end for pages in reverse order. + * @param int|string|null $end the end page number or null for single page + * (or list if $start is an array) + * @param string|null $handle the handle of the file to use. Can be null if + * only a single file was added. + * @param string|null $qualifier the page number qualifier, either 'even' + * or 'odd' or null for none + * @param string $rotation the rotation to apply to the pages. + * @return Command the command instance for method chaining + */ + public function addPageRange($start, $end = null, $handle = null, $qualifier = null, $rotation = null) + { + $this->checkExecutionStatus(); + if (is_array($start)) { + if ($handle !== null) { + $start = array_map(function ($p) use ($handle) { + return $handle . $p; + }, $start); + } + $range = implode(' ', $start); + } else { + $range = $handle . $start; + if ($end) { + $range .= '-' . $end; + } + $range .= $qualifier . $rotation; + } + $this->_operationArgument[] = $range; + return $this; + } + + /** + * @param string|null $filename the filename to add as 'output' option or + * null if none + * @return bool whether the command was executed successfully + */ + public function execute($filename = null) + { + $this->checkExecutionStatus(); + $this->processInputFiles(); + $this->processOperation(); + $this->processOptions($filename); + return parent::execute(); + } + + /** + * Process input PDF files and create respective command arguments + */ + protected function processInputFiles() + { + $passwords = array(); + foreach ($this->_files as $handle => $file) { + $this->addArg($handle . '=', $file['name']); + if ($file['password'] !== null) { + $passwords[$handle] = $file['password']; + } + } + if ($passwords !== array()) { + $this->addArg('input_pw'); + foreach ($passwords as $handle => $password) { + $this->addArg($handle . '=', $password); + } + } + } + + /** + * Process options and create respective command arguments + * @param string|null $filename if provided an 'output' option will be + * added + */ + protected function processOptions($filename = null) + { + // output must be first option after operation + if ($filename !== null) { + $this->addArg('output', $filename, true); + } + foreach ($this->_options as $option) { + if (is_array($option)) { + $this->addArg($option[0], $option[1], $option[2]); + } else { + $this->addArg($option); + } + } + } + + /** + * Process opearation and create respective command arguments + */ + protected function processOperation() + { + if ($this->_operation !== null) { + $value = $this->_operationArgument ? $this->_operationArgument : null; + if ($value instanceof TmpFile) { + $value = (string) $value; + } + $this->addArg($this->_operation, $value, $this->_escapeOperationArgument); + } + } + + /** + * Ensure that the command was not exectued yet. Throws exception + * otherwise. + * @throws \Exception + */ + protected function checkExecutionStatus() + { + if ($this->getExecuted()) { + throw new \Exception('Operation was already executed'); + } + } +} diff --git a/common/vendor/mikehaertl/php-pdftk/src/DataFields.php b/common/vendor/mikehaertl/php-pdftk/src/DataFields.php new file mode 100644 index 00000000..a5b40f74 --- /dev/null +++ b/common/vendor/mikehaertl/php-pdftk/src/DataFields.php @@ -0,0 +1,166 @@ + + * @author Michael Härtl + * @license http://www.opensource.org/licenses/MIT + */ +class DataFields extends ArrayObject +{ + private $_string; + private $_array; + + /** + * DataFields constructor. + * + * @param string $input + * @param int $flags + * @param string $iterator_class + */ + public function __construct($input = null, $flags = 0, $iterator_class = "ArrayIterator") + { + $this->_string = $input ?: ''; + $this->_array = self::parse($this->_string); + + return parent::__construct($this->_array, $flags, $iterator_class); + } + + /** + * @return string + */ + public function __toString() + { + return $this->_string; + } + + /** + * @return array + */ + public function __toArray() + { + return $this->_array; + } + + /** + * Parse the output of dump_data_fields into an array. + * + * The string to parse can either be a single block of `Xyz:value` lines + * or a set of such blocks, separated by and starting with `---`. + * + * + * Here's an example: + * + * ``` + * --- + * FieldType: Text + * FieldName: Text1 + * FieldFlags: 0 + * FieldValue: University of Missouri : Ray-Holland + * extended line value + * FieldValueDefault: University of Missouri : Ray-Holland + * extended line2 value + * FieldJustification: Left + * FieldMaxLength: 99 + * --- + * FieldType: Text + * FieldName: Text2 + * ... + * ... + * ``` + * + * @param $input the string to parse + * @return array the parsed result + */ + public static function parse($input) + { + if (strncmp('---', $input, 3) === 0) { + // Split blocks only if '---' is followed by 'FieldType' + $blocks = preg_split( + '/^---(\r\n|\n|\r)(?=FieldType:)/m', + substr($input, 3) + ); + return array_map('\mikehaertl\pdftk\DataFields::parseBlock', $blocks); + } else { + return self::parseBlock($input); + } + } + + /** + * Parses a block of this form: + * + * ``` + * Name1: Value1 + * Name2: Value2 + * Name3: Value3 + * ... + * ``` + * + * @param string $block the block to parse + * @return array the parsed block values indexed by respective names + */ + public static function parseBlock($block) + { + $data = array(); + $lines = preg_split("/(\r\n|\n|\r)/", trim($block)); + $continueKey = null; + foreach ($lines as $n => $line) { + if ($continueKey !== null) { + $data[$continueKey] .= "\n" . $line; + if (!self::lineContinues($lines, $n, $continueKey)) { + $continueKey = null; + } + } elseif (preg_match('/([^:]*): ?(.*)/', $line, $match)) { + $key = $match[1]; + $value = $match[2]; + // Convert multiple keys like 'FieldStateOption' or 'FieldValue' + // from Choice fields to array + if (isset($data[$key])) { + $data[$key] = (array) $data[$key]; + $data[$key][] = $value; + } else { + $data[$key] = $value; + } + if (self::lineContinues($lines, $n, $key)) { + $continueKey = $key; + } + } + } + return $data; + } + + /** + * Checks whether the value for the given line number continues on the next + * line, i.e. is a multiline string. + * + * This can be the case for 'FieldValue' and 'FieldValueDefault' keys. To + * find the end of the string we don't simply test for /^Field/, as this + * would also match multiline strings where a line starts with 'Field'. + * + * Instead we assume that the string is always followed by one of these + * keys: + * + * - 'FieldValue:' + * - 'FieldValueDefault:' + * - 'FieldJustification:' + * + * @param array $lines all lines of the block + * @param int $n the 0-based index of the current line + * @param string the key for the value. Only 'FieldValue' and + * 'FieldValueDefault' can span multiple lines + * @return bool whether the value continues in line n + 1 + */ + protected static function lineContinues($lines, $n, $key) + { + return + in_array($key, array('FieldValue', 'FieldValueDefault')) && + array_key_exists($n + 1, $lines) && + !preg_match('/^Field(Value|ValueDefault|Justification):/', $lines[$n + 1]); + } +} diff --git a/common/vendor/mikehaertl/php-pdftk/src/FdfFile.php b/common/vendor/mikehaertl/php-pdftk/src/FdfFile.php new file mode 100644 index 00000000..269ec0a8 --- /dev/null +++ b/common/vendor/mikehaertl/php-pdftk/src/FdfFile.php @@ -0,0 +1,84 @@ + + * @license http://www.opensource.org/licenses/MIT + */ +class FdfFile extends File +{ + // FDF file header + const FDF_HEADER = <<> >> +endobj +trailer +<> +%%EOF +FDF; + + /** + * Constructor + * + * @param array $data the form data as name => value + * @param string|null $suffix the optional suffix for the tmp file + * @param string|null $suffix the optional prefix for the tmp file. If null + * 'php_tmpfile_' is used. + * @param string|null $directory directory where the file should be + * created. Autodetected if not provided. + * @param string|null $encoding of the data. Default is 'UTF-8'. + */ + public function __construct($data, $suffix = null, $prefix = null, $directory = null, $encoding = 'UTF-8') + { + if ($directory === null) { + $directory = self::getTempDir(); + } + $suffix = '.fdf'; + $prefix = 'php_pdftk_fdf_'; + + $this->_fileName = tempnam($directory, $prefix); + $newName = $this->_fileName . $suffix; + rename($this->_fileName, $newName); + $this->_fileName = $newName; + + if (!function_exists('mb_convert_encoding')) { + throw new \Exception('MB extension required.'); + } + + $fields = ''; + foreach ($data as $key => $value) { + // Create UTF-16BE string encode as ASCII hex + // See http://blog.tremily.us/posts/PDF_forms/ + $utf16Value = mb_convert_encoding($value, 'UTF-16BE', $encoding); + + /* Also create UTF-16BE encoded key, this allows field names containing + * german umlauts and most likely many other "special" characters. + * See issue #17 (https://github.com/mikehaertl/php-pdftk/issues/17) + */ + $utf16Key = mb_convert_encoding($key, 'UTF-16BE', $encoding); + + // Escape parenthesis + $utf16Value = strtr($utf16Value, array('(' => '\\(', ')' => '\\)')); + $fields .= "<>\n"; + } + + // Use fwrite, since file_put_contents() messes around with character encoding + $fp = fopen($this->_fileName, 'w'); + fwrite($fp, self::FDF_HEADER); + fwrite($fp, $fields); + fwrite($fp, self::FDF_FOOTER); + fclose($fp); + } +} diff --git a/common/vendor/mikehaertl/php-pdftk/src/InfoFields.php b/common/vendor/mikehaertl/php-pdftk/src/InfoFields.php new file mode 100644 index 00000000..09f46556 --- /dev/null +++ b/common/vendor/mikehaertl/php-pdftk/src/InfoFields.php @@ -0,0 +1,139 @@ + + * @license http://www.opensource.org/licenses/MIT + */ +class InfoFields extends ArrayObject +{ + private $_string; + + private $_array; + + /** + * InfoFields constructor. + * + * @param string $input + * @param int $flags + * @param string $iterator_class + */ + public function __construct($input = null, $flags = 0, $iterator_class = "ArrayIterator") + { + $this->_string = $input ?: ''; + $this->_array = $this->parseData($this->_string); + + return parent::__construct($this->_array, $flags, $iterator_class); + } + + /** + * @return string + */ + public function __toString() + { + return $this->_string; + } + + /** + * @return array + */ + public function __toArray() + { + return $this->_array; + } + + /** + * Parse the output of dump_data into something usable. + * + * The expected string looks similar to this: + * + * InfoBegin + * InfoKey: Creator + * InfoValue: Adobe Acrobat Pro DC 15.0 + * InfoBegin + * InfoKey: Producer + * InfoValue: XYZ + * PdfID0: 1fdce9ed1153ab4c973334b512a67997 + * PdfID1: c7acc878cda02ad7bb401fa8080a8929 + * NumberOfPages: 11 + * BookmarkBegin + * BookmarkTitle: First bookmark + * BookmarkLevel: 1 + * BookmarkPageNumber: 1 + * BookmarkBegin + * BookmarkTitle: Second bookmark + * BookmarkLevel: 1 + * BookmarkPageNumber: 2 + * + * @param $dataString + * @return array + */ + private function parseData($dataString) + { + $output = array(); + foreach (explode(PHP_EOL, $dataString) as $line) { + $trimmedLine = trim($line); + // Parse blocks of the form: + // AbcBegin + // AbcData1: Value1 + // AbcData2: Value2 + // AbcBegin + // AbcData1: Value3 + // AbcData2: Value4 + // ... + if (preg_match('/^(\w+)Begin$/', $trimmedLine, $matches)) { + // Previous group ended - if any - so add it to output + if (!empty($group) && !empty($groupData)) { + $output[$group][] = $groupData; + } + // Now start next group + $group = $matches[1]; // Info, PageMedia, ... + if (!isset($output[$group])) { + $output[$group] = array(); + } + $groupData = array(); + continue; + } + if (!empty($group)) { + // Check for AbcData1: Value1 + if (preg_match("/^$group(\w+): ?(.*)$/", $trimmedLine, $matches)) { + $groupData[$matches[1]] = $matches[2]; + continue; + } else { + // Something else, so group ended + if (!empty($groupData)) { + $output[$group][] = $groupData; + $groupData = array(); + } + $group = null; + } + } + if (preg_match('/([^:]*): ?(.*)/', $trimmedLine, $matches)) { + $output[$matches[1]] = $matches[2]; + } + } + // There could be a final group left if it was not followed by another + // line in the loop + if (!empty($group) && !empty($groupData)) { + $output[$group][] = $groupData; + } + + // Info group is a list of ['Key' => 'x', 'Value' => 'y'], so + // convert it to ['x' => 'y', ...] + if (isset($output['Info'])) { + $data = array(); + foreach ($output['Info'] as $infoGroup) { + if (isset($infoGroup['Key'], $infoGroup['Value'])) { + $data[$infoGroup['Key']] = $infoGroup['Value']; + } + } + $output['Info'] = $data; + } + return $output; + } +} diff --git a/common/vendor/mikehaertl/php-pdftk/src/InfoFile.php b/common/vendor/mikehaertl/php-pdftk/src/InfoFile.php new file mode 100644 index 00000000..d07b86cc --- /dev/null +++ b/common/vendor/mikehaertl/php-pdftk/src/InfoFile.php @@ -0,0 +1,61 @@ + + * @license http://www.opensource.org/licenses/MIT + */ +class InfoFile extends File +{ + + /** + * Constructor + * + * @param array $data the form data as name => value + * @param string|null $suffix the optional suffix for the tmp file + * @param string|null $suffix the optional prefix for the tmp file. If null 'php_tmpfile_' is used. + * @param string|null $directory directory where the file should be created. Autodetected if not provided. + * @param string|null $encoding of the data. Default is 'UTF-8'. + */ + public function __construct($data, $suffix = null, $prefix = null, $directory = null, $encoding = 'UTF-8') + { + if ($directory === null) { + $directory = self::getTempDir(); + } + $suffix = '.txt'; + $prefix = 'php_pdftk_info_'; + + $this->_fileName = tempnam($directory, $prefix); + $newName = $this->_fileName . $suffix; + rename($this->_fileName, $newName); + $this->_fileName = $newName; + + if (!function_exists('mb_convert_encoding')) { + throw new \Exception('MB extension required.'); + } + + $fields = ''; + foreach ($data as $key => $value) { + // Always convert to UTF-8 + if ($encoding !== 'UTF-8' && function_exists('mb_convert_encoding')) { + $value = mb_convert_encoding($value, 'UTF-8', $encoding); + $key = mb_convert_encoding($key, 'UTF-8', $encoding); + $value = defined('ENT_XML1') ? htmlspecialchars($key, ENT_XML1, 'UTF-8') : htmlspecialchars($key); + $key = defined('ENT_XML1') ? htmlspecialchars($value, ENT_XML1, 'UTF-8') : htmlspecialchars($value); + } + $fields .= "InfoBegin\nInfoKey: $key\nInfoValue: $value\n"; + } + + // Use fwrite, since file_put_contents() messes around with character encoding + $fp = fopen($this->_fileName, 'w'); + fwrite($fp, $fields); + fclose($fp); + } +} diff --git a/common/vendor/mikehaertl/php-pdftk/src/Pdf.php b/common/vendor/mikehaertl/php-pdftk/src/Pdf.php new file mode 100644 index 00000000..31270b6d --- /dev/null +++ b/common/vendor/mikehaertl/php-pdftk/src/Pdf.php @@ -0,0 +1,732 @@ + + * @license http://www.opensource.org/licenses/MIT + */ +class Pdf +{ + // The prefix for temporary files + const TMP_PREFIX = 'tmp_php_pdftk_'; + + /** + * @var bool whether to ignore any errors if some non-empty output file was + * still created. Default is false. + */ + public $ignoreWarnings = false; + + /** + * @var null|string an optional directory where temporary files should be + * created. If left empty the directory is autodetected. + */ + public $tempDir; + + /** + * @var File the temporary output file + */ + protected $_tmpFile; + + /** + * @var string the content type of the tmp output + */ + protected $_tmpOutputContentType = 'application/pdf'; + + /** + * @var Command the command instance that executes pdftk + */ + protected $_command; + + /** + * @var int a counter for autogenerated handles + */ + protected $_handle = 0; + + /** + * @var string the error message + */ + protected $_error = ''; + + /** + * @var string|null the output filename. If null (default) a tmp file is + * used as output. If false, no output option is added at all. + */ + protected $_output; + + /** + * @var string the PDF data as returned from getData() + */ + protected $_data; + protected $_data_utf8; + + /** + * @var DataFields the PDF form field data as returned from getDataFields() + */ + protected $_dataFields; + protected $_dataFields_utf8; + + /** + * @var Pdf[]|null if the input was an instance, we keep a reference here, + * so that it won't get unlinked before this object gets destroyed + */ + protected $_pdfs; + + /** + * @param string|Pdf|array $pdf a pdf filename or Pdf instance or an array + * of filenames/instances indexed by a handle. The array values can also + * be arrays of the form array($filename, $password) if some files are + * password protected. + * @param array $options Options to pass to set on the Command instance, + * e.g. the pdftk binary path + */ + public function __construct($pdf = null, $options = array()) + { + $command = $this->getCommand(); + if ($options !== array()) { + $command->setOptions($options); + } + if (is_string($pdf) || $pdf instanceof Pdf) { + $this->addFile($pdf); + } elseif (is_array($pdf)) { + foreach ($pdf as $handle => $file) { + if (is_array($file)) { + $this->addFile($file[0], $handle, $file[1]); + } else { + $this->addFile($file, $handle); + } + } + } + } + + /** + * @param string|Pdf $name the PDF filename or Pdf instance to add for + * processing + * @param string|null $handle one or more uppercase letters A..Z to + * reference this file later. If no handle is provided, an internal handle + * is autocreated, consuming the range Z..A + * @param string|null $password the owner (or user) password if any + * @return Pdf the pdf instance for method chaining + */ + public function addFile($name, $handle = null, $password = null) + { + if ($handle === null || is_numeric($handle)) { + $handle = $this->nextHandle(); + } + if ($name instanceof Pdf) { + // Keep a reference to the object to prevent unlinking + $this->_pdfs[] = $name; + if (!$name->getCommand()->getExecuted()) { + // @todo: Catch errors! + $name->execute(); + } + $name = (string) $name->getTmpFile(); + } + $this->getCommand()->addFile($name, $handle, $password); + return $this; + } + + /** + * Assemble (catenate) pages from the input files. + * + * Values for rotation are (in degrees): north: 0, east: 90, south: 180, + * west: 270, left: -90, right: +90, down: +180. left, right and down make + * relative adjustments to a page's rotation. Note: Older pdftk versions + * use N, E, S, W, L, R, and D instead. + * + * Example: + * + * $pdf = new Pdf; + * $pdf->addFile('file1.pdf', 'A') + * ->addFile('file2.pdf', 'B') + * ->cat(array(1,3),'B')) // pages 1 and 3 of file B + * ->cat(1, 5, 'A', 'odd') // pages 1, 3, 5 of file A + * ->cat('end', 5, 'B') // pages 5 to end of file B in reverse order + * ->cat(null, null, 'B', 'east') // All pages from file B rotated by 90 degree + * ->saveAs('out.pdf'); + * or + * $files = ['file1.pdf', 'file2.pdf', 'file3.pdf']; + * $pdf = new Pdf($files); + * $pdf->cat() // all files, all pages + * ->saveAs('out.pdf'); + * + * @param int|string|array|null $start the start page number or an array of page + * numbers. If an array, the other arguments will be ignored. $start can + * also be bigger than $end for pages in reverse order. If $start is null all + * pages of all files will be added. + * @param int|string|null $end the end page number or null for single page + * (or list if $start is an array) + * @param string|null $handle the handle of the file to use. Can be null if + * only a single file was added. + * @param string|null $qualifier the page number qualifier, either 'even' + * or 'odd' or null for none + * @param string $rotation the rotation to apply to the pages. + * @return Pdf the pdf instance for method chaining + */ + public function cat($start = null, $end = null, $handle = null, $qualifier = null, $rotation = null) + { + $this->getCommand() + ->setOperation('cat') + ->addPageRange($start, $end, $handle, $qualifier, $rotation); + return $this; + } + + /** + * Shuffle pages from the input files. + * + * This works the same as cat(), but each call to this method creates a + * "stream" of pages. The outfile will be assembled by adding one page from + * each stream at a time. + * + * Example: + * + * $pdf = new Pdf; + * $pdf1 = $pdf->addFile('file1.pdf'); + * $pdf->shuffle($pdf1, array(1,3,2)) + * ->shuffle($pdf1, array(4,5,9) + * ->saveAs('out.pdf'); + * + * This will give the page order 1, 4, 3, 5, 2, 9 in the out.pdf + * + * @param string $handle the handle of the input file to use + * @param int|array $start the start page number or an array of page + * numbers. + * @param int|null $end the end page number or null for single page (or + * list if $start is an array) + * @param string|null $qualifier the page number qualifier, either 'even' + * or 'odd' or null for none + * @param string $rotation the rotation to apply to the pages. See cat() + * for more details. + * @return Pdf the pdf instance for method chaining + */ + public function shuffle($start, $end = null, $handle = null, $qualifier = null, $rotation = null) + { + $this->getCommand() + ->setOperation('shuffle') + ->addPageRange($start, $end, $handle, $qualifier, $rotation); + return $this; + } + + /** + * Split the PDF document into pages + * + * @param string|null $filepattern the output name in sprintf format or + * null for default 'pg_%04d.pdf' + * @return bool whether the burst operation was successful + */ + public function burst($filepattern = null) + { + $this->constrainSingleFile(); + $this->getCommand()->setOperation('burst'); + $this->_output = $filepattern === null ? 'pg_%04d.pdf' : $filepattern; + return $this->execute(); + } + + /** + * Attach files to the PDF + * + * @param array $files the list of full paths to the files to attach + * @param string $toPage the page to add the attachment to. If omitted the + * files are attached at the document level. + * @return bool whether the operation was successful + */ + public function attachFiles($files, $toPage = null) + { + $this->constrainSingleFile(); + if ($toPage !== null) { + $files[] = 'to_page'; + $files[] = $toPage; + } + $this->getCommand() + ->setOperation('attach_files') + ->setOperationArgument($files, true); + return $this; + } + + /** + * Copy all attachments from the PDF to the given directory + * + * @param string|null $dir the output directory + * @return bool whether the operation was successful + */ + public function unpackFiles($dir = null) + { + $this->constrainSingleFile(); + $this->getCommand()->setOperation('unpack_files'); + $this->_output = $dir; + return $this->execute(); + } + + /** + * Generate the FDF file for a single PDF file. + * + * @param string $name name of the FDF file + * @return bool whether the pdf is generated successful + */ + public function generateFdfFile($name) + { + $this->constrainSingleFile(); + $this->getCommand()->setOperation('generate_fdf'); + $this->_output = $name; + return $this->execute(); + } + + /** + * Fill a PDF form + * + * @param string|array $data either a XFDF/FDF filename or an array with + * form field data (name => value) + * @param string $encoding the encoding of the data. Default is 'UTF-8'. + * @param bool $dropXfa whether to drop XFA forms (see dropXfa()). Default + * is true. + * @param string $format the file format to use for form filling when + * passing an array in `$data`. This can be `xfdf` or `fdf`. `xfdf` should + * give best results so you should not have to change the default. + * @return Pdf the pdf instance for method chaining + */ + public function fillForm($data, $encoding = 'UTF-8', $dropXfa = true, $format = 'xfdf') + { + $this->constrainSingleFile(); + if (is_array($data)) { + $className = '\mikehaertl\pdftk\\' . ($format === 'xfdf' ? 'XfdfFile' : 'FdfFile'); + $data = new $className($data, null, null, $this->tempDir, $encoding); + } + $this->getCommand() + ->setOperation('fill_form') + ->setOperationArgument($data, true); + + if ($dropXfa) { + $this->dropXfa(); + } + return $this; + } + + /** + * Update meta data of PDF + * + * @param string|array $data either a InfoFile filename or an array with + * form field data (name => value) + * @param string the encoding of the data. Default is 'UTF-8'. + * @return Pdf the pdf instance for method chaining + */ + public function updateInfo($data, $encoding = 'UTF-8') + { + $this->constrainSingleFile(); + if (is_array($data)) { + $data = new InfoFile($data, null, null, $this->tempDir, $encoding); + } + $this->getCommand() + ->setOperation($encoding == 'UTF-8' ? 'update_info_utf8' : 'update_info') + ->setOperationArgument($data, true); + + return $this; + } + + /** + * Apply a PDF as watermark to the background of a single PDF file. + * + * The PDF file must have a transparent background for the watermark to be + * visible. + * + * @param string $file name of the background PDF file. Only the first page + * is used. + * @return Pdf the pdf instance for method chaining + */ + public function background($file) + { + $this->constrainSingleFile(); + $this->getCommand() + ->setOperation('background') + ->setOperationArgument($file, true); + return $this; + } + + /** + * Apply multiple PDF pages as watermark to the corresponding pages of a + * single PDF file. + * + * If $file has fewer pages than the PDF file then the last page is + * repeated as background. + * + * @param string $file name of the background PDF file. + * @return Pdf the pdf instance for method chaining + */ + public function multiBackground($file) + { + $this->getCommand() + ->setOperation('multibackground') + ->setOperationArgument($file, true); + return $this; + } + + /** + * Add $file as overlay to a single PDF file. + * + * The $file should have a transparent background. + * + * @param string $file name of the PDF file to add as overlay. Only the + * first page is used. + * @return Pdf the pdf instance for method chaining + */ + public function stamp($file) + { + $this->constrainSingleFile(); + $this->getCommand() + ->setOperation('stamp') + ->setOperationArgument($file, true); + return $this; + } + + /** + * Add multiple pages from $file as overlay to the corresponding pages of a + * single PDF file. + * + * If $file has fewer pages than the PDF file then the last page is + * repeated as overlay. + * + * @param string $file name of the PDF file to add as overlay + * @return Pdf the pdf instance for method chaining + */ + public function multiStamp($file) + { + $this->getCommand() + ->setOperation('multistamp') + ->setOperationArgument($file, true); + return $this; + } + + /** + * @param bool $utf8 whether to dump the data UTF-8 encoded. Default is + * true. + * @return InfoFields|bool meta data about the PDF or false on failure + */ + public function getData($utf8 = true) + { + $property = $utf8 ? '_data_utf8' : '_data'; + if ($this->$property === null) { + $command = $this->getCommand(); + $command->setOperation($utf8 ? 'dump_data_utf8' : 'dump_data'); + if (!$command->execute()) { + $this->_error = $command->getError(); + return false; + } else { + $this->$property = new InfoFields(trim($command->getOutput())); + } + } + return $this->$property; + } + + /** + * @param bool $utf8 whether to dump the data UTF-8 encoded. Default is + * true. + * @return DataFields|bool data about the PDF form fields or false on + * failure + */ + public function getDataFields($utf8 = true) + { + $property = $utf8 ? '_dataFields_utf8' : '_dataFields'; + if ($this->$property === null) { + $command = $this->getCommand(); + $command->setOperation($utf8 ? 'dump_data_fields_utf8' : 'dump_data_fields'); + if (!$command->execute()) { + $this->_error = $command->getError(); + return false; + } else { + $this->$property = new DataFields(trim($command->getOutput())); + } + } + return $this->$property; + } + + /** + * Set PDF permissions + * + * + * @param string|null $permissions list of space separated permissions or + * null for none. The available permissions are Printing, DegradedPrinting, + * ModifyContents, Assembly, CopyContents, ScreenReaders, + * ModifyAnnotations, FillIn, AllFeatures. + * @return Pdf the pdf instance for method chaining + */ + public function allow($permissions = null) + { + $this->getCommand() + ->addOption('allow', $permissions, false); + return $this; + } + + /** + * Flatten the PDF form fields values into a single PDF file. + * + * @return Pdf the pdf instance for method chaining + */ + public function flatten() + { + $this->getCommand() + ->addOption('flatten'); + return $this; + } + + /** + * Restore/remove compression + * + * @param bool $compress whether to restore (default) or remove the + * compression + * @return Pdf the pdf instance for method chaining + */ + public function compress($compress = true) + { + $this->getCommand() + ->addOption($compress ? 'compress' : 'uncompress'); + return $this; + } + + /** + * When combining multiple PDFs, use either the first or last ID in the + * output. If not called, a new ID is created. + * + * @param string $id, either 'first' (default) or 'last' + * @return Pdf the pdf instance for method chaining + */ + public function keepId($id = 'first') + { + $this->getCommand() + ->addOption($id === 'first' ? 'keep_first_id' : 'keep_final_id'); + return $this; + } + + /** + * Set need_appearances flag in PDF + * + * This flag makes sure, that a PDF reader takes care of rendering form + * field content, even if it contains non ASCII characters. You should + * always use this option if you fill in forms e.g. with Unicode + * characters. You can't combine this option with flatten() though! + * + * @return Pdf the pdf instance for method chaining + */ + public function needAppearances() + { + $this->getCommand() + ->addOption('need_appearances'); + return $this; + } + + /** + * Drop XFA data from forms created with newer Acrobat. + * + * Newer PDF forms contain both, the newer XFA and the older AcroForm form + * fields. PDF readers can use both, but will prefer XFA if present. Since + * pdftk can only fill in AcroForm data you should always add this option + * when filling in forms with pdftk. + * + * @return Pdf the pdf instance for method chaining + */ + public function dropXfa() + { + $this->getCommand() + ->addOption('drop_xfa'); + return $this; + } + + /** + * Drop XMP meta data + * + * Newer PDFs can contain both, new style XMP data and old style info + * directory. PDF readers can use both, but will prefer XMP if present. + * Since pdftk can only update the info directory you should always add + * this option when updating PDF info. + * + * @return Pdf the pdf instance for method chaining + */ + public function dropXmp() + { + $this->getCommand() + ->addOption('drop_xmp'); + return $this; + } + + /** + * @param string $password the owner password to set on the output PDF + * @return Pdf the pdf instance for method chaining + */ + public function setPassword($password) + { + $this->getCommand() + ->addOption('owner_pw', $password, true); + return $this; + } + + /** + * @param string $password the user password to set on the output PDF + * @return Pdf the pdf instance for method chaining + */ + public function setUserPassword($password) + { + $this->getCommand() + ->addOption('user_pw', $password, true); + return $this; + } + + /** + * @param int $strength the password encryption strength. Default is 128 + * @return Pdf the pdf instance for method chaining + */ + public function passwordEncryption($strength = 128) + { + $this->getCommand() + ->addOption($strength == 128 ? 'encrypt_128bit' : 'encrypt_40bit'); + return $this; + } + + /** + * Execute the operation and save the output file + * + * @param string $name of output file + * @return bool whether the PDF could be processed and saved + */ + public function saveAs($name) + { + if (!$this->getCommand()->getExecuted() && !$this->execute()) { + return false; + } + $tmpFile = (string) $this->getTmpFile(); + if (!copy($tmpFile, $name)) { + $this->_error = "Could not copy PDF from tmp location '$tmpFile' to '$name'"; + return false; + } + return true; + } + + /** + * Send PDF to client, either inline or as download (triggers PDF creation) + * + * @param string|null $filename the filename to send. If empty, the PDF is + * streamed inline. + * @param bool $inline whether to force inline display of the PDF, even if + * filename is present. + * @param array $headers a list of additional HTTP headers to send in the + * response as an array. The array keys are the header names like + * 'Cache-Control' and the array values the header value strings to send. + * Each array value can also be another array of strings if the same header + * should be sent multiple times. This can also be used to override + * automatically created headers like 'Expires' or 'Content-Length'. To suppress + * automatically created headers, `false` can also be used as header value. + * @return bool whether PDF was created successfully + */ + public function send($filename = null, $inline = false, $headers = array()) + { + if (!$this->getCommand()->getExecuted() && !$this->execute()) { + return false; + } + $this->getTmpFile()->send($filename, $this->_tmpOutputContentType, $inline, $headers); + return true; + } + + /** + * Get the raw PDF contents (triggers PDF creation). + * + * @return string|bool the PDF content as a string or `false` if the PDF + * wasn't created successfully. + */ + public function toString() + { + if (!$this->getCommand()->getExecuted() && !$this->execute()) { + return false; + } + return file_get_contents($this->getTmpFile()->getFileName()); + } + + /** + * @return Command the command instance that executes pdftk + */ + public function getCommand() + { + if ($this->_command === null) { + $this->_command = new Command; + } + return $this->_command; + } + + /** + * @return File the temporary output file instance + */ + public function getTmpFile() + { + if ($this->_tmpFile === null) { + $this->_tmpFile = new File('', '.pdf', self::TMP_PREFIX, $this->tempDir); + } + return $this->_tmpFile; + } + + /** + * @return string the error message or an empty string if none + */ + public function getError() + { + return $this->_error; + } + + /** + * Execute the pdftk command and store the output file to a temporary + * location or $this->_output if set. You should probably never call this + * method unless you only need a temporary PDF file as result. + * + * @return bool whether the command was executed successfully + */ + public function execute() + { + $command = $this->getCommand(); + if ($command->getExecuted()) { + return false; + } + + if ($this->_output === false) { + $filename = null; + } else { + $filename = $this->_output ? $this->_output : (string) $this->getTmpFile(); + } + if (!$command->execute($filename)) { + $this->_error = $command->getError(); + if ($filename && !(file_exists($filename) && filesize($filename) !== 0 && $this->ignoreWarnings)) { + return false; + } + } + return true; + } + + /** + * Make sure, that only one file is present + */ + protected function constrainSingleFile() + { + if ($this->getCommand()->getFileCount() > 1) { + throw new \Exception('This operation can only process single files'); + } + } + + /** + * @return string the next handle in the series A, B, C, ... Z, AA, AB... + */ + protected function nextHandle() + { + // N.B. Multi-character handles are only available in pdftk 1.45+ + + $i = $this->_handle++; + $char = 'A'; + while ($i-- > 0) { + $char++; + } + + return $char; + } +} diff --git a/common/vendor/mikehaertl/php-pdftk/src/XfdfFile.php b/common/vendor/mikehaertl/php-pdftk/src/XfdfFile.php new file mode 100644 index 00000000..7204f989 --- /dev/null +++ b/common/vendor/mikehaertl/php-pdftk/src/XfdfFile.php @@ -0,0 +1,230 @@ + field value + * 'Firstname' => 'John', + * + * // Hierarchical/nested fields in dot notation + * 'Address.Street' => 'Some Street', + * 'Address.City' => 'Any City', + * + * // Multi value fields + * 'Pets' => ['Cat', 'Mouse'], + * ] + * ``` + * + * This will result in the following XML structure (header/footer omitted): + * + * ``` + * + * John + * + * + * + * Some Street + * + * + * Any City + * + * + * + * Cat + * Mouse + * + * ``` + * + * @author Tomas Holy + * @author Michael Härtl + * @license http://www.opensource.org/licenses/MIT + */ +class XfdfFile extends File +{ + // XFDF file header + const XFDF_HEADER = << + + + +FDF; + + // XFDF file footer + const XFDF_FOOTER = << + + +FDF; + + /** + * Constructor + * + * + * @param array $data the form data as name => value + * @param string|null $suffix the optional suffix for the tmp file + * @param string|null $prefix the optional prefix for the tmp file. If null + * 'php_tmpfile_' is used. + * @param string|null $directory directory where the file should be + * created. Autodetected if not provided. + * @param string|null $encoding of the data. Default is 'UTF-8'. + */ + public function __construct($data, $suffix = null, $prefix = null, $directory = null, $encoding = 'UTF-8') + { + if ($directory === null) { + $directory = self::getTempDir(); + } + if ($suffix === null) { + $suffix = '.xfdf'; + } + if ($prefix === null) { + $prefix = 'php_pdftk_xfdf_'; + } + + $tempfile = tempnam($directory, $prefix); + $this->_fileName = $tempfile . $suffix; + rename($tempfile, $this->_fileName); + + $fields = $this->parseData($data, $encoding); + $this->writeXml($fields); + } + + /** + * Parses an array of key/value data into a nested array structure. + * + * The data may use keys in dot notation (#55). Values can also be arrays in + * case of multi value fields (#148). To make both distinguishable in the + * result array keys that represent field names are prefixed with `_`. This + * also allows for numeric field names (#260). + * + * For example an array like this: + * + * ``` + * [ + * 'a' => 'value a', + * 'b.x' => 'value b.x', + * 'b.y' => 'value b.y', + * + * 'c.0' => 'val c.0', + * 'c.1' => 'val c.1', + * + * 'd' => ['m1', 'm2'], + * ] + * ``` + * + * Will become: + * + * ``` + * [ + * '_a' => 'value a', + * '_b' => [ + * '_x' => 'value b.x', + * '_y' => 'value b.y', + * ], + * '_c' => [ + * '_0' => 'value c.0', + * '_1' => 'value c.1', + * ], + * '_d' => [ + * // notice the missing underscore in the keys + * 0 => 'm1', + * 1 => 'm2', + * ], + * ] + * + * + * @param mixed $data the data to parse + * @param string the encoding of the data + * @return array the result array in UTF-8 encoding with dot keys converted + * to nested arrays + */ + protected function parseData($data, $encoding) + { + $result = array(); + foreach ($data as $key => $value) { + if ($encoding !== 'UTF-8' && function_exists('mb_convert_encoding')) { + $key = mb_convert_encoding($key, 'UTF-8', $encoding); + $value = mb_convert_encoding($value, 'UTF-8', $encoding); + } + if (strpos($key, '.') === false) { + $result['_' . $key] = $value; + } else { + $target = &$result; + $keyParts = explode('.', $key); + $lastPart = array_pop($keyParts); + foreach ($keyParts as $part) { + if (!isset($target['_' . $part])) { + $target['_' . $part] = array(); + } + $target = &$target['_' . $part]; + } + $target['_' . $lastPart] = $value; + } + } + return $result; + } + + /** + * Write the given fields to an XML file + * + * @param array $fields the fields in a nested array structure + */ + protected function writeXml($fields) + { + // Use fwrite, since file_put_contents() messes around with character encoding + $fp = fopen($this->_fileName, 'w'); + fwrite($fp, self::XFDF_HEADER); + $this->writeFields($fp, $fields); + fwrite($fp, self::XFDF_FOOTER); + fclose($fp); + } + + /** + * Write the fields to the given filepointer + * + * @param int $fp + * @param mixed[] $fields an array of field values as returned by + * `parseData()`. + */ + protected function writeFields($fp, $fields) + { + foreach ($fields as $key => $value) { + $key = $this->xmlEncode(substr($key,1)); + fwrite($fp, "\n"); + if (!is_array($value)) { + $value = array($value); + } + if (array_key_exists(0, $value)) { + // Numeric keys: single or multi-value field + foreach($value as $val) { + $val = $this->xmlEncode($val); + fwrite($fp, "$val\n"); + } + } else { + // String keys: nested/hierarchical fields + $this->writeFields($fp, $value); + } + fwrite($fp, "\n"); + } + } + + /** + * @param string $value the value to encode + * @return string the value correctly encoded for use in a XML document + */ + protected function xmlEncode($value) + { + return defined('ENT_XML1') ? + htmlspecialchars($value, ENT_XML1, 'UTF-8') : + htmlspecialchars($value); + } +} diff --git a/common/vendor/mikehaertl/php-shellcommand/.github/workflows/tests.yml b/common/vendor/mikehaertl/php-shellcommand/.github/workflows/tests.yml new file mode 100644 index 00000000..94c02ab7 --- /dev/null +++ b/common/vendor/mikehaertl/php-shellcommand/.github/workflows/tests.yml @@ -0,0 +1,48 @@ +name: Tests +on: pull_request +jobs: + phpunit: + name: PHP ${{ matrix.php }} + runs-on: ubuntu-latest + strategy: + matrix: + php: + - "5.3" + - "5.4" + - "5.5" + - "5.6" + - "7.0" + - "7.1" + - "7.2" + - "7.3" + - "7.4" + - "8.0" + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + tools: composer:v2 + + - name: Update composer + run: composer self-update + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install composer packages + run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi + + - name: Run phpunit + run: vendor/bin/phpunit --color=always diff --git a/common/vendor/mikehaertl/php-shellcommand/CHANGELOG.md b/common/vendor/mikehaertl/php-shellcommand/CHANGELOG.md new file mode 100644 index 00000000..0448f28e --- /dev/null +++ b/common/vendor/mikehaertl/php-shellcommand/CHANGELOG.md @@ -0,0 +1,49 @@ +# CHANGELOG + +## 1.2.2 + + * Issue #16: Command on different drive didn't work on windows + +## 1.2.1 + + * Issue #1: Command with spaces didn't work on windows + +## 1.2.0 + + * Add option to return untrimmed output and error + +## 1.1.0 + + * Issue #7: UTF-8 encoded arguments where truncated + +## 1.0.7 + + * Issue #6: Solve `proc_open()` pipe configuration for both, Windows / Linux + +## 1.0.6 + + * Undid `proc_open()` changes as it broke error capturing + +## 1.0.5 + + * Improve `proc_open()` pipe configuration + +## 1.0.4 + + * Add `$useExec` option to fix Windows issues (#3) + +## 1.0.3 + + * Add `getExecuted()` to find out execution status of the command + +## 1.0.2 + + * Add `$escape` parameter to `addArg()` to override escaping settings per call + +## 1.0.1 + + * Minor fixes + +## 1.0.0 + + * Initial release diff --git a/common/vendor/mikehaertl/php-shellcommand/LICENSE b/common/vendor/mikehaertl/php-shellcommand/LICENSE new file mode 100644 index 00000000..c60edfb5 --- /dev/null +++ b/common/vendor/mikehaertl/php-shellcommand/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Michael Härtl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/common/vendor/mikehaertl/php-shellcommand/README.md b/common/vendor/mikehaertl/php-shellcommand/README.md new file mode 100644 index 00000000..6697397c --- /dev/null +++ b/common/vendor/mikehaertl/php-shellcommand/README.md @@ -0,0 +1,145 @@ +php-shellcommand +=========== + +[![GitHub Tests](https://github.com/mikehaertl/php-shellcommand/workflows/Tests/badge.svg)](https://github.com/mikehaertl/php-shellcommand/actions) +[![Packagist Version](https://img.shields.io/packagist/v/mikehaertl/php-shellcommand?label=version)](https://packagist.org/packages/mikehaertl/php-shellcommand) +[![Packagist Downloads](https://img.shields.io/packagist/dt/mikehaertl/php-shellcommand)](https://packagist.org/packages/mikehaertl/php-shellcommand) +[![GitHub license](https://img.shields.io/github/license/mikehaertl/php-shellcommand)](https://github.com/mikehaertl/php-shellcommand/blob/master/LICENSE) +[![Packagist PHP Version Support](https://img.shields.io/packagist/php-v/mikehaertl/php-shellcommand)](https://packagist.org/packages/mikehaertl/php-shellcommand) + +php-shellcommand provides a simple object oriented interface to execute shell commands. + +## Installing + +### Prerequisites + +Your php version must be `5.4` or later. + +### Installing with composer + +This package can be installed easily using composer. + +``` +composer require mikehaertl/php-shellcommand +``` + +## Features + + * Catches `stdOut`, `stdErr` and `exitCode` + * Handle argument escaping + * Pass environment vars and other options to `proc_open()` + +## Examples + +### Basic Example + +```php +execute()) { + echo $command->getOutput(); +} else { + echo $command->getError(); + $exitCode = $command->getExitCode(); +} +``` + +### Advanced Features + +```php +// Create command with options array +$command = new Command(array( + 'command' => '/usr/local/bin/mycommand', + + // Will be passed as environment variables to the command + 'procEnv' => array( + 'DEMOVAR' => 'demovalue' + ), + + // Will be passed as options to proc_open() + 'procOptions' => array( + 'bypass_shell' => true, + ), +)); + +// Add arguments with correct escaping: +// results in --name='d'\''Artagnan' +$command->addArg('--name=', "d'Artagnan"); + +// Add argument with several values +// results in --keys key1 key2 +$command->addArg('--keys', array('key1','key2')); + +// Add string to pipe to command on standard input +$command->setStdIn('string'); +``` + +## API + +### Properties + + * `$escapeArgs`: Whether to escape any argument passed through `addArg()`. Default is `true`. + * `$escapeCommand`: Whether to escape the command passed to `setCommand()` or the constructor. + This is only useful if `$escapeArgs` is `false`. Default is `false`. + * `$useExec`: Whether to use `exec()` instead of `proc_open()`. This is a workaround for OS which + have problems with `proc_open()`. Default is `false`. + * `$captureStdErr`: Whether to capture stderr when `useExec` is set. This will try to redirect + the otherwhise unavailable `stderr` to `stdout`, so that both have the same content on error. + Default is `true`. + * `$procCwd`: The initial working dir passed to `proc_open()`. Default is `null` for current + PHP working dir. + * `$procEnv`: An array with environment variables to pass to `proc_open()`. Default is `null` for none. + * `$procOptions`: An array of `other_options` for `proc_open()`. Default is `null` for none. + * `$nonBlockingMode`: Whether to set the stdin/stdout/stderr streams to non-blocking + mode when `proc_open()` is used. This allows to have huge inputs/outputs + without making the process hang. The default is `null` which will enable + the feature on Non-Windows systems. Set it to `true` or `false` to manually + enable/disable it. Note that it doesn't work on Windows. + * `$timeout`: The time in seconds after which the command should be + terminated. This only works in non-blocking mode. Default is `null` which + means the process is never terminated. + * `$locale`: The locale to (temporarily) set with `setlocale()` before running the command. + This can be set to e.g. `en_US.UTF-8` if you have issues with UTF-8 encoded arguments. + +You can configure all these properties via an array that you pass in the constructor. You can also +pass `command`, `execCommand` and `args` as options. This will call the respective setter (`setCommand()`, +`setExecCommand()`, etc.). + +### Methods + + * `__construct($options = null)` + * `$options`: either a command string or an options array (see `setOptions()`) + * `setOptions($options)`: Set command options + * `$options`: array of name => value options that should be applied to the object. + You can also pass options that use a setter, e.g. you can pass a `command` option which + will be passed to `setCommand().` + * `setCommand($command)`: Set command + * `$command`: The command or full command string to execute, like `gzip` or `gzip -d`. + You can still call `addArg()` to add more arguments to the command. If `$escapeCommand` was + set to `true`, the command gets escaped through `escapeshellcmd()`. + * `getCommand()`: The command that was set through `setCommand()` or passed to the constructor. + * `getExecCommand()`: The full command string to execute. + * `setArgs($args)`: Set argument as string + * `$args`: The command arguments as string. Note, that these will not get escaped! + * `getArgs()`: The command arguments that where set through `setArgs()` or `addArg()`, as string + * `addArg($key, $value=null, $escape=null)`: Add argument with correct escaping + * `$key`: The argument key to add e.g. `--feature` or `--name=`. If the key does not end with + and `=`, the `$value` will be separated by a space, if any. Keys are not escaped unless + `$value` is null and `$escape` is `true`. + * `$value`: The optional argument value which will get escaped if `$escapeArgs` is true. + An array can be passed to add more than one value for a key, e.g. `addArg('--exclude', array('val1','val2'))` + which will create the option "--exclude 'val1' 'val2'". + * `$escape`: If set, this overrides the `$escapeArgs` setting and enforces escaping/no escaping + * `setStdIn()`: String or resource to supply to command via standard input. + * `getOutput()`: The command output as string. Empty if none. + * `getError()`: The error message, either stderr or internal message. Empty if no error. + * `getStdErr()`: The stderr output. Empty if none. + * `getExitCode()`: The exit code. + * `getExecuted()`: Whether the command was successfully executed. + * `execute()`: Executes the command and returns `true` on success, `false` otherwhise. + +> **Note:** `getError()`, `getStdErr()` and `getOutput()` return the trimmed output. +> You can pass `false` to these methods if you need any possible line breaks at the end. diff --git a/common/vendor/mikehaertl/php-shellcommand/composer.json b/common/vendor/mikehaertl/php-shellcommand/composer.json new file mode 100644 index 00000000..a638fca6 --- /dev/null +++ b/common/vendor/mikehaertl/php-shellcommand/composer.json @@ -0,0 +1,28 @@ +{ + "name": "mikehaertl/php-shellcommand", + "description": "An object oriented interface to shell commands", + "keywords": ["shell"], + "license": "MIT", + "authors": [ + { + "name": "Michael Härtl", + "email": "haertl.mike@gmail.com" + } + ], + "require": { + "php": ">= 5.3.0" + }, + "require-dev": { + "phpunit/phpunit": ">4.0 <=9.4" + }, + "autoload": { + "psr-4": { + "mikehaertl\\shellcommand\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "tests\\": "tests" + } + } +} diff --git a/common/vendor/mikehaertl/php-shellcommand/src/Command.php b/common/vendor/mikehaertl/php-shellcommand/src/Command.php new file mode 100644 index 00000000..c5527244 --- /dev/null +++ b/common/vendor/mikehaertl/php-shellcommand/src/Command.php @@ -0,0 +1,546 @@ + + * @license http://www.opensource.org/licenses/MIT + */ +class Command +{ + /** + * @var bool whether to escape any argument passed through `addArg()`. + * Default is `true`. + */ + public $escapeArgs = true; + + /** + * @var bool whether to escape the command passed to `setCommand()` or the + * constructor. This is only useful if `$escapeArgs` is `false`. Default + * is `false`. + */ + public $escapeCommand = false; + + /** + * @var bool whether to use `exec()` instead of `proc_open()`. This can be + * used on Windows system to workaround some quirks there. Note, that any + * errors from your command will be output directly to the PHP output + * stream. `getStdErr()` will also not work anymore and thus you also won't + * get the error output from `getError()` in this case. You also can't pass + * any environment variables to the command if this is enabled. Default is + * `false`. + */ + public $useExec = false; + + /** + * @var bool whether to capture stderr (2>&1) when `useExec` is true. This + * will try to redirect the stderr to stdout and provide the complete + * output of both in `getStdErr()` and `getError()`. Default is `true`. + */ + public $captureStdErr = true; + + /** + * @var string|null the initial working dir for `proc_open()`. Default is + * `null` for current PHP working dir. + */ + public $procCwd; + + /** + * @var array|null an array with environment variables to pass to + * `proc_open()`. Default is `null` for none. + */ + public $procEnv; + + /** + * @var array|null an array of other_options for `proc_open()`. Default is + * `null` for none. + */ + public $procOptions; + + /** + * @var bool|null whether to set the stdin/stdout/stderr streams to + * non-blocking mode when `proc_open()` is used. This allows to have huge + * inputs/outputs without making the process hang. The default is `null` + * which will enable the feature on Non-Windows systems. Set it to `true` + * or `false` to manually enable/disable it. It does not work on Windows. + */ + public $nonBlockingMode; + + /** + * @var int the time in seconds after which a command should be terminated. + * This only works in non-blocking mode. Default is `null` which means the + * process is never terminated. + */ + public $timeout; + + /** + * @var null|string the locale to temporarily set before calling + * `escapeshellargs()`. Default is `null` for none. + */ + public $locale; + + /** + * @var null|string|resource to pipe to standard input + */ + protected $_stdIn; + + /** + * @var string the command to execute + */ + protected $_command; + + /** + * @var array the list of command arguments + */ + protected $_args = array(); + + /** + * @var string the stdout output + */ + protected $_stdOut = ''; + + /** + * @var string the stderr output + */ + protected $_stdErr = ''; + + /** + * @var int the exit code + */ + protected $_exitCode; + + /** + * @var string the error message + */ + protected $_error = ''; + + /** + * @var bool whether the command was successfully executed + */ + protected $_executed = false; + + /** + * @param string|array $options either a command string or an options array + * @see setOptions + */ + public function __construct($options = null) + { + if (is_array($options)) { + $this->setOptions($options); + } elseif (is_string($options)) { + $this->setCommand($options); + } + } + + /** + * @param array $options array of name => value options that should be + * applied to the object You can also pass options that use a setter, e.g. + * you can pass a `fileName` option which will be passed to + * `setFileName()`. + * @throws \Exception + * @return static for method chaining + */ + public function setOptions($options) + { + foreach ($options as $key => $value) { + if (property_exists($this, $key)) { + $this->$key = $value; + } else { + $method = 'set'.ucfirst($key); + if (method_exists($this, $method)) { + call_user_func(array($this,$method), $value); + } else { + throw new \Exception("Unknown configuration option '$key'"); + } + } + } + return $this; + } + + /** + * @param string $command the command or full command string to execute, + * like 'gzip' or 'gzip -d'. You can still call addArg() to add more + * arguments to the command. If $escapeCommand was set to true, the command + * gets escaped with escapeshellcmd(). + * @return static for method chaining + */ + public function setCommand($command) + { + if ($this->escapeCommand) { + $command = escapeshellcmd($command); + } + if ($this->getIsWindows()) { + // Make sure to switch to correct drive like "E:" first if we have + // a full path in command + if (isset($command[1]) && $command[1]===':') { + $position = 1; + // Could be a quoted absolute path because of spaces. + // i.e. "C:\Program Files (x86)\file.exe" + } elseif (isset($command[2]) && $command[2]===':') { + $position = 2; + } else { + $position = false; + } + + // Absolute path. If it's a relative path, let it slide. + if ($position) { + $command = sprintf( + $command[$position - 1] . ': && cd %s && %s', + escapeshellarg(dirname($command)), + escapeshellarg(basename($command)) + ); + } + } + $this->_command = $command; + return $this; + } + + /** + * @param string|resource $stdIn If set, the string will be piped to the + * command via standard input. This enables the same functionality as + * piping on the command line. It can also be a resource like a file + * handle or a stream in which case its content will be piped into the + * command like an input redirection. + * @return static for method chaining + */ + public function setStdIn($stdIn) { + $this->_stdIn = $stdIn; + return $this; + } + + /** + * @return string|null the command that was set through setCommand() or + * passed to the constructor. `null` if none. + */ + public function getCommand() + { + return $this->_command; + } + + /** + * @return string|bool the full command string to execute. If no command + * was set with setCommand() or passed to the constructor it will return + * `false`. + */ + public function getExecCommand() + { + $command = $this->getCommand(); + if (!$command) { + $this->_error = 'Could not locate any executable command'; + return false; + } + + $args = $this->getArgs(); + return $args ? $command.' '.$args : $command; + } + + /** + * @param string $args the command arguments as string. Note that these + * will not get escaped! + * @return static for method chaining + */ + public function setArgs($args) + { + $this->_args = array($args); + return $this; + } + + /** + * @return string the command args that where set with setArgs() or added + * with addArg() separated by spaces + */ + public function getArgs() + { + return implode(' ', $this->_args); + } + + /** + * @param string $key the argument key to add e.g. `--feature` or + * `--name=`. If the key does not end with and `=`, the $value will be + * separated by a space, if any. Keys are not escaped unless $value is null + * and $escape is `true`. + * @param string|array|null $value the optional argument value which will + * get escaped if $escapeArgs is true. An array can be passed to add more + * than one value for a key, e.g. `addArg('--exclude', + * array('val1','val2'))` which will create the option `'--exclude' 'val1' + * 'val2'`. + * @param bool|null $escape if set, this overrides the $escapeArgs setting + * and enforces escaping/no escaping + * @return static for method chaining + */ + public function addArg($key, $value = null, $escape = null) + { + $doEscape = $escape !== null ? $escape : $this->escapeArgs; + $useLocale = $doEscape && $this->locale !== null; + + if ($useLocale) { + $locale = setlocale(LC_CTYPE, 0); // Returns current locale setting + setlocale(LC_CTYPE, $this->locale); + } + if ($value === null) { + $this->_args[] = $doEscape ? escapeshellarg($key) : $key; + } else { + if (substr($key, -1) === '=') { + $separator = '='; + $argKey = substr($key, 0, -1); + } else { + $separator = ' '; + $argKey = $key; + } + $argKey = $doEscape ? escapeshellarg($argKey) : $argKey; + + if (is_array($value)) { + $params = array(); + foreach ($value as $v) { + $params[] = $doEscape ? escapeshellarg($v) : $v; + } + $this->_args[] = $argKey . $separator . implode(' ', $params); + } else { + $this->_args[] = $argKey . $separator . + ($doEscape ? escapeshellarg($value) : $value); + } + } + if ($useLocale) { + setlocale(LC_CTYPE, $locale); + } + + return $this; + } + + /** + * @param bool $trim whether to `trim()` the return value. The default is `true`. + * @return string the command output (stdout). Empty if none. + */ + public function getOutput($trim = true) + { + return $trim ? trim($this->_stdOut) : $this->_stdOut; + } + + /** + * @param bool $trim whether to `trim()` the return value. The default is `true`. + * @return string the error message, either stderr or an internal message. + * Empty string if none. + */ + public function getError($trim = true) + { + return $trim ? trim($this->_error) : $this->_error; + } + + /** + * @param bool $trim whether to `trim()` the return value. The default is `true`. + * @return string the stderr output. Empty if none. + */ + public function getStdErr($trim = true) + { + return $trim ? trim($this->_stdErr) : $this->_stdErr; + } + + /** + * @return int|null the exit code or null if command was not executed yet + */ + public function getExitCode() + { + return $this->_exitCode; + } + + /** + * @return string whether the command was successfully executed + */ + public function getExecuted() + { + return $this->_executed; + } + + /** + * Execute the command + * + * @return bool whether execution was successful. If `false`, error details + * can be obtained from getError(), getStdErr() and getExitCode(). + */ + public function execute() + { + $command = $this->getExecCommand(); + + if (!$command) { + return false; + } + + if ($this->useExec) { + $execCommand = $this->captureStdErr ? "$command 2>&1" : $command; + exec($execCommand, $output, $this->_exitCode); + $this->_stdOut = implode("\n", $output); + if ($this->_exitCode !== 0) { + $this->_stdErr = $this->_stdOut; + $this->_error = empty($this->_stdErr) ? 'Command failed' : $this->_stdErr; + return false; + } + } else { + $isInputStream = $this->_stdIn !== null && + is_resource($this->_stdIn) && + in_array(get_resource_type($this->_stdIn), array('file', 'stream')); + $isInputString = is_string($this->_stdIn); + $hasInput = $isInputStream || $isInputString; + $hasTimeout = $this->timeout !== null && $this->timeout > 0; + + $descriptors = array( + 1 => array('pipe','w'), + 2 => array('pipe', $this->getIsWindows() ? 'a' : 'w'), + ); + if ($hasInput) { + $descriptors[0] = array('pipe', 'r'); + } + + + // Issue #20 Set non-blocking mode to fix hanging processes + $nonBlocking = $this->nonBlockingMode === null ? + !$this->getIsWindows() : $this->nonBlockingMode; + + $startTime = $hasTimeout ? time() : 0; + $process = proc_open($command, $descriptors, $pipes, $this->procCwd, $this->procEnv, $this->procOptions); + + if (is_resource($process)) { + + if ($nonBlocking) { + stream_set_blocking($pipes[1], false); + stream_set_blocking($pipes[2], false); + if ($hasInput) { + $writtenBytes = 0; + $isInputOpen = true; + stream_set_blocking($pipes[0], false); + if ($isInputStream) { + stream_set_blocking($this->_stdIn, false); + } + } + + // Due to the non-blocking streams we now have to check in + // a loop if the process is still running. We also need to + // ensure that all the pipes are written/read alternately + // until there's nothing left to write/read. + $isRunning = true; + while ($isRunning) { + $status = proc_get_status($process); + $isRunning = $status['running']; + + // We first write to stdIn if we have an input. For big + // inputs it will only write until the input buffer of + // the command is full (the command may now wait that + // we read the output buffers - see below). So we may + // have to continue writing in another cycle. + // + // After everything is written it's safe to close the + // input pipe. + if ($isRunning && $hasInput && $isInputOpen) { + if ($isInputStream) { + $written = stream_copy_to_stream($this->_stdIn, $pipes[0], 16 * 1024, $writtenBytes); + if ($written === false || $written === 0) { + $isInputOpen = false; + fclose($pipes[0]); + } else { + $writtenBytes += $written; + } + } else { + if ($writtenBytes < strlen($this->_stdIn)) { + $writtenBytes += fwrite($pipes[0], substr($this->_stdIn, $writtenBytes)); + } else { + $isInputOpen = false; + fclose($pipes[0]); + } + } + } + + // Read out the output buffers because if they are full + // the command may block execution. We do this even if + // $isRunning is `false`, because there could be output + // left in the buffers. + // + // The latter is only an assumption and needs to be + // verified - but it does not hurt either and works as + // expected. + // + while (($out = fgets($pipes[1])) !== false) { + $this->_stdOut .= $out; + } + while (($err = fgets($pipes[2])) !== false) { + $this->_stdErr .= $err; + } + + $runTime = $hasTimeout ? time() - $startTime : 0; + if ($isRunning && $hasTimeout && $runTime >= $this->timeout) { + // Only send a SIGTERM and handle status in the next cycle + proc_terminate($process); + } + + if (!$isRunning) { + $this->_exitCode = $status['exitcode']; + if ($this->_exitCode !== 0 && empty($this->_stdErr)) { + if ($status['stopped']) { + $signal = $status['stopsig']; + $this->_stdErr = "Command stopped by signal $signal"; + } elseif ($status['signaled']) { + $signal = $status['termsig']; + $this->_stdErr = "Command terminated by signal $signal"; + } else { + $this->_stdErr = 'Command unexpectedly terminated without error message'; + } + } + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($process); + } else { + // The command is still running. Let's wait some + // time before we start the next cycle. + usleep(10000); + } + } + } else { + if ($hasInput) { + if ($isInputStream) { + stream_copy_to_stream($this->_stdIn, $pipes[0]); + } elseif ($isInputString) { + fwrite($pipes[0], $this->_stdIn); + } + fclose($pipes[0]); + } + $this->_stdOut = stream_get_contents($pipes[1]); + $this->_stdErr = stream_get_contents($pipes[2]); + fclose($pipes[1]); + fclose($pipes[2]); + $this->_exitCode = proc_close($process); + } + + if ($this->_exitCode !== 0) { + $this->_error = $this->_stdErr ? + $this->_stdErr : + "Failed without error message: $command (Exit code: {$this->_exitCode})"; + return false; + } + } else { + $this->_error = "Could not run command $command"; + return false; + } + } + + $this->_executed = true; + + return true; + } + + /** + * @return bool whether we are on a Windows OS + */ + public function getIsWindows() + { + return strncasecmp(PHP_OS, 'WIN', 3)===0; + } + + /** + * @return string the current command string to execute + */ + public function __toString() + { + return (string) $this->getExecCommand(); + } +} diff --git a/common/vendor/mikehaertl/php-tmpfile/.github/workflows/tests.yml b/common/vendor/mikehaertl/php-tmpfile/.github/workflows/tests.yml new file mode 100644 index 00000000..94c02ab7 --- /dev/null +++ b/common/vendor/mikehaertl/php-tmpfile/.github/workflows/tests.yml @@ -0,0 +1,48 @@ +name: Tests +on: pull_request +jobs: + phpunit: + name: PHP ${{ matrix.php }} + runs-on: ubuntu-latest + strategy: + matrix: + php: + - "5.3" + - "5.4" + - "5.5" + - "5.6" + - "7.0" + - "7.1" + - "7.2" + - "7.3" + - "7.4" + - "8.0" + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + tools: composer:v2 + + - name: Update composer + run: composer self-update + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install composer packages + run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi + + - name: Run phpunit + run: vendor/bin/phpunit --color=always diff --git a/common/vendor/mikehaertl/php-tmpfile/LICENSE b/common/vendor/mikehaertl/php-tmpfile/LICENSE new file mode 100644 index 00000000..28dbb2a2 --- /dev/null +++ b/common/vendor/mikehaertl/php-tmpfile/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Michael Härtl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/common/vendor/mikehaertl/php-tmpfile/README.md b/common/vendor/mikehaertl/php-tmpfile/README.md new file mode 100644 index 00000000..86d7b4a5 --- /dev/null +++ b/common/vendor/mikehaertl/php-tmpfile/README.md @@ -0,0 +1,64 @@ +php-tmpfile +=========== + +[![GitHub Tests](https://github.com/mikehaertl/php-tmpfile/workflows/Tests/badge.svg)](https://github.com/mikehaertl/php-tmpfile/actions) +[![Packagist Version](https://img.shields.io/packagist/v/mikehaertl/php-tmpfile?label=version)](https://packagist.org/packages/mikehaertl/php-tmpfile) +[![Packagist Downloads](https://img.shields.io/packagist/dt/mikehaertl/php-tmpfile)](https://packagist.org/packages/mikehaertl/php-tmpfile) +[![GitHub license](https://img.shields.io/github/license/mikehaertl/php-tmpfile)](https://github.com/mikehaertl/php-tmpfile/blob/master/LICENSE) + +A convenience class for temporary files. + +## Features + + * Create temporary file with arbitrary content + * Delete file after use (can be disabled) + * Send file to client, either inline or with save dialog, optionally with custom HTTP headers + * Save file locally + +## Examples + +```php +send('home.html'); +// ... with custom content type (autodetected otherwhise) +$file->send('home.html', 'application/pdf'); +// ... for inline display (download dialog otherwhise) +$file->send('home.html', 'application/pdf', true); +// ... with custom headers +$file->send('home.html', 'application/pdf', true, [ + 'X-Header' => 'Example', +]); + +// save to disk +$file->saveAs('/dir/test.html'); + +// Access file name and directory +echo $file->getFileName(); +echo $file->getTempDir(); +``` + +If you want to keep the temporary file, e.g. for debugging, you can set the `$delete` property to false: + +```php +delete = false; +``` + +Default HTTP headers can also be added: +```php +send('home.html'); +``` diff --git a/common/vendor/mikehaertl/php-tmpfile/composer.json b/common/vendor/mikehaertl/php-tmpfile/composer.json new file mode 100644 index 00000000..c28c46a3 --- /dev/null +++ b/common/vendor/mikehaertl/php-tmpfile/composer.json @@ -0,0 +1,26 @@ +{ + "name": "mikehaertl/php-tmpfile", + "description": "A convenience class for temporary files", + "keywords": ["files"], + "license": "MIT", + "authors": [ + { + "name": "Michael Härtl", + "email": "haertl.mike@gmail.com" + } + ], + "require-dev": { + "php": ">=5.3.0", + "phpunit/phpunit": ">4.0 <=9.4" + }, + "autoload": { + "psr-4": { + "mikehaertl\\tmp\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "tests\\": "tests" + } + } +} diff --git a/common/vendor/mikehaertl/php-tmpfile/src/File.php b/common/vendor/mikehaertl/php-tmpfile/src/File.php new file mode 100644 index 00000000..22712601 --- /dev/null +++ b/common/vendor/mikehaertl/php-tmpfile/src/File.php @@ -0,0 +1,197 @@ + + * @license http://www.opensource.org/licenses/MIT + */ +class File +{ + const DEFAULT_CONTENT_TYPE = 'application/octet-stream'; + + /** + * @var bool whether to delete the tmp file when it's no longer referenced + * or when the request ends. Default is `true`. + */ + public $delete = true; + + /** + * @var array the list of static default headers to send when `send()` is + * called as key/value pairs. + */ + public static $defaultHeaders = array( + 'Pragma' => 'public', + 'Expires' => 0, + 'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0', + 'Content-Transfer-Encoding' => 'binary', + ); + + /** + * @var string the name of this file + */ + protected $_fileName; + + /** + * Constructor + * + * @param string $content the tmp file content + * @param string|null $suffix the optional suffix for the tmp file + * @param string|null $prefix the optional prefix for the tmp file. If null + * 'php_tmpfile_' is used. + * @param string|null $directory directory where the file should be + * created. Autodetected if not provided. + */ + public function __construct($content, $suffix = null, $prefix = null, $directory = null) + { + if ($directory === null) { + $directory = self::getTempDir(); + } + + if ($prefix === null) { + $prefix = 'php_tmpfile_'; + } + + $this->_fileName = tempnam($directory,$prefix); + if ($suffix !== null) { + $newName = $this->_fileName . $suffix; + rename($this->_fileName, $newName); + $this->_fileName = $newName; + } + file_put_contents($this->_fileName, $content); + } + + /** + * Delete tmp file on shutdown if `$delete` is `true` + */ + public function __destruct() + { + if ($this->delete && file_exists($this->_fileName)) { + unlink($this->_fileName); + } + } + + /** + * Send tmp file to client, either inline or as download + * + * @param string|null $filename the filename to send. If empty, the file is + * streamed inline. + * @param string|null $contentType the Content-Type header to send. If + * `null` the type is auto-detected and if that fails + * 'application/octet-stream' is used. + * @param bool $inline whether to force inline display of the file, even if + * filename is present. + * @param array $headers a list of additional HTTP headers to send in the + * response as an array. The array keys are the header names like + * 'Cache-Control' and the array values the header value strings to send. + * Each array value can also be another array of strings if the same header + * should be sent multiple times. This can also be used to override + * automatically created headers like 'Expires' or 'Content-Length'. To suppress + * automatically created headers, `false` can also be used as header value. + */ + public function send($filename = null, $contentType = null, $inline = false, $headers = array()) + { + $headers = array_merge(self::$defaultHeaders, $headers); + + if ($contentType !== null) { + $headers['Content-Type'] = $contentType; + } elseif (!isset($headers['Content-Type'])) { + $contentType = @mime_content_type($this->_filename); + if ($contentType === false) { + $contentType = self::DEFAULT_CONTENT_TYPE; + } + $headers['Content-Type'] = $contentType; + } + + if (!isset($headers['Content-Length'])) { + // #11 Undefined index: HTTP_USER_AGENT + $userAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : ''; + + // #84: Content-Length leads to "network connection was lost" on iOS + $isIOS = preg_match('/i(phone|pad|pod)/i', $userAgent); + if (!$isIOS) { + $headers['Content-Length'] = filesize($this->_fileName); + } + } + + if (($filename !== null || $inline) && !isset($headers['Content-Disposition'])) { + $disposition = $inline ? 'inline' : 'attachment'; + $encodedFilename = rawurlencode($filename); + $headers['Content-Disposition'] = "$disposition; " . + "filename=\"$filename\"; " . + "filename*=UTF-8''$encodedFilename"; + } + + $this->sendHeaders($headers); + readfile($this->_fileName); + } + + /** + * @param string $name the name to save the file as + * @return bool whether the file could be saved + */ + public function saveAs($name) + { + return copy($this->_fileName, $name); + } + + /** + * @return string the full file name + */ + public function getFileName() + { + return $this->_fileName; + } + + /** + * @return string the path to the temp directory + */ + public static function getTempDir() + { + if (function_exists('sys_get_temp_dir')) { + return sys_get_temp_dir(); + } elseif ( + ($tmp = getenv('TMP')) || + ($tmp = getenv('TEMP')) || + ($tmp = getenv('TMPDIR')) + ) { + return realpath($tmp); + } else { + return '/tmp'; + } + } + + /** + * @return string the full file name + */ + public function __toString() + { + return $this->_fileName; + } + + /** + * Send the given list of headers + * + * @param array $headers the list of headers to send as key/value pairs. + * Value can either be a string or an array of strings to send the same + * header multiple times. + */ + protected function sendHeaders($headers) + { + foreach ($headers as $name => $value) { + if ($value === false) { + continue; + } + if (is_array($value)) { + foreach ($value as $v) { + header("$name: $v"); + } + } else { + header("$name: $value"); + } + } + } +}