Version Description
- 2021-03-09 =
- New: Form input file label is now editable
- Improved: Sections with Membership enabled now have an indicator icon
- Fixed: Some images could experience blurriness
- Fixed: Clicking Global Blocks conditions options would do nothing
- Fixed: Carousel content flicker
- Fixed: Carousel options in responsive mode
- Fixed: Form Submit align option
- Fixed: Form Custom HTML template copy to clipboard
- Fixed: Form Integrations Popup scroll in Safari
- Fixed: Accordion drag and drop
- Fixed: Accordion adjusting browser scroll position even when not needed
- Fixed: Slider Prev and Next buttons outline
- Fixed: Slider padding option
- Fixed: Image sizes
- Fixed: Timeline border CSS
- Fixed: PostContent CSS
- Fixed: WooCommerce Add to Cart CSS
- Fixed: Typography option issues in Safari
- Fixed: Slider option CSS
- Fixed: Saving a block or a layout would fail if the browser did not support screenshot saving
- Fixed: Browser back button not working after clicking an anchor to a block on the page
- Fixed: Do not render Terms page on checkout if it created with Brizy
- Fixed: White label prefix for multi-site
- Fixed: Compatibility with BBpress plugin
- Fixed: Page shop as front page template issue
- Fixed: Exclude rule
- Fixed: Dynamic content in Membership block
- Fixed: Get post lists API
- Fixed: Compatibility with ThemeFuse old themes
- Fixed: Avoid duplicate global blocks on create blocks
- Fixed: Add script dependencies from placeholder
- Fixed: Preview for autosaves
- Fixed: Archive element on Author archive template not available
- Fixed: Possibility to crop an image by its Dynamic URL
- Fixed: Updated .POT file
- Fixed: Added an alert comment when removing a function used in other plugins
- Fixed: Global Blocks would not appear in category pages made with Brizy Template
- Fixed: PostContent CSS
- Fixed: WooCommerce Add to Cart CSS
Download this release
Release Info
Developer | themefusecom |
Plugin | Brizy – Page Builder |
Version | 2.2.9.1 |
Comparing to | |
See all releases |
Code changes from version 2.2.9 to 2.2.9.1
- README.md +2 -2
- brizy.php +2 -2
- readme.txt +2 -2
- vendor/autoload.php +7 -0
- vendor/composer/ClassLoader.php +477 -0
- vendor/composer/InstalledVersions.php +24 -0
- vendor/composer/LICENSE +21 -0
- vendor/composer/autoload_classmap.php +10 -0
- vendor/composer/autoload_files.php +13 -0
- vendor/composer/autoload_namespaces.php +12 -0
- vendor/composer/autoload_psr4.php +16 -0
- vendor/composer/autoload_real.php +75 -0
- vendor/composer/autoload_static.php +107 -0
- vendor/composer/installed.json +128 -0
- vendor/composer/installed.php +24 -0
- vendor/composer/platform_check.php +26 -0
- vendor/shortpixel/shortpixel-php/LICENSE +21 -0
- vendor/shortpixel/shortpixel-php/lib/ShortPixel.php +459 -0
- vendor/shortpixel/shortpixel-php/lib/ShortPixel/Client.php +542 -0
- vendor/shortpixel/shortpixel-php/lib/ShortPixel/Commander.php +244 -0
- vendor/shortpixel/shortpixel-php/lib/ShortPixel/Exception.php +37 -0
- vendor/shortpixel/shortpixel-php/lib/ShortPixel/Lock.php +103 -0
- vendor/shortpixel/shortpixel-php/lib/ShortPixel/Persister.php +23 -0
- vendor/shortpixel/shortpixel-php/lib/ShortPixel/Result.php +444 -0
- vendor/shortpixel/shortpixel-php/lib/ShortPixel/SPCache.php +67 -0
- vendor/shortpixel/shortpixel-php/lib/ShortPixel/SPLog.php +154 -0
- vendor/shortpixel/shortpixel-php/lib/ShortPixel/Settings.php +111 -0
- vendor/shortpixel/shortpixel-php/lib/ShortPixel/Source.php +166 -0
- vendor/shortpixel/shortpixel-php/lib/ShortPixel/notify/ProgressNotifier.php +105 -0
- vendor/shortpixel/shortpixel-php/lib/ShortPixel/notify/ProgressNotifierFileQ.php +43 -0
- vendor/shortpixel/shortpixel-php/lib/ShortPixel/notify/ProgressNotifierMemcache.php +43 -0
- vendor/shortpixel/shortpixel-php/lib/ShortPixel/persist/ExifPersister.php +135 -0
- vendor/shortpixel/shortpixel-php/lib/ShortPixel/persist/PNGMetadataExtractor.php +389 -0
- vendor/shortpixel/shortpixel-php/lib/ShortPixel/persist/PNGReader.php +95 -0
- vendor/shortpixel/shortpixel-php/lib/ShortPixel/persist/TextPersister.php +644 -0
- vendor/shortpixel/shortpixel-php/lib/cmdShortpixelOptimize.php +327 -0
- vendor/shortpixel/shortpixel-php/lib/data/shortpixel.crt +29 -0
- vendor/shortpixel/shortpixel-php/lib/no-composer.php +12 -0
- vendor/shortpixel/shortpixel-php/lib/shortpixel-php-req.php +41 -0
- vendor/shortpixel/shortpixel-php/phpunit.xml +13 -0
- vendor/symfony/deprecation-contracts/LICENSE +19 -0
- vendor/symfony/deprecation-contracts/function.php +27 -0
- vendor/symfony/dotenv/.gitattributes +4 -0
- vendor/symfony/dotenv/Dotenv.php +571 -0
- vendor/symfony/dotenv/Exception/ExceptionInterface.php +21 -0
- vendor/symfony/dotenv/Exception/FormatException.php +34 -0
- vendor/symfony/dotenv/Exception/FormatExceptionContext.php +49 -0
- vendor/symfony/dotenv/Exception/PathException.php +25 -0
- vendor/symfony/dotenv/LICENSE +19 -0
- vendor/symfony/dotenv/Tests/DotenvTest.php +499 -0
- vendor/symfony/polyfill-ctype/Ctype.php +227 -0
- vendor/symfony/polyfill-ctype/LICENSE +19 -0
- vendor/symfony/polyfill-ctype/bootstrap.php +50 -0
- vendor/symfony/polyfill-ctype/bootstrap80.php +46 -0
README.md
CHANGED
@@ -3,7 +3,7 @@ Contributors: themefuse<br>
|
|
3 |
Requires at least: 4.5<br>
|
4 |
Tested up to: 5.6<br>
|
5 |
Requires PHP: 5.6<br>
|
6 |
-
Stable tag: 2.2.9<br>
|
7 |
License: GPLv3<br>
|
8 |
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
9 |
|
@@ -118,7 +118,7 @@ $bodyHtml = apply_filters( 'brizy_content', $html->get_body(), Brizy_Editor_Proj
|
|
118 |
|
119 |
## Changelog
|
120 |
|
121 |
-
### 2.2.9 - 2021-03-09
|
122 |
* New: Form input file label is now editable
|
123 |
* Improved: Sections with Membership enabled now have an indicator icon
|
124 |
* Fixed: Some images could experience blurriness
|
3 |
Requires at least: 4.5<br>
|
4 |
Tested up to: 5.6<br>
|
5 |
Requires PHP: 5.6<br>
|
6 |
+
Stable tag: 2.2.9.1<br>
|
7 |
License: GPLv3<br>
|
8 |
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
9 |
|
118 |
|
119 |
## Changelog
|
120 |
|
121 |
+
### 2.2.9.1 - 2021-03-09
|
122 |
* New: Form input file label is now editable
|
123 |
* Improved: Sections with Membership enabled now have an indicator icon
|
124 |
* Fixed: Some images could experience blurriness
|
brizy.php
CHANGED
@@ -5,7 +5,7 @@
|
|
5 |
* Plugin URI: https://brizy.io/
|
6 |
* Author: Brizy.io
|
7 |
* Author URI: https://brizy.io/
|
8 |
-
* Version: 2.2.9
|
9 |
* Text Domain: brizy
|
10 |
* License: GPLv3
|
11 |
* Domain Path: /languages
|
@@ -19,7 +19,7 @@ if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && stripos( $_SERVER['HTTP_X_FO
|
|
19 |
|
20 |
define( 'BRIZY_DEVELOPMENT', false );
|
21 |
define( 'BRIZY_LOG', false );
|
22 |
-
define( 'BRIZY_VERSION', '2.2.9' );
|
23 |
define( 'BRIZY_EDITOR_VERSION', BRIZY_DEVELOPMENT ? 'dev' : '173-wp' );
|
24 |
define( 'BRIZY_SYNC_VERSION', '173' );
|
25 |
define( 'BRIZY_FILE', __FILE__ );
|
5 |
* Plugin URI: https://brizy.io/
|
6 |
* Author: Brizy.io
|
7 |
* Author URI: https://brizy.io/
|
8 |
+
* Version: 2.2.9.1
|
9 |
* Text Domain: brizy
|
10 |
* License: GPLv3
|
11 |
* Domain Path: /languages
|
19 |
|
20 |
define( 'BRIZY_DEVELOPMENT', false );
|
21 |
define( 'BRIZY_LOG', false );
|
22 |
+
define( 'BRIZY_VERSION', '2.2.9.1' );
|
23 |
define( 'BRIZY_EDITOR_VERSION', BRIZY_DEVELOPMENT ? 'dev' : '173-wp' );
|
24 |
define( 'BRIZY_SYNC_VERSION', '173' );
|
25 |
define( 'BRIZY_FILE', __FILE__ );
|
readme.txt
CHANGED
@@ -4,7 +4,7 @@ Tags: page builder, website builder, brizy, editor, visual editor, unyson, wysiw
|
|
4 |
Requires at least: 4.5
|
5 |
Tested up to: 5.6
|
6 |
Requires PHP: 5.6
|
7 |
-
Stable tag: 2.2.9
|
8 |
License: GPLv3
|
9 |
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
10 |
|
@@ -133,7 +133,7 @@ Don't worry if you make a mistake or delete something that you shouldn't have. W
|
|
133 |
|
134 |
== Changelog ==
|
135 |
|
136 |
-
= 2.2.9 - 2021-03-09 =
|
137 |
* New: Form input file label is now editable
|
138 |
* Improved: Sections with Membership enabled now have an indicator icon
|
139 |
* Fixed: Some images could experience blurriness
|
4 |
Requires at least: 4.5
|
5 |
Tested up to: 5.6
|
6 |
Requires PHP: 5.6
|
7 |
+
Stable tag: 2.2.9.1
|
8 |
License: GPLv3
|
9 |
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
10 |
|
133 |
|
134 |
== Changelog ==
|
135 |
|
136 |
+
= 2.2.9.1 - 2021-03-09 =
|
137 |
* New: Form input file label is now editable
|
138 |
* Improved: Sections with Membership enabled now have an indicator icon
|
139 |
* Fixed: Some images could experience blurriness
|
vendor/autoload.php
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload.php @generated by Composer
|
4 |
+
|
5 |
+
require_once __DIR__ . '/composer/autoload_real.php';
|
6 |
+
|
7 |
+
return ComposerAutoloaderInit830e2064261ef899b6a9d442bbe99fee::getLoader();
|
vendor/composer/ClassLoader.php
ADDED
@@ -0,0 +1,477 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of Composer.
|
5 |
+
*
|
6 |
+
* (c) Nils Adermann <naderman@naderman.de>
|
7 |
+
* Jordi Boggiano <j.boggiano@seld.be>
|
8 |
+
*
|
9 |
+
* For the full copyright and license information, please view the LICENSE
|
10 |
+
* file that was distributed with this source code.
|
11 |
+
*/
|
12 |
+
|
13 |
+
namespace Composer\Autoload;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
17 |
+
*
|
18 |
+
* $loader = new \Composer\Autoload\ClassLoader();
|
19 |
+
*
|
20 |
+
* // register classes with namespaces
|
21 |
+
* $loader->add('Symfony\Component', __DIR__.'/component');
|
22 |
+
* $loader->add('Symfony', __DIR__.'/framework');
|
23 |
+
*
|
24 |
+
* // activate the autoloader
|
25 |
+
* $loader->register();
|
26 |
+
*
|
27 |
+
* // to enable searching the include path (eg. for PEAR packages)
|
28 |
+
* $loader->setUseIncludePath(true);
|
29 |
+
*
|
30 |
+
* In this example, if you try to use a class in the Symfony\Component
|
31 |
+
* namespace or one of its children (Symfony\Component\Console for instance),
|
32 |
+
* the autoloader will first look for the class under the component/
|
33 |
+
* directory, and it will then fallback to the framework/ directory if not
|
34 |
+
* found before giving up.
|
35 |
+
*
|
36 |
+
* This class is loosely based on the Symfony UniversalClassLoader.
|
37 |
+
*
|
38 |
+
* @author Fabien Potencier <fabien@symfony.com>
|
39 |
+
* @author Jordi Boggiano <j.boggiano@seld.be>
|
40 |
+
* @see https://www.php-fig.org/psr/psr-0/
|
41 |
+
* @see https://www.php-fig.org/psr/psr-4/
|
42 |
+
*/
|
43 |
+
class ClassLoader
|
44 |
+
{
|
45 |
+
private $vendorDir;
|
46 |
+
|
47 |
+
// PSR-4
|
48 |
+
private $prefixLengthsPsr4 = array();
|
49 |
+
private $prefixDirsPsr4 = array();
|
50 |
+
private $fallbackDirsPsr4 = array();
|
51 |
+
|
52 |
+
// PSR-0
|
53 |
+
private $prefixesPsr0 = array();
|
54 |
+
private $fallbackDirsPsr0 = array();
|
55 |
+
|
56 |
+
private $useIncludePath = false;
|
57 |
+
private $classMap = array();
|
58 |
+
private $classMapAuthoritative = false;
|
59 |
+
private $missingClasses = array();
|
60 |
+
private $apcuPrefix;
|
61 |
+
|
62 |
+
private static $registeredLoaders = array();
|
63 |
+
|
64 |
+
public function __construct($vendorDir = null)
|
65 |
+
{
|
66 |
+
$this->vendorDir = $vendorDir;
|
67 |
+
}
|
68 |
+
|
69 |
+
public function getPrefixes()
|
70 |
+
{
|
71 |
+
if (!empty($this->prefixesPsr0)) {
|
72 |
+
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
73 |
+
}
|
74 |
+
|
75 |
+
return array();
|
76 |
+
}
|
77 |
+
|
78 |
+
public function getPrefixesPsr4()
|
79 |
+
{
|
80 |
+
return $this->prefixDirsPsr4;
|
81 |
+
}
|
82 |
+
|
83 |
+
public function getFallbackDirs()
|
84 |
+
{
|
85 |
+
return $this->fallbackDirsPsr0;
|
86 |
+
}
|
87 |
+
|
88 |
+
public function getFallbackDirsPsr4()
|
89 |
+
{
|
90 |
+
return $this->fallbackDirsPsr4;
|
91 |
+
}
|
92 |
+
|
93 |
+
public function getClassMap()
|
94 |
+
{
|
95 |
+
return $this->classMap;
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* @param array $classMap Class to filename map
|
100 |
+
*/
|
101 |
+
public function addClassMap(array $classMap)
|
102 |
+
{
|
103 |
+
if ($this->classMap) {
|
104 |
+
$this->classMap = array_merge($this->classMap, $classMap);
|
105 |
+
} else {
|
106 |
+
$this->classMap = $classMap;
|
107 |
+
}
|
108 |
+
}
|
109 |
+
|
110 |
+
/**
|
111 |
+
* Registers a set of PSR-0 directories for a given prefix, either
|
112 |
+
* appending or prepending to the ones previously set for this prefix.
|
113 |
+
*
|
114 |
+
* @param string $prefix The prefix
|
115 |
+
* @param array|string $paths The PSR-0 root directories
|
116 |
+
* @param bool $prepend Whether to prepend the directories
|
117 |
+
*/
|
118 |
+
public function add($prefix, $paths, $prepend = false)
|
119 |
+
{
|
120 |
+
if (!$prefix) {
|
121 |
+
if ($prepend) {
|
122 |
+
$this->fallbackDirsPsr0 = array_merge(
|
123 |
+
(array) $paths,
|
124 |
+
$this->fallbackDirsPsr0
|
125 |
+
);
|
126 |
+
} else {
|
127 |
+
$this->fallbackDirsPsr0 = array_merge(
|
128 |
+
$this->fallbackDirsPsr0,
|
129 |
+
(array) $paths
|
130 |
+
);
|
131 |
+
}
|
132 |
+
|
133 |
+
return;
|
134 |
+
}
|
135 |
+
|
136 |
+
$first = $prefix[0];
|
137 |
+
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
138 |
+
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
139 |
+
|
140 |
+
return;
|
141 |
+
}
|
142 |
+
if ($prepend) {
|
143 |
+
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
144 |
+
(array) $paths,
|
145 |
+
$this->prefixesPsr0[$first][$prefix]
|
146 |
+
);
|
147 |
+
} else {
|
148 |
+
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
149 |
+
$this->prefixesPsr0[$first][$prefix],
|
150 |
+
(array) $paths
|
151 |
+
);
|
152 |
+
}
|
153 |
+
}
|
154 |
+
|
155 |
+
/**
|
156 |
+
* Registers a set of PSR-4 directories for a given namespace, either
|
157 |
+
* appending or prepending to the ones previously set for this namespace.
|
158 |
+
*
|
159 |
+
* @param string $prefix The prefix/namespace, with trailing '\\'
|
160 |
+
* @param array|string $paths The PSR-4 base directories
|
161 |
+
* @param bool $prepend Whether to prepend the directories
|
162 |
+
*
|
163 |
+
* @throws \InvalidArgumentException
|
164 |
+
*/
|
165 |
+
public function addPsr4($prefix, $paths, $prepend = false)
|
166 |
+
{
|
167 |
+
if (!$prefix) {
|
168 |
+
// Register directories for the root namespace.
|
169 |
+
if ($prepend) {
|
170 |
+
$this->fallbackDirsPsr4 = array_merge(
|
171 |
+
(array) $paths,
|
172 |
+
$this->fallbackDirsPsr4
|
173 |
+
);
|
174 |
+
} else {
|
175 |
+
$this->fallbackDirsPsr4 = array_merge(
|
176 |
+
$this->fallbackDirsPsr4,
|
177 |
+
(array) $paths
|
178 |
+
);
|
179 |
+
}
|
180 |
+
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
181 |
+
// Register directories for a new namespace.
|
182 |
+
$length = strlen($prefix);
|
183 |
+
if ('\\' !== $prefix[$length - 1]) {
|
184 |
+
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
185 |
+
}
|
186 |
+
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
187 |
+
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
188 |
+
} elseif ($prepend) {
|
189 |
+
// Prepend directories for an already registered namespace.
|
190 |
+
$this->prefixDirsPsr4[$prefix] = array_merge(
|
191 |
+
(array) $paths,
|
192 |
+
$this->prefixDirsPsr4[$prefix]
|
193 |
+
);
|
194 |
+
} else {
|
195 |
+
// Append directories for an already registered namespace.
|
196 |
+
$this->prefixDirsPsr4[$prefix] = array_merge(
|
197 |
+
$this->prefixDirsPsr4[$prefix],
|
198 |
+
(array) $paths
|
199 |
+
);
|
200 |
+
}
|
201 |
+
}
|
202 |
+
|
203 |
+
/**
|
204 |
+
* Registers a set of PSR-0 directories for a given prefix,
|
205 |
+
* replacing any others previously set for this prefix.
|
206 |
+
*
|
207 |
+
* @param string $prefix The prefix
|
208 |
+
* @param array|string $paths The PSR-0 base directories
|
209 |
+
*/
|
210 |
+
public function set($prefix, $paths)
|
211 |
+
{
|
212 |
+
if (!$prefix) {
|
213 |
+
$this->fallbackDirsPsr0 = (array) $paths;
|
214 |
+
} else {
|
215 |
+
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
216 |
+
}
|
217 |
+
}
|
218 |
+
|
219 |
+
/**
|
220 |
+
* Registers a set of PSR-4 directories for a given namespace,
|
221 |
+
* replacing any others previously set for this namespace.
|
222 |
+
*
|
223 |
+
* @param string $prefix The prefix/namespace, with trailing '\\'
|
224 |
+
* @param array|string $paths The PSR-4 base directories
|
225 |
+
*
|
226 |
+
* @throws \InvalidArgumentException
|
227 |
+
*/
|
228 |
+
public function setPsr4($prefix, $paths)
|
229 |
+
{
|
230 |
+
if (!$prefix) {
|
231 |
+
$this->fallbackDirsPsr4 = (array) $paths;
|
232 |
+
} else {
|
233 |
+
$length = strlen($prefix);
|
234 |
+
if ('\\' !== $prefix[$length - 1]) {
|
235 |
+
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
236 |
+
}
|
237 |
+
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
238 |
+
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
239 |
+
}
|
240 |
+
}
|
241 |
+
|
242 |
+
/**
|
243 |
+
* Turns on searching the include path for class files.
|
244 |
+
*
|
245 |
+
* @param bool $useIncludePath
|
246 |
+
*/
|
247 |
+
public function setUseIncludePath($useIncludePath)
|
248 |
+
{
|
249 |
+
$this->useIncludePath = $useIncludePath;
|
250 |
+
}
|
251 |
+
|
252 |
+
/**
|
253 |
+
* Can be used to check if the autoloader uses the include path to check
|
254 |
+
* for classes.
|
255 |
+
*
|
256 |
+
* @return bool
|
257 |
+
*/
|
258 |
+
public function getUseIncludePath()
|
259 |
+
{
|
260 |
+
return $this->useIncludePath;
|
261 |
+
}
|
262 |
+
|
263 |
+
/**
|
264 |
+
* Turns off searching the prefix and fallback directories for classes
|
265 |
+
* that have not been registered with the class map.
|
266 |
+
*
|
267 |
+
* @param bool $classMapAuthoritative
|
268 |
+
*/
|
269 |
+
public function setClassMapAuthoritative($classMapAuthoritative)
|
270 |
+
{
|
271 |
+
$this->classMapAuthoritative = $classMapAuthoritative;
|
272 |
+
}
|
273 |
+
|
274 |
+
/**
|
275 |
+
* Should class lookup fail if not found in the current class map?
|
276 |
+
*
|
277 |
+
* @return bool
|
278 |
+
*/
|
279 |
+
public function isClassMapAuthoritative()
|
280 |
+
{
|
281 |
+
return $this->classMapAuthoritative;
|
282 |
+
}
|
283 |
+
|
284 |
+
/**
|
285 |
+
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
286 |
+
*
|
287 |
+
* @param string|null $apcuPrefix
|
288 |
+
*/
|
289 |
+
public function setApcuPrefix($apcuPrefix)
|
290 |
+
{
|
291 |
+
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
292 |
+
}
|
293 |
+
|
294 |
+
/**
|
295 |
+
* The APCu prefix in use, or null if APCu caching is not enabled.
|
296 |
+
*
|
297 |
+
* @return string|null
|
298 |
+
*/
|
299 |
+
public function getApcuPrefix()
|
300 |
+
{
|
301 |
+
return $this->apcuPrefix;
|
302 |
+
}
|
303 |
+
|
304 |
+
/**
|
305 |
+
* Registers this instance as an autoloader.
|
306 |
+
*
|
307 |
+
* @param bool $prepend Whether to prepend the autoloader or not
|
308 |
+
*/
|
309 |
+
public function register($prepend = false)
|
310 |
+
{
|
311 |
+
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
312 |
+
|
313 |
+
if (null === $this->vendorDir) {
|
314 |
+
//no-op
|
315 |
+
} elseif ($prepend) {
|
316 |
+
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
317 |
+
} else {
|
318 |
+
unset(self::$registeredLoaders[$this->vendorDir]);
|
319 |
+
self::$registeredLoaders[$this->vendorDir] = $this;
|
320 |
+
}
|
321 |
+
}
|
322 |
+
|
323 |
+
/**
|
324 |
+
* Unregisters this instance as an autoloader.
|
325 |
+
*/
|
326 |
+
public function unregister()
|
327 |
+
{
|
328 |
+
spl_autoload_unregister(array($this, 'loadClass'));
|
329 |
+
|
330 |
+
if (null !== $this->vendorDir) {
|
331 |
+
unset(self::$registeredLoaders[$this->vendorDir]);
|
332 |
+
}
|
333 |
+
}
|
334 |
+
|
335 |
+
/**
|
336 |
+
* Loads the given class or interface.
|
337 |
+
*
|
338 |
+
* @param string $class The name of the class
|
339 |
+
* @return bool|null True if loaded, null otherwise
|
340 |
+
*/
|
341 |
+
public function loadClass($class)
|
342 |
+
{
|
343 |
+
if ($file = $this->findFile($class)) {
|
344 |
+
includeFile($file);
|
345 |
+
|
346 |
+
return true;
|
347 |
+
}
|
348 |
+
}
|
349 |
+
|
350 |
+
/**
|
351 |
+
* Finds the path to the file where the class is defined.
|
352 |
+
*
|
353 |
+
* @param string $class The name of the class
|
354 |
+
*
|
355 |
+
* @return string|false The path if found, false otherwise
|
356 |
+
*/
|
357 |
+
public function findFile($class)
|
358 |
+
{
|
359 |
+
// class map lookup
|
360 |
+
if (isset($this->classMap[$class])) {
|
361 |
+
return $this->classMap[$class];
|
362 |
+
}
|
363 |
+
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
364 |
+
return false;
|
365 |
+
}
|
366 |
+
if (null !== $this->apcuPrefix) {
|
367 |
+
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
368 |
+
if ($hit) {
|
369 |
+
return $file;
|
370 |
+
}
|
371 |
+
}
|
372 |
+
|
373 |
+
$file = $this->findFileWithExtension($class, '.php');
|
374 |
+
|
375 |
+
// Search for Hack files if we are running on HHVM
|
376 |
+
if (false === $file && defined('HHVM_VERSION')) {
|
377 |
+
$file = $this->findFileWithExtension($class, '.hh');
|
378 |
+
}
|
379 |
+
|
380 |
+
if (null !== $this->apcuPrefix) {
|
381 |
+
apcu_add($this->apcuPrefix.$class, $file);
|
382 |
+
}
|
383 |
+
|
384 |
+
if (false === $file) {
|
385 |
+
// Remember that this class does not exist.
|
386 |
+
$this->missingClasses[$class] = true;
|
387 |
+
}
|
388 |
+
|
389 |
+
return $file;
|
390 |
+
}
|
391 |
+
|
392 |
+
/**
|
393 |
+
* Returns the currently registered loaders indexed by their corresponding vendor directories.
|
394 |
+
*
|
395 |
+
* @return self[]
|
396 |
+
*/
|
397 |
+
public static function getRegisteredLoaders()
|
398 |
+
{
|
399 |
+
return self::$registeredLoaders;
|
400 |
+
}
|
401 |
+
|
402 |
+
private function findFileWithExtension($class, $ext)
|
403 |
+
{
|
404 |
+
// PSR-4 lookup
|
405 |
+
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
406 |
+
|
407 |
+
$first = $class[0];
|
408 |
+
if (isset($this->prefixLengthsPsr4[$first])) {
|
409 |
+
$subPath = $class;
|
410 |
+
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
411 |
+
$subPath = substr($subPath, 0, $lastPos);
|
412 |
+
$search = $subPath . '\\';
|
413 |
+
if (isset($this->prefixDirsPsr4[$search])) {
|
414 |
+
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
415 |
+
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
416 |
+
if (file_exists($file = $dir . $pathEnd)) {
|
417 |
+
return $file;
|
418 |
+
}
|
419 |
+
}
|
420 |
+
}
|
421 |
+
}
|
422 |
+
}
|
423 |
+
|
424 |
+
// PSR-4 fallback dirs
|
425 |
+
foreach ($this->fallbackDirsPsr4 as $dir) {
|
426 |
+
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
427 |
+
return $file;
|
428 |
+
}
|
429 |
+
}
|
430 |
+
|
431 |
+
// PSR-0 lookup
|
432 |
+
if (false !== $pos = strrpos($class, '\\')) {
|
433 |
+
// namespaced class name
|
434 |
+
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
435 |
+
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
436 |
+
} else {
|
437 |
+
// PEAR-like class name
|
438 |
+
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
439 |
+
}
|
440 |
+
|
441 |
+
if (isset($this->prefixesPsr0[$first])) {
|
442 |
+
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
443 |
+
if (0 === strpos($class, $prefix)) {
|
444 |
+
foreach ($dirs as $dir) {
|
445 |
+
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
446 |
+
return $file;
|
447 |
+
}
|
448 |
+
}
|
449 |
+
}
|
450 |
+
}
|
451 |
+
}
|
452 |
+
|
453 |
+
// PSR-0 fallback dirs
|
454 |
+
foreach ($this->fallbackDirsPsr0 as $dir) {
|
455 |
+
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
456 |
+
return $file;
|
457 |
+
}
|
458 |
+
}
|
459 |
+
|
460 |
+
// PSR-0 include paths.
|
461 |
+
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
462 |
+
return $file;
|
463 |
+
}
|
464 |
+
|
465 |
+
return false;
|
466 |
+
}
|
467 |
+
}
|
468 |
+
|
469 |
+
/**
|
470 |
+
* Scope isolated include.
|
471 |
+
*
|
472 |
+
* Prevents access to $this/self from included files.
|
473 |
+
*/
|
474 |
+
function includeFile($file)
|
475 |
+
{
|
476 |
+
include $file;
|
477 |
+
}
|
vendor/composer/InstalledVersions.php
CHANGED
@@ -89,19 +89,43 @@ private static $installed = array (
|
|
89 |
),
|
90 |
'reference' => '9ce61fd297fd2922fe771debea8b24dfd219a49a',
|
91 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
'symfony/deprecation-contracts' =>
|
93 |
array (
|
|
|
|
|
94 |
'aliases' =>
|
95 |
array (
|
96 |
0 => '2.3.x-dev',
|
97 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
98 |
),
|
99 |
'symfony/polyfill-ctype' =>
|
100 |
array (
|
|
|
|
|
101 |
'aliases' =>
|
102 |
array (
|
103 |
0 => '1.22.x-dev',
|
104 |
),
|
|
|
105 |
),
|
106 |
'twig/twig' =>
|
107 |
array (
|
89 |
),
|
90 |
'reference' => '9ce61fd297fd2922fe771debea8b24dfd219a49a',
|
91 |
),
|
92 |
+
'shortpixel/shortpixel-php' =>
|
93 |
+
array (
|
94 |
+
'pretty_version' => 'dev-master',
|
95 |
+
'version' => 'dev-master',
|
96 |
+
'aliases' =>
|
97 |
+
array (
|
98 |
+
),
|
99 |
+
'reference' => 'b54bbdc934ca3a688dd374002772bb0f637f454e',
|
100 |
+
),
|
101 |
'symfony/deprecation-contracts' =>
|
102 |
array (
|
103 |
+
'pretty_version' => 'dev-main',
|
104 |
+
'version' => 'dev-main',
|
105 |
'aliases' =>
|
106 |
array (
|
107 |
0 => '2.3.x-dev',
|
108 |
),
|
109 |
+
'reference' => 'c154763cf2c552cc07ad9388c8a62e80adc2864f',
|
110 |
+
),
|
111 |
+
'symfony/dotenv' =>
|
112 |
+
array (
|
113 |
+
'pretty_version' => '5.x-dev',
|
114 |
+
'version' => '5.9999999.9999999.9999999-dev',
|
115 |
+
'aliases' =>
|
116 |
+
array (
|
117 |
+
),
|
118 |
+
'reference' => 'f4f1b78f7b50aa7f1de3b038041063fbbab3218e',
|
119 |
),
|
120 |
'symfony/polyfill-ctype' =>
|
121 |
array (
|
122 |
+
'pretty_version' => 'dev-main',
|
123 |
+
'version' => 'dev-main',
|
124 |
'aliases' =>
|
125 |
array (
|
126 |
0 => '1.22.x-dev',
|
127 |
),
|
128 |
+
'reference' => 'c6c942b1ac76c82448322025e084cadc56048b4e',
|
129 |
),
|
130 |
'twig/twig' =>
|
131 |
array (
|
vendor/composer/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
Copyright (c) Nils Adermann, Jordi Boggiano
|
3 |
+
|
4 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
5 |
+
of this software and associated documentation files (the "Software"), to deal
|
6 |
+
in the Software without restriction, including without limitation the rights
|
7 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8 |
+
copies of the Software, and to permit persons to whom the Software is furnished
|
9 |
+
to do so, subject to the following conditions:
|
10 |
+
|
11 |
+
The above copyright notice and this permission notice shall be included in all
|
12 |
+
copies or substantial portions of the Software.
|
13 |
+
|
14 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20 |
+
THE SOFTWARE.
|
21 |
+
|
vendor/composer/autoload_classmap.php
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_classmap.php @generated by Composer
|
4 |
+
|
5 |
+
$vendorDir = dirname(dirname(__FILE__));
|
6 |
+
$baseDir = dirname($vendorDir);
|
7 |
+
|
8 |
+
return array(
|
9 |
+
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
10 |
+
);
|
vendor/composer/autoload_files.php
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_files.php @generated by Composer
|
4 |
+
|
5 |
+
$vendorDir = dirname(dirname(__FILE__));
|
6 |
+
$baseDir = dirname($vendorDir);
|
7 |
+
|
8 |
+
return array(
|
9 |
+
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
|
10 |
+
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
|
11 |
+
'8ec4222c68e580a23520eef4abe4380f' => $vendorDir . '/shortpixel/shortpixel-php/lib/ShortPixel.php',
|
12 |
+
'c93afce03290e70ec0d051b69a50edb0' => $vendorDir . '/shortpixel/shortpixel-php/lib/ShortPixel/Exception.php',
|
13 |
+
);
|
vendor/composer/autoload_namespaces.php
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_namespaces.php @generated by Composer
|
4 |
+
|
5 |
+
$vendorDir = dirname(dirname(__FILE__));
|
6 |
+
$baseDir = dirname($vendorDir);
|
7 |
+
|
8 |
+
return array(
|
9 |
+
'Twig_' => array($vendorDir . '/twig/twig/lib'),
|
10 |
+
'Gaufrette' => array($vendorDir . '/knplabs/gaufrette/src'),
|
11 |
+
'Brizy' => array($vendorDir . '/bagrinsergiu/brizy-migration-utils/src'),
|
12 |
+
);
|
vendor/composer/autoload_psr4.php
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_psr4.php @generated by Composer
|
4 |
+
|
5 |
+
$vendorDir = dirname(dirname(__FILE__));
|
6 |
+
$baseDir = dirname($vendorDir);
|
7 |
+
|
8 |
+
return array(
|
9 |
+
'enshrined\\svgSanitize\\' => array($vendorDir . '/enshrined/svg-sanitize/src'),
|
10 |
+
'Twig\\' => array($vendorDir . '/twig/twig/src'),
|
11 |
+
'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'),
|
12 |
+
'Symfony\\Component\\Dotenv\\' => array($vendorDir . '/symfony/dotenv'),
|
13 |
+
'ShortPixel\\' => array($vendorDir . '/shortpixel/shortpixel-php/lib/ShortPixel'),
|
14 |
+
'BrizyMerge\\' => array($vendorDir . '/bagrinsergiu/brizy-merge-page-assets/lib'),
|
15 |
+
'BrizyMergeTests\\' => array($vendorDir . '/bagrinsergiu/brizy-merge-page-assets/tests'),
|
16 |
+
);
|
vendor/composer/autoload_real.php
ADDED
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_real.php @generated by Composer
|
4 |
+
|
5 |
+
class ComposerAutoloaderInit830e2064261ef899b6a9d442bbe99fee
|
6 |
+
{
|
7 |
+
private static $loader;
|
8 |
+
|
9 |
+
public static function loadClassLoader($class)
|
10 |
+
{
|
11 |
+
if ('Composer\Autoload\ClassLoader' === $class) {
|
12 |
+
require __DIR__ . '/ClassLoader.php';
|
13 |
+
}
|
14 |
+
}
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @return \Composer\Autoload\ClassLoader
|
18 |
+
*/
|
19 |
+
public static function getLoader()
|
20 |
+
{
|
21 |
+
if (null !== self::$loader) {
|
22 |
+
return self::$loader;
|
23 |
+
}
|
24 |
+
|
25 |
+
require __DIR__ . '/platform_check.php';
|
26 |
+
|
27 |
+
spl_autoload_register(array('ComposerAutoloaderInit830e2064261ef899b6a9d442bbe99fee', 'loadClassLoader'), true, true);
|
28 |
+
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
|
29 |
+
spl_autoload_unregister(array('ComposerAutoloaderInit830e2064261ef899b6a9d442bbe99fee', 'loadClassLoader'));
|
30 |
+
|
31 |
+
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
32 |
+
if ($useStaticLoader) {
|
33 |
+
require __DIR__ . '/autoload_static.php';
|
34 |
+
|
35 |
+
call_user_func(\Composer\Autoload\ComposerStaticInit830e2064261ef899b6a9d442bbe99fee::getInitializer($loader));
|
36 |
+
} else {
|
37 |
+
$map = require __DIR__ . '/autoload_namespaces.php';
|
38 |
+
foreach ($map as $namespace => $path) {
|
39 |
+
$loader->set($namespace, $path);
|
40 |
+
}
|
41 |
+
|
42 |
+
$map = require __DIR__ . '/autoload_psr4.php';
|
43 |
+
foreach ($map as $namespace => $path) {
|
44 |
+
$loader->setPsr4($namespace, $path);
|
45 |
+
}
|
46 |
+
|
47 |
+
$classMap = require __DIR__ . '/autoload_classmap.php';
|
48 |
+
if ($classMap) {
|
49 |
+
$loader->addClassMap($classMap);
|
50 |
+
}
|
51 |
+
}
|
52 |
+
|
53 |
+
$loader->register(true);
|
54 |
+
|
55 |
+
if ($useStaticLoader) {
|
56 |
+
$includeFiles = Composer\Autoload\ComposerStaticInit830e2064261ef899b6a9d442bbe99fee::$files;
|
57 |
+
} else {
|
58 |
+
$includeFiles = require __DIR__ . '/autoload_files.php';
|
59 |
+
}
|
60 |
+
foreach ($includeFiles as $fileIdentifier => $file) {
|
61 |
+
composerRequire830e2064261ef899b6a9d442bbe99fee($fileIdentifier, $file);
|
62 |
+
}
|
63 |
+
|
64 |
+
return $loader;
|
65 |
+
}
|
66 |
+
}
|
67 |
+
|
68 |
+
function composerRequire830e2064261ef899b6a9d442bbe99fee($fileIdentifier, $file)
|
69 |
+
{
|
70 |
+
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
71 |
+
require $file;
|
72 |
+
|
73 |
+
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
74 |
+
}
|
75 |
+
}
|
vendor/composer/autoload_static.php
ADDED
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// autoload_static.php @generated by Composer
|
4 |
+
|
5 |
+
namespace Composer\Autoload;
|
6 |
+
|
7 |
+
class ComposerStaticInit830e2064261ef899b6a9d442bbe99fee
|
8 |
+
{
|
9 |
+
public static $files = array (
|
10 |
+
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
|
11 |
+
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
|
12 |
+
'8ec4222c68e580a23520eef4abe4380f' => __DIR__ . '/..' . '/shortpixel/shortpixel-php/lib/ShortPixel.php',
|
13 |
+
'c93afce03290e70ec0d051b69a50edb0' => __DIR__ . '/..' . '/shortpixel/shortpixel-php/lib/ShortPixel/Exception.php',
|
14 |
+
);
|
15 |
+
|
16 |
+
public static $prefixLengthsPsr4 = array (
|
17 |
+
'e' =>
|
18 |
+
array (
|
19 |
+
'enshrined\\svgSanitize\\' => 22,
|
20 |
+
),
|
21 |
+
'T' =>
|
22 |
+
array (
|
23 |
+
'Twig\\' => 5,
|
24 |
+
),
|
25 |
+
'S' =>
|
26 |
+
array (
|
27 |
+
'Symfony\\Polyfill\\Ctype\\' => 23,
|
28 |
+
'Symfony\\Component\\Dotenv\\' => 25,
|
29 |
+
'ShortPixel\\' => 11,
|
30 |
+
),
|
31 |
+
'B' =>
|
32 |
+
array (
|
33 |
+
'BrizyMerge\\' => 11,
|
34 |
+
'BrizyMergeTests\\' => 16,
|
35 |
+
),
|
36 |
+
);
|
37 |
+
|
38 |
+
public static $prefixDirsPsr4 = array (
|
39 |
+
'enshrined\\svgSanitize\\' =>
|
40 |
+
array (
|
41 |
+
0 => __DIR__ . '/..' . '/enshrined/svg-sanitize/src',
|
42 |
+
),
|
43 |
+
'Twig\\' =>
|
44 |
+
array (
|
45 |
+
0 => __DIR__ . '/..' . '/twig/twig/src',
|
46 |
+
),
|
47 |
+
'Symfony\\Polyfill\\Ctype\\' =>
|
48 |
+
array (
|
49 |
+
0 => __DIR__ . '/..' . '/symfony/polyfill-ctype',
|
50 |
+
),
|
51 |
+
'Symfony\\Component\\Dotenv\\' =>
|
52 |
+
array (
|
53 |
+
0 => __DIR__ . '/..' . '/symfony/dotenv',
|
54 |
+
),
|
55 |
+
'ShortPixel\\' =>
|
56 |
+
array (
|
57 |
+
0 => __DIR__ . '/..' . '/shortpixel/shortpixel-php/lib/ShortPixel',
|
58 |
+
),
|
59 |
+
'BrizyMerge\\' =>
|
60 |
+
array (
|
61 |
+
0 => __DIR__ . '/..' . '/bagrinsergiu/brizy-merge-page-assets/lib',
|
62 |
+
),
|
63 |
+
'BrizyMergeTests\\' =>
|
64 |
+
array (
|
65 |
+
0 => __DIR__ . '/..' . '/bagrinsergiu/brizy-merge-page-assets/tests',
|
66 |
+
),
|
67 |
+
);
|
68 |
+
|
69 |
+
public static $prefixesPsr0 = array (
|
70 |
+
'T' =>
|
71 |
+
array (
|
72 |
+
'Twig_' =>
|
73 |
+
array (
|
74 |
+
0 => __DIR__ . '/..' . '/twig/twig/lib',
|
75 |
+
),
|
76 |
+
),
|
77 |
+
'G' =>
|
78 |
+
array (
|
79 |
+
'Gaufrette' =>
|
80 |
+
array (
|
81 |
+
0 => __DIR__ . '/..' . '/knplabs/gaufrette/src',
|
82 |
+
),
|
83 |
+
),
|
84 |
+
'B' =>
|
85 |
+
array (
|
86 |
+
'Brizy' =>
|
87 |
+
array (
|
88 |
+
0 => __DIR__ . '/..' . '/bagrinsergiu/brizy-migration-utils/src',
|
89 |
+
),
|
90 |
+
),
|
91 |
+
);
|
92 |
+
|
93 |
+
public static $classMap = array (
|
94 |
+
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
95 |
+
);
|
96 |
+
|
97 |
+
public static function getInitializer(ClassLoader $loader)
|
98 |
+
{
|
99 |
+
return \Closure::bind(function () use ($loader) {
|
100 |
+
$loader->prefixLengthsPsr4 = ComposerStaticInit830e2064261ef899b6a9d442bbe99fee::$prefixLengthsPsr4;
|
101 |
+
$loader->prefixDirsPsr4 = ComposerStaticInit830e2064261ef899b6a9d442bbe99fee::$prefixDirsPsr4;
|
102 |
+
$loader->prefixesPsr0 = ComposerStaticInit830e2064261ef899b6a9d442bbe99fee::$prefixesPsr0;
|
103 |
+
$loader->classMap = ComposerStaticInit830e2064261ef899b6a9d442bbe99fee::$classMap;
|
104 |
+
|
105 |
+
}, null, ClassLoader::class);
|
106 |
+
}
|
107 |
+
}
|
vendor/composer/installed.json
CHANGED
@@ -263,6 +263,64 @@
|
|
263 |
"homepage": "https://select2.org/",
|
264 |
"install-path": "../select2/select2"
|
265 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
266 |
{
|
267 |
"name": "symfony/deprecation-contracts",
|
268 |
"version": "dev-main",
|
@@ -330,6 +388,76 @@
|
|
330 |
],
|
331 |
"install-path": "../symfony/deprecation-contracts"
|
332 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
333 |
{
|
334 |
"name": "symfony/polyfill-ctype",
|
335 |
"version": "dev-main",
|
263 |
"homepage": "https://select2.org/",
|
264 |
"install-path": "../select2/select2"
|
265 |
},
|
266 |
+
{
|
267 |
+
"name": "shortpixel/shortpixel-php",
|
268 |
+
"version": "dev-master",
|
269 |
+
"version_normalized": "dev-master",
|
270 |
+
"source": {
|
271 |
+
"type": "git",
|
272 |
+
"url": "https://github.com/short-pixel-optimizer/shortpixel-php.git",
|
273 |
+
"reference": "b54bbdc934ca3a688dd374002772bb0f637f454e"
|
274 |
+
},
|
275 |
+
"dist": {
|
276 |
+
"type": "zip",
|
277 |
+
"url": "https://api.github.com/repos/short-pixel-optimizer/shortpixel-php/zipball/b54bbdc934ca3a688dd374002772bb0f637f454e",
|
278 |
+
"reference": "b54bbdc934ca3a688dd374002772bb0f637f454e",
|
279 |
+
"shasum": ""
|
280 |
+
},
|
281 |
+
"require": {
|
282 |
+
"ext-curl": "*",
|
283 |
+
"ext-json": "*",
|
284 |
+
"lib-curl": ">=7.20.0",
|
285 |
+
"php": ">=5.3.0"
|
286 |
+
},
|
287 |
+
"require-dev": {
|
288 |
+
"phpunit/phpunit": "~4.0",
|
289 |
+
"symfony/yaml": "~2.0"
|
290 |
+
},
|
291 |
+
"time": "2021-01-27T09:52:48+00:00",
|
292 |
+
"type": "library",
|
293 |
+
"installation-source": "source",
|
294 |
+
"autoload": {
|
295 |
+
"files": [
|
296 |
+
"lib/ShortPixel.php",
|
297 |
+
"lib/ShortPixel/Exception.php"
|
298 |
+
],
|
299 |
+
"psr-4": {
|
300 |
+
"ShortPixel\\": "lib/ShortPixel/"
|
301 |
+
}
|
302 |
+
},
|
303 |
+
"notification-url": "https://packagist.org/downloads/",
|
304 |
+
"license": [
|
305 |
+
"MIT"
|
306 |
+
],
|
307 |
+
"authors": [
|
308 |
+
{
|
309 |
+
"name": "Simon Duduica",
|
310 |
+
"email": "simon@shortpixel.com"
|
311 |
+
}
|
312 |
+
],
|
313 |
+
"description": "ShortPixel PHP SDK. Read more at https://shortpixel.com/api-tools",
|
314 |
+
"homepage": "https://shortpixel.com/api",
|
315 |
+
"keywords": [
|
316 |
+
"api",
|
317 |
+
"compress",
|
318 |
+
"images",
|
319 |
+
"optimize",
|
320 |
+
"shortpixel"
|
321 |
+
],
|
322 |
+
"install-path": "../shortpixel/shortpixel-php"
|
323 |
+
},
|
324 |
{
|
325 |
"name": "symfony/deprecation-contracts",
|
326 |
"version": "dev-main",
|
388 |
],
|
389 |
"install-path": "../symfony/deprecation-contracts"
|
390 |
},
|
391 |
+
{
|
392 |
+
"name": "symfony/dotenv",
|
393 |
+
"version": "5.x-dev",
|
394 |
+
"version_normalized": "5.9999999.9999999.9999999-dev",
|
395 |
+
"source": {
|
396 |
+
"type": "git",
|
397 |
+
"url": "https://github.com/symfony/dotenv.git",
|
398 |
+
"reference": "f4f1b78f7b50aa7f1de3b038041063fbbab3218e"
|
399 |
+
},
|
400 |
+
"dist": {
|
401 |
+
"type": "zip",
|
402 |
+
"url": "https://api.github.com/repos/symfony/dotenv/zipball/f4f1b78f7b50aa7f1de3b038041063fbbab3218e",
|
403 |
+
"reference": "f4f1b78f7b50aa7f1de3b038041063fbbab3218e",
|
404 |
+
"shasum": ""
|
405 |
+
},
|
406 |
+
"require": {
|
407 |
+
"php": ">=7.2.5",
|
408 |
+
"symfony/deprecation-contracts": "^2.1"
|
409 |
+
},
|
410 |
+
"require-dev": {
|
411 |
+
"symfony/process": "^4.4|^5.0"
|
412 |
+
},
|
413 |
+
"time": "2021-01-11T10:34:08+00:00",
|
414 |
+
"type": "library",
|
415 |
+
"installation-source": "source",
|
416 |
+
"autoload": {
|
417 |
+
"psr-4": {
|
418 |
+
"Symfony\\Component\\Dotenv\\": ""
|
419 |
+
},
|
420 |
+
"exclude-from-classmap": [
|
421 |
+
"/Tests/"
|
422 |
+
]
|
423 |
+
},
|
424 |
+
"notification-url": "https://packagist.org/downloads/",
|
425 |
+
"license": [
|
426 |
+
"MIT"
|
427 |
+
],
|
428 |
+
"authors": [
|
429 |
+
{
|
430 |
+
"name": "Fabien Potencier",
|
431 |
+
"email": "fabien@symfony.com"
|
432 |
+
},
|
433 |
+
{
|
434 |
+
"name": "Symfony Community",
|
435 |
+
"homepage": "https://symfony.com/contributors"
|
436 |
+
}
|
437 |
+
],
|
438 |
+
"description": "Registers environment variables from a .env file",
|
439 |
+
"homepage": "https://symfony.com",
|
440 |
+
"keywords": [
|
441 |
+
"dotenv",
|
442 |
+
"env",
|
443 |
+
"environment"
|
444 |
+
],
|
445 |
+
"funding": [
|
446 |
+
{
|
447 |
+
"url": "https://symfony.com/sponsor",
|
448 |
+
"type": "custom"
|
449 |
+
},
|
450 |
+
{
|
451 |
+
"url": "https://github.com/fabpot",
|
452 |
+
"type": "github"
|
453 |
+
},
|
454 |
+
{
|
455 |
+
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
456 |
+
"type": "tidelift"
|
457 |
+
}
|
458 |
+
],
|
459 |
+
"install-path": "../symfony/dotenv"
|
460 |
+
},
|
461 |
{
|
462 |
"name": "symfony/polyfill-ctype",
|
463 |
"version": "dev-main",
|
vendor/composer/installed.php
CHANGED
@@ -65,19 +65,43 @@
|
|
65 |
),
|
66 |
'reference' => '9ce61fd297fd2922fe771debea8b24dfd219a49a',
|
67 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
'symfony/deprecation-contracts' =>
|
69 |
array (
|
|
|
|
|
70 |
'aliases' =>
|
71 |
array (
|
72 |
0 => '2.3.x-dev',
|
73 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
),
|
75 |
'symfony/polyfill-ctype' =>
|
76 |
array (
|
|
|
|
|
77 |
'aliases' =>
|
78 |
array (
|
79 |
0 => '1.22.x-dev',
|
80 |
),
|
|
|
81 |
),
|
82 |
'twig/twig' =>
|
83 |
array (
|
65 |
),
|
66 |
'reference' => '9ce61fd297fd2922fe771debea8b24dfd219a49a',
|
67 |
),
|
68 |
+
'shortpixel/shortpixel-php' =>
|
69 |
+
array (
|
70 |
+
'pretty_version' => 'dev-master',
|
71 |
+
'version' => 'dev-master',
|
72 |
+
'aliases' =>
|
73 |
+
array (
|
74 |
+
),
|
75 |
+
'reference' => 'b54bbdc934ca3a688dd374002772bb0f637f454e',
|
76 |
+
),
|
77 |
'symfony/deprecation-contracts' =>
|
78 |
array (
|
79 |
+
'pretty_version' => 'dev-main',
|
80 |
+
'version' => 'dev-main',
|
81 |
'aliases' =>
|
82 |
array (
|
83 |
0 => '2.3.x-dev',
|
84 |
),
|
85 |
+
'reference' => 'c154763cf2c552cc07ad9388c8a62e80adc2864f',
|
86 |
+
),
|
87 |
+
'symfony/dotenv' =>
|
88 |
+
array (
|
89 |
+
'pretty_version' => '5.x-dev',
|
90 |
+
'version' => '5.9999999.9999999.9999999-dev',
|
91 |
+
'aliases' =>
|
92 |
+
array (
|
93 |
+
),
|
94 |
+
'reference' => 'f4f1b78f7b50aa7f1de3b038041063fbbab3218e',
|
95 |
),
|
96 |
'symfony/polyfill-ctype' =>
|
97 |
array (
|
98 |
+
'pretty_version' => 'dev-main',
|
99 |
+
'version' => 'dev-main',
|
100 |
'aliases' =>
|
101 |
array (
|
102 |
0 => '1.22.x-dev',
|
103 |
),
|
104 |
+
'reference' => 'c6c942b1ac76c82448322025e084cadc56048b4e',
|
105 |
),
|
106 |
'twig/twig' =>
|
107 |
array (
|
vendor/composer/platform_check.php
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
// platform_check.php @generated by Composer
|
4 |
+
|
5 |
+
$issues = array();
|
6 |
+
|
7 |
+
if (!(PHP_VERSION_ID >= 70205)) {
|
8 |
+
$issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.5". You are running ' . PHP_VERSION . '.';
|
9 |
+
}
|
10 |
+
|
11 |
+
if ($issues) {
|
12 |
+
if (!headers_sent()) {
|
13 |
+
header('HTTP/1.1 500 Internal Server Error');
|
14 |
+
}
|
15 |
+
if (!ini_get('display_errors')) {
|
16 |
+
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
17 |
+
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
|
18 |
+
} elseif (!headers_sent()) {
|
19 |
+
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
20 |
+
}
|
21 |
+
}
|
22 |
+
trigger_error(
|
23 |
+
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
24 |
+
E_USER_ERROR
|
25 |
+
);
|
26 |
+
}
|
vendor/shortpixel/shortpixel-php/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
The MIT License
|
2 |
+
|
3 |
+
Copyright (c) 2014-2016 Shortpixel
|
4 |
+
|
5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6 |
+
of this software and associated documentation files (the "Software"), to deal
|
7 |
+
in the Software without restriction, including without limitation the rights
|
8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9 |
+
copies of the Software, and to permit persons to whom the Software is
|
10 |
+
furnished to do so, subject to the following conditions:
|
11 |
+
|
12 |
+
The above copyright notice and this permission notice shall be included in
|
13 |
+
all copies or substantial portions of the Software.
|
14 |
+
|
15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21 |
+
THE SOFTWARE.
|
vendor/shortpixel/shortpixel-php/lib/ShortPixel.php
ADDED
@@ -0,0 +1,459 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace ShortPixel;
|
4 |
+
|
5 |
+
class ShortPixel {
|
6 |
+
const LIBRARY_CODE = "sp-sdk";
|
7 |
+
const VERSION = "1.7.1";
|
8 |
+
const DEBUG_LOG = false;
|
9 |
+
|
10 |
+
const MAX_ALLOWED_FILES_PER_CALL = 10;
|
11 |
+
const MAX_ALLOWED_FILES_PER_WEB_CALL = 30;
|
12 |
+
const MAX_API_ALLOWED_FILES_PER_WEB_CALL = 150;
|
13 |
+
const CLIENT_MAX_BODY_SIZE = 48; // in MBytes.
|
14 |
+
const MAX_RETRIES = 6;
|
15 |
+
|
16 |
+
const LOSSY_EXIF_TAG = "SPXLY";
|
17 |
+
const LOSSLESS_EXIF_TAG = "SPXLL";
|
18 |
+
|
19 |
+
const RESIZE_OUTER = 1;
|
20 |
+
const RESIZE_INNER = 3;
|
21 |
+
|
22 |
+
private static $key = NULL;
|
23 |
+
private static $client = NULL;
|
24 |
+
private static $options = array(
|
25 |
+
"lossy" => 1, // 1 - lossy, 2 - glossy, 0 - lossless
|
26 |
+
"keep_exif" => 0, // 1 - EXIF is preserved, 0 - EXIF is removed
|
27 |
+
"resize" => 0, // 0 - don't resize, 1 - outer resize, 3 - inner resize
|
28 |
+
"resize_width" => null, // in pixels. null means no resize
|
29 |
+
"resize_height" => null, // in pixels. null means no resize
|
30 |
+
"cmyk2rgb" => 1, // convert CMYK to RGB: 1 yes, 0 no
|
31 |
+
"convertto" => "", // if '+webp' then also the WebP version will be generated, if +avif then also the AVIF version will be generated. Specify both with +webp|+avif
|
32 |
+
"user" => "", //set the user needed for HTTP AUTH of the base_url
|
33 |
+
"pass" => "", //se the pass needed for HTTP AUTH of the base_url
|
34 |
+
// **** return options ****
|
35 |
+
"notify_me" => null, // should contain full URL of of notification script (notify.php) - to be implemented
|
36 |
+
"wait" => 30, // seconds
|
37 |
+
// **** local options ****
|
38 |
+
"total_wait" => 30, //seconds
|
39 |
+
"base_url" => null, // base url of the images - used to generate the path for toFile by extracting from original URL and using the remaining path as relative path to base_path
|
40 |
+
"base_source_path" => "", // base path of the local files
|
41 |
+
"base_path" => false, // base path to save the files
|
42 |
+
"backup_path" => false, // backup path, relative to the optimization folder (base_source_path)
|
43 |
+
// **** persist options ****
|
44 |
+
"persist_type" => null, // null - don't persist, otherwise "text" (.shortpixel text file in each folder), "exif" (mark in the EXIF that the image has been optimized) or "mysql" (to be implemented)
|
45 |
+
"persist_name" => ".shortpixel",
|
46 |
+
"notify_progress" => false,
|
47 |
+
"cache_time" => 0 // number of seconds to cache the folder results - the *Persister classes will cache the getTodo results and retrieve them from memcache if it's available.
|
48 |
+
//"persist_user" => "user", // only for mysql
|
49 |
+
//"persist_pass" => "pass" // only for mysql
|
50 |
+
// "" => null,
|
51 |
+
);
|
52 |
+
private static $curlOptions = array();
|
53 |
+
|
54 |
+
public static $PROCESSABLE_EXTENSIONS = array('jpg', 'jpeg', 'jpe', 'jfif', 'jif', 'gif', 'png', 'pdf');
|
55 |
+
|
56 |
+
private static $persistersRegistry = array();
|
57 |
+
|
58 |
+
/**
|
59 |
+
* @param $key - the ShortPixel API Key
|
60 |
+
*/
|
61 |
+
public static function setKey($key) {
|
62 |
+
self::$key = $key;
|
63 |
+
self::$client = NULL;
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* @param $options - set the ShortPxiel options. Options defaults are the following:
|
68 |
+
* "lossy" => 1, // 1 - lossy, 0 - lossless
|
69 |
+
"keep_exif" => 0, // 1 - EXIF is preserved, 0 - EXIF is removed
|
70 |
+
"resize_width" => null, // in pixels. null means no resize
|
71 |
+
"resize_height" => null,
|
72 |
+
"cmyk2rgb" => 1,
|
73 |
+
"convertto" => "", // if '+webp' then also the WebP version will be generated, if +avif then also the AVIF version will be generated. Specify both with +webp|+avif
|
74 |
+
"notify_me" => null, // should contain full URL of of notification script (notify.php)- TO BE IMPLEMENTED
|
75 |
+
"wait" => 30,
|
76 |
+
//local options
|
77 |
+
"total_wait" => 30,
|
78 |
+
"base_url" => null, // base url of the images - used to generate the path for toFile by extracting from original URL and using the remaining path as relative path to base_path
|
79 |
+
"base_path" => "/tmp", // base path for the saved files
|
80 |
+
*/
|
81 |
+
public static function setOptions($options) {
|
82 |
+
self::$options = array_merge(self::$options, $options);
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* add custom cURL options. These provided options will take precedence to the default options that are passed to all cURL calls but not to others that are specific to each call (CURLOPT_TIMEOUT, CURLOPT_CUSTOMREQUEST)
|
87 |
+
* or to library's user agent.
|
88 |
+
* @param array $curlOptions Key-value pairs
|
89 |
+
*/
|
90 |
+
public static function setCurlOptions($curlOptions) {
|
91 |
+
self::$curlOptions = $curlOptions + self::$curlOptions;
|
92 |
+
}
|
93 |
+
|
94 |
+
/**
|
95 |
+
* @return the API Key in use
|
96 |
+
*/
|
97 |
+
public static function getKey() {
|
98 |
+
return self::$key;
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* @param $name - option name
|
103 |
+
* @return the option value or false if not found
|
104 |
+
*/
|
105 |
+
public static function opt($name) {
|
106 |
+
return isset(self::$options[$name]) ? self::$options[$name] : false;
|
107 |
+
}
|
108 |
+
|
109 |
+
/**
|
110 |
+
* @return the current options array
|
111 |
+
*/
|
112 |
+
public static function options() {
|
113 |
+
return self::$options;
|
114 |
+
}
|
115 |
+
|
116 |
+
/**
|
117 |
+
* @return Client singleton
|
118 |
+
* @throws AccountException
|
119 |
+
*/
|
120 |
+
public static function getClient() {
|
121 |
+
if (!self::$key) {
|
122 |
+
throw new AccountException("Provide an API key with ShortPixel\setKey(...)", -6);
|
123 |
+
}
|
124 |
+
|
125 |
+
if (!self::$client) {
|
126 |
+
self::$client = new Client(self::$curlOptions);
|
127 |
+
}
|
128 |
+
|
129 |
+
return self::$client;
|
130 |
+
}
|
131 |
+
|
132 |
+
public static function getPersister($context = null) {
|
133 |
+
if(!self::$options["persist_type"]) {
|
134 |
+
return null;
|
135 |
+
}
|
136 |
+
if($context && isset(self::$persistersRegistry[self::$options["persist_type"] . $context])) {
|
137 |
+
return self::$persistersRegistry[self::$options["persist_type"] . $context];
|
138 |
+
}
|
139 |
+
|
140 |
+
$persister = null;
|
141 |
+
switch(self::$options["persist_type"]) {
|
142 |
+
case "exif":
|
143 |
+
$persister = new persist\ExifPersister(self::$options);
|
144 |
+
break;
|
145 |
+
case "mysql":
|
146 |
+
return null;
|
147 |
+
case "text":
|
148 |
+
$persister = new persist\TextPersister(self::$options);
|
149 |
+
break;
|
150 |
+
default:
|
151 |
+
throw new PersistException("Unknown persist type: " . self::$options["persist_type"]);
|
152 |
+
}
|
153 |
+
|
154 |
+
if($context) {
|
155 |
+
self::$persistersRegistry[self::$options["persist_type"] . $context] = $persister;
|
156 |
+
}
|
157 |
+
return $persister;
|
158 |
+
}
|
159 |
+
|
160 |
+
static public function isProcessable($path) {
|
161 |
+
return in_array(strtolower(pathinfo($path, PATHINFO_EXTENSION)), \ShortPixel\ShortPixel::$PROCESSABLE_EXTENSIONS);
|
162 |
+
}
|
163 |
+
|
164 |
+
static public function log($msg) {
|
165 |
+
if(ShortPixel::DEBUG_LOG) {
|
166 |
+
@file_put_contents(__DIR__ . '/splog.txt', date("Y-m-d H:i:s") . " - " . $msg . " \n\n", FILE_APPEND);
|
167 |
+
}
|
168 |
+
}
|
169 |
+
}
|
170 |
+
|
171 |
+
ShortPixel::setOptions(array('base_path' => sys_get_temp_dir()));
|
172 |
+
|
173 |
+
|
174 |
+
/**
|
175 |
+
* stub for ShortPixel::setKey()
|
176 |
+
* @param $key - the ShortPixel API Key
|
177 |
+
*/
|
178 |
+
function setKey($key) {
|
179 |
+
return ShortPixel::setKey($key);
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* stub for ShortPixel::setOptions()
|
184 |
+
* @return the current options array
|
185 |
+
*/
|
186 |
+
function setOptions($options) {
|
187 |
+
return ShortPixel::setOptions($options);
|
188 |
+
}
|
189 |
+
|
190 |
+
/**
|
191 |
+
* stub for ShortPixel::setOptions()
|
192 |
+
* @return the current options array
|
193 |
+
*/
|
194 |
+
function setCurlOptions($options) {
|
195 |
+
return ShortPixel::setCurlOptions($options);
|
196 |
+
}
|
197 |
+
|
198 |
+
/**
|
199 |
+
* stub for ShortPixel::opt()
|
200 |
+
* @param $name - name of the option
|
201 |
+
* @return the option
|
202 |
+
*/
|
203 |
+
function opt($name) {
|
204 |
+
return ShortPixel::opt($name);
|
205 |
+
}
|
206 |
+
|
207 |
+
/**
|
208 |
+
* Stub for Source::fromFiles
|
209 |
+
* @param $path - the file path on the local drive
|
210 |
+
* @return Commander - the class that handles the optimization commands
|
211 |
+
* @throws ClientException
|
212 |
+
*/
|
213 |
+
function fromFiles($path) {
|
214 |
+
$source = new Source();
|
215 |
+
return $source->fromFiles($path);
|
216 |
+
}
|
217 |
+
|
218 |
+
function fromFile($path) {
|
219 |
+
return fromFiles($path);
|
220 |
+
}
|
221 |
+
|
222 |
+
/**
|
223 |
+
* Stub for Source::folderInfo
|
224 |
+
* @param $path - the file path on the local drive
|
225 |
+
* @param bool $recurse - boolean - go into subfolders or not
|
226 |
+
* @param bool $fileList - return the list of files with optimization status (only current folder, not subfolders)
|
227 |
+
* @param array $exclude - array of folder names that you want to exclude from the optimization
|
228 |
+
* @param bool $persistPath - the path where to look for the metadata, if different from the $path
|
229 |
+
* @param int $recurseDepth - how many subfolders deep to go. Defaults to PHP_INT_MAX
|
230 |
+
* @param bool $retrySkipped - if true, all skipped files will be reset to pending with retries = 0
|
231 |
+
* @return object|void (object)array('status', 'total', 'succeeded', 'pending', 'same', 'failed')
|
232 |
+
* @throws PersistException
|
233 |
+
*/
|
234 |
+
function folderInfo($path, $recurse = true, $fileList = false, $exclude = array(), $persistPath = false, $recurseDepth = PHP_INT_MAX, $retrySkipped = false) {
|
235 |
+
$source = new Source();
|
236 |
+
return $source->folderInfo($path, $recurse, $fileList, $exclude, $persistPath, $recurseDepth, $retrySkipped);
|
237 |
+
}
|
238 |
+
|
239 |
+
/**
|
240 |
+
* Stub for Source::fromFolder
|
241 |
+
* @param $path - the file path on the local drive
|
242 |
+
* @param $maxFiles - maximum number of files to select from the folder
|
243 |
+
* @param $exclude - exclude files based on regex patterns
|
244 |
+
* @param $persistPath - the path where to store the metadata, if different from the $path (usually the target path)
|
245 |
+
* @param $maxTotalFileSize - max summed up file size in MB
|
246 |
+
* @return Commander - the class that handles the optimization commands
|
247 |
+
* @throws ClientException
|
248 |
+
*/
|
249 |
+
function fromFolder($path, $maxFiles = ShortPixel::MAX_ALLOWED_FILES_PER_CALL, $exclude = array(), $persistPath = false, $maxTotalFileSize = ShortPixel::CLIENT_MAX_BODY_SIZE, $recurseDepth = PHP_INT_MAX) {
|
250 |
+
$source = new Source();
|
251 |
+
return $source->fromFolder($path, $maxFiles, $exclude, $persistPath, $maxTotalFileSize, $recurseDepth);
|
252 |
+
}
|
253 |
+
|
254 |
+
/**
|
255 |
+
* Stub for Source::fromWebFolder
|
256 |
+
* @param $path - the file path on the local drive
|
257 |
+
* @param $webPath - the corresponding web path for the file path
|
258 |
+
* @param array $exclude - exclude files based on regex patterns
|
259 |
+
* @param bool $persistFolder - the path where to store the metadata, if different from the $path (usually the target path)
|
260 |
+
* @param int $recurseDepth - how many subfolders deep to go. Defaults to PHP_INT_MAX
|
261 |
+
* @return Commander - the class that handles the optimization commands
|
262 |
+
* @throws ClientException
|
263 |
+
*/
|
264 |
+
function fromWebFolder($path, $webPath, $exclude = array(), $persistFolder = false, $recurseDepth = PHP_INT_MAX) {
|
265 |
+
$source = new Source();
|
266 |
+
return $source->fromWebFolder($path, $webPath, $exclude, $persistFolder, $recurseDepth);
|
267 |
+
}
|
268 |
+
|
269 |
+
function fromBuffer($name, $contents) {
|
270 |
+
$source = new Source();
|
271 |
+
return $source->fromBuffer($name, $contents);
|
272 |
+
}
|
273 |
+
|
274 |
+
/**
|
275 |
+
* Stub for Source::fromUrls
|
276 |
+
* @param $urls - the array of urls to be optimized
|
277 |
+
* @return Commander - the class that handles the optimization commands
|
278 |
+
* @throws ClientException
|
279 |
+
*/
|
280 |
+
function fromUrls($urls) {
|
281 |
+
$source = new Source();
|
282 |
+
return $source->fromUrls($urls);
|
283 |
+
}
|
284 |
+
|
285 |
+
/**
|
286 |
+
*/
|
287 |
+
function isOptimized($path) {
|
288 |
+
$persist = ShortPixel::getPersister($path);
|
289 |
+
if($persist) {
|
290 |
+
return $persist->isOptimized($path);
|
291 |
+
} else {
|
292 |
+
throw new Exception("No persister available");
|
293 |
+
}
|
294 |
+
}
|
295 |
+
|
296 |
+
function validate() {
|
297 |
+
try {
|
298 |
+
ShortPixel::getClient()->request("post");
|
299 |
+
} catch (ClientException $e) {
|
300 |
+
return true;
|
301 |
+
}
|
302 |
+
}
|
303 |
+
|
304 |
+
function recurseCopy($source, $dest) {
|
305 |
+
foreach (
|
306 |
+
$iterator = new \RecursiveIteratorIterator(
|
307 |
+
new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS),
|
308 |
+
\RecursiveIteratorIterator::SELF_FIRST) as $item
|
309 |
+
) {
|
310 |
+
$target = $dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName();
|
311 |
+
if ($item->isDir()) {
|
312 |
+
if(!@mkdir($target)) {
|
313 |
+
throw new PersistException("Could not create directory $target. Please check rights.");
|
314 |
+
}
|
315 |
+
} else {
|
316 |
+
if(!@copy($item, $target)) {
|
317 |
+
throw new PersistException("Could not copy file $item to $target. Please check rights.");
|
318 |
+
}
|
319 |
+
}
|
320 |
+
}
|
321 |
+
}
|
322 |
+
|
323 |
+
function delTree($dir, $keepBase = true) {
|
324 |
+
$files = array_diff(scandir($dir), array('.','..'));
|
325 |
+
foreach ($files as $file) {
|
326 |
+
(is_dir("$dir/$file")) ? delTree("$dir/$file", false) : unlink("$dir/$file");
|
327 |
+
}
|
328 |
+
return $keepBase ? true : rmdir($dir);
|
329 |
+
}
|
330 |
+
|
331 |
+
/**
|
332 |
+
* a basename alternative that deals OK with multibyte charsets (e.g. Arabic)
|
333 |
+
* @param string $Path
|
334 |
+
* @return string
|
335 |
+
*/
|
336 |
+
function MB_basename($Path, $suffix = false){
|
337 |
+
$Separator = " qq ";
|
338 |
+
$qqPath = preg_replace("/[^ ]/u", $Separator."\$0".$Separator, $Path);
|
339 |
+
if(!$qqPath) { //this is not an UTF8 string!!
|
340 |
+
$pathElements = explode('/', $Path);
|
341 |
+
$fileName = end($pathElements);
|
342 |
+
$pos = strpos($fileName, $suffix);
|
343 |
+
if($pos !== false) {
|
344 |
+
return substr($fileName, 0, $pos);
|
345 |
+
}
|
346 |
+
return $fileName;
|
347 |
+
}
|
348 |
+
$suffix = preg_replace("/[^ ]/u", $Separator."\$0".$Separator, $suffix);
|
349 |
+
$Base = basename($qqPath, $suffix);
|
350 |
+
$Base = str_replace($Separator, "", $Base);
|
351 |
+
return $Base;
|
352 |
+
}
|
353 |
+
|
354 |
+
if(!function_exists('mb_detect_encoding')) {
|
355 |
+
function mb_detect_encoding($string, $enc=null) {
|
356 |
+
|
357 |
+
static $list = array('utf-8', 'iso-8859-1', 'windows-1251');
|
358 |
+
|
359 |
+
foreach ($list as $item) {
|
360 |
+
$sample = @iconv($item, $item, $string);
|
361 |
+
if (md5($sample) == md5($string)) {
|
362 |
+
if ($enc == $item) { return true; } else { return $item; }
|
363 |
+
}
|
364 |
+
}
|
365 |
+
return null;
|
366 |
+
}
|
367 |
+
}
|
368 |
+
|
369 |
+
function spdbg($var, $msg) {
|
370 |
+
echo("DEBUG $msg : "); var_dump($var);
|
371 |
+
}
|
372 |
+
|
373 |
+
function spdbgd($var, $msg) {
|
374 |
+
die(spdbg($var, $msg));
|
375 |
+
}
|
376 |
+
|
377 |
+
function normalizePath($path) {
|
378 |
+
$patterns = array('~/{2,}~', '~/(\./)+~', '~([^/\.]+/(?R)*\.{2,}/)~', '~\.\./~');
|
379 |
+
$replacements = array('/', '/', '', '');
|
380 |
+
return preg_replace($patterns, $replacements, $path);
|
381 |
+
}
|
382 |
+
|
383 |
+
function getMemcache() {
|
384 |
+
$mc = false;
|
385 |
+
if(class_exists('\Memcached')) {
|
386 |
+
$mc = new \Memcached();
|
387 |
+
$mc->addServer('127.0.0.1', '11211');
|
388 |
+
if(!@$mc->getStats()) {
|
389 |
+
$mc = false;
|
390 |
+
}
|
391 |
+
}
|
392 |
+
elseif(class_exists('\Memcache')) {
|
393 |
+
$mc = new \Memcache();
|
394 |
+
$mc->addServer('127.0.0.1', '11211');
|
395 |
+
if(!@$mc->connect('127.0.0.1', '11211')) {
|
396 |
+
$mc = false;
|
397 |
+
} else {
|
398 |
+
$mc->close();
|
399 |
+
}
|
400 |
+
}
|
401 |
+
return $mc;
|
402 |
+
}
|
403 |
+
|
404 |
+
if ( ! function_exists( 'json_last_error_msg' ) ) {
|
405 |
+
/**
|
406 |
+
* Retrieves the error string of the last json_encode() or json_decode() call.
|
407 |
+
*
|
408 |
+
* @since 4.4.0
|
409 |
+
*
|
410 |
+
* @internal This is a compatibility function for PHP <5.5
|
411 |
+
*
|
412 |
+
* @return bool|string Returns the error message on success, "No Error" if no error has occurred,
|
413 |
+
* or false on failure.
|
414 |
+
*/
|
415 |
+
function json_last_error_msg()
|
416 |
+
{
|
417 |
+
// See https://core.trac.wordpress.org/ticket/27799.
|
418 |
+
if (!function_exists('json_last_error')) {
|
419 |
+
return false;
|
420 |
+
}
|
421 |
+
|
422 |
+
$last_error_code = json_last_error();
|
423 |
+
|
424 |
+
// Just in case JSON_ERROR_NONE is not defined.
|
425 |
+
$error_code_none = defined('JSON_ERROR_NONE') ? JSON_ERROR_NONE : 0;
|
426 |
+
|
427 |
+
switch (true) {
|
428 |
+
case $last_error_code === $error_code_none:
|
429 |
+
return 'No error';
|
430 |
+
|
431 |
+
case defined('JSON_ERROR_DEPTH') && JSON_ERROR_DEPTH === $last_error_code:
|
432 |
+
return 'Maximum stack depth exceeded';
|
433 |
+
|
434 |
+
case defined('JSON_ERROR_STATE_MISMATCH') && JSON_ERROR_STATE_MISMATCH === $last_error_code:
|
435 |
+
return 'State mismatch (invalid or malformed JSON)';
|
436 |
+
|
437 |
+
case defined('JSON_ERROR_CTRL_CHAR') && JSON_ERROR_CTRL_CHAR === $last_error_code:
|
438 |
+
return 'Control character error, possibly incorrectly encoded';
|
439 |
+
|
440 |
+
case defined('JSON_ERROR_SYNTAX') && JSON_ERROR_SYNTAX === $last_error_code:
|
441 |
+
return 'Syntax error';
|
442 |
+
|
443 |
+
case defined('JSON_ERROR_UTF8') && JSON_ERROR_UTF8 === $last_error_code:
|
444 |
+
return 'Malformed UTF-8 characters, possibly incorrectly encoded';
|
445 |
+
|
446 |
+
case defined('JSON_ERROR_RECURSION') && JSON_ERROR_RECURSION === $last_error_code:
|
447 |
+
return 'Recursion detected';
|
448 |
+
|
449 |
+
case defined('JSON_ERROR_INF_OR_NAN') && JSON_ERROR_INF_OR_NAN === $last_error_code:
|
450 |
+
return 'Inf and NaN cannot be JSON encoded';
|
451 |
+
|
452 |
+
case defined('JSON_ERROR_UNSUPPORTED_TYPE') && JSON_ERROR_UNSUPPORTED_TYPE === $last_error_code:
|
453 |
+
return 'Type is not supported';
|
454 |
+
|
455 |
+
default:
|
456 |
+
return 'An unknown error occurred';
|
457 |
+
}
|
458 |
+
}
|
459 |
+
}
|
vendor/shortpixel/shortpixel-php/lib/ShortPixel/Client.php
ADDED
@@ -0,0 +1,542 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace ShortPixel;
|
4 |
+
|
5 |
+
|
6 |
+
class Client {
|
7 |
+
|
8 |
+
public static function API_DOMAIN() {
|
9 |
+
return "api.shortpixel.com";
|
10 |
+
//* DEVELOPMENT !! */ return "devapi2.shortpixel.com";
|
11 |
+
}
|
12 |
+
|
13 |
+
private $options, $customOptions, $logger;
|
14 |
+
|
15 |
+
public static function API_URL() {
|
16 |
+
return "https://" . self::API_DOMAIN();
|
17 |
+
|
18 |
+
}
|
19 |
+
public static function API_ENDPOINT() {
|
20 |
+
return self::API_URL() . "/v2/reducer.php";
|
21 |
+
}
|
22 |
+
|
23 |
+
public static function API_UPLOAD_ENDPOINT() {
|
24 |
+
return self::API_URL() . "/v2/post-reducer.php";
|
25 |
+
}
|
26 |
+
|
27 |
+
public static function API_STATUS_ENDPOINT() {
|
28 |
+
return self::API_URL() . "/v2/api-status.php";
|
29 |
+
}
|
30 |
+
|
31 |
+
public static function IMAGE_STATUS_ENDPOINT() {
|
32 |
+
return self::API_URL() . "/v2/image-status.php";
|
33 |
+
}
|
34 |
+
|
35 |
+
public static function CLEANUP_ENDPOINT() {
|
36 |
+
return self::API_URL() . "/v2/cleanup.php";
|
37 |
+
}
|
38 |
+
|
39 |
+
public static function userAgent() {
|
40 |
+
$curl = curl_version();
|
41 |
+
return "ShortPixel/" . ShortPixel::VERSION . " PHP/" . PHP_VERSION . " curl/" . $curl["version"];
|
42 |
+
}
|
43 |
+
|
44 |
+
private static function caBundle() {
|
45 |
+
return dirname(__DIR__) . "/data/shortpixel.crt";
|
46 |
+
}
|
47 |
+
|
48 |
+
function __construct($curlOptions) {
|
49 |
+
$this->customOptions = $curlOptions;
|
50 |
+
$this->logger = SPLog::Get(SPLog::PRODUCER_CLIENT);
|
51 |
+
$this->options = $curlOptions + array(
|
52 |
+
CURLOPT_RETURNTRANSFER => true,
|
53 |
+
CURLOPT_BINARYTRANSFER => true,
|
54 |
+
CURLOPT_HEADER => true,
|
55 |
+
CURLOPT_TIMEOUT => 60,
|
56 |
+
//CURLOPT_CAINFO => self::caBundle(),
|
57 |
+
CURLOPT_SSL_VERIFYPEER => false, //TODO true
|
58 |
+
CURLOPT_SSL_VERIFYHOST => false, //TODO remove
|
59 |
+
CURLOPT_USERAGENT => self::userAgent(),
|
60 |
+
);
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Does the CURL request to the ShortPixel API
|
65 |
+
* @param $method 'post' or 'get'
|
66 |
+
* @param null $body - the POST fields
|
67 |
+
* @param array $header - HTTP headers
|
68 |
+
* @return array - metadata from the API
|
69 |
+
* @throws ConnectionException
|
70 |
+
*/
|
71 |
+
function request($method, $body = NULL, $header = array()){
|
72 |
+
if ($body) {
|
73 |
+
foreach($body as $key => $val) {
|
74 |
+
if($val === null) {
|
75 |
+
unset($body[$key]);
|
76 |
+
}
|
77 |
+
}
|
78 |
+
}
|
79 |
+
|
80 |
+
ShortPixel::log("REQUEST BODY: " . json_encode($body));
|
81 |
+
|
82 |
+
$retUrls = array("body" => array(), "headers" => array(), "fileMappings" => array());
|
83 |
+
$retPend = array("body" => array(), "headers" => array(), "fileMappings" => array());
|
84 |
+
$retFiles = array("body" => array(), "headers" => array(), "fileMappings" => array());
|
85 |
+
|
86 |
+
if(isset($body["urllist"])) {
|
87 |
+
$retUrls = $this->requestInternal($method, $body, $header);
|
88 |
+
}
|
89 |
+
if(isset($body["pendingURLs"])) {
|
90 |
+
unset($body["urllist"]);
|
91 |
+
//some files might have already been processed as relaunches in the given max time
|
92 |
+
foreach($retUrls["body"] as $url) {
|
93 |
+
//first remove it from the files list as the file was uploaded properly
|
94 |
+
if($url->Status->Code != -102 && $url->Status->Code != -106) {
|
95 |
+
$notExpired[] = $url;
|
96 |
+
if(!isset($body["pendingURLs"][$url->OriginalURL])) {
|
97 |
+
$lala = "cucu";
|
98 |
+
} else
|
99 |
+
$unsetPath = $body["pendingURLs"][$url->OriginalURL];
|
100 |
+
if(isset($body["files"]) && ($key = array_search($unsetPath, $body["files"])) !== false) {
|
101 |
+
unset($body["files"][$key]);
|
102 |
+
}
|
103 |
+
}
|
104 |
+
//now from the pendingURLs if we already have an answer with urllist
|
105 |
+
if(isset($body["pendingURLs"][$url->OriginalURL])) {
|
106 |
+
$retUrls["fileMappings"][$url->OriginalURL] = $body["pendingURLs"][$url->OriginalURL];
|
107 |
+
unset($body["pendingURLs"][$url->OriginalURL]);
|
108 |
+
}
|
109 |
+
}
|
110 |
+
if(count($body["pendingURLs"])) {
|
111 |
+
$retPend = $this->requestInternal($method, $body, $header);
|
112 |
+
if(isset($retPend['body']->Status->Code) && $retPend['body']->Status->Code < 0) { //something's wrong (API key?)
|
113 |
+
throw new ClientException($retPend['body']->Status->Message, $retPend['body']->Status->Code);
|
114 |
+
|
115 |
+
}
|
116 |
+
if(isset($body["files"])) {
|
117 |
+
$notExpired = array();
|
118 |
+
foreach($retPend['body'] as $detail) {
|
119 |
+
if($detail->Status->Code != -102) { // -102 is expired, means we need to resend the image through post
|
120 |
+
$notExpired[] = $detail;
|
121 |
+
$unsetPath = $body["pendingURLs"][$detail->OriginalURL];
|
122 |
+
if(($key = array_search($unsetPath, $body["files"])) !== false) {
|
123 |
+
unset($body["files"][$key]);
|
124 |
+
}
|
125 |
+
}
|
126 |
+
}
|
127 |
+
$retPend['body'] = $notExpired;
|
128 |
+
}
|
129 |
+
}
|
130 |
+
}
|
131 |
+
if (isset($body["files"]) && count($body["files"]) ||
|
132 |
+
isset($body["buffers"]) && count($body["buffers"])) {
|
133 |
+
unset($body["pendingURLs"]);
|
134 |
+
$retFiles = $this->requestInternal($method, $body, $header);
|
135 |
+
}
|
136 |
+
|
137 |
+
if(!isset($retUrls["body"]->Status) && !isset($retPend["body"]->Status) && !isset($retFiles["body"]->Status)
|
138 |
+
&& (!is_array($retUrls["body"]) || !is_array($retPend["body"]) || !is_array($retFiles["body"]))) {
|
139 |
+
throw new Exception("Request inconsistent status. Please contact support.");
|
140 |
+
}
|
141 |
+
|
142 |
+
$body = isset($retUrls["body"]->Status)
|
143 |
+
? $retUrls["body"]
|
144 |
+
: (isset($retPend["body"]->Status)
|
145 |
+
? $retPend["body"]
|
146 |
+
: (isset($retFiles["body"]->Status)
|
147 |
+
? $retFiles["body"] :
|
148 |
+
array_merge($retUrls["body"], $retPend["body"], $retFiles["body"])));
|
149 |
+
|
150 |
+
$theReturn = (object) array("body" => $body,
|
151 |
+
"headers" => array_unique(array_merge($retUrls["headers"], $retPend["headers"], $retFiles["headers"])),
|
152 |
+
"fileMappings" => array_merge($retUrls["fileMappings"], $retPend["fileMappings"], $retFiles["fileMappings"]));
|
153 |
+
ShortPixel::log("REQUEST RETURNS: " . json_encode($theReturn));
|
154 |
+
return $theReturn;
|
155 |
+
}
|
156 |
+
|
157 |
+
function requestInternal($method, $body = NULL, $header = array()){
|
158 |
+
$request = curl_init();
|
159 |
+
curl_setopt_array($request, $this->options);
|
160 |
+
|
161 |
+
$files = $urls = false;
|
162 |
+
|
163 |
+
if (isset($body["urllist"])) { //images are sent as a list of URLs
|
164 |
+
$this->prepareJSONRequest(self::API_ENDPOINT(), $request, $body, $method, $header);
|
165 |
+
}
|
166 |
+
elseif(isset($body["pendingURLs"])) {
|
167 |
+
//prepare the pending items request
|
168 |
+
$urls = array();
|
169 |
+
$fileCount = 1;
|
170 |
+
foreach($body["pendingURLs"] as $url => $path) {
|
171 |
+
$urls["url" . $fileCount] = $url;
|
172 |
+
$fileCount++;
|
173 |
+
}
|
174 |
+
$pendingURLs = $body["pendingURLs"];
|
175 |
+
unset($body["pendingURLs"]);
|
176 |
+
$body["file_urls"] = $urls;
|
177 |
+
$this->prepareJSONRequest(self::API_UPLOAD_ENDPOINT(), $request, $body, $method, $header);
|
178 |
+
}
|
179 |
+
elseif (isset($body["files"]) || isset($body["buffers"])) {
|
180 |
+
$files = $this->prepareMultiPartRequest($request, $body, $header);
|
181 |
+
}
|
182 |
+
else {
|
183 |
+
return array("body" => array(), "headers" => array(), "fileMappings" => array());
|
184 |
+
}
|
185 |
+
|
186 |
+
//spdbgd(rawurldecode($body['urllist'][1]), "body");
|
187 |
+
|
188 |
+
list($details, $headers, $status, $response) = $this->sendRequest($request,6);
|
189 |
+
|
190 |
+
//TODO delete later
|
191 |
+
/* for($i = 0; $i < 6; $i++) { //curl_setopt($request, CURLOPT_TIMEOUT, 120);curl_setopt($request, CURLOPT_VERBOSE, true);
|
192 |
+
$response = curl_exec($request);
|
193 |
+
if(!curl_errno($request)) {
|
194 |
+
break;
|
195 |
+
} else {
|
196 |
+
ShortPixel::log("CURL ERROR: " . curl_error($request) . " (BODY: $response)");
|
197 |
+
}
|
198 |
+
}
|
199 |
+
|
200 |
+
if(curl_errno($request)) {
|
201 |
+
throw new ConnectionException("Error while connecting: " . curl_error($request) . "");
|
202 |
+
}
|
203 |
+
if (!is_string($response)) {
|
204 |
+
$message = sprintf("%s (#%d)", curl_error($request), curl_errno($request));
|
205 |
+
curl_close($request);
|
206 |
+
throw new ConnectionException("Error while connecting: " . $message);
|
207 |
+
}
|
208 |
+
|
209 |
+
$status = curl_getinfo($request, CURLINFO_HTTP_CODE);
|
210 |
+
$headerSize = curl_getinfo($request, CURLINFO_HEADER_SIZE);
|
211 |
+
curl_close($request);
|
212 |
+
|
213 |
+
$headers = self::parseHeaders(substr($response, 0, $headerSize));
|
214 |
+
$body = substr($response, $headerSize);
|
215 |
+
|
216 |
+
$details = json_decode($body);
|
217 |
+
|
218 |
+
if (!$details) {
|
219 |
+
$message = sprintf("Error while parsing response (Status: %s): %s (#%d)", $status,
|
220 |
+
PHP_VERSION_ID >= 50500 ? json_last_error_msg() : "Error",
|
221 |
+
json_last_error());
|
222 |
+
$details = (object) array(
|
223 |
+
"raw" => $body,
|
224 |
+
"error" => "ParseError",
|
225 |
+
"message" => $message . "( " . $body . ")",
|
226 |
+
"Status" => (object)array("Code" => -1, "Message" => "ParseError: " . $message)
|
227 |
+
);
|
228 |
+
}
|
229 |
+
*/
|
230 |
+
if(getenv("SHORTPIXEL_DEBUG")) {
|
231 |
+
$info = "DETAILS\n";
|
232 |
+
if(is_array($details)) {
|
233 |
+
foreach($details as $det) {
|
234 |
+
$info .= $det->Status->Code . " " . $det->OriginalURL . (isset($det->localPath) ? "({$det->localPath})" : "" ) . "\n";
|
235 |
+
}
|
236 |
+
} else {
|
237 |
+
$info = $response;
|
238 |
+
}
|
239 |
+
}
|
240 |
+
|
241 |
+
$fileMappings = array();
|
242 |
+
if($files) {
|
243 |
+
$fileMappings = array();
|
244 |
+
foreach($details as $detail) {
|
245 |
+
if(isset($detail->Key) && isset($files[$detail->Key])){
|
246 |
+
$fileMappings[$detail->OriginalURL] = $files[$detail->Key];
|
247 |
+
}
|
248 |
+
}
|
249 |
+
} elseif($urls) {
|
250 |
+
$fileMappings = $pendingURLs;
|
251 |
+
}
|
252 |
+
|
253 |
+
if(getenv("SHORTPIXEL_DEBUG")) {
|
254 |
+
$info .= "FILE MAPPINGS\n";
|
255 |
+
foreach($fileMappings as $key => $val) {
|
256 |
+
$info .= "$key -> $val\n";
|
257 |
+
}
|
258 |
+
}
|
259 |
+
|
260 |
+
if ($status >= 200 && $status <= 299) {
|
261 |
+
return array("body" => $details, "headers" => $headers, "fileMappings" => $fileMappings);
|
262 |
+
}
|
263 |
+
|
264 |
+
throw Exception::create($details->message, $details->error, $status);
|
265 |
+
}
|
266 |
+
|
267 |
+
protected function sendRequest($request, $tries) {
|
268 |
+
for($i = 0; $i < $tries; $i++) { //curl_setopt($request, CURLOPT_TIMEOUT, 120);curl_setopt($request, CURLOPT_VERBOSE, true);
|
269 |
+
$response = curl_exec($request);
|
270 |
+
$this->logger->log(SPLog::PRODUCER_CLIENT, "RAW RESPONSE: " . $response);
|
271 |
+
if(!curl_errno($request)) {
|
272 |
+
break;
|
273 |
+
} else {
|
274 |
+
ShortPixel::log("CURL ERROR: " . curl_error($request) . " (BODY: $response)");
|
275 |
+
}
|
276 |
+
}
|
277 |
+
|
278 |
+
if(curl_errno($request)) {
|
279 |
+
throw new ConnectionException("Error while connecting: " . curl_error($request) . "");
|
280 |
+
}
|
281 |
+
if (!is_string($response)) {
|
282 |
+
$message = sprintf("%s (#%d)", curl_error($request), curl_errno($request));
|
283 |
+
curl_close($request);
|
284 |
+
throw new ConnectionException("Error while connecting: " . $message);
|
285 |
+
}
|
286 |
+
|
287 |
+
$status = curl_getinfo($request, CURLINFO_HTTP_CODE);
|
288 |
+
$headerSize = curl_getinfo($request, CURLINFO_HEADER_SIZE);
|
289 |
+
curl_close($request);
|
290 |
+
|
291 |
+
$headers = self::parseHeaders(substr($response, 0, $headerSize));
|
292 |
+
$body = substr($response, $headerSize);
|
293 |
+
|
294 |
+
$details = json_decode($body);
|
295 |
+
|
296 |
+
if (!$details) {
|
297 |
+
$message = sprintf("Error while parsing response (Status: %s): %s (#%d)", $status,
|
298 |
+
PHP_VERSION_ID >= 50500 ? json_last_error_msg() : "Error",
|
299 |
+
json_last_error());
|
300 |
+
$details = (object) array(
|
301 |
+
"raw" => $body,
|
302 |
+
"error" => "ParseError",
|
303 |
+
"message" => $message . "( " . $body . ")",
|
304 |
+
"Status" => (object)array("Code" => -1, "Message" => "ParseError: " . $message)
|
305 |
+
);
|
306 |
+
ShortPixel::log("JSON Error while parsing response: " . json_encode($details));
|
307 |
+
}
|
308 |
+
return array($details, $headers, $status, $response);
|
309 |
+
}
|
310 |
+
|
311 |
+
protected function prepareJSONRequest($endpoint, $request, $body, $method, $header) {
|
312 |
+
//to escape the + from "+webp"
|
313 |
+
if(isset($body["convertto"]) && $body["convertto"]) {
|
314 |
+
$converts = explode('|', $body["convertto"]);
|
315 |
+
$body["convertto"] = implode('|', array_map('urlencode', $converts ));
|
316 |
+
}
|
317 |
+
// if(isset($body["urllist"])) {
|
318 |
+
// aici folosim ceva de genul: parse_url si apoi pe partea de path: str_replace('%2F', '/', rawurlencode($this->filePath)
|
319 |
+
// $body["urllist"] = array_map('rawurlencode', $body["urllist"]);
|
320 |
+
// }
|
321 |
+
$body = json_encode($body);
|
322 |
+
|
323 |
+
array_push($header, "Content-Type: application/json");
|
324 |
+
curl_setopt($request, CURLOPT_URL, $endpoint);
|
325 |
+
curl_setopt($request, CURLOPT_CUSTOMREQUEST, strtoupper($method));
|
326 |
+
curl_setopt($request, CURLOPT_HTTPHEADER, $header);
|
327 |
+
if ($body) {
|
328 |
+
curl_setopt($request, CURLOPT_POSTFIELDS, $body);
|
329 |
+
}
|
330 |
+
}
|
331 |
+
|
332 |
+
|
333 |
+
protected function prepareMultiPartRequest($request, $body, $header) {
|
334 |
+
$files = array();
|
335 |
+
$fileCount = 1;
|
336 |
+
//to escape the + from "+webp"
|
337 |
+
if($body["convertto"]) {
|
338 |
+
$body["convertto"] = urlencode($body["convertto"]);
|
339 |
+
}
|
340 |
+
if(isset($body["files"])) {
|
341 |
+
foreach($body["files"] as $filePath) {
|
342 |
+
$files["file" . $fileCount] = $filePath;
|
343 |
+
$fileCount++;
|
344 |
+
}
|
345 |
+
}
|
346 |
+
$buffers = array();
|
347 |
+
if(isset($body["buffers"])) {
|
348 |
+
foreach($body["buffers"] as $name => $contents) {
|
349 |
+
$files["file" . $fileCount] = $name;
|
350 |
+
$buffers["file" . $fileCount] = $contents;
|
351 |
+
$fileCount++;
|
352 |
+
}
|
353 |
+
unset($body["buffers"]);
|
354 |
+
}
|
355 |
+
$body["file_paths"] = json_encode($files);
|
356 |
+
unset($body["files"]);
|
357 |
+
curl_setopt($request, CURLOPT_URL, Client::API_UPLOAD_ENDPOINT());
|
358 |
+
$this->curl_custom_postfields($request, $body, $files, $header, $buffers);
|
359 |
+
return $files;
|
360 |
+
}
|
361 |
+
|
362 |
+
function curl_custom_postfields($ch, array $assoc = array(), array $files = array(), $header = array(), $buffers = array()) {
|
363 |
+
|
364 |
+
// invalid characters for "name" and "filename"
|
365 |
+
static $disallow = array("\0", "\"", "\r", "\n");
|
366 |
+
|
367 |
+
// build normal parameters
|
368 |
+
foreach ($assoc as $k => $v) {
|
369 |
+
$k = str_replace($disallow, "_", $k);
|
370 |
+
$body[] = implode("\r\n", array(
|
371 |
+
"Content-Disposition: form-data; name=\"{$k}\"",
|
372 |
+
"",
|
373 |
+
filter_var($v),
|
374 |
+
));
|
375 |
+
}
|
376 |
+
|
377 |
+
// build file parameters
|
378 |
+
$fileContents = array();
|
379 |
+
foreach ($files as $k => $v) {
|
380 |
+
switch (true) {
|
381 |
+
case true === $v = realpath(filter_var($v)):
|
382 |
+
case is_file($v):
|
383 |
+
case is_readable($v):
|
384 |
+
$fileContents[$k] = file_get_contents($v);
|
385 |
+
// continue; // or return false, throw new InvalidArgumentException
|
386 |
+
}
|
387 |
+
}
|
388 |
+
$fileContents = array_merge($fileContents, $buffers);
|
389 |
+
|
390 |
+
foreach ($fileContents as $k => $data) {
|
391 |
+
$pp = explode(DIRECTORY_SEPARATOR, $files[$k]);
|
392 |
+
$v = end($pp);
|
393 |
+
$k = str_replace($disallow, "_", $k);
|
394 |
+
$v = str_replace($disallow, "_", $v);
|
395 |
+
$body[] = implode("\r\n", array(
|
396 |
+
"Content-Disposition: form-data; name=\"{$k}\"; filename=\"{$v}\"",
|
397 |
+
"Content-Type: application/octet-stream",
|
398 |
+
"",
|
399 |
+
$data,
|
400 |
+
));
|
401 |
+
}
|
402 |
+
|
403 |
+
// generate safe boundary
|
404 |
+
do {
|
405 |
+
$boundary = "---------------------" . md5(mt_rand() . microtime());
|
406 |
+
} while (preg_grep("/{$boundary}/", $body));
|
407 |
+
|
408 |
+
// add boundary for each parameters
|
409 |
+
array_walk($body, function (&$part) use ($boundary) {
|
410 |
+
$part = "--{$boundary}\r\n{$part}";
|
411 |
+
});
|
412 |
+
|
413 |
+
// add final boundary
|
414 |
+
$body[] = "--{$boundary}--";
|
415 |
+
$body[] = "";
|
416 |
+
|
417 |
+
// set options
|
418 |
+
return @curl_setopt_array($ch, array(
|
419 |
+
CURLOPT_POST => true,
|
420 |
+
CURLOPT_BINARYTRANSFER => true,
|
421 |
+
CURLOPT_RETURNTRANSFER => true,
|
422 |
+
CURLOPT_TIMEOUT => 300, //to be able to handle via post large files up to 48M which might take a long time to upload.
|
423 |
+
CURLOPT_POSTFIELDS => implode("\r\n", $body),
|
424 |
+
CURLOPT_HTTPHEADER => array_merge(array(
|
425 |
+
"Expect: 100-continue",
|
426 |
+
"Content-Type: multipart/form-data; boundary={$boundary}", // change Content-Type
|
427 |
+
), $header),
|
428 |
+
));
|
429 |
+
}
|
430 |
+
|
431 |
+
protected static function parseHeaders($headers) {
|
432 |
+
if (!is_array($headers)) {
|
433 |
+
$headers = explode("\r\n", $headers);
|
434 |
+
}
|
435 |
+
|
436 |
+
$res = array();
|
437 |
+
foreach ($headers as $header) {
|
438 |
+
if (empty($header)) continue;
|
439 |
+
$split = explode(":", $header, 2);
|
440 |
+
if (count($split) === 2) {
|
441 |
+
$res[strtolower($split[0])] = trim($split[1]);
|
442 |
+
}
|
443 |
+
}
|
444 |
+
return $res;
|
445 |
+
}
|
446 |
+
|
447 |
+
function download($sourceURL, $target, $expectedSize = false) {
|
448 |
+
$targetTemp = substr($target, 0, 245) . ".sptemp";
|
449 |
+
$fp = @fopen ($targetTemp, 'w+'); // open file handle
|
450 |
+
if(!$fp) {
|
451 |
+
//file cannot be opened, probably no rights or path disappeared
|
452 |
+
if(!is_dir(dirname($target))) {
|
453 |
+
throw new ClientException("The file path cannot be found.", -15);
|
454 |
+
} else {
|
455 |
+
throw new ClientException("Temp file cannot be created inside " . dirname($targetTemp) . ". Please check rights.", -16);
|
456 |
+
}
|
457 |
+
}
|
458 |
+
|
459 |
+
$ch = curl_init($sourceURL);
|
460 |
+
// curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // enable if you want
|
461 |
+
curl_setopt_array($ch, $this->customOptions);
|
462 |
+
curl_setopt($ch, CURLOPT_FILE, $fp); // output to file
|
463 |
+
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); //previously 1. Changed it because it conflicts with some clients open_basedir (php.ini) settings (https://secure.helpscout.net/conversation/859529984/16086?folderId=1117588)
|
464 |
+
curl_setopt($ch, CURLOPT_TIMEOUT, 10000); // some large value to allow curl to run for a long time
|
465 |
+
curl_setopt($ch, CURLOPT_USERAGENT, $this->options[CURLOPT_USERAGENT]);
|
466 |
+
// curl_setopt($ch, CURLOPT_VERBOSE, true); // Enable this line to see debug prints
|
467 |
+
curl_exec($ch);
|
468 |
+
|
469 |
+
curl_close($ch); // closing curl handle
|
470 |
+
fclose($fp); // closing file handle
|
471 |
+
$actualSize = filesize($targetTemp);
|
472 |
+
if(!$expectedSize || $expectedSize == $actualSize) {
|
473 |
+
if(!@rename($targetTemp, $target)) {
|
474 |
+
@unlink($targetTemp);
|
475 |
+
throw new ClientException("File cannot be renamed. Please check rights.", -16);
|
476 |
+
}
|
477 |
+
} else {
|
478 |
+
// ATENTIE!!!!! daca s-a oprit aici e un caz de fisier cu dimensiunea diferita, de verificat
|
479 |
+
@unlink($targetTemp);
|
480 |
+
return -$actualSize; //will retry
|
481 |
+
}
|
482 |
+
return true;
|
483 |
+
}
|
484 |
+
|
485 |
+
function apiStatus($key, $domainToCheck = false, $imgCount = 0, $thumbsCount = 0) {
|
486 |
+
$request = curl_init();
|
487 |
+
curl_setopt_array($request, $this->options);
|
488 |
+
//$this->prepareJSONRequest(self::API_STATUS_ENDPOINT(), $request, array('key' => $key), 'post', array());
|
489 |
+
curl_setopt($request, CURLOPT_URL, self::API_STATUS_ENDPOINT());
|
490 |
+
curl_setopt($request, CURLOPT_CUSTOMREQUEST, 'POST');
|
491 |
+
curl_setopt($request, CURLOPT_HTTPHEADER, array());
|
492 |
+
$params = array('key' => $key);
|
493 |
+
if($domainToCheck) {
|
494 |
+
$params['DomainCheck'] = $domainToCheck;
|
495 |
+
$params['ImagesCount'] = $imgCount;
|
496 |
+
$params['ThumbsCount'] = $thumbsCount;
|
497 |
+
|
498 |
+
}
|
499 |
+
curl_setopt($request, CURLOPT_POSTFIELDS, $params);
|
500 |
+
return $this->sendRequest($request, 1);
|
501 |
+
}
|
502 |
+
|
503 |
+
/**
|
504 |
+
* Method that checks the status of an image being optimized
|
505 |
+
* @param $key
|
506 |
+
* @param $url
|
507 |
+
* @return array
|
508 |
+
* @throws ConnectionException
|
509 |
+
*/
|
510 |
+
function imageStatus($key, $url) {
|
511 |
+
$request = curl_init();
|
512 |
+
curl_setopt_array($request, $this->options);
|
513 |
+
//$this->prepareJSONRequest(self::API_STATUS_ENDPOINT(), $request, array('key' => $key), 'post', array());
|
514 |
+
curl_setopt($request, CURLOPT_URL, self::IMAGE_STATUS_ENDPOINT());
|
515 |
+
curl_setopt($request, CURLOPT_CUSTOMREQUEST, 'POST');
|
516 |
+
curl_setopt($request, CURLOPT_HTTPHEADER, array());
|
517 |
+
$params = array('key' => $key, 'url' => $url);
|
518 |
+
curl_setopt($request, CURLOPT_POSTFIELDS, json_encode($params));
|
519 |
+
return $this->sendRequest($request, 1);
|
520 |
+
}
|
521 |
+
|
522 |
+
/**
|
523 |
+
* method that dumps the image from the optimization queue so the optimized version isn't available any more.
|
524 |
+
* Useful when you MIGHT need to optimize another image with the same URL - but with different contents - in the next
|
525 |
+
* hour and you don't want to have to keep a status to tell you if you need to use refresh() or not...
|
526 |
+
* @param $key
|
527 |
+
* @param $urllist
|
528 |
+
* @return array
|
529 |
+
* @throws ConnectionException
|
530 |
+
*/
|
531 |
+
function imageCleanup($key, $urllist) {
|
532 |
+
$request = curl_init();
|
533 |
+
curl_setopt_array($request, $this->options);
|
534 |
+
//$this->prepareJSONRequest(self::API_STATUS_ENDPOINT(), $request, array('key' => $key), 'post', array());
|
535 |
+
curl_setopt($request, CURLOPT_URL, self::CLEANUP_ENDPOINT());
|
536 |
+
curl_setopt($request, CURLOPT_CUSTOMREQUEST, 'POST');
|
537 |
+
curl_setopt($request, CURLOPT_HTTPHEADER, array());
|
538 |
+
$params = array('key' => $key, 'urllist' => $urllist);
|
539 |
+
curl_setopt($request, CURLOPT_POSTFIELDS, json_encode($params));
|
540 |
+
return $this->sendRequest($request, 1);
|
541 |
+
}
|
542 |
+
}
|
vendor/shortpixel/shortpixel-php/lib/ShortPixel/Commander.php
ADDED
@@ -0,0 +1,244 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* User: simon
|
4 |
+
* Date: 04.04.2016
|
5 |
+
* Time: 14:01
|
6 |
+
*/
|
7 |
+
|
8 |
+
namespace ShortPixel;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Class Commander - handles optimization commands such as lossless/lossy, resize, wait, notify_me etc.
|
12 |
+
* @package ShortPixel
|
13 |
+
*/
|
14 |
+
class Commander {
|
15 |
+
private $data, $source, $commands, $logger;
|
16 |
+
|
17 |
+
public function __construct($data, Source $source) {
|
18 |
+
$this->source = $source;
|
19 |
+
$this->data = $data;
|
20 |
+
$this->logger = SPLog::Get(SPLog::PRODUCER_CTRL);
|
21 |
+
//$options = ShortPixel::options();
|
22 |
+
$this->commands = array();//('lossy' => 0 + $options["lossy"]);
|
23 |
+
if(isset($data['refresh']) && $data['refresh']) {
|
24 |
+
$this->refresh();
|
25 |
+
}
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @param int $type 1 - lossy (default), 2 - glossy, 0 - lossless
|
30 |
+
* @return $this
|
31 |
+
*/
|
32 |
+
public function optimize($type = 1) {
|
33 |
+
$this->commands = array_merge($this->commands, array("lossy" => $type));
|
34 |
+
return $this;
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* resize the image - performs an outer resize (meaning the image will preserve aspect ratio and have the smallest sizes that allow a rectangle with given width and height to fit inside the resized image)
|
39 |
+
* @param $width
|
40 |
+
* @param $height
|
41 |
+
* @param bool $inner - default, false, true to resize to maximum width or height (both smaller or equal)
|
42 |
+
* @return $this
|
43 |
+
*/
|
44 |
+
public function resize($width, $height, $inner = false) {
|
45 |
+
$this->commands = array_merge($this->commands, array("resize" => ($inner ? ShortPixel::RESIZE_INNER : ShortPixel::RESIZE_OUTER), "resize_width" => $width, "resize_height" => $height));
|
46 |
+
return $this;
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* @param bool|true $keep
|
51 |
+
* @return $this
|
52 |
+
*/
|
53 |
+
public function keepExif($keep = true) {
|
54 |
+
$this->commands = array_merge($this->commands, array("keep_exif" => $keep ? 1 : 0));
|
55 |
+
return $this;
|
56 |
+
}
|
57 |
+
|
58 |
+
|
59 |
+
|
60 |
+
/**
|
61 |
+
* @param bool|true $generate - default true, meaning generates WebP.
|
62 |
+
* @return $this
|
63 |
+
*/
|
64 |
+
public function generateWebP($generate = true) {
|
65 |
+
$convertto = isset($this->commands['convertto']) ? explode('|', $this->commands['convertto']) : array();
|
66 |
+
$convertto[] = '+webp';
|
67 |
+
$this->commands = array_merge($this->commands, array("convertto" => implode('|', array_unique($convertto))));
|
68 |
+
return $this;
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* @param bool|true $generate - default true, meaning generates WebP.
|
73 |
+
* @return $this
|
74 |
+
*/
|
75 |
+
public function generateAVIF($generate = true) {
|
76 |
+
$convertto = isset($this->commands['convertto']) ? explode('|', $this->commands['convertto']) : array();
|
77 |
+
$convertto[] = '+avif';
|
78 |
+
$this->commands = array_merge($this->commands, array("convertto" => implode('|', array_unique($convertto))));
|
79 |
+
return $this;
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* @param bool|true $refresh - if true, tells the server to discard the already optimized image and redo the optimization with the new settings.
|
84 |
+
* @return $this
|
85 |
+
*/
|
86 |
+
public function refresh($refresh = true) {
|
87 |
+
$this->commands = array_merge($this->commands, array("refresh" => $refresh ? 1 : 0));
|
88 |
+
return $this;
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* will wait for the optimization to finish but not more than $seconds. The wait on the ShortPixel Server side can be a maximum of 30 seconds, for longer waits subsequent server requests will be sent.
|
93 |
+
* @param int $seconds
|
94 |
+
* @return $this
|
95 |
+
*/
|
96 |
+
public function wait($seconds = 30) {
|
97 |
+
$seconds = max(0, intval($seconds));
|
98 |
+
$this->commands = array_merge($this->commands, array("wait" => min($seconds, 30), "total_wait" => $seconds));
|
99 |
+
return $this;
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Not yet implemented
|
104 |
+
* @param $callbackURL the full url of the notify.php script that handles the notification postback
|
105 |
+
* @return mixed
|
106 |
+
*/
|
107 |
+
public function notifyMe($callbackURL) {
|
108 |
+
throw new ClientException("NotifyMe not yet implemented");
|
109 |
+
$this->commands = array_merge($this->commands, array("notify_me" => $callbackURL));
|
110 |
+
return $this->execute();
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* call forwarder to Result - when a command is not understood by the Commander it could be a Result method like toFiles or toBuffer
|
115 |
+
* @param $method
|
116 |
+
* @param $args
|
117 |
+
* @return mixed
|
118 |
+
* @throws ClientException
|
119 |
+
*/
|
120 |
+
public function __call($method, $args) {
|
121 |
+
if (method_exists("ShortPixel\Result", $method)) {
|
122 |
+
//execute the commands and forward to Result
|
123 |
+
if(isset($this->data["files"]) && !count($this->data["files"]) ||
|
124 |
+
isset($this->data["urllist"]) && !count($this->data["urllist"]) ||
|
125 |
+
isset($this->data["buffers"]) && !count($this->data["buffers"])) {
|
126 |
+
//empty data - no files, no need to send anything, just return an empty result
|
127 |
+
return (object) array(
|
128 |
+
'status' => array('code' => 2, 'message' => 'success'),
|
129 |
+
'succeeded' => array(),
|
130 |
+
'pending' => array(),
|
131 |
+
'failed' => array(),
|
132 |
+
'same' => array());
|
133 |
+
}
|
134 |
+
for($i = 0; $i < 6; $i++) {
|
135 |
+
$return = $this->execute(true);
|
136 |
+
$this->logger->log(SPLog::PRODUCER_CTRL, "EXECUTE RETURNED: ", $return);
|
137 |
+
if(!isset($return->body->Status->Code) || !in_array($return->body->Status->Code, array(-305, -404, -500))) {
|
138 |
+
break;
|
139 |
+
}
|
140 |
+
// error -404: The maximum number of URLs in the optimization queue reached, wait a bit and retry.
|
141 |
+
// error -500: maintenance mode
|
142 |
+
sleep((10 + 3 * $i) * ($return->body->Status->Code == -500 ? 6 : 1)); //sleep six times longer if maintenance mode. This gives about 15 minutes in total, then it will throw exception.
|
143 |
+
}
|
144 |
+
|
145 |
+
if(isset($return->body->Status->Code) && $return->body->Status->Code < 0) {
|
146 |
+
ShortPixel::log("ERROR THROWN: " . $return->body->Status->Message . (isset($return->body->raw) ? "(Server sent: " . substr($return->body->raw, 0, 200) . "...)" : "") . " CODE: " . $return->body->Status->Code);
|
147 |
+
throw new AccountException($return->body->Status->Message . (isset($return->body->raw) ? "(Server sent: " . substr($return->body->raw, 0, 200) . "...)" : ""), $return->body->Status->Code);
|
148 |
+
}
|
149 |
+
return call_user_func_array(array(new Result($this, $return), $method), $args);
|
150 |
+
}
|
151 |
+
else {
|
152 |
+
throw new ClientException('Unknown function '.__CLASS__.':'.$method, E_USER_ERROR);
|
153 |
+
}
|
154 |
+
}
|
155 |
+
|
156 |
+
/**
|
157 |
+
* @internal
|
158 |
+
* @param bool|false $wait
|
159 |
+
* @return mixed
|
160 |
+
* @throws AccountException
|
161 |
+
*/
|
162 |
+
public function execute($wait = false){
|
163 |
+
if($wait && !isset($this->commands['wait'])) {
|
164 |
+
$this->commands = array_merge($this->commands, array("wait" => ShortPixel::opt("wait"), "total_wait" => ShortPixel::opt("total_wait")));
|
165 |
+
}
|
166 |
+
ShortPixel::log("EXECUTE OPTIONS: " . json_encode(ShortPixel::options()) . " COMMANDS: " . json_encode($this->commands) . " DATA: " . json_encode($this->data));
|
167 |
+
return ShortPixel::getClient()->request("post", array_merge(ShortPixel::options(), $this->commands, $this->data));
|
168 |
+
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* @internal
|
172 |
+
* @param $pending
|
173 |
+
* @return bool|mixed
|
174 |
+
* @throws ClientException
|
175 |
+
*/
|
176 |
+
public function relaunch($ctx) {
|
177 |
+
ShortPixel::log("RELAUNCH CTX: " . json_encode($ctx) . " COMMANDS: " . json_encode($this->commands) . " DATA: " . json_encode($this->data));
|
178 |
+
if(!count($ctx->body) &&
|
179 |
+
(isset($this->data["files"]) && !count($this->data["files"]) ||
|
180 |
+
isset($this->data["urllist"]) && !count($this->data["urllist"]))) return false; //nothing to do
|
181 |
+
|
182 |
+
//decrease the total wait and exit while if time expired
|
183 |
+
$this->commands["total_wait"] = max(0, $this->commands["total_wait"] - min($this->commands["wait"], 30));
|
184 |
+
if($this->commands['total_wait'] == 0) return false;
|
185 |
+
|
186 |
+
$pendingURLs = array();
|
187 |
+
//currently we relaunch only if we have the URLs that for posted files should be returned in the first pass.
|
188 |
+
$type = isset($ctx->body[0]->OriginalURL) ? 'URL' : 'FILE';
|
189 |
+
foreach($ctx->body as $pend) {
|
190 |
+
if($type == 'URL') {
|
191 |
+
if($pend->OriginalURL && !in_array($pend->OriginalURL, $pendingURLs)) {
|
192 |
+
$pendingURLs[$pend->OriginalURL] = $pend->OriginalFile;
|
193 |
+
}
|
194 |
+
} else {
|
195 |
+
//for now
|
196 |
+
throw new ClientException("Not implemented (Commander->execute())");
|
197 |
+
}
|
198 |
+
}
|
199 |
+
$this->commands["refresh"] = 0;
|
200 |
+
if($type == 'URL' && count($pendingURLs)) {
|
201 |
+
$this->data["pendingURLs"] = $pendingURLs;
|
202 |
+
//$this->data["fileMappings"] = $ctx->fileMappings;
|
203 |
+
}
|
204 |
+
return $this->execute();
|
205 |
+
|
206 |
+
}
|
207 |
+
|
208 |
+
public function getCommands() {
|
209 |
+
return $this->commands;
|
210 |
+
}
|
211 |
+
|
212 |
+
public function getData() {
|
213 |
+
return $this->data;
|
214 |
+
}
|
215 |
+
|
216 |
+
/* public function setCommand($key, $value) {
|
217 |
+
return $this->commands[$key] = $value;
|
218 |
+
}
|
219 |
+
*/
|
220 |
+
|
221 |
+
public function isDone($item) {
|
222 |
+
//remove from local files list
|
223 |
+
if(isset($this->data["files"]) && is_array($this->data["files"])) {
|
224 |
+
if (isset($item->OriginalFile)) {
|
225 |
+
$this->data["files"] = array_diff($this->data["files"], array($item->OriginalFile));
|
226 |
+
}
|
227 |
+
elseif (isset($item->SavedFile)) {
|
228 |
+
$this->data["files"] = array_diff($this->data["files"], array($item->SavedFile));
|
229 |
+
}
|
230 |
+
elseif(isset($item->OriginalURL) && isset($this->data["pendingURLs"][$item->OriginalURL])) {
|
231 |
+
$this->data["files"] = array_diff($this->data["files"], array($this->data["pendingURLs"][$item->OriginalURL]));
|
232 |
+
}
|
233 |
+
}
|
234 |
+
//remove from pending URLs
|
235 |
+
if(isset($item->OriginalURL)) {
|
236 |
+
if(isset($this->data["pendingURLs"][$item->OriginalURL])) {
|
237 |
+
unset($this->data["pendingURLs"][$item->OriginalURL]);
|
238 |
+
}
|
239 |
+
elseif(isset($this->data["urllist"]) && in_array($item->OriginalURL, $this->data["urllist"])) {
|
240 |
+
$this->data["urllist"] = array_values(array_diff($this->data["urllist"], array($item->OriginalURL)));
|
241 |
+
}
|
242 |
+
}
|
243 |
+
}
|
244 |
+
}
|
vendor/shortpixel/shortpixel-php/lib/ShortPixel/Exception.php
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace ShortPixel;
|
4 |
+
|
5 |
+
class Exception extends \Exception {
|
6 |
+
public static function create($message, $type, $status) {
|
7 |
+
if ($status == 401 || $status == 429) {
|
8 |
+
$klass = "ShortPixel\AccountException";
|
9 |
+
} else if($status >= 400 && $status <= 499) {
|
10 |
+
$klass = "ShortPixel\ClientException";
|
11 |
+
} else if($status >= 500 && $status <= 599) {
|
12 |
+
$klass = "ShortPixel\ServerException";
|
13 |
+
} else {
|
14 |
+
$klass = "ShortPixel\Exception";
|
15 |
+
}
|
16 |
+
|
17 |
+
if (empty($message)) $message = "No message was provided";
|
18 |
+
return new $klass($type . ": " . $message, $status);
|
19 |
+
}
|
20 |
+
|
21 |
+
function __construct($message, $code = 0, $parent = NULL, $type = NULL, $status = NULL) {
|
22 |
+
if ($status) {
|
23 |
+
parent::__construct($message . " (HTTP " . $status . "/" . $type . ")", $code, $parent);
|
24 |
+
} else {
|
25 |
+
parent::__construct($message, $code, $parent);
|
26 |
+
}
|
27 |
+
}
|
28 |
+
}
|
29 |
+
|
30 |
+
class AccountException extends Exception {}
|
31 |
+
class ClientException extends Exception {
|
32 |
+
const NO_FILE_FOUND = -1;
|
33 |
+
}
|
34 |
+
class ServerException extends Exception {}
|
35 |
+
class ConnectionException extends Exception {}
|
36 |
+
class PersistException extends Exception {}
|
37 |
+
|
vendor/shortpixel/shortpixel-php/lib/ShortPixel/Lock.php
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* User: simon
|
4 |
+
* Date: 21.12.2017
|
5 |
+
* Time: 23:41
|
6 |
+
*/
|
7 |
+
namespace ShortPixel;
|
8 |
+
|
9 |
+
class Lock {
|
10 |
+
const FOLDER_LOCK_FILE = '.sp-lock';
|
11 |
+
|
12 |
+
private $processId, $targetFolder, $clearLock, $releaseTo, $timeout;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* @param $processId
|
16 |
+
* @param $targetFolder
|
17 |
+
* @param bool|false $clearLock
|
18 |
+
* @param string|false $releaseTo a string that if found in the lock file, will make lock() release the lock instead of updating it
|
19 |
+
* - use together with requestLock to pass the lock between concurent processes with different priority (call requestLock with the same value as $requester on the script that should take the lock).
|
20 |
+
*/
|
21 |
+
function __construct($processId, $targetFolder, $clearLock = false, $releaseTo = false, $timeout = 360) {
|
22 |
+
$this->processId = $processId;
|
23 |
+
$this->targetFolder = $targetFolder;
|
24 |
+
$this->clearLock = $clearLock;
|
25 |
+
$this->releaseTo = $releaseTo;
|
26 |
+
$this->timeout = $timeout;
|
27 |
+
$this->logger = SPLog::Get(SPLog::PRODUCER_PERSISTER);
|
28 |
+
}
|
29 |
+
|
30 |
+
function setTimeout($timeout) {
|
31 |
+
$this->timeout = $timeout;
|
32 |
+
}
|
33 |
+
|
34 |
+
function lockFile() {
|
35 |
+
return $this->targetFolder . '/' . self::FOLDER_LOCK_FILE;
|
36 |
+
}
|
37 |
+
|
38 |
+
function readLock() {
|
39 |
+
if(file_exists($this->lockFile())) {
|
40 |
+
$lock = file_get_contents($this->targetFolder . '/' . self::FOLDER_LOCK_FILE);
|
41 |
+
return explode("=", $lock);
|
42 |
+
}
|
43 |
+
return false;
|
44 |
+
}
|
45 |
+
|
46 |
+
function lock() {
|
47 |
+
//check if the folder is not locked by another ShortPixel process
|
48 |
+
if(!$this->clearLock && ($lock = $this->readLock()) !== false) {
|
49 |
+
$time = explode('!', $lock[1]);
|
50 |
+
if(count($lock) >= 2 && $lock[0] != $this->processId && $time[0] > time() - (isset($time[1]) ? $time[1] : $this->timeout)) {
|
51 |
+
//a lock was placed on the file and it's not yet expired as per its set timeout
|
52 |
+
throw new \Exception($this->getLockMsg($lock, $this->targetFolder), -19);
|
53 |
+
}
|
54 |
+
elseif(count($lock) >= 4 && $lock[2] == $this->releaseTo) {
|
55 |
+
// a request to release the lock was received
|
56 |
+
unlink($this->lockFile());
|
57 |
+
throw new \Exception("A lock release was requested by " . $this->releaseTo, -20);
|
58 |
+
}
|
59 |
+
}
|
60 |
+
if(FALSE === @file_put_contents($this->lockFile(), $this->processId . "=" . time() . '!' . $this->timeout . (strlen($this->releaseTo) ? "=" . $this->releaseTo : ''))) {
|
61 |
+
throw new ClientException("Could not write lock file " . $this->lockFile() . ". Please check rights.", -16);
|
62 |
+
}
|
63 |
+
$this->logger->log(SPLog::PRODUCER_PERSISTER, "{$this->processId} locked " . dirname($this->lockFile()) . " for {$this->timeout} sec.");
|
64 |
+
}
|
65 |
+
|
66 |
+
function requestLock($requester) {
|
67 |
+
if(($lock = $this->readLock()) !== false) {
|
68 |
+
if(isset($lock[2]) && $lock[2] == $requester) {
|
69 |
+
//the script that locked the folder will accept a request from $requester to give the lock
|
70 |
+
//mark in the lock a request to release it
|
71 |
+
if(FALSE === @file_put_contents($this->lockFile(), $lock[0] . "=" . $lock[1] . "=" . $requester . "=true")) {
|
72 |
+
throw new ClientException("Could not update lock file " . $this->lockFile() . ". Please check rights.", -16);
|
73 |
+
}
|
74 |
+
} else {
|
75 |
+
//the script will not accept a request from $requester, maybe the lock is old?
|
76 |
+
$this->lock();
|
77 |
+
return;
|
78 |
+
}
|
79 |
+
}
|
80 |
+
//now wait for the other process to release the lock, a bit more than its expiry time - in case it was left there...
|
81 |
+
$expiry = max(1, 365 - (time() - $lock[1]));
|
82 |
+
for($i = 0; $i < $expiry; $i++) {
|
83 |
+
if(file_exists($this->lockFile())) {
|
84 |
+
sleep(1);
|
85 |
+
} else {
|
86 |
+
break;
|
87 |
+
}
|
88 |
+
}
|
89 |
+
$this->lock();
|
90 |
+
}
|
91 |
+
|
92 |
+
function unlock() {
|
93 |
+
if(($lock = $this->readLock()) !== false) {
|
94 |
+
if($lock[0] == $this->processId) {
|
95 |
+
unlink($this->lockFile());
|
96 |
+
}
|
97 |
+
}
|
98 |
+
}
|
99 |
+
|
100 |
+
function getLockMsg($lock, $folder) {
|
101 |
+
return SPLog::format("The folder is locked by a different ShortPixel process ({$lock[0]}). Exiting. \n\n\033[31mIf you're SURE no other ShortPixel process is running, you can remove the lock with \n\n >\033[34m rm " . $folder . '/' . self::FOLDER_LOCK_FILE . " \033[0m \n");
|
102 |
+
}
|
103 |
+
}
|
vendor/shortpixel/shortpixel-php/lib/ShortPixel/Persister.php
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* User: simon
|
4 |
+
* Date: 19.08.2016
|
5 |
+
* Time: 18:01
|
6 |
+
*/
|
7 |
+
namespace ShortPixel;
|
8 |
+
|
9 |
+
interface Persister {
|
10 |
+
|
11 |
+
static function IGNORED_BY_DEFAULT();
|
12 |
+
function __construct($options);
|
13 |
+
function isOptimized($path);
|
14 |
+
function getOptimizationData($path);
|
15 |
+
function info($path, $recurse = true, $fileList = false, $exclude = array(), $persistPath = false);
|
16 |
+
function getTodo($path, $count, $exclude = array(), $persistFolder = false, $maxTotalFileSize = false, $recurseDepth = PHP_INT_MAX);
|
17 |
+
function getNextTodo($path, $count);
|
18 |
+
function doneGet();
|
19 |
+
function setPending($path, $optData);
|
20 |
+
function setOptimized($path, $optData);
|
21 |
+
function setFailed($path, $optData);
|
22 |
+
function setSkipped($path, $optData);
|
23 |
+
}
|
vendor/shortpixel/shortpixel-php/lib/ShortPixel/Result.php
ADDED
@@ -0,0 +1,444 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace ShortPixel;
|
4 |
+
use ShortPixel\notify\ProgressNotifier;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Result - handles the result of the optimization (saves to file or returns a buffer, etc)
|
8 |
+
* @package ShortPixel
|
9 |
+
*/
|
10 |
+
class Result {
|
11 |
+
protected $commander, $ctx, $logger;
|
12 |
+
|
13 |
+
public function __construct($commander, $context) {
|
14 |
+
$this->commander = $commander;
|
15 |
+
$this->ctx = $context;
|
16 |
+
$this->logger = SPLog::Get(SPLog::PRODUCER_RESULT);
|
17 |
+
}
|
18 |
+
|
19 |
+
/**
|
20 |
+
* returns the metadata provided by the optimizer
|
21 |
+
* @return mixed
|
22 |
+
*/
|
23 |
+
public function ctx() {
|
24 |
+
return $this->ctx;
|
25 |
+
}
|
26 |
+
|
27 |
+
public function toBuffer() {
|
28 |
+
return $this->ctx;
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* @param null $path - path to save the file to
|
33 |
+
* @param null $fileName - filename of the saved file. If it's an array, then one entry for each optimized file, in the same order.
|
34 |
+
* @param null $bkPath - the path to save a backup of the original file
|
35 |
+
* @return object containig lists with succeeded, pending, failed and same items (same means the image did not need optimization)
|
36 |
+
* @throws AccountException
|
37 |
+
* @throws ClientException
|
38 |
+
*/
|
39 |
+
public function toFiles($path = null, $fileName = null, $bkPath = null) {
|
40 |
+
|
41 |
+
// echo(" PATH: $path BkPath: $bkPath");
|
42 |
+
// spdbgd($this->ctx, 'context');
|
43 |
+
$thisDir = str_replace(DIRECTORY_SEPARATOR, '/', (getcwd() ? getcwd() : __DIR__));
|
44 |
+
|
45 |
+
if($path) {
|
46 |
+
if( (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' && preg_match('/^[a-zA-Z]:\//', $path) === 0) //it's Windows and no drive letter X:
|
47 |
+
|| (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' && substr($path, 0, 1) !== '/')) { //it's not Windows and doesn't start with a /
|
48 |
+
$path = (ShortPixel::opt("base_path") ?: $thisDir) . '/' . $path;
|
49 |
+
}
|
50 |
+
}
|
51 |
+
if(!$bkPath && ShortPixel::opt("backup_path")) {
|
52 |
+
$bkPath = ShortPixel::opt("backup_path");
|
53 |
+
}
|
54 |
+
if($bkPath && strpos($bkPath,'/') !== 0) { //it's a relative path
|
55 |
+
$bkPath = normalizePath(rtrim($path, '/') . '/' . $bkPath);
|
56 |
+
}
|
57 |
+
$i = 0;
|
58 |
+
$succeeded = $pending = $failed = $same = array();
|
59 |
+
|
60 |
+
$cmds = array_merge(ShortPixel::options(), $this->commander->getCommands());
|
61 |
+
|
62 |
+
while(true) {
|
63 |
+
$items = $this->ctx->body;
|
64 |
+
if(!is_array($items) || count($items) == 0) {
|
65 |
+
throw new AccountException("Result received no items to save!", -1);
|
66 |
+
// return (object)array( 'status' => array('code' => 2, 'message' => 'Folder completely optimized'));
|
67 |
+
}
|
68 |
+
//check API key errors
|
69 |
+
if(isset($items->Status->Code) && $items->Status->Code < 0) {
|
70 |
+
throw new AccountException($items->Status->Message, $items->Status->Code);
|
71 |
+
}
|
72 |
+
// No API level error
|
73 |
+
$retry = false;
|
74 |
+
foreach($items as $item) {
|
75 |
+
|
76 |
+
$targetPath = $path; $originalPath = $baseUrl = false;
|
77 |
+
$this->logger->log(SPLog::PRODUCER_RESULT, "RESULT toFiles while CTX:", $this->ctx->fileMappings);
|
78 |
+
|
79 |
+
if($this->ctx->fileMappings && count($this->ctx->fileMappings)) { // it was optimized from a local file, fileMappings contains the mappings from the local files to the internal ShortPixel URLs
|
80 |
+
$originalPath = isset($this->ctx->fileMappings[$item->OriginalURL]) ? $this->ctx->fileMappings[$item->OriginalURL] : false;
|
81 |
+
//
|
82 |
+
$this->logger->log(SPLog::PRODUCER_RESULT, "ORIGINAL PATH:" . $originalPath);
|
83 |
+
if(ShortPixel::opt("base_source_path") && $originalPath) {
|
84 |
+
$origPathParts = explode('/', str_replace(ShortPixel::opt("base_source_path"). "/", "", $originalPath));
|
85 |
+
$origFileName = $origPathParts[count($origPathParts) - 1];
|
86 |
+
unset($origPathParts[count($origPathParts) - 1]);
|
87 |
+
$relativePath = implode('/', $origPathParts);
|
88 |
+
} elseif($originalPath) {
|
89 |
+
$origPathParts = explode('/', $originalPath);
|
90 |
+
$origFileName = $origPathParts[count($origPathParts) - 1];
|
91 |
+
$relativePath = "";
|
92 |
+
} elseif(isset($item->OriginalFileName)) {
|
93 |
+
$origFileName = $item->OriginalFileName;
|
94 |
+
$relativePath = "";
|
95 |
+
} else {
|
96 |
+
throw new ClientException("Cannot determine a filename to save to." . $item->OriginalURL . " CTX: " . json_encode($this->ctx->fileMappings));
|
97 |
+
}
|
98 |
+
} elseif(isset($item->OriginalURL)) { // it was optimized from a URL
|
99 |
+
$baseUrl = ShortPixel::opt("base_url");
|
100 |
+
if($baseUrl && strlen($baseUrl)) {
|
101 |
+
$origURLParts = explode('/', trim(rawurldecode(str_replace($baseUrl, "", $item->OriginalURL)), '/'));
|
102 |
+
$origFileName = $origURLParts[count($origURLParts) - 1];
|
103 |
+
unset($origURLParts[count($origURLParts) - 1]);
|
104 |
+
$relativePath = implode('/', $origURLParts);
|
105 |
+
} else {
|
106 |
+
$origURLParts = explode('/', $item->OriginalURL);
|
107 |
+
$origFileName = $origURLParts[count($origURLParts) - 1];
|
108 |
+
$relativePath = "";
|
109 |
+
}
|
110 |
+
$originalPath = ShortPixel::opt("base_source_path") . '/' . (strlen($relativePath) ? $relativePath . '/' : '') . $origFileName;
|
111 |
+
} else { // something is wrong
|
112 |
+
throw(new ClientException("Malformed response. Please contact support."));
|
113 |
+
}
|
114 |
+
if(!$targetPath) { //se pare ca trebuie oricum
|
115 |
+
$targetPath = (ShortPixel::opt("base_path") ?: $thisDir) . '/' . $relativePath;
|
116 |
+
} elseif(ShortPixel::opt("base_source_path") && strlen($relativePath)) {
|
117 |
+
$targetPath .= '/' . $relativePath;
|
118 |
+
}
|
119 |
+
|
120 |
+
if($fileName) {
|
121 |
+
if(is_array($fileName)) {
|
122 |
+
if(!isset($fileName[$i])) {
|
123 |
+
throw new ClientException('Names array contains less names than the files sent to optimization');
|
124 |
+
}
|
125 |
+
$fn = $fileName[$i];
|
126 |
+
} else {
|
127 |
+
if($i > 0) {
|
128 |
+
$dotExt = '.' . pathinfo($fileName, PATHINFO_EXTENSION);
|
129 |
+
$fn = basename($fileName, $dotExt) . "_" . $i . $dotExt;
|
130 |
+
} else {
|
131 |
+
$fn = $fileName;
|
132 |
+
}
|
133 |
+
}
|
134 |
+
} else {
|
135 |
+
$fn = $origFileName;
|
136 |
+
}
|
137 |
+
$target = $targetPath . '/' . $fn;
|
138 |
+
|
139 |
+
if($originalPath) {
|
140 |
+
$item->OriginalFile = $originalPath;
|
141 |
+
if(!mb_detect_encoding($originalPath, 'UTF-8', true)) { $item->OriginalFileUTF8 = utf8_encode($originalPath); }
|
142 |
+
}
|
143 |
+
$item->SavedFile = $target;
|
144 |
+
if(!mb_detect_encoding($target, 'UTF-8', true)) { $item->SavedFileUTF8 = utf8_encode($target); }
|
145 |
+
|
146 |
+
//TODO: that one is a hack until the API waiting bug is fixed. Afterwards, just throw an exception
|
147 |
+
if( $item->Status->Code == 2
|
148 |
+
&& ( $item->LossySize == 0 && $item->LoselessSize == 0
|
149 |
+
|| $item->WebPLossyURL != 'NA' && ($item->WebPLossySize == 'NA' || !$item->WebPLossySize )
|
150 |
+
|| $item->WebPLosslessURL != 'NA' && ($item->WebPLosslessSize == 'NA' || !$item->WebPLosslessSize)
|
151 |
+
|| $item->AVIFLossyURL != 'NA' && ($item->AVIFLossySize == 'NA' || !$item->AVIFLossySize )
|
152 |
+
|| $item->AVIFLosslessURL != 'NA' && ($item->AVIFLosslessSize == 'NA' || !$item->AVIFLosslessSize))) {
|
153 |
+
$item->Status->Code = 1;
|
154 |
+
}
|
155 |
+
|
156 |
+
if($item->Status->Code == 1) {
|
157 |
+
$found = $this->findItem($item, $pending, "OriginalURL");
|
158 |
+
if($found === false) {
|
159 |
+
$item->Retries = 1;
|
160 |
+
$pending[] = $item;
|
161 |
+
$this->persist($item, $cmds, 'pending');
|
162 |
+
} else {
|
163 |
+
$pending[$found]->Retries += 1;
|
164 |
+
}
|
165 |
+
$this->logger->log(SPLog::PRODUCER_RESULT, "PENDING, RETRIES:", $pending[$found]->Retries);
|
166 |
+
continue;
|
167 |
+
}
|
168 |
+
elseif ($item->Status->Code != 2) {
|
169 |
+
$this->removeItem($item, $pending, "OriginalURL");
|
170 |
+
if($item->Status->Code == -113) {
|
171 |
+
throw new AccountException("Too many inaccessible URLs from the same domain, please check accessibility and try again.", "-113");
|
172 |
+
}
|
173 |
+
if($item->Status->Code == -102 || $item->Status->Code == -106) {
|
174 |
+
// -102 is expired, means we need to resend the image through post
|
175 |
+
// -106 is file was not downloaded due to access restrictions - if these are uploaded files it looks like a bug in the API
|
176 |
+
// TODO find and fix
|
177 |
+
if(isset($this->ctx->fileMappings[$item->OriginalURL])) {
|
178 |
+
unset($this->ctx->fileMappings[$item->OriginalURL]);
|
179 |
+
}
|
180 |
+
$item->OriginalURL = false;
|
181 |
+
}
|
182 |
+
|
183 |
+
if($item->Status->Code == -201 || $item->Status->Code == -202) { //unrecoverable, no need to retry
|
184 |
+
$st = 'skip';
|
185 |
+
} else { //will persist as 'pending' if retries < MAX_RETRIES
|
186 |
+
$st = 'error';
|
187 |
+
}
|
188 |
+
$status = $this->persist($item, $cmds, $st);
|
189 |
+
if($status == 'pending') {
|
190 |
+
$retry = true;
|
191 |
+
} else {
|
192 |
+
$failed[] = $item;
|
193 |
+
$this->commander->isDone($item);
|
194 |
+
$this->removeItem($item, $pending, "OriginalURL");
|
195 |
+
}
|
196 |
+
$this->logger->log(SPLog::PRODUCER_RESULT, "FAILED WITH CODE:", $item->Status->Code);
|
197 |
+
continue;
|
198 |
+
}
|
199 |
+
//IF file is locally accessible, the source file size should be the same as the size downloaded by (or posted to) ShortPixel servers
|
200 |
+
elseif(clearstatcache(true, $originalPath) || file_exists($originalPath) && filesize($originalPath) != $item->OriginalSize) {
|
201 |
+
$item->Status->Code = -110;
|
202 |
+
$item->Status->Message = "Wrong original size. Expected (local source file): " . filesize($originalPath) . " downloaded by ShortPixel: " . $item->OriginalSize;
|
203 |
+
$status = $this->persist($item, $cmds, 'error');
|
204 |
+
if($status == 'pending') {
|
205 |
+
$retry = true;
|
206 |
+
} else {
|
207 |
+
$failed[] = $item;
|
208 |
+
$this->commander->isDone($item);
|
209 |
+
$this->removeItem($item, $pending, "OriginalURL");
|
210 |
+
}
|
211 |
+
$this->logger->log(SPLog::PRODUCER_RESULT, "ERROR:", $item->Status->Message);
|
212 |
+
continue;
|
213 |
+
}
|
214 |
+
elseif($item->PercentImprovement == 0) {
|
215 |
+
//sometimes the percent is 0 and the size is different (by some octets) so put the correct size in place
|
216 |
+
if(file_exists($originalPath)) {
|
217 |
+
clearstatcache(true, $originalPath);
|
218 |
+
if($cmds["lossy"] > 0) {
|
219 |
+
$item->LossySize = filesize($originalPath);
|
220 |
+
} else {
|
221 |
+
$item->LoselessSize = filesize($originalPath);
|
222 |
+
}
|
223 |
+
if(!file_exists($target)) {
|
224 |
+
//this is a case when the target is different from the original path, so just copy the file over
|
225 |
+
@copy($originalPath, $target);
|
226 |
+
}
|
227 |
+
}
|
228 |
+
$this->checkSaveWebP($item, $target, $cmds);
|
229 |
+
$this->checkSaveAVIF($item, $target, $cmds);
|
230 |
+
$same[] = $item;
|
231 |
+
$this->removeItem($item, $pending, "OriginalURL");
|
232 |
+
$this->persist($item, $cmds);
|
233 |
+
$this->commander->isDone($item);
|
234 |
+
$this->logger->log(SPLog::PRODUCER_RESULT, "IMPROVEMENT 0");
|
235 |
+
continue;
|
236 |
+
}
|
237 |
+
|
238 |
+
if(!is_dir($targetPath) && !@mkdir($targetPath, 0777, true)) { //create the folder
|
239 |
+
throw new ClientException("The destination path $targetPath cannot be found.");
|
240 |
+
}
|
241 |
+
|
242 |
+
//Now that's an optimized image indeed
|
243 |
+
try {
|
244 |
+
$bkCrtFilePath = false;
|
245 |
+
if($bkPath && $originalPath) {
|
246 |
+
$bkCrtPath = rtrim($bkPath, '/') . '/' . (strlen($relativePath) ? $relativePath . '/' : '');
|
247 |
+
if(!is_dir($bkCrtPath) && !@mkdir($bkCrtPath, 0777, true)) {
|
248 |
+
throw new Exception("Cannot create backup folder " . $bkCrtPath, -1);
|
249 |
+
}
|
250 |
+
$bkCrtFilePath = $bkCrtPath . MB_basename($originalPath);
|
251 |
+
if(!file_exists($bkCrtFilePath)) { //just make sure we're not overwriting the original in any case
|
252 |
+
if (!copy($originalPath, $bkCrtFilePath) && !file_exists($bkCrtFilePath)) {
|
253 |
+
throw new Exception("Cannot copy to backup folder " . $bkCrtPath, -1);
|
254 |
+
}
|
255 |
+
}
|
256 |
+
}
|
257 |
+
|
258 |
+
//try to figure out a backup URL
|
259 |
+
if($baseUrl) {
|
260 |
+
if($originalPath !== $targetPath) {
|
261 |
+
$item->BackupURL = $item->OriginalURL;
|
262 |
+
} elseif($bkCrtFilePath) {
|
263 |
+
$deltaPos = strspn($path ^ $bkPath, "\0");
|
264 |
+
$delta = substr($path, $deltaPos);
|
265 |
+
$bkDelta = substr($bkPath, $deltaPos);
|
266 |
+
if(strpos($baseUrl, $delta) > 0) {
|
267 |
+
//only in this case there can be a backup URL (the backup path is inside the web root)
|
268 |
+
$item->BackupURL = str_replace($delta, $bkDelta, $baseUrl) . '/' . (strlen($relativePath) ? $relativePath . '/' : '') . MB_basename($originalPath);
|
269 |
+
}
|
270 |
+
}
|
271 |
+
}
|
272 |
+
|
273 |
+
$optURL = $cmds["lossy"] > 0 ? $item->LossyURL : $item->LosslessURL; //this works also for glossy (2)
|
274 |
+
$optSize = $cmds["lossy"] > 0 ? $item->LossySize : $item->LoselessSize;
|
275 |
+
|
276 |
+
$downloadOK = ShortPixel::getClient()->download($optURL, $target, $optSize);
|
277 |
+
if($downloadOK <= 0) { // the size is wrong - probably in metadata, retry the image altogether
|
278 |
+
$found = $this->findItem($item, $pending, "OriginalURL");
|
279 |
+
if($found === false) {
|
280 |
+
$item->Status->Code = 1;
|
281 |
+
$item->Status->Message = "Pending";
|
282 |
+
$item->Retries = 1;
|
283 |
+
$pending[] = $item;
|
284 |
+
$this->persist($item, $cmds, 'pending');
|
285 |
+
} elseif($pending[$found]->Retries <= 3) {
|
286 |
+
$pending[$found]->Retries += 1;
|
287 |
+
} else {
|
288 |
+
$item->Status->Code = -1;
|
289 |
+
$item->Status->Message = "Wrong size, expected $optSize downloaded " . (-$downloadOK);
|
290 |
+
$failed[] = $item;
|
291 |
+
$this->commander->isDone($item);
|
292 |
+
$this->removeItem($item, $pending, "OriginalURL");
|
293 |
+
}
|
294 |
+
$this->logger->log(SPLog::PRODUCER_RESULT, "DOWNLOAD NOK? " . $downloadOK . " URL: " . $optURL, $item->Status->Message);
|
295 |
+
continue;
|
296 |
+
}
|
297 |
+
$this->checkSaveWebP($item, $target, $cmds);
|
298 |
+
$this->checkSaveAVIF($item, $target, $cmds);
|
299 |
+
}
|
300 |
+
catch(ClientException $e) {
|
301 |
+
$failed[] = $item;
|
302 |
+
$item->Status->Message = $e->getMessage();
|
303 |
+
$this->persist($item, $cmds, 'error');
|
304 |
+
$this->logger->log(SPLog::PRODUCER_RESULT, "CLIENT EXCEPTION:", $e->getMessage());
|
305 |
+
continue;
|
306 |
+
}
|
307 |
+
|
308 |
+
$succeeded[] = $item;
|
309 |
+
$this->persist($item, $cmds);
|
310 |
+
|
311 |
+
//remove from pending
|
312 |
+
$this->removeItem($item, $pending, "OriginalURL"); //TODO check if fromURL and if not, use file path
|
313 |
+
//tell the commander that the item is done so it won't be relaunched
|
314 |
+
$this->commander->isDone($item);
|
315 |
+
$i++;
|
316 |
+
}
|
317 |
+
|
318 |
+
//For the pending items relaunch, or if any item that needs to be retried from file (-102 or -106)
|
319 |
+
if($retry || count($pending)) {
|
320 |
+
if(!$item->OriginalURL) foreach($pending as $pend) {
|
321 |
+
if(!isset($this->ctx->fileMappings[$pend->OriginalURL])) {
|
322 |
+
$pend = false;
|
323 |
+
}
|
324 |
+
}
|
325 |
+
$this->logger->log(SPLog::PRODUCER_RESULT, "RELAUNCH");
|
326 |
+
$this->ctx = $this->commander->relaunch((object)array("body" => $pending, "headers" => $this->ctx->headers, "fileMappings" => $this->ctx->fileMappings));
|
327 |
+
} else {
|
328 |
+
break;
|
329 |
+
}
|
330 |
+
if($this->ctx == false) { //time's up
|
331 |
+
break;
|
332 |
+
}
|
333 |
+
}
|
334 |
+
|
335 |
+
$message = ShortPixel::opt('persist_type') == 'text' ? 'pending' : ($pending ? 'pending' : ($failed ? 'error' : 'success'));
|
336 |
+
$ret = (object) array(
|
337 |
+
'status' => array(
|
338 |
+
'code' => ($message == 'success' ? 2 : 1),
|
339 |
+
'message' => $message),
|
340 |
+
'succeeded' => $succeeded,
|
341 |
+
'pending' => $pending,
|
342 |
+
'failed' => $failed,
|
343 |
+
'same' => $same
|
344 |
+
);
|
345 |
+
if($cmds['notify_progress']) {
|
346 |
+
//notify the progress to whom it may concern :)
|
347 |
+
$notifier = ProgressNotifier::constructNotifier($path);
|
348 |
+
$notifier->recordProgress($ret);
|
349 |
+
}
|
350 |
+
|
351 |
+
return $ret;
|
352 |
+
}
|
353 |
+
|
354 |
+
private function checkSaveWebP($item, $target, $cmds)
|
355 |
+
{
|
356 |
+
if (isset($item->WebPLossyURL) && $item->WebPLossyURL !== 'NA') { //a WebP image was generated as per the options, download and save it too
|
357 |
+
$webpTarget = $targetWebPFile = dirname($target) . '/' . MB_basename($target, '.' . pathinfo($target, PATHINFO_EXTENSION)) . ".webp";
|
358 |
+
$optWebPURL = $cmds["lossy"] > 0 ? $item->WebPLossyURL : $item->WebPLosslessURL;
|
359 |
+
ShortPixel::getClient()->download($optWebPURL, $webpTarget);
|
360 |
+
$item->WebPSavedFile = $webpTarget;
|
361 |
+
}
|
362 |
+
}
|
363 |
+
|
364 |
+
private function checkSaveAVIF($item, $target, $cmds)
|
365 |
+
{
|
366 |
+
if (isset($item->AVIFLossyURL) && $item->AVIFLossyURL !== 'NA') { //an AVIF image was generated as per the options, download and save it too
|
367 |
+
$avifTarget = $targetAVIFFile = dirname($target) . '/' . MB_basename($target, '.' . pathinfo($target, PATHINFO_EXTENSION)) . ".avif";
|
368 |
+
$optAVIFURL = $cmds["lossy"] > 0 ? $item->AVIFLossyURL : $item->AVIFLosslessURL;
|
369 |
+
ShortPixel::getClient()->download($optAVIFURL, $avifTarget);
|
370 |
+
$item->AVIFSavedFile = $avifTarget;
|
371 |
+
}
|
372 |
+
}
|
373 |
+
|
374 |
+
private function persist($item, $cmds, $status = 'success') {
|
375 |
+
$pers = ShortPixel::getPersister();
|
376 |
+
if($pers) {
|
377 |
+
$optParams = $this->optimizationParams($item, $cmds);
|
378 |
+
if($status == 'pending') {
|
379 |
+
$optParams['message'] = $item->OriginalURL;
|
380 |
+
return $pers->setPending($item->SavedFile, $optParams);
|
381 |
+
} elseif ($status == 'error') {
|
382 |
+
$optParams['message'] = $item->Status->Message;
|
383 |
+
return $pers->setFailed($item->SavedFile, $optParams);
|
384 |
+
} elseif ($status == 'skip') {
|
385 |
+
$optParams['message'] = $item->Status->Message;
|
386 |
+
return $pers->setSkipped($item->SavedFile, $optParams, 'skip');
|
387 |
+
} else {
|
388 |
+
return $pers->setOptimized($item->SavedFile, $optParams);
|
389 |
+
}
|
390 |
+
}
|
391 |
+
}
|
392 |
+
|
393 |
+
private function optimizationParams($item, $cmds) {
|
394 |
+
$optimizedSize = $item->Status->Code == 2 ? ($cmds["lossy"] > 0 ? $item->LossySize : $item->LoselessSize) : 0;
|
395 |
+
|
396 |
+
return array(
|
397 |
+
"compressionType" => $cmds["lossy"] == 1 ? 'lossy' : ($cmds["lossy"] == 2 ? 'glossy' : 'lossless'),
|
398 |
+
"keepExif" => isset($cmds['keep_exif']) ? $cmds['keep_exif'] : ShortPixel::opt("keep_exif"),
|
399 |
+
"cmyk2rgb" => isset($cmds['cmyk2rgb']) ? $cmds['cmyk2rgb'] : ShortPixel::opt("cmyk2rgb"),
|
400 |
+
"resize" => isset($cmds['resize'])
|
401 |
+
? $cmds['resize']
|
402 |
+
: ((isset($cmds['resize_width']) && $cmds['resize_width'] > 0)
|
403 |
+
? 1 : (ShortPixel::opt("resize_width") ? 1 : 0)),
|
404 |
+
"resizeWidth" => isset($cmds['resize_width']) ? $cmds['resize_width'] : ShortPixel::opt("resize_width"),
|
405 |
+
"resizeHeight" => isset($cmds['resize_height']) ? $cmds['resize_height'] : ShortPixel::opt("resize_height"),
|
406 |
+
"percent" => isset($item->PercentImprovement) ? number_format(100.0 - 100.0 * $optimizedSize / $item->OriginalSize, 2) : 0, //$item->PercentImprovement : 0,
|
407 |
+
"optimizedSize" => $optimizedSize,
|
408 |
+
"changeDate" => time(),
|
409 |
+
"message" => null //will be set by persist() if 'error'
|
410 |
+
);
|
411 |
+
}
|
412 |
+
|
413 |
+
/**
|
414 |
+
* finds if an array contains an item, comparing the property given as key
|
415 |
+
* @param $item
|
416 |
+
* @param $arr
|
417 |
+
* @param $key
|
418 |
+
* @return the position that was removed, false if not found
|
419 |
+
*/
|
420 |
+
private function findItem($item, $arr, $key) {
|
421 |
+
for($j = 0; $j < count($arr); $j++) {
|
422 |
+
if($arr[$j]->$key == $item->$key) {
|
423 |
+
return $j;
|
424 |
+
}
|
425 |
+
}
|
426 |
+
return false;
|
427 |
+
}
|
428 |
+
|
429 |
+
/**
|
430 |
+
* removes the item if found in array (with findItem)
|
431 |
+
* @param $item
|
432 |
+
* @param $arr
|
433 |
+
* @param $key
|
434 |
+
* @return true if removed, false if not found
|
435 |
+
*/
|
436 |
+
private function removeItem($item, &$arr, $key) {
|
437 |
+
$j = $this->findItem($item, $arr, $key);
|
438 |
+
if($j !== false) {
|
439 |
+
array_splice($arr, $j);
|
440 |
+
return true;
|
441 |
+
}
|
442 |
+
return false;
|
443 |
+
}
|
444 |
+
}
|
vendor/shortpixel/shortpixel-php/lib/ShortPixel/SPCache.php
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* User: simon
|
4 |
+
* Date: 27.07.2018
|
5 |
+
*/
|
6 |
+
|
7 |
+
namespace ShortPixel;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class SPCache will cache in memcached the objects received, if memcached is available
|
11 |
+
* @package ShortPixel
|
12 |
+
*/
|
13 |
+
class SPCache {
|
14 |
+
private static $instance;
|
15 |
+
private $mc;
|
16 |
+
private $local;
|
17 |
+
private $time;
|
18 |
+
|
19 |
+
private function __construct() {
|
20 |
+
$this->time = \ShortPixel\opt("cache_time");
|
21 |
+
$this->mc = \ShortPixel\getMemcache();
|
22 |
+
if(!$this->mc) {
|
23 |
+
$this->local = array();
|
24 |
+
}
|
25 |
+
}
|
26 |
+
|
27 |
+
public function fetch($key) {
|
28 |
+
if($this->mc) {
|
29 |
+
return $this->mc->get($key);
|
30 |
+
} elseif(isset($this->local[$key]) && time() - $this->local[$key]['time'] < $this->time) {
|
31 |
+
return $this->local[$key]['value'];
|
32 |
+
}
|
33 |
+
return false;
|
34 |
+
}
|
35 |
+
|
36 |
+
public function store($key, $value) {
|
37 |
+
if($this->time) {
|
38 |
+
if($this->mc) {
|
39 |
+
return $this->mc->set($key, $value, $this->time);
|
40 |
+
} else {
|
41 |
+
$this->local[$key] = array('value' => $value, 'time' => time());
|
42 |
+
}
|
43 |
+
}
|
44 |
+
return false;
|
45 |
+
}
|
46 |
+
|
47 |
+
public function delete($key) {
|
48 |
+
if($this->mc) {
|
49 |
+
return $this->mc->delete($key);
|
50 |
+
} elseif(isset($this->local[$key])) {
|
51 |
+
unset($this->local[$key]);
|
52 |
+
}
|
53 |
+
return false;
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* returns the current logger. If the logger is not set to log from that producer or if the log is not initialized, will return a dummy logger which doesn't log.
|
58 |
+
* @return SPCache
|
59 |
+
*/
|
60 |
+
public static function Get() {
|
61 |
+
if(!isset(self::$instance)) {
|
62 |
+
self::$instance = new SPCache();
|
63 |
+
}
|
64 |
+
return self::$instance;
|
65 |
+
}
|
66 |
+
|
67 |
+
}
|
vendor/shortpixel/shortpixel-php/lib/ShortPixel/SPLog.php
ADDED
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* User: simon
|
4 |
+
* Date: 26.02.2018
|
5 |
+
*/
|
6 |
+
namespace ShortPixel;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class SPLog logs messages or not based on which source (producer)
|
10 |
+
* @package ShortPixel
|
11 |
+
*/
|
12 |
+
class SPLog {
|
13 |
+
const PRODUCER_NONE = 0;
|
14 |
+
const PRODUCER_CMD = 1; //0b00000001;
|
15 |
+
const PRODUCER_CMD_VERBOSE = 2; //0b00000010;
|
16 |
+
const PRODUCER_PERSISTER = 4; //0b00000100;
|
17 |
+
const PRODUCER_CLIENT = 8; //0b00001000;
|
18 |
+
const PRODUCER_RESULT = 16; //0b00010000;
|
19 |
+
const PRODUCER_WEB = 32; //0b00100000;
|
20 |
+
const PRODUCER_CTRL = 64; //0b01000000;
|
21 |
+
|
22 |
+
const FLAG_NONE = 0;
|
23 |
+
const FLAG_MEMORY = 1;
|
24 |
+
|
25 |
+
const TARGET_CONSOLE = 1;
|
26 |
+
const TARGET_FILE = 2;
|
27 |
+
|
28 |
+
private static $instance, $dummy;
|
29 |
+
|
30 |
+
private $processId;
|
31 |
+
private $target;
|
32 |
+
private $targetName;
|
33 |
+
private $acceptedProducers;
|
34 |
+
private $time;
|
35 |
+
private $loggedAlready;
|
36 |
+
private $flags;
|
37 |
+
|
38 |
+
private function __construct($processId, $acceptedProducers, $target, $targetName, $flags = self::FLAG_NONE) {
|
39 |
+
$this->processId = $processId;
|
40 |
+
$this->acceptedProducers = $acceptedProducers;
|
41 |
+
$this->target = $target;
|
42 |
+
$this->targetName = $targetName;
|
43 |
+
$this->time = microtime(true);
|
44 |
+
$this->loggedAlready = array();
|
45 |
+
$this->flags = $flags;
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* formats a log message
|
50 |
+
* @param $processId
|
51 |
+
* @param $msg
|
52 |
+
* @param $time
|
53 |
+
* @return string
|
54 |
+
*/
|
55 |
+
public static function format($msg, $processId = false, $time = false, $flags = self::FLAG_NONE) {
|
56 |
+
return "\n" . ($processId ? "$processId@" : "")
|
57 |
+
. date("Y-m-d H:i:s")
|
58 |
+
. ($time ? " (" . number_format(microtime(true) - $time, 2) . "s)" : "")
|
59 |
+
. ($flags | self::FLAG_MEMORY ? " (M: " . number_format(memory_get_usage()) . ")" : ""). " > $msg\n";
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Log the message if the logger is configured to log from this producer
|
64 |
+
* @param $producer SPLog::PRODUCER_* - the source of logging ( one of the SPLog::PRODUCER_* values )
|
65 |
+
* @param $msg $string the actual message
|
66 |
+
* @param bool $object
|
67 |
+
*/
|
68 |
+
public function log($producer, $msg, $object = false) {
|
69 |
+
if(!($this->acceptedProducers & $producer)) { return; }
|
70 |
+
|
71 |
+
$msgFmt = self::format($msg, $this->processId, $this->time, $this->flags);
|
72 |
+
if($object) {
|
73 |
+
$msgFmt .= " " . json_encode($object);
|
74 |
+
}
|
75 |
+
$this->logRaw($msgFmt);
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Log only the first call with that key
|
80 |
+
* @param $key
|
81 |
+
* @param $producer
|
82 |
+
* @param $msg
|
83 |
+
* @param $object
|
84 |
+
*/
|
85 |
+
public function logFirst($key, $producer, $msg, $object = false) {
|
86 |
+
if(!($this->acceptedProducers & $producer)) { return; }
|
87 |
+
|
88 |
+
if(!in_array($key, $this->loggedAlready)) {
|
89 |
+
$this->loggedAlready[] = $key;
|
90 |
+
$this->log($producer, $msg, $object);
|
91 |
+
}
|
92 |
+
|
93 |
+
}
|
94 |
+
|
95 |
+
public function clearLogged($producer, $key) {
|
96 |
+
if(!($this->acceptedProducers & $producer)) { return; }
|
97 |
+
|
98 |
+
if (($idx = array_search($key, $this->loggedAlready)) !== false) {
|
99 |
+
unset($this->loggedAlready[$idx]);
|
100 |
+
}
|
101 |
+
}
|
102 |
+
|
103 |
+
/**
|
104 |
+
* logs a message regardless of the producer setting and without formatting
|
105 |
+
* @param $msg
|
106 |
+
*/
|
107 |
+
public function logRaw($msg){
|
108 |
+
switch($this->target) {
|
109 |
+
case self::TARGET_CONSOLE:
|
110 |
+
echo($msg);
|
111 |
+
break;
|
112 |
+
case self::TARGET_FILE:
|
113 |
+
|
114 |
+
}
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Log the message if the logger is configured to log from this producer AND EXIT ANYWAY
|
119 |
+
* @param $producer the source of logging ( one of the SPLog::PRODUCER_* values )
|
120 |
+
* @param $msg the actual message
|
121 |
+
* @param bool $object
|
122 |
+
*/
|
123 |
+
public function bye($producer, $msg, $object = false) {
|
124 |
+
$this->log($producer, $msg, $object); echo("\n");die();
|
125 |
+
}
|
126 |
+
|
127 |
+
/**
|
128 |
+
* init the logger singleton
|
129 |
+
* @param $processId
|
130 |
+
* @param $acceptedProducers - the producers from which the logger will log, ignoring gracefully the others
|
131 |
+
* @param int $target - the log type
|
132 |
+
* @param bool|false $targetName the log name if needed
|
133 |
+
* @return SPLog the newly created logger instance
|
134 |
+
*/
|
135 |
+
public static function Init($processId, $acceptedProducers, $target = self::TARGET_CONSOLE, $targetName = false, $flags = SPLog::FLAG_NONE) {
|
136 |
+
self::$instance = new SPLog($processId, $acceptedProducers, $target, $targetName, $flags);
|
137 |
+
return self::$instance;
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* returns the current logger. If the logger is not set to log from that producer or if the log is not initialized, will return a dummy logger which doesn't log.
|
142 |
+
* @param $producer
|
143 |
+
* @return SPLog
|
144 |
+
*/
|
145 |
+
public static function Get($producer) {
|
146 |
+
if( !(self::$instance && ($producer & self::$instance->acceptedProducers)) ) {
|
147 |
+
if(!isset(self::$dummy)) {
|
148 |
+
self::$dummy = new SPLog(0, self::PRODUCER_NONE, self::TARGET_CONSOLE, false);
|
149 |
+
}
|
150 |
+
return self::$dummy;
|
151 |
+
}
|
152 |
+
return self::$instance;
|
153 |
+
}
|
154 |
+
}
|
vendor/shortpixel/shortpixel-php/lib/ShortPixel/Settings.php
ADDED
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace ShortPixel;
|
4 |
+
|
5 |
+
|
6 |
+
class Settings {
|
7 |
+
const FOLDER_INI_NAME = '.sp-options';
|
8 |
+
private $INI_PATH;
|
9 |
+
private $settings;
|
10 |
+
|
11 |
+
function __construct($iniPath = false) {
|
12 |
+
$this->INI_PATH = $iniPath;
|
13 |
+
$this->settings = array();
|
14 |
+
if(file_exists($this->INI_PATH)) {
|
15 |
+
$this->settings = parse_ini_file($this->INI_PATH);
|
16 |
+
}
|
17 |
+
}
|
18 |
+
|
19 |
+
function get($key) {
|
20 |
+
return(isset($this->settings[$key]) ? $this->settings[$key] : false);
|
21 |
+
}
|
22 |
+
|
23 |
+
function persistApiKeyAndSettings($data) {
|
24 |
+
if(isset($data['API_KEY']) && strlen($data['API_KEY']) == 20) {
|
25 |
+
if(file_exists($this->INI_PATH)) {
|
26 |
+
unlink($this->INI_PATH);
|
27 |
+
}
|
28 |
+
$strSettings = "[SHORTPIXEL]\nAPI_KEY=" . $data['API_KEY'] . "\n";
|
29 |
+
$settings = $this->post2options($data);
|
30 |
+
foreach($settings as $key => $val) {
|
31 |
+
$strSettings .= $key . '=' . $val . "\n";
|
32 |
+
}
|
33 |
+
$settings['API_KEY'] = $data['API_KEY'];
|
34 |
+
|
35 |
+
if(!@file_put_contents($this->INI_PATH, $strSettings)) {
|
36 |
+
return array("error" => "Could not write properties file " . $this->INI_PATH . ". Please check rights.");
|
37 |
+
}
|
38 |
+
$this->settings = $settings;
|
39 |
+
return array("success" => "API Key set: " . $data['API_KEY']);
|
40 |
+
} else {
|
41 |
+
return array("error" => "API Key should be 20 characters long.");
|
42 |
+
}
|
43 |
+
}
|
44 |
+
|
45 |
+
function post2options($post) {
|
46 |
+
$data = array();
|
47 |
+
if(isset($post['type'])) $data['lossy'] = $post['type'] == 'lossy' ? 1 : ($post['type'] == 'glossy' ? 2 : 0);
|
48 |
+
$data['keep_exif'] = isset($post['removeExif']) ? 0 : 1;
|
49 |
+
$data['cmyk2rgb'] = isset($post['cmyk2rgb']) ? 1 : 0;
|
50 |
+
$data['resize'] = isset($post['resize']) ? ($post['resize_type'] == 'outer' ? 1 : 3) : 0;
|
51 |
+
if($data['resize'] && isset($post['width'])) $data['resize_width'] = $post['width'];
|
52 |
+
if($data['resize'] && isset($post['height'])) $data['resize_height'] = $post['height'];
|
53 |
+
|
54 |
+
$convertto = isset($post['webp']) ? '|+webp' : '';
|
55 |
+
$convertto .= isset($post['avif']) ? '|+avif' : '';
|
56 |
+
$data['convertto'] = '' . substr($convertto, 1);
|
57 |
+
|
58 |
+
if(isset($post['backup_path'])) {
|
59 |
+
$data['backup_path'] = $post['backup_path'];
|
60 |
+
}
|
61 |
+
if(isset($post['exclude'])) {
|
62 |
+
$data['exclude'] = $post['exclude'];
|
63 |
+
}
|
64 |
+
if(isset($post['user']) && isset($post['pass'])) {
|
65 |
+
$data['user'] = $post['user'];
|
66 |
+
$data['pass'] = $post['pass'];
|
67 |
+
}
|
68 |
+
if(isset($post['base_url']) && strlen($post['base_url'])) {
|
69 |
+
$data['base_url'] = rtrim($post['base_url'], '/');
|
70 |
+
} elseif (isset($post['change_base_url']) && strlen($post['change_base_url'])) {
|
71 |
+
$data['base_url'] = rtrim($post['change_base_url'], '/');
|
72 |
+
}
|
73 |
+
return $data;
|
74 |
+
}
|
75 |
+
|
76 |
+
static function pathToRelative($path, $reference) {
|
77 |
+
$pa = explode('/', trim($path, '/'));
|
78 |
+
$ra = explode('/', trim($reference, '/'));
|
79 |
+
$res = array();
|
80 |
+
for($i = 0, $same = true; $i < max(count($pa), count($ra)); $i++) {
|
81 |
+
if($same && isset($pa[$i]) && isset($ra[$i]) && $pa[$i] == $ra[$i]) continue;
|
82 |
+
$same = false;
|
83 |
+
if(isset($ra[$i])) array_unshift($res, '..');
|
84 |
+
if(isset($pa[$i])) $res[] = $pa[$i];
|
85 |
+
}
|
86 |
+
return implode('/', $res);
|
87 |
+
}
|
88 |
+
|
89 |
+
function persistFolderSettings($data, $path) {
|
90 |
+
$strSettings = "[SHORTPIXEL]\n";
|
91 |
+
foreach($this->post2options($data) as $key => $val) {
|
92 |
+
if(!in_array($key, array("API_KEY", "folder", "")))
|
93 |
+
$strSettings .= $key . '=' . (is_numeric($val) ? $val : '"' . $val . '"') . "\n";
|
94 |
+
}
|
95 |
+
return @file_put_contents($path . '/' . self::FOLDER_INI_NAME, $strSettings);
|
96 |
+
}
|
97 |
+
|
98 |
+
function addOptions($options) {
|
99 |
+
array_merge($this->settings, $options);
|
100 |
+
}
|
101 |
+
|
102 |
+
function readOptions($path) {
|
103 |
+
$options = $this->settings;
|
104 |
+
if($path && file_exists($path . '/' . self::FOLDER_INI_NAME)) {
|
105 |
+
$options = array_merge($options, parse_ini_file($path . '/' . self::FOLDER_INI_NAME));
|
106 |
+
}
|
107 |
+
unset($options['API_KEY']);
|
108 |
+
return $options;
|
109 |
+
|
110 |
+
}
|
111 |
+
}
|
vendor/shortpixel/shortpixel-php/lib/ShortPixel/Source.php
ADDED
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace ShortPixel;
|
4 |
+
|
5 |
+
class Source {
|
6 |
+
private $urls;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* @param $paths
|
10 |
+
* @param $basePath - common base path used to determine the subfolders that will be created in the destination
|
11 |
+
* @param null $pending
|
12 |
+
* @param bool $refresh
|
13 |
+
* @return Commander - the class that handles the optimization commands
|
14 |
+
* @throws ClientException
|
15 |
+
* @internal param $path - the file path on the local drive
|
16 |
+
*/
|
17 |
+
public function fromFiles($paths, $basePath = null, $pending = null, $refresh = false) {
|
18 |
+
if(!is_array($paths)) {
|
19 |
+
$paths = array($paths);
|
20 |
+
}
|
21 |
+
if(count($paths) > ShortPixel::MAX_ALLOWED_FILES_PER_CALL) {
|
22 |
+
throw new ClientException("Maximum 10 local images allowed per call.");
|
23 |
+
}
|
24 |
+
$files = array();
|
25 |
+
foreach($paths as $path) {
|
26 |
+
if (!file_exists($path)) throw new ClientException("File not found: " . $path);
|
27 |
+
$files[] = $path;
|
28 |
+
}
|
29 |
+
$data = array(
|
30 |
+
"plugin_version" => ShortPixel::LIBRARY_CODE . " " . ShortPixel::VERSION,
|
31 |
+
"key" => ShortPixel::getKey(),
|
32 |
+
"files" => $files
|
33 |
+
);
|
34 |
+
if($refresh) { //don't put it in the array above because false will overwrite the commands refresh. If only set when true, will just force a refresh when needed.
|
35 |
+
$data["refresh"] = 1;
|
36 |
+
}
|
37 |
+
if($pending && count($pending)) {
|
38 |
+
$data["pendingURLs"] = $pending;
|
39 |
+
}
|
40 |
+
|
41 |
+
return new Commander($data, $this);
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* returns the optimization counters of the folder and subfolders
|
46 |
+
* @param $path - the file path on the local drive
|
47 |
+
* @param bool $recurse - boolean - go into subfolders or not
|
48 |
+
* @param bool $fileList - return the list of files with optimization status (only current folder, not subfolders)
|
49 |
+
* @param array $exclude - array of folder names that you want to exclude from the optimization
|
50 |
+
* @param bool $persistPath - the path where to look for the metadata, if different from the $path
|
51 |
+
* @param int $recurseDepth - how many subfolders deep to go. Defaults to PHP_INT_MAX
|
52 |
+
* @param bool $retrySkipped - if true, all skipped files will be reset to pending with retries = 0
|
53 |
+
* @return object|void (object)array('status', 'total', 'succeeded', 'pending', 'same', 'failed')
|
54 |
+
* @throws PersistException
|
55 |
+
*/
|
56 |
+
public function folderInfo($path, $recurse = true, $fileList = false, $exclude = array(), $persistPath = false, $recurseDepth = PHP_INT_MAX, $retrySkipped = false){
|
57 |
+
$persister = ShortPixel::getPersister($path);
|
58 |
+
if(!$persister) {
|
59 |
+
throw new PersistException("Persist is not enabled in options, needed for fetching folder info");
|
60 |
+
}
|
61 |
+
return $persister->info($path, $recurse, $fileList, $exclude, $persistPath, $recurseDepth, $retrySkipped);
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* processes a chunk of MAX_ALLOWED files from the folder, based on the persisted information about which images are processed and which not. This information is offered by the Persister object.
|
66 |
+
* @param $path - the folder path on the local drive
|
67 |
+
* @param int $maxFiles - maximum number of files to select from the folder
|
68 |
+
* @param array $exclude - exclude files based on regex patterns
|
69 |
+
* @param bool $persistFolder - the path where to store the metadata, if different from the $path (usually the target path)
|
70 |
+
* @param int $maxTotalFileSize - max summed up file size in MB
|
71 |
+
* @param int $recurseDepth - how many subfolders deep to go. Defaults to PHP_INT_MAX
|
72 |
+
* @return Commander - the class that handles the optimization commands
|
73 |
+
* @throws ClientException
|
74 |
+
* @throws PersistException
|
75 |
+
*/
|
76 |
+
public function fromFolder($path, $maxFiles = 0, $exclude = array(), $persistFolder = false, $maxTotalFileSize = ShortPixel::CLIENT_MAX_BODY_SIZE, $recurseDepth = PHP_INT_MAX) {
|
77 |
+
if($maxFiles == 0) {
|
78 |
+
$maxFiles = ShortPixel::MAX_ALLOWED_FILES_PER_CALL;
|
79 |
+
}
|
80 |
+
//sanitize
|
81 |
+
$maxFiles = max(1, min(ShortPixel::MAX_ALLOWED_FILES_PER_CALL, intval($maxFiles)));
|
82 |
+
|
83 |
+
$persister = ShortPixel::getPersister($path);
|
84 |
+
if(!$persister) {
|
85 |
+
throw new PersistException("Persist_type is not enabled in options, needed for folder optimization");
|
86 |
+
}
|
87 |
+
$paths = $persister->getTodo($path, $maxFiles, $exclude, $persistFolder, $maxTotalFileSize, $recurseDepth);
|
88 |
+
if($paths) {
|
89 |
+
ShortPixel::setOptions(array("base_source_path" => $path));
|
90 |
+
return $this->fromFiles($paths->files, null, $paths->filesPending);
|
91 |
+
}
|
92 |
+
throw new ClientException("Couldn't find any processable file at given path ($path).", 2);
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* processes a chunk of MAX_ALLOWED URLs from a folder that is accessible via web at the $webPath location,
|
97 |
+
* based on the persisted information about which images are processed and which not. This information is offered by the Persister object.
|
98 |
+
* @param $path - the folder path on the local drive
|
99 |
+
* @param $webPath - the web URL of the folder
|
100 |
+
* @param array $exclude - exclude files based on regex patterns
|
101 |
+
* @param bool $persistFolder - the path where to store the metadata, if different from the $path (usually the target path)
|
102 |
+
* @param int $recurseDepth - how many subfolders deep to go. Defaults to PHP_INT_MAX
|
103 |
+
* @return Commander - the class that handles the optimization commands
|
104 |
+
* @throws ClientException
|
105 |
+
* @throws PersistException
|
106 |
+
*/
|
107 |
+
public function fromWebFolder($path, $webPath, $exclude = array(), $persistFolder = false, $recurseDepth = PHP_INT_MAX) {
|
108 |
+
|
109 |
+
$path = rtrim($path, '/');
|
110 |
+
$webPath = rtrim($webPath, '/');
|
111 |
+
$persister = ShortPixel::getPersister();
|
112 |
+
if($persister === null) {
|
113 |
+
//cannot optimize from folder without persister.
|
114 |
+
throw new PersistException("Persist_type is not enabled in options, needed for folder optimization");
|
115 |
+
}
|
116 |
+
$paths = $persister->getTodo($path, ShortPixel::MAX_ALLOWED_FILES_PER_WEB_CALL, $exclude, $persistFolder, $recurseDepth);
|
117 |
+
$repl = (object)array("path" => $path, "web" => $webPath);
|
118 |
+
if($paths && count($paths->files)) {
|
119 |
+
$items = array_merge($paths->files, array_values($paths->filesPending)); //not impossible to have filesPending - for example optimized partially without webPath then added it
|
120 |
+
array_walk(
|
121 |
+
$items,
|
122 |
+
function(&$item, $key, $repl){
|
123 |
+
$item = implode('/', array_map('rawurlencode', explode('/', str_replace($repl->path, '', $item))));
|
124 |
+
$item = $repl->web . $item;
|
125 |
+
}, $repl);
|
126 |
+
ShortPixel::setOptions(array("base_url" => $webPath, "base_source_path" => $path));
|
127 |
+
|
128 |
+
return $this->fromUrls($items);
|
129 |
+
}
|
130 |
+
//folder is either empty, either fully optimized, in both cases it's optimized :)
|
131 |
+
throw new ClientException("Couldn't find any processable file at given path ($path).", 2);
|
132 |
+
}
|
133 |
+
|
134 |
+
public function fromBuffer($name, $contents) {
|
135 |
+
return new Commander(array(
|
136 |
+
"plugin_version" => ShortPixel::LIBRARY_CODE . " " . ShortPixel::VERSION,
|
137 |
+
"key" => ShortPixel::getKey(),
|
138 |
+
"buffers" => array($name => $contents),
|
139 |
+
// don't add it if false, otherwise will overwrite the refresh command //"refresh" => false
|
140 |
+
), $this);
|
141 |
+
}
|
142 |
+
|
143 |
+
/**
|
144 |
+
* @param $urls - the array of urls to be optimized
|
145 |
+
* @return Commander - the class that handles the optimization commands
|
146 |
+
* @throws ClientException
|
147 |
+
*/
|
148 |
+
public function fromUrls($urls) {
|
149 |
+
if(!is_array($urls)) {
|
150 |
+
$urls = array($urls);
|
151 |
+
}
|
152 |
+
if(count($urls) > ShortPixel::MAX_API_ALLOWED_FILES_PER_WEB_CALL) {
|
153 |
+
throw new ClientException("Maximum 100 images allowed per call.");
|
154 |
+
}
|
155 |
+
|
156 |
+
$this->urls = array_map ('utf8_encode', $urls);
|
157 |
+
$data = array(
|
158 |
+
"plugin_version" => ShortPixel::LIBRARY_CODE . " " . ShortPixel::VERSION,
|
159 |
+
"key" => ShortPixel::getKey(),
|
160 |
+
"urllist" => $this->urls,
|
161 |
+
// don't add it if false, otherwise will overwrite the refresh command //"refresh" => false
|
162 |
+
);
|
163 |
+
|
164 |
+
return new Commander($data, $this);
|
165 |
+
}
|
166 |
+
}
|
vendor/shortpixel/shortpixel-php/lib/ShortPixel/notify/ProgressNotifier.php
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* User: simon
|
4 |
+
* Date: 16.03.2018
|
5 |
+
*/
|
6 |
+
namespace ShortPixel\notify;
|
7 |
+
|
8 |
+
abstract class ProgressNotifier {
|
9 |
+
protected $path;
|
10 |
+
|
11 |
+
public function __construct($path) {
|
12 |
+
$this->path = $path;
|
13 |
+
}
|
14 |
+
|
15 |
+
public function recordProgress($info, $replace = false) {
|
16 |
+
$data = $this->getData();
|
17 |
+
$data = $data ? $data : new \stdClass();
|
18 |
+
if(isset($info->status)) {
|
19 |
+
$data->status = $info->status;
|
20 |
+
}
|
21 |
+
if(isset($info->total)) {
|
22 |
+
$data->total = 0 + $info->total;
|
23 |
+
}
|
24 |
+
if($replace) {
|
25 |
+
$data->succeeded = $data->failed = $data->same = 0;
|
26 |
+
}
|
27 |
+
if(isset($info->failed)) {
|
28 |
+
$data->failed = (isset($data->failed) ? $data->failed : 0) + (is_array($info->failed) ? count($info->failed) : 0 + $info->failed);
|
29 |
+
}
|
30 |
+
if(isset($info->same)) {
|
31 |
+
$data->same = (isset($data->same) ? $data->same : 0) + (is_array($info->same) ? count($info->same) : 0 + $info->same);
|
32 |
+
}
|
33 |
+
$succeeded = array();
|
34 |
+
if(isset($info->succeeded)) {
|
35 |
+
$data->succeeded = (isset($data->succeeded) ? $data->succeeded : 0) + (is_array($info->succeeded) ? count($info->succeeded) : 0 + $info->succeeded);
|
36 |
+
if(is_array($info->succeeded)) {
|
37 |
+
$succeeded = $info->succeeded;
|
38 |
+
}
|
39 |
+
}
|
40 |
+
$data->succeededList = array_slice(array_merge($succeeded, (isset($data->succeededList) ? $data->succeededList : array())), 0, 20);
|
41 |
+
if(!count($data->succeededList)) unset($data->succeededList);
|
42 |
+
|
43 |
+
$same = array();
|
44 |
+
if(isset($info->same)) {
|
45 |
+
$data->same = (isset($data->same) ? $data->same : 0) + (is_array($info->same) ? count($info->same) : 0 + $info->same);
|
46 |
+
if(is_array($info->same)) {
|
47 |
+
$same = $info->same;
|
48 |
+
}
|
49 |
+
}
|
50 |
+
$data->sameList = array_slice(array_merge($same, (isset($data->sameList) ? $data->sameList : array())), 0, 20);
|
51 |
+
if(!count($data->sameList)) unset($data->sameList);
|
52 |
+
|
53 |
+
$failed = array();
|
54 |
+
if(isset($info->failed)) {
|
55 |
+
$data->failed = (isset($data->failed) ? $data->failed : 0) + (is_array($info->failed) ? count($info->failed) : 0 + $info->failed);
|
56 |
+
if(is_array($info->failed)) {
|
57 |
+
for($i = 0; $i < count($info->failed); $i++) {
|
58 |
+
$info->failed[$i]->TimeStamp = date("Y-m-d H:i:s");
|
59 |
+
}
|
60 |
+
$failed = $info->failed;
|
61 |
+
}
|
62 |
+
}
|
63 |
+
$data->failedList = array_slice(array_merge($failed, (isset($data->failedList) ? $data->failedList : array())), 0, 100);
|
64 |
+
if(!count($data->failedList)) unset($data->failedList);
|
65 |
+
|
66 |
+
$this->setData($data);
|
67 |
+
}
|
68 |
+
|
69 |
+
public abstract function getData();
|
70 |
+
public abstract function setData($data);
|
71 |
+
public abstract function set($type, $data);
|
72 |
+
public abstract function get($type);
|
73 |
+
|
74 |
+
public function enqueueFailedImages(){
|
75 |
+
|
76 |
+
}
|
77 |
+
public function getFailedImages(){
|
78 |
+
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Add to a queue info about the last optimized images. The queue is limited to maximum 20 images - the newest
|
83 |
+
* @return mixed
|
84 |
+
*/
|
85 |
+
public function enqueueDoneImages() {
|
86 |
+
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* @return array - list of the last maximum 20 images optimized.
|
91 |
+
*/
|
92 |
+
public function getDoneImages() {
|
93 |
+
|
94 |
+
}
|
95 |
+
|
96 |
+
public static function constructNotifier($path) {
|
97 |
+
$mc = \ShortPixel\getMemcache();
|
98 |
+
if($mc) {
|
99 |
+
$notifier = new ProgressNotifierMemcache($path);
|
100 |
+
$notifier->setMemcache($mc);
|
101 |
+
return $notifier;
|
102 |
+
}
|
103 |
+
return new ProgressNotifierFileQ($path);
|
104 |
+
}
|
105 |
+
}
|
vendor/shortpixel/shortpixel-php/lib/ShortPixel/notify/ProgressNotifierFileQ.php
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* User: simon
|
4 |
+
* Date: 16.03.2018
|
5 |
+
*/
|
6 |
+
namespace ShortPixel\notify;
|
7 |
+
|
8 |
+
class ProgressNotifierFileQ extends ProgressNotifier {
|
9 |
+
|
10 |
+
public function get($type)
|
11 |
+
{
|
12 |
+
$data = $this->getData();
|
13 |
+
return isset($data[$type]) ? $data[$type] : false;
|
14 |
+
}
|
15 |
+
|
16 |
+
public function set($type, $value)
|
17 |
+
{
|
18 |
+
$data = $this->getData();
|
19 |
+
if(!is_array($data)) {
|
20 |
+
$data = [];
|
21 |
+
}
|
22 |
+
$data[$type] = $value;
|
23 |
+
$this->setData($data);
|
24 |
+
}
|
25 |
+
|
26 |
+
const PROGRESS_FILE_NAME = '.sp-progress';
|
27 |
+
|
28 |
+
public function getFilePath() {
|
29 |
+
return rtrim( $this->path, '/\\' ) . '/' . self::PROGRESS_FILE_NAME;
|
30 |
+
}
|
31 |
+
|
32 |
+
public function getData() {
|
33 |
+
$file = $this->getFilePath();
|
34 |
+
if(file_exists($file)) {
|
35 |
+
return json_decode(file_get_contents($file));
|
36 |
+
}
|
37 |
+
return false;
|
38 |
+
}
|
39 |
+
|
40 |
+
function setData($data) {
|
41 |
+
file_put_contents($this->getFilePath(), json_encode($data));
|
42 |
+
}
|
43 |
+
}
|
vendor/shortpixel/shortpixel-php/lib/ShortPixel/notify/ProgressNotifierMemcache.php
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* User: simon
|
4 |
+
* Date: 16.03.2018
|
5 |
+
*/
|
6 |
+
namespace ShortPixel\notify;
|
7 |
+
|
8 |
+
class ProgressNotifierMemcache extends ProgressNotifier {
|
9 |
+
protected $mc;
|
10 |
+
protected $key;
|
11 |
+
|
12 |
+
public function __construct($path) {
|
13 |
+
parent::__construct($path);
|
14 |
+
$this->key = md5($path);
|
15 |
+
}
|
16 |
+
|
17 |
+
public function setMemcache($mc) {
|
18 |
+
$this->mc = $mc;
|
19 |
+
}
|
20 |
+
|
21 |
+
public function set($type, $val)
|
22 |
+
{
|
23 |
+
$data = $this->mc->get($this->key);
|
24 |
+
$data[$type] = $val;
|
25 |
+
$this->mc->set($this->key, $data);
|
26 |
+
}
|
27 |
+
|
28 |
+
public function get($type)
|
29 |
+
{
|
30 |
+
$data = $this->mc->get($this->key);
|
31 |
+
return isset($data[$type]) ? $data[$type] : false;
|
32 |
+
}
|
33 |
+
|
34 |
+
public function getData()
|
35 |
+
{
|
36 |
+
return $this->mc->get($this->key);
|
37 |
+
}
|
38 |
+
|
39 |
+
public function setData($data)
|
40 |
+
{
|
41 |
+
$this->mc->set($this->key, $data);
|
42 |
+
}
|
43 |
+
}
|
vendor/shortpixel/shortpixel-php/lib/ShortPixel/persist/ExifPersister.php
ADDED
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* User: simon
|
4 |
+
* Date: 19.08.2016
|
5 |
+
* Time: 18:05
|
6 |
+
*/
|
7 |
+
namespace ShortPixel\persist;
|
8 |
+
|
9 |
+
use \ShortPixel\Persister;
|
10 |
+
|
11 |
+
class ExifPersister implements Persister {
|
12 |
+
|
13 |
+
|
14 |
+
function __construct($options)
|
15 |
+
{
|
16 |
+
// nothing to do, the ExifPersister doesn't need any configuration
|
17 |
+
}
|
18 |
+
|
19 |
+
public static function IGNORED_BY_DEFAULT() {
|
20 |
+
return array();
|
21 |
+
}
|
22 |
+
|
23 |
+
function isOptimized($path)
|
24 |
+
{
|
25 |
+
switch(exif_imagetype($path)) {
|
26 |
+
case IMAGETYPE_JPEG:
|
27 |
+
case IMAGETYPE_JPEG2000:
|
28 |
+
case IMAGETYPE_TIFF_II:
|
29 |
+
case IMAGETYPE_TIFF_MM:
|
30 |
+
$exif = @exif_read_data($path, 0, true);
|
31 |
+
if($exif===false) return false;
|
32 |
+
foreach ($exif as $key => $section) {
|
33 |
+
if($key == "EXIF"){
|
34 |
+
foreach ($section as $name => $val) {
|
35 |
+
if($name === "UserComment") {
|
36 |
+
$code = substr($val, -5);
|
37 |
+
if($code === \ShortPixel\ShortPixel::LOSSLESS_EXIF_TAG || $code === \ShortPixel\ShortPixel::LOSSY_EXIF_TAG) {
|
38 |
+
return true;
|
39 |
+
}
|
40 |
+
}
|
41 |
+
}
|
42 |
+
}
|
43 |
+
}
|
44 |
+
break;
|
45 |
+
case IMAGETYPE_PNG:
|
46 |
+
$png = new PNGReader($path);
|
47 |
+
$sections = $png->get_sections();
|
48 |
+
$png = PNGMetadataExtractor::getMetadata($path);
|
49 |
+
if(isset($png["text"])) {
|
50 |
+
if(is_array($png["text"])){
|
51 |
+
foreach($png["text"] as $key => $item) {
|
52 |
+
if($key == "APP1_Profile" && isset($item["x-default"])) {
|
53 |
+
$lines = explode("\n", $item["x-default"]);
|
54 |
+
if(isset($lines[7])){
|
55 |
+
$val = trim(substr(hex2bin($lines[7]), -6));
|
56 |
+
if($val === \ShortPixel\ShortPixel::LOSSLESS_EXIF_TAG || $val === \ShortPixel\ShortPixel::LOSSY_EXIF_TAG)
|
57 |
+
return true;
|
58 |
+
}
|
59 |
+
}
|
60 |
+
}
|
61 |
+
} else {
|
62 |
+
|
63 |
+
}
|
64 |
+
}
|
65 |
+
/*if(isset($sections["COMMENT"]) && $sections["COMMENT"] == "SPXLL") {
|
66 |
+
return true;
|
67 |
+
}*/
|
68 |
+
}
|
69 |
+
return false;
|
70 |
+
}
|
71 |
+
|
72 |
+
function info($path, $recurse = true, $fileList = false, $exclude = array(), $persistPath = false) {
|
73 |
+
throw new Exception("Not implemented");
|
74 |
+
}
|
75 |
+
|
76 |
+
function getTodo($path, $count, $exclude = array(), $persistFolder = false, $maxTotalFileSize = false, $recurseDepth = PHP_INT_MAX)
|
77 |
+
{
|
78 |
+
$results = array();
|
79 |
+
$this->getTodoRecursive($path, $count, array_values(array_merge($exclude, array('.','..'))), $results, $recurseDepth);
|
80 |
+
return $results;
|
81 |
+
}
|
82 |
+
|
83 |
+
private function getTodoRecursive($path, &$count, $ignore, &$results, $recurseDepth) {
|
84 |
+
if($count <= 0) return;
|
85 |
+
$files = scandir($path);
|
86 |
+
foreach($files as $t) {
|
87 |
+
if($count <= 0) return;
|
88 |
+
if(in_array($t, $ignore)) continue;
|
89 |
+
$tpath = rtrim($path, '/') . '/' . $t;
|
90 |
+
if (is_dir($tpath)) {
|
91 |
+
if($recurseDepth <= 0) continue;
|
92 |
+
self::getTodoRecursive($tpath, $count, $ignore, $results, $recurseDepth -1);
|
93 |
+
} elseif( \ShortPixel\ShortPixel::isProcessable($t)
|
94 |
+
&& !$this->isOptimized($tpath)) {
|
95 |
+
$results[] = $tpath;
|
96 |
+
$count--;
|
97 |
+
}
|
98 |
+
}
|
99 |
+
}
|
100 |
+
|
101 |
+
function getOptimizationData($path)
|
102 |
+
{
|
103 |
+
// TODO: Implement getOptimizationData() method.
|
104 |
+
}
|
105 |
+
|
106 |
+
function getNextTodo($path, $count)
|
107 |
+
{
|
108 |
+
// TODO: Implement getNextTodo() method.
|
109 |
+
}
|
110 |
+
|
111 |
+
function doneGet()
|
112 |
+
{
|
113 |
+
// TODO: Implement doneGet() method.
|
114 |
+
}
|
115 |
+
|
116 |
+
function setPending($path, $optData)
|
117 |
+
{
|
118 |
+
// TODO: Implement setPending() method.
|
119 |
+
}
|
120 |
+
|
121 |
+
function setOptimized($path, $optData)
|
122 |
+
{
|
123 |
+
// TODO: Implement setOptimized() method.
|
124 |
+
}
|
125 |
+
|
126 |
+
function setFailed($path, $optData)
|
127 |
+
{
|
128 |
+
// TODO: Implement setFailed() method.
|
129 |
+
}
|
130 |
+
|
131 |
+
function setSkipped($path, $optData)
|
132 |
+
{
|
133 |
+
// TODO: Implement setSkipped() method.
|
134 |
+
}
|
135 |
+
}
|
vendor/shortpixel/shortpixel-php/lib/ShortPixel/persist/PNGMetadataExtractor.php
ADDED
@@ -0,0 +1,389 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace ShortPixel\persist;
|
4 |
+
|
5 |
+
class PNGMetadataExtractor {
|
6 |
+
private static $pngSig;
|
7 |
+
|
8 |
+
private static $crcSize;
|
9 |
+
|
10 |
+
private static $textChunks;
|
11 |
+
|
12 |
+
const VERSION = 1;
|
13 |
+
const MAX_CHUNK_SIZE = 3145728; // 3 megabytes
|
14 |
+
|
15 |
+
static function getMetadata( $filename ) {
|
16 |
+
self::$pngSig = pack( "C8", 137, 80, 78, 71, 13, 10, 26, 10 );
|
17 |
+
self::$crcSize = 4;
|
18 |
+
/* based on list at http://owl.phy.queensu.ca/~phil/exiftool/TagNames/PNG.html#TextualData
|
19 |
+
* and http://www.w3.org/TR/PNG/#11keywords
|
20 |
+
*/
|
21 |
+
self::$textChunks = array(
|
22 |
+
'xml:com.adobe.xmp' => 'xmp',
|
23 |
+
# Artist is unofficial. Author is the recommended
|
24 |
+
# keyword in the PNG spec. However some people output
|
25 |
+
# Artist so support both.
|
26 |
+
'artist' => 'Artist',
|
27 |
+
'model' => 'Model',
|
28 |
+
'make' => 'Make',
|
29 |
+
'author' => 'Artist',
|
30 |
+
'comment' => 'PNGFileComment',
|
31 |
+
'description' => 'ImageDescription',
|
32 |
+
'title' => 'ObjectName',
|
33 |
+
'copyright' => 'Copyright',
|
34 |
+
# Source as in original device used to make image
|
35 |
+
# not as in who gave you the image
|
36 |
+
'source' => 'Model',
|
37 |
+
'software' => 'Software',
|
38 |
+
'disclaimer' => 'Disclaimer',
|
39 |
+
'warning' => 'ContentWarning',
|
40 |
+
'url' => 'Identifier', # Not sure if this is best mapping. Maybe WebStatement.
|
41 |
+
'label' => 'Label',
|
42 |
+
'creation time' => 'DateTimeDigitized',
|
43 |
+
'raw profile type app1' => 'APP1_Profile',
|
44 |
+
/* Other potentially useful things - Document */
|
45 |
+
);
|
46 |
+
|
47 |
+
$frameCount = 0;
|
48 |
+
$loopCount = 1;
|
49 |
+
$text = array();
|
50 |
+
$duration = 0.0;
|
51 |
+
$bitDepth = 0;
|
52 |
+
$colorType = 'unknown';
|
53 |
+
|
54 |
+
if ( !$filename ) {
|
55 |
+
throw new Exception( __METHOD__ . ": No file name specified" );
|
56 |
+
} elseif ( !file_exists( $filename ) || is_dir( $filename ) ) {
|
57 |
+
throw new Exception( __METHOD__ . ": File $filename does not exist" );
|
58 |
+
}
|
59 |
+
|
60 |
+
$fh = fopen( $filename, 'rb' );
|
61 |
+
|
62 |
+
if ( !$fh ) {
|
63 |
+
throw new Exception( __METHOD__ . ": Unable to open file $filename" );
|
64 |
+
}
|
65 |
+
|
66 |
+
// Check for the PNG header
|
67 |
+
$buf = fread( $fh, 8 );
|
68 |
+
if ( $buf != self::$pngSig ) {
|
69 |
+
throw new Exception( __METHOD__ . ": Not a valid PNG file; header: $buf" );
|
70 |
+
}
|
71 |
+
|
72 |
+
// Read chunks
|
73 |
+
while ( !feof( $fh ) ) {
|
74 |
+
$buf = fread( $fh, 4 );
|
75 |
+
if ( !$buf || strlen( $buf ) < 4 ) {
|
76 |
+
throw new Exception( __METHOD__ . ": Read error" );
|
77 |
+
}
|
78 |
+
$chunk = unpack( "N", $buf );
|
79 |
+
$chunk_size = $chunk[1];
|
80 |
+
|
81 |
+
if ( $chunk_size < 0 ) {
|
82 |
+
throw new Exception( __METHOD__ . ": Chunk size too big for unpack" );
|
83 |
+
}
|
84 |
+
|
85 |
+
$chunk_type = fread( $fh, 4 );
|
86 |
+
if ( !$chunk_type || strlen( $chunk_type ) < 4 ) {
|
87 |
+
throw new Exception( __METHOD__ . ": Read error" );
|
88 |
+
}
|
89 |
+
|
90 |
+
if ( $chunk_type == "IHDR" ) {
|
91 |
+
$buf = self::read( $fh, $chunk_size );
|
92 |
+
if ( !$buf || strlen( $buf ) < $chunk_size ) {
|
93 |
+
throw new Exception( __METHOD__ . ": Read error" );
|
94 |
+
}
|
95 |
+
$bitDepth = ord( substr( $buf, 8, 1 ) );
|
96 |
+
// Detect the color type in British English as per the spec
|
97 |
+
// http://www.w3.org/TR/PNG/#11IHDR
|
98 |
+
switch ( ord( substr( $buf, 9, 1 ) ) ) {
|
99 |
+
case 0:
|
100 |
+
$colorType = 'greyscale';
|
101 |
+
break;
|
102 |
+
case 2:
|
103 |
+
$colorType = 'truecolour';
|
104 |
+
break;
|
105 |
+
case 3:
|
106 |
+
$colorType = 'index-coloured';
|
107 |
+
break;
|
108 |
+
case 4:
|
109 |
+
$colorType = 'greyscale-alpha';
|
110 |
+
break;
|
111 |
+
case 6:
|
112 |
+
$colorType = 'truecolour-alpha';
|
113 |
+
break;
|
114 |
+
default:
|
115 |
+
$colorType = 'unknown';
|
116 |
+
break;
|
117 |
+
}
|
118 |
+
} elseif ( $chunk_type == "acTL" ) {
|
119 |
+
$buf = fread( $fh, $chunk_size );
|
120 |
+
if ( !$buf || strlen( $buf ) < $chunk_size || $chunk_size < 4 ) {
|
121 |
+
throw new Exception( __METHOD__ . ": Read error" );
|
122 |
+
}
|
123 |
+
|
124 |
+
$actl = unpack( "Nframes/Nplays", $buf );
|
125 |
+
$frameCount = $actl['frames'];
|
126 |
+
$loopCount = $actl['plays'];
|
127 |
+
} elseif ( $chunk_type == "fcTL" ) {
|
128 |
+
$buf = self::read( $fh, $chunk_size );
|
129 |
+
if ( !$buf || strlen( $buf ) < $chunk_size ) {
|
130 |
+
throw new Exception( __METHOD__ . ": Read error" );
|
131 |
+
}
|
132 |
+
$buf = substr( $buf, 20 );
|
133 |
+
if ( strlen( $buf ) < 4 ) {
|
134 |
+
throw new Exception( __METHOD__ . ": Read error" );
|
135 |
+
}
|
136 |
+
|
137 |
+
$fctldur = unpack( "ndelay_num/ndelay_den", $buf );
|
138 |
+
if ( $fctldur['delay_den'] == 0 ) {
|
139 |
+
$fctldur['delay_den'] = 100;
|
140 |
+
}
|
141 |
+
if ( $fctldur['delay_num'] ) {
|
142 |
+
$duration += $fctldur['delay_num'] / $fctldur['delay_den'];
|
143 |
+
}
|
144 |
+
} elseif ( $chunk_type == "iTXt" ) {
|
145 |
+
// Extracts iTXt chunks, uncompressing if necessary.
|
146 |
+
$buf = self::read( $fh, $chunk_size );
|
147 |
+
$items = array();
|
148 |
+
if ( preg_match(
|
149 |
+
'/^([^\x00]{1,79})\x00(\x00|\x01)\x00([^\x00]*)(.)[^\x00]*\x00(.*)$/Ds',
|
150 |
+
$buf, $items )
|
151 |
+
) {
|
152 |
+
/* $items[1] = text chunk name, $items[2] = compressed flag,
|
153 |
+
* $items[3] = lang code (or ""), $items[4]= compression type.
|
154 |
+
* $items[5] = content
|
155 |
+
*/
|
156 |
+
|
157 |
+
// Theoretically should be case-sensitive, but in practise...
|
158 |
+
$items[1] = strtolower( $items[1] );
|
159 |
+
if ( !isset( self::$textChunks[$items[1]] ) ) {
|
160 |
+
// Only extract textual chunks on our list.
|
161 |
+
fseek( $fh, self::$crcSize, SEEK_CUR );
|
162 |
+
continue;
|
163 |
+
}
|
164 |
+
|
165 |
+
$items[3] = strtolower( $items[3] );
|
166 |
+
if ( $items[3] == '' ) {
|
167 |
+
// if no lang specified use x-default like in xmp.
|
168 |
+
$items[3] = 'x-default';
|
169 |
+
}
|
170 |
+
|
171 |
+
// if compressed
|
172 |
+
if ( $items[2] == "\x01" ) {
|
173 |
+
if ( function_exists( 'gzuncompress' ) && $items[4] === "\x00" ) {
|
174 |
+
$errLevel = error_reporting(E_ERROR | E_PARSE);
|
175 |
+
$items[5] = gzuncompress( $items[5] );
|
176 |
+
error_reporting($errLevel);
|
177 |
+
|
178 |
+
if ( $items[5] === false ) {
|
179 |
+
// decompression failed
|
180 |
+
wfDebug( __METHOD__ . ' Error decompressing iTxt chunk - ' . $items[1] . "\n" );
|
181 |
+
fseek( $fh, self::$crcSize, SEEK_CUR );
|
182 |
+
continue;
|
183 |
+
}
|
184 |
+
} else {
|
185 |
+
wfDebug( __METHOD__ . ' Skipping compressed png iTXt chunk due to lack of zlib,'
|
186 |
+
. " or potentially invalid compression method\n" );
|
187 |
+
fseek( $fh, self::$crcSize, SEEK_CUR );
|
188 |
+
continue;
|
189 |
+
}
|
190 |
+
}
|
191 |
+
$finalKeyword = self::$textChunks[$items[1]];
|
192 |
+
$text[$finalKeyword][$items[3]] = $items[5];
|
193 |
+
$text[$finalKeyword]['_type'] = 'lang';
|
194 |
+
} else {
|
195 |
+
// Error reading iTXt chunk
|
196 |
+
throw new Exception( __METHOD__ . ": Read error on iTXt chunk" );
|
197 |
+
}
|
198 |
+
} elseif ( $chunk_type == 'tEXt' ) {
|
199 |
+
$buf = self::read( $fh, $chunk_size );
|
200 |
+
|
201 |
+
// In case there is no \x00 which will make explode fail.
|
202 |
+
if ( strpos( $buf, "\x00" ) === false ) {
|
203 |
+
throw new Exception( __METHOD__ . ": Read error on tEXt chunk" );
|
204 |
+
}
|
205 |
+
|
206 |
+
list( $keyword, $content ) = explode( "\x00", $buf, 2 );
|
207 |
+
if ( $keyword === '' || $content === '' ) {
|
208 |
+
throw new Exception( __METHOD__ . ": Read error on tEXt chunk" );
|
209 |
+
}
|
210 |
+
|
211 |
+
// Theoretically should be case-sensitive, but in practise...
|
212 |
+
$keyword = strtolower( $keyword );
|
213 |
+
if ( !isset( self::$textChunks[$keyword] ) ) {
|
214 |
+
// Don't recognize chunk, so skip.
|
215 |
+
fseek( $fh, self::$crcSize, SEEK_CUR );
|
216 |
+
continue;
|
217 |
+
}
|
218 |
+
$errLevel = error_reporting(E_ERROR | E_PARSE);
|
219 |
+
$content = iconv( 'ISO-8859-1', 'UTF-8', $content );
|
220 |
+
error_reporting($errLevel);
|
221 |
+
|
222 |
+
if ( $content === false ) {
|
223 |
+
throw new Exception( __METHOD__ . ": Read error (error with iconv)" );
|
224 |
+
}
|
225 |
+
|
226 |
+
$finalKeyword = self::$textChunks[$keyword];
|
227 |
+
$text[$finalKeyword]['x-default'] = $content;
|
228 |
+
$text[$finalKeyword]['_type'] = 'lang';
|
229 |
+
} elseif ( $chunk_type == 'zTXt' ) {
|
230 |
+
if ( function_exists( 'gzuncompress' ) ) {
|
231 |
+
$buf = self::read( $fh, $chunk_size );
|
232 |
+
|
233 |
+
// In case there is no \x00 which will make explode fail.
|
234 |
+
if ( strpos( $buf, "\x00" ) === false ) {
|
235 |
+
throw new Exception( __METHOD__ . ": Read error on zTXt chunk" );
|
236 |
+
}
|
237 |
+
|
238 |
+
list( $keyword, $postKeyword ) = explode( "\x00", $buf, 2 );
|
239 |
+
if ( $keyword === '' || $postKeyword === '' ) {
|
240 |
+
throw new Exception( __METHOD__ . ": Read error on zTXt chunk" );
|
241 |
+
}
|
242 |
+
// Theoretically should be case-sensitive, but in practise...
|
243 |
+
$keyword = strtolower( $keyword );
|
244 |
+
|
245 |
+
if ( !isset( self::$textChunks[$keyword] ) ) {
|
246 |
+
// Don't recognize chunk, so skip.
|
247 |
+
fseek( $fh, self::$crcSize, SEEK_CUR );
|
248 |
+
continue;
|
249 |
+
}
|
250 |
+
$compression = substr( $postKeyword, 0, 1 );
|
251 |
+
$content = substr( $postKeyword, 1 );
|
252 |
+
if ( $compression !== "\x00" ) {
|
253 |
+
wfDebug( __METHOD__ . " Unrecognized compression method in zTXt ($keyword). Skipping.\n" );
|
254 |
+
fseek( $fh, self::$crcSize, SEEK_CUR );
|
255 |
+
continue;
|
256 |
+
}
|
257 |
+
|
258 |
+
$errLevel = error_reporting(E_ERROR | E_PARSE);
|
259 |
+
$content = gzuncompress( $content );
|
260 |
+
error_reporting($errLevel);
|
261 |
+
|
262 |
+
if ( $content === false ) {
|
263 |
+
// decompression failed
|
264 |
+
wfDebug( __METHOD__ . ' Error decompressing zTXt chunk - ' . $keyword . "\n" );
|
265 |
+
fseek( $fh, self::$crcSize, SEEK_CUR );
|
266 |
+
continue;
|
267 |
+
}
|
268 |
+
|
269 |
+
$errLevel = error_reporting(E_ERROR | E_PARSE);
|
270 |
+
$content = iconv( 'ISO-8859-1', 'UTF-8', $content );
|
271 |
+
error_reporting($errLevel);
|
272 |
+
|
273 |
+
if ( $content === false ) {
|
274 |
+
throw new Exception( __METHOD__ . ": Read error (error with iconv)" );
|
275 |
+
}
|
276 |
+
|
277 |
+
$finalKeyword = self::$textChunks[$keyword];
|
278 |
+
$text[$finalKeyword]['x-default'] = $content;
|
279 |
+
$text[$finalKeyword]['_type'] = 'lang';
|
280 |
+
} else {
|
281 |
+
wfDebug( __METHOD__ . " Cannot decompress zTXt chunk due to lack of zlib. Skipping.\n" );
|
282 |
+
fseek( $fh, $chunk_size, SEEK_CUR );
|
283 |
+
}
|
284 |
+
} elseif ( $chunk_type == 'tIME' ) {
|
285 |
+
// last mod timestamp.
|
286 |
+
if ( $chunk_size !== 7 ) {
|
287 |
+
throw new Exception( __METHOD__ . ": tIME wrong size" );
|
288 |
+
}
|
289 |
+
$buf = self::read( $fh, $chunk_size );
|
290 |
+
if ( !$buf || strlen( $buf ) < $chunk_size ) {
|
291 |
+
throw new Exception( __METHOD__ . ": Read error" );
|
292 |
+
}
|
293 |
+
|
294 |
+
// Note: spec says this should be UTC.
|
295 |
+
$t = unpack( "ny/Cm/Cd/Ch/Cmin/Cs", $buf );
|
296 |
+
$strTime = sprintf( "%04d%02d%02d%02d%02d%02d",
|
297 |
+
$t['y'], $t['m'], $t['d'], $t['h'],
|
298 |
+
$t['min'], $t['s'] );
|
299 |
+
|
300 |
+
$exifTime = wfTimestamp( TS_EXIF, $strTime );
|
301 |
+
|
302 |
+
if ( $exifTime ) {
|
303 |
+
$text['DateTime'] = $exifTime;
|
304 |
+
}
|
305 |
+
} elseif ( $chunk_type == 'pHYs' ) {
|
306 |
+
// how big pixels are (dots per meter).
|
307 |
+
if ( $chunk_size !== 9 ) {
|
308 |
+
throw new Exception( __METHOD__ . ": pHYs wrong size" );
|
309 |
+
}
|
310 |
+
|
311 |
+
$buf = self::read( $fh, $chunk_size );
|
312 |
+
if ( !$buf || strlen( $buf ) < $chunk_size ) {
|
313 |
+
throw new Exception( __METHOD__ . ": Read error" );
|
314 |
+
}
|
315 |
+
|
316 |
+
$dim = unpack( "Nwidth/Nheight/Cunit", $buf );
|
317 |
+
if ( $dim['unit'] == 1 ) {
|
318 |
+
// Need to check for negative because php
|
319 |
+
// doesn't deal with super-large unsigned 32-bit ints well
|
320 |
+
if ( $dim['width'] > 0 && $dim['height'] > 0 ) {
|
321 |
+
// unit is meters
|
322 |
+
// (as opposed to 0 = undefined )
|
323 |
+
$text['XResolution'] = $dim['width']
|
324 |
+
. '/100';
|
325 |
+
$text['YResolution'] = $dim['height']
|
326 |
+
. '/100';
|
327 |
+
$text['ResolutionUnit'] = 3;
|
328 |
+
// 3 = dots per cm (from Exif).
|
329 |
+
}
|
330 |
+
}
|
331 |
+
} elseif ( $chunk_type == "IEND" ) {
|
332 |
+
break;
|
333 |
+
} else {
|
334 |
+
fseek( $fh, $chunk_size, SEEK_CUR );
|
335 |
+
}
|
336 |
+
fseek( $fh, self::$crcSize, SEEK_CUR );
|
337 |
+
}
|
338 |
+
fclose( $fh );
|
339 |
+
|
340 |
+
if ( $loopCount > 1 ) {
|
341 |
+
$duration *= $loopCount;
|
342 |
+
}
|
343 |
+
|
344 |
+
if ( isset( $text['DateTimeDigitized'] ) ) {
|
345 |
+
// Convert date format from rfc2822 to exif.
|
346 |
+
foreach ( $text['DateTimeDigitized'] as $name => &$value ) {
|
347 |
+
if ( $name === '_type' ) {
|
348 |
+
continue;
|
349 |
+
}
|
350 |
+
|
351 |
+
// @todo FIXME: Currently timezones are ignored.
|
352 |
+
// possibly should be wfTimestamp's
|
353 |
+
// responsibility. (at least for numeric TZ)
|
354 |
+
$formatted = wfTimestamp( TS_EXIF, $value );
|
355 |
+
if ( $formatted ) {
|
356 |
+
// Only change if we could convert the
|
357 |
+
// date.
|
358 |
+
// The png standard says it should be
|
359 |
+
// in rfc2822 format, but not required.
|
360 |
+
// In general for the exif stuff we
|
361 |
+
// prettify the date if we can, but we
|
362 |
+
// display as-is if we cannot or if
|
363 |
+
// it is invalid.
|
364 |
+
// So do the same here.
|
365 |
+
|
366 |
+
$value = $formatted;
|
367 |
+
}
|
368 |
+
}
|
369 |
+
}
|
370 |
+
|
371 |
+
return array(
|
372 |
+
'frameCount' => $frameCount,
|
373 |
+
'loopCount' => $loopCount,
|
374 |
+
'duration' => $duration,
|
375 |
+
'text' => $text,
|
376 |
+
'bitDepth' => $bitDepth,
|
377 |
+
'colorType' => $colorType,
|
378 |
+
);
|
379 |
+
}
|
380 |
+
|
381 |
+
private static function read( $fh, $size ) {
|
382 |
+
if ( $size > self::MAX_CHUNK_SIZE ) {
|
383 |
+
throw new Exception( __METHOD__ . ': Chunk size of ' . $size .
|
384 |
+
' too big. Max size is: ' . self::MAX_CHUNK_SIZE );
|
385 |
+
}
|
386 |
+
|
387 |
+
return fread( $fh, $size );
|
388 |
+
}
|
389 |
+
}
|
vendor/shortpixel/shortpixel-php/lib/ShortPixel/persist/PNGReader.php
ADDED
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace ShortPixel\persist;
|
4 |
+
|
5 |
+
class PNGReader
|
6 |
+
{
|
7 |
+
private $_chunks;
|
8 |
+
private $_fp;
|
9 |
+
|
10 |
+
function __construct($file) {
|
11 |
+
if (!file_exists($file)) {
|
12 |
+
throw new Exception('File does not exist');
|
13 |
+
}
|
14 |
+
|
15 |
+
$this->_chunks = array ();
|
16 |
+
|
17 |
+
// Open the file
|
18 |
+
$this->_fp = fopen($file, 'r');
|
19 |
+
|
20 |
+
if (!$this->_fp)
|
21 |
+
throw new Exception('Unable to open file');
|
22 |
+
|
23 |
+
// Read the magic bytes and verify
|
24 |
+
$header = fread($this->_fp, 8);
|
25 |
+
|
26 |
+
if ($header != "\x89PNG\x0d\x0a\x1a\x0a")
|
27 |
+
throw new Exception('Is not a valid PNG image');
|
28 |
+
|
29 |
+
// Loop through the chunks. Byte 0-3 is length, Byte 4-7 is type
|
30 |
+
$chunkHeader = fread($this->_fp, 8);
|
31 |
+
|
32 |
+
while ($chunkHeader) {
|
33 |
+
// Extract length and type from binary data
|
34 |
+
$chunk = @unpack('Nsize/a4type', $chunkHeader);
|
35 |
+
|
36 |
+
// Store position into internal array
|
37 |
+
if (!isset($this->_chunks[$chunk['type']]) || $this->_chunks[$chunk['type']] === null)
|
38 |
+
$this->_chunks[$chunk['type']] = array ();
|
39 |
+
$this->_chunks[$chunk['type']][] = array (
|
40 |
+
'offset' => ftell($this->_fp),
|
41 |
+
'size' => $chunk['size']
|
42 |
+
);
|
43 |
+
|
44 |
+
// Skip to next chunk (over body and CRC)
|
45 |
+
fseek($this->_fp, $chunk['size'] + 4, SEEK_CUR);
|
46 |
+
|
47 |
+
// Read next chunk header
|
48 |
+
$chunkHeader = fread($this->_fp, 8);
|
49 |
+
}
|
50 |
+
}
|
51 |
+
|
52 |
+
function __destruct() { fclose($this->_fp); }
|
53 |
+
|
54 |
+
// Returns all chunks of said type
|
55 |
+
public function get_chunks($type) {
|
56 |
+
if (!isset($this->_chunks[$type]) || $this->_chunks[$type] === null)
|
57 |
+
return null;
|
58 |
+
|
59 |
+
$chunks = array ();
|
60 |
+
|
61 |
+
foreach ($this->_chunks[$type] as $chunk) {
|
62 |
+
if ($chunk['size'] > 0) {
|
63 |
+
fseek($this->_fp, $chunk['offset'], SEEK_SET);
|
64 |
+
$chunks[] = fread($this->_fp, $chunk['size']);
|
65 |
+
} else {
|
66 |
+
$chunks[] = '';
|
67 |
+
}
|
68 |
+
}
|
69 |
+
|
70 |
+
return $chunks;
|
71 |
+
}
|
72 |
+
|
73 |
+
public function get_sections() {
|
74 |
+
$rawTextData = $this->get_chunks('tEXt');
|
75 |
+
if($rawTextData === null) {
|
76 |
+
//$rawTextData = gzinflate($this->get_chunks('zTXt'));
|
77 |
+
}
|
78 |
+
|
79 |
+
$metadata = array();
|
80 |
+
|
81 |
+
if(!is_array($rawTextData)) return $metadata;
|
82 |
+
|
83 |
+
foreach($rawTextData as $data) {
|
84 |
+
$sections = explode("\0", $data);
|
85 |
+
|
86 |
+
if($sections > 1) {
|
87 |
+
$key = array_shift($sections);
|
88 |
+
$metadata[$key] = implode("\0", $sections);
|
89 |
+
} else {
|
90 |
+
$metadata[] = $data;
|
91 |
+
}
|
92 |
+
}
|
93 |
+
return $metadata;
|
94 |
+
}
|
95 |
+
}
|
vendor/shortpixel/shortpixel-php/lib/ShortPixel/persist/TextPersister.php
ADDED
@@ -0,0 +1,644 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* User: simon
|
4 |
+
* Date: 19.08.2016
|
5 |
+
* Time: 18:05
|
6 |
+
*/
|
7 |
+
namespace ShortPixel\persist;
|
8 |
+
|
9 |
+
use ShortPixel\ClientException;
|
10 |
+
use ShortPixel\Lock;
|
11 |
+
use ShortPixel\notify\ProgressNotifierFileQ;
|
12 |
+
use \ShortPixel\Persister;
|
13 |
+
use ShortPixel\Settings;
|
14 |
+
use \ShortPixel\ShortPixel;
|
15 |
+
use \ShortPixel\Client;
|
16 |
+
use \ShortPixel\SPCache;
|
17 |
+
use ShortPixel\SPLog;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Class TextPersister - save the optimization information in .shortpixel files in the current folder of the images
|
21 |
+
* @package ShortPixel\persist
|
22 |
+
*/
|
23 |
+
class TextPersister implements Persister {
|
24 |
+
|
25 |
+
private $fp;
|
26 |
+
private $options;
|
27 |
+
private $logger;
|
28 |
+
private $cache;
|
29 |
+
private STATIC $ALLOWED_STATUSES = array('pending', 'success', 'skip', 'deleted');
|
30 |
+
private STATIC $ALLOWED_TYPES = array('I', 'D');
|
31 |
+
|
32 |
+
const FLAG_WEBP = 2;//second bit set like in "10"
|
33 |
+
const FLAG_AVIF = 32;//6th bit set like in "100000"
|
34 |
+
|
35 |
+
const LINE_LENGTH = 465; //including the \r\n at the end
|
36 |
+
const LINE_LENGTH_V2 = 610; //including the \r\n at the end - NOT YET USED
|
37 |
+
|
38 |
+
function __construct($options)
|
39 |
+
{
|
40 |
+
$this->options = $options;
|
41 |
+
$this->fp = array();
|
42 |
+
$this->logger = SPLog::Get(SPLog::PRODUCER_PERSISTER);
|
43 |
+
$this->cache = SPCache::Get();
|
44 |
+
}
|
45 |
+
|
46 |
+
public static function IGNORED_BY_DEFAULT() {
|
47 |
+
return array('.','..',ShortPixel::opt('persist_name'),Settings::FOLDER_INI_NAME,Lock::FOLDER_LOCK_FILE,ProgressNotifierFileQ::PROGRESS_FILE_NAME,'ShortPixelBackups');
|
48 |
+
}
|
49 |
+
|
50 |
+
function isOptimized($path)
|
51 |
+
{
|
52 |
+
if(!file_exists($path)) {
|
53 |
+
return false;
|
54 |
+
}
|
55 |
+
$fp = $this->openMetaFile(dirname($path), 'read');
|
56 |
+
if(!$fp) {
|
57 |
+
return false;
|
58 |
+
}
|
59 |
+
|
60 |
+
while (($line = fgets($fp)) !== FALSE) {
|
61 |
+
$data = $this->parse($line);
|
62 |
+
if($data->file === \ShortPixel\MB_basename($path) && $data->status == 'success' ) {
|
63 |
+
return true;
|
64 |
+
}
|
65 |
+
}
|
66 |
+
fclose($fp);
|
67 |
+
|
68 |
+
return false;
|
69 |
+
}
|
70 |
+
|
71 |
+
protected function ignored($exclude) {
|
72 |
+
return array_values(array_merge(self::IGNORED_BY_DEFAULT(), is_array($exclude) ? $exclude : array()));
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* @param $path - the file path on the local drive
|
77 |
+
* @param bool $recurse - boolean - go into subfolders or not
|
78 |
+
* @param bool $fileList - return the list of files with optimization status (only current folder, not subfolders)
|
79 |
+
* @param array $exclude - array of folder names that you want to exclude from the optimization
|
80 |
+
* @param bool $persistPath - the path where to look for the metadata, if different from the $path
|
81 |
+
* @param int $recurseDepth - how many subfolders deep to go. Defaults to PHP_INT_MAX
|
82 |
+
* @param bool $retrySkipped - if true, all skipped files will be reset to pending with retries = 0
|
83 |
+
* @return object|void (object)array('status', 'total', 'succeeded', 'pending', 'same', 'failed')
|
84 |
+
* @throws PersistException
|
85 |
+
*/
|
86 |
+
function info($path, $recurse = true, $fileList = false, $exclude = array(), $persistPath = false, $recurseDepth = PHP_INT_MAX, $retrySkipped = false) {
|
87 |
+
if($persistPath === false) {
|
88 |
+
$persistPath = $path;
|
89 |
+
}
|
90 |
+
$toClose = false; $persistFolder = false;
|
91 |
+
$info = array('status' => 'error', 'message' => "Unknown error, please contact support.", 'code' => -999);
|
92 |
+
|
93 |
+
try {
|
94 |
+
if(is_dir($path)) {
|
95 |
+
try {
|
96 |
+
$persistFolder = $persistPath;
|
97 |
+
$toClose = $this->openMetaFileIfNeeded($persistFolder);
|
98 |
+
$fp = $this->getMetaFile($persistPath);
|
99 |
+
$dataArr = $this->readMetaFile($fp);
|
100 |
+
} catch(ClientException $e) {
|
101 |
+
if(is_dir($persistPath) && file_exists($persistPath . '/' . ShortPixel::opt("persist_name"))) {
|
102 |
+
throw $e; //rethrow, there's a problem with the meta file.
|
103 |
+
}
|
104 |
+
$dataArr = array(); //there's no problem if the metadata file is missing and cannot be created, for the info call
|
105 |
+
}
|
106 |
+
|
107 |
+
$info = (object)array('status' => 'pending', 'total' => 0, 'succeeded' => 0, 'pending' => 0, 'same' => 0, 'failed' => 0, 'totalSize'=> 0, 'totalOptimizedSize'=> 0, 'todo' => null);
|
108 |
+
$files = scandir($path);
|
109 |
+
$ignore = $this->ignored($exclude);
|
110 |
+
|
111 |
+
foreach($files as $file) {
|
112 |
+
$filePath = $path . '/' . $file;
|
113 |
+
$targetFilePath = $persistPath . '/' . $file;
|
114 |
+
if (in_array($file, $ignore)
|
115 |
+
|| (!ShortPixel::isProcessable($file) && !is_dir($filePath))
|
116 |
+
|| isset($dataArr[$file]) && $dataArr[$file]->status == 'deleted'
|
117 |
+
) {
|
118 |
+
continue;
|
119 |
+
}
|
120 |
+
if (is_dir($filePath)) {
|
121 |
+
if(!$recurse || $recurseDepth <= 0) continue;
|
122 |
+
$subInfo = $this->info($filePath, $recurse, $fileList, $exclude, $targetFilePath, $recurseDepth - 1);
|
123 |
+
if($subInfo->status == 'error') {
|
124 |
+
$info = $subInfo;
|
125 |
+
break;
|
126 |
+
}
|
127 |
+
$info->total += $subInfo->total;
|
128 |
+
$info->succeeded += $subInfo->succeeded;
|
129 |
+
$info->pending += $subInfo->pending;
|
130 |
+
$info->same += $subInfo->same;
|
131 |
+
$info->failed += $subInfo->failed;
|
132 |
+
$info->totalSize += $subInfo->totalSize;
|
133 |
+
$info->totalOptimizedSize += $subInfo->totalOptimizedSize;
|
134 |
+
}
|
135 |
+
else {
|
136 |
+
$info->total++;
|
137 |
+
if(!isset($dataArr[$file]) || $dataArr[$file]->status == 'pending') {
|
138 |
+
$info->pending++;
|
139 |
+
$this->logger->log(SPLog::PRODUCER_PERSISTER, "TextPersister->info - PENDING STATUS: $path/$file");
|
140 |
+
}
|
141 |
+
elseif( $dataArr[$file]->status == 'success' && $this->isChanged($dataArr[$file], $file, $persistPath, $path)
|
142 |
+
// || ($dataArr[$file]->status == 'skip' && ($dataArr[$file]->retries <= ShortPixel::MAX_RETRIES || $retrySkipped))) {
|
143 |
+
|| ($dataArr[$file]->status == 'skip' && $retrySkipped)) {
|
144 |
+
//file changed since last optimized, mark it as pending
|
145 |
+
$dataArr[$file]->status = 'pending';
|
146 |
+
if($dataArr[$file]->status == 'skip' && $retrySkipped) {
|
147 |
+
$dataArr[$file]->retries = 0;
|
148 |
+
} else {
|
149 |
+
$this->logger->log(SPLog::PRODUCER_PERSISTER, "TextPersister->info - CHANGED - REVERT TO PENDING: $path/$file");
|
150 |
+
}
|
151 |
+
$this->updateMeta($dataArr[$file], $fp);
|
152 |
+
$info->pending++;
|
153 |
+
}
|
154 |
+
elseif($dataArr[$file]->status == 'success') {
|
155 |
+
if($dataArr[$file]->percent > 0) {
|
156 |
+
$info->succeeded++;
|
157 |
+
$info->totalOptimizedSize += $dataArr[$file]->optimizedSize;
|
158 |
+
$info->totalSize += round(100.0 * $dataArr[$file]->optimizedSize / (100.0 - $dataArr[$file]->percent));
|
159 |
+
} else {
|
160 |
+
$info->same++;
|
161 |
+
}
|
162 |
+
}
|
163 |
+
elseif($dataArr[$file]->status == 'skip'){
|
164 |
+
$info->failed++;
|
165 |
+
}
|
166 |
+
}
|
167 |
+
if($fileList) $info->fileList = $dataArr;
|
168 |
+
}
|
169 |
+
|
170 |
+
if(isset($info->pending) && $info->pending == 0 && $info->status !== 'error') {
|
171 |
+
$info->status = 'success';
|
172 |
+
}
|
173 |
+
if($info->status !== 'error') {
|
174 |
+
$info->todo = $this->getTodoInternal($files, $dataArr, $fp, $path, 1, $exclude, $persistPath, ShortPixel::CLIENT_MAX_BODY_SIZE, $recurseDepth);
|
175 |
+
}
|
176 |
+
}
|
177 |
+
else {
|
178 |
+
if(!file_exists($persistPath)) {
|
179 |
+
throw new ClientException("File not found: $persistPath", -15);
|
180 |
+
}
|
181 |
+
$persistFolder = dirname($persistPath);
|
182 |
+
$meta = $toClose = false;
|
183 |
+
try {
|
184 |
+
$toClose = $this->openMetaFileIfNeeded($persistFolder);
|
185 |
+
$meta = $this->findMeta($persistPath);
|
186 |
+
} catch(ClientException $e) {
|
187 |
+
if(is_dir($persistFolder) && file_exists($persistFolder . '/' . ShortPixel::opt("persist_name"))) {
|
188 |
+
throw $e;
|
189 |
+
}
|
190 |
+
}
|
191 |
+
|
192 |
+
if(!$meta) {
|
193 |
+
$info = (object)array('status' => 'pending');
|
194 |
+
} else {
|
195 |
+
$info = (object)array('status' => $meta->getStatus());
|
196 |
+
}
|
197 |
+
|
198 |
+
}
|
199 |
+
}
|
200 |
+
catch(ClientException $e) {
|
201 |
+
$info = (object)array('status' => 'error', 'message' => $e->getMessage(), 'code' => $e->getCode());
|
202 |
+
}
|
203 |
+
catch(\Exception $e) { //that should've been a finally but we need to be PHP5.4 compatible...
|
204 |
+
if($toClose) {
|
205 |
+
$this->closeMetaFile($persistFolder);
|
206 |
+
}
|
207 |
+
throw $e;
|
208 |
+
}
|
209 |
+
if($toClose) {
|
210 |
+
$this->closeMetaFile($persistFolder);
|
211 |
+
}
|
212 |
+
return $info;
|
213 |
+
}
|
214 |
+
|
215 |
+
function getTodo($path, $count, $exclude = array(), $persistPath = false, $maxTotalFileSizeMb = ShortPixel::CLIENT_MAX_BODY_SIZE, $recurseDepth = PHP_INT_MAX)
|
216 |
+
{
|
217 |
+
if(!file_exists($path) || !is_dir($path)) {
|
218 |
+
$this->logger->log(SPLog::PRODUCER_PERSISTER, "TextPersister->getTodo - file not found or not a directory: $path");
|
219 |
+
return array();
|
220 |
+
}
|
221 |
+
if(!$persistPath) {$persistPath = $path;}
|
222 |
+
|
223 |
+
$toClose = $this->openMetaFileIfNeeded($persistPath);
|
224 |
+
$fp = $this->getMetaFile($persistPath);
|
225 |
+
|
226 |
+
$files = scandir($path);
|
227 |
+
$dataArr = $this->readMetaFile($fp);
|
228 |
+
|
229 |
+
$ret = $this->getTodoInternal($files, $dataArr, $fp, $path, $count, $exclude, $persistPath, $maxTotalFileSizeMb, $recurseDepth);
|
230 |
+
|
231 |
+
if($toClose) { $this->closeMetaFile($persistPath); }
|
232 |
+
|
233 |
+
if(count($ret->files) + count($ret->filesPending) + $ret->filesWaiting == 0) {
|
234 |
+
$this->logger->logFirst($path, SPLog::PRODUCER_PERSISTER, "TextPersister->getTodo - FOR $path RETURN NONE");
|
235 |
+
} else {
|
236 |
+
$this->logger->clearLogged(SPLog::PRODUCER_PERSISTER, $path);
|
237 |
+
$this->logger->log(SPLog::PRODUCER_PERSISTER, "TextPersister->getTodo - FOR $path RETURN", $ret);
|
238 |
+
}
|
239 |
+
return $ret;
|
240 |
+
}
|
241 |
+
|
242 |
+
|
243 |
+
protected function getTodoInternal(&$files, &$dataArr, $fp, $path, $count, $exclude, $persistPath, $maxTotalFileSizeMb, $recurseDepth)
|
244 |
+
{
|
245 |
+
$results = array();
|
246 |
+
$pendingURLs = array();
|
247 |
+
$ignore = $this->ignored($exclude);
|
248 |
+
$remain = $count;
|
249 |
+
$maxTotalFileSize = $maxTotalFileSizeMb * pow(1024, 2);
|
250 |
+
$totalFileSize = 0;
|
251 |
+
$filesWaiting = 0;
|
252 |
+
foreach($files as $file) {
|
253 |
+
$filePath = $path . '/' . $file;
|
254 |
+
$targetPath = $persistPath . '/' . $file;
|
255 |
+
if(in_array($file, $ignore)) {
|
256 |
+
continue; //and do not log
|
257 |
+
}
|
258 |
+
if(!file_exists($filePath)) {
|
259 |
+
continue; // strange but found this for a client..., on windows: HS ID 711715228
|
260 |
+
}
|
261 |
+
if( (!ShortPixel::isProcessable($file) && !is_dir($filePath))
|
262 |
+
|| isset($dataArr[$file]) && $dataArr[$file]->status == 'deleted'
|
263 |
+
|| isset($dataArr[$file])
|
264 |
+
&& ( $dataArr[$file]->status == 'success' && !$this->isChanged($dataArr[$file], $file, $persistPath, $path)
|
265 |
+
|| $dataArr[$file]->status == 'skip') ) {
|
266 |
+
if(!isset($dataArr[$file]) || $dataArr[$file]->status !== 'success')
|
267 |
+
$this->logger->logFirst($filePath, SPLog::PRODUCER_PERSISTER, "TextPersister->getTodo - SKIPPING $path/$file - status " . (isset($dataArr[$file]) ? $dataArr[$file]->status : "not processable"));
|
268 |
+
continue;
|
269 |
+
}
|
270 |
+
//if retried too many times recently {
|
271 |
+
if(isset($dataArr[$file]) && $dataArr[$file]->status == 'pending') {
|
272 |
+
$retries = $dataArr[$file]->retries;
|
273 |
+
//over 3 retries wait a minute for each, over 5 retries 2 min. for each, over 10 retries 5 min for each, over 10 retries, 10 min. for each.
|
274 |
+
$delta = max(0, $retries - 2) * 60 + max(0, $retries - 5) * 60 + max(0, $retries - 10) * 180 + max(0, $retries - 20) * 450;
|
275 |
+
if($dataArr[$file]->changeDate > time() - $delta) {
|
276 |
+
$filesWaiting++;
|
277 |
+
$this->logger->logFirst($filePath, SPLog::PRODUCER_PERSISTER, "TextPersister->getTodo - TOO MANY RETRIES for $file");
|
278 |
+
continue;
|
279 |
+
}
|
280 |
+
}
|
281 |
+
if(is_dir($filePath)) {
|
282 |
+
if($recurseDepth <= 0) continue;
|
283 |
+
if(!isset($dataArr[$file])) {
|
284 |
+
$dataArr[$file] = $this->newMeta($filePath);
|
285 |
+
$dataArr[$file]->filePos = $this->appendMeta($dataArr[$file], $fp);
|
286 |
+
}
|
287 |
+
$resultsSubfolder = $this->cache->fetch($filePath);
|
288 |
+
if(!$resultsSubfolder) {
|
289 |
+
$resultsSubfolder = $this->getTodo($filePath, $count, $exclude, $targetPath, $maxTotalFileSizeMb, $recurseDepth - 1);
|
290 |
+
if(!count($resultsSubfolder->files)) {
|
291 |
+
//cache the folders with nothing to do.
|
292 |
+
$this->logger->log(SPLog::PRODUCER_PERSISTER, "TextPersister->getTodo - Nothing to do for: $filePath. Caching.");
|
293 |
+
$this->cache->store($filePath, $resultsSubfolder);
|
294 |
+
}
|
295 |
+
} else {
|
296 |
+
$this->logger->log(SPLog::PRODUCER_PERSISTER, "TextPersister->getTodo - Cache says nothing to do for: $filePath");
|
297 |
+
}
|
298 |
+
if(count($resultsSubfolder->files)) {
|
299 |
+
return $resultsSubfolder;
|
300 |
+
} elseif($dataArr[$file]->status != 'success' && !$resultsSubfolder->filesWaiting) {//otherwise ignore the folder but mark it as succeeded;
|
301 |
+
$dataArr[$file]->status = 'success';
|
302 |
+
$this->updateMeta($dataArr[$file], $fp);
|
303 |
+
}
|
304 |
+
} else {
|
305 |
+
$toUpdate = false; //will defer updating the record only if we finally add the image (if the image is too large for this set will not add it in the end
|
306 |
+
clearstatcache(true, $targetPath);
|
307 |
+
if(isset($dataArr[$file])) {
|
308 |
+
if( ($dataArr[$file]->status == 'success')
|
309 |
+
&& (filesize($targetPath) !== $dataArr[$file]->optimizedSize)) {
|
310 |
+
// a file with the wrong size
|
311 |
+
$dataArr[$file]->status = 'pending';
|
312 |
+
$dataArr[$file]->optimizedSize = 0;
|
313 |
+
$dataArr[$file]->changeDate = time();
|
314 |
+
$toUpdate = true;
|
315 |
+
if(time() - strtotime($dataArr[$file]->changeDate) < 1800) { //need to refresh the file processing on the server
|
316 |
+
$this->updateMeta($dataArr[$file], $fp);
|
317 |
+
return (object)array('files' => array($filePath), 'filesPending' => array(), 'filesWaiting' => 0, 'refresh' => true);
|
318 |
+
}
|
319 |
+
}
|
320 |
+
elseif($dataArr[$file]->status == 'error') {
|
321 |
+
if($dataArr[$file]->retries >= ShortPixel::MAX_RETRIES) {
|
322 |
+
$dataArr[$file]->status = 'skip';
|
323 |
+
$this->updateMeta($dataArr[$file], $fp);
|
324 |
+
continue;
|
325 |
+
} else {
|
326 |
+
$dataArr[$file]->retries += 1;
|
327 |
+
$toUpdate = true;
|
328 |
+
}
|
329 |
+
}
|
330 |
+
|
331 |
+
elseif($dataArr[$file]->status == 'pending' && preg_match("/http[s]{0,1}:\/\/" . Client::API_DOMAIN() . "/", $dataArr[$file]->message)) {
|
332 |
+
//elseif($dataArr[$file]->status == 'pending' && strpos($dataArr[$file]->message, str_replace("https://", "http://",\ShortPixel\Client::API_URL())) === 0) {
|
333 |
+
//the file is already uploaded and the call should be made with the existent URL on the optimization server
|
334 |
+
$apiURL = $dataArr[$file]->message;
|
335 |
+
$pendingURLs[$apiURL] = $filePath;
|
336 |
+
}
|
337 |
+
}
|
338 |
+
elseif(!isset($dataArr[$file])) {
|
339 |
+
$dataArr[$file] = $this->newMeta($filePath);
|
340 |
+
$dataArr[$file]->filePos = $this->appendMeta($dataArr[$file], $fp);
|
341 |
+
}
|
342 |
+
|
343 |
+
clearstatcache(true, $filePath);
|
344 |
+
if(filesize($filePath) + $totalFileSize > $maxTotalFileSize){
|
345 |
+
if(filesize($filePath) > $maxTotalFileSize) { //skip this as it won't ever be selected with current settings
|
346 |
+
$dataArr[$file]->status = 'skip';
|
347 |
+
if(filesize($filePath) > ShortPixel::CLIENT_MAX_BODY_SIZE * pow(1024, 2)) {
|
348 |
+
$dataArr[$file]->retries = 99;
|
349 |
+
}
|
350 |
+
$dataArr[$file]->message = 'File larger than the set limit of ' . $maxTotalFileSizeMb . 'MBytes';
|
351 |
+
$this->updateMeta($dataArr[$file], $fp); //this one is too big, we skipped it, just continue with next.
|
352 |
+
}
|
353 |
+
continue; //the total file size would exceed the limit so leave this image out for now. If it's not too large by itself, will take it in the next pass.
|
354 |
+
}
|
355 |
+
if($toUpdate) {
|
356 |
+
$this->updateMeta($dataArr[$file], $fp);
|
357 |
+
}
|
358 |
+
$results[] = $filePath;
|
359 |
+
$totalFileSize += filesize($filePath);
|
360 |
+
$remain--;
|
361 |
+
|
362 |
+
if($remain <= 0) {
|
363 |
+
break;
|
364 |
+
}
|
365 |
+
}
|
366 |
+
}
|
367 |
+
|
368 |
+
return (object)array('files' => $results, 'filesPending' => $pendingURLs, 'filesWaiting' => $filesWaiting, 'refresh' => false);
|
369 |
+
}
|
370 |
+
/**
|
371 |
+
* @param $data - the .shortpixel metadata
|
372 |
+
* @param $file - the file basename
|
373 |
+
* @param $persistPath - the target path for the optimized files and for the .shortpixel metadata
|
374 |
+
* @param $sourcePath - the path of the original images
|
375 |
+
* @return bool true if the image is optimized but needs to be reoptimized because it changed
|
376 |
+
*/
|
377 |
+
protected function isChanged($data, $file, $persistPath, $sourcePath ) {
|
378 |
+
clearstatcache(true, $sourcePath);
|
379 |
+
return $persistPath === $sourcePath && filesize($sourcePath . '/' . $file) != $data->optimizedSize
|
380 |
+
|| $persistPath !== $sourcePath && $data->originalSize > 0 && filesize($sourcePath . '/' . $file) != $data->originalSize;
|
381 |
+
}
|
382 |
+
|
383 |
+
function getNextTodo($path, $count)
|
384 |
+
{
|
385 |
+
// TODO: Implement getNextTodo() method.
|
386 |
+
}
|
387 |
+
|
388 |
+
function doneGet()
|
389 |
+
{
|
390 |
+
// TODO: Implement doneGet() method.
|
391 |
+
}
|
392 |
+
|
393 |
+
function getOptimizationData($path)
|
394 |
+
{
|
395 |
+
// TODO: Implement getOptimizationData() method.
|
396 |
+
}
|
397 |
+
|
398 |
+
function setPending($path, $optData) {
|
399 |
+
return $this->setStatus($path, $optData, 'pending');
|
400 |
+
}
|
401 |
+
|
402 |
+
function setOptimized($path, $optData = array()) {
|
403 |
+
return $this->setStatus($path, $optData, 'success');
|
404 |
+
}
|
405 |
+
|
406 |
+
function setFailed($path, $optData) {
|
407 |
+
return $this->setStatus($path, $optData, 'error');
|
408 |
+
}
|
409 |
+
|
410 |
+
function setSkipped($path, $optData) {
|
411 |
+
return $this->setStatus($path, $optData, 'skip');
|
412 |
+
}
|
413 |
+
|
414 |
+
protected function setStatus($path, $optData, $status) {
|
415 |
+
$toClose = $this->openMetaFileIfNeeded(dirname($path));
|
416 |
+
$fp = $this->getMetaFile(dirname($path));
|
417 |
+
|
418 |
+
$meta = $this->findMeta($path);
|
419 |
+
if($meta) {
|
420 |
+
$meta->retries++;
|
421 |
+
$meta->changeDate = time();
|
422 |
+
} else {
|
423 |
+
$meta = $this->newMeta($path);
|
424 |
+
}
|
425 |
+
$meta->status = $status == 'error' ? $meta->retries > ShortPixel::MAX_RETRIES ? 'skip' : 'pending' : $status;
|
426 |
+
$metaArr = array_merge((array)$meta, $optData);
|
427 |
+
if(isset($meta->filePos)) {
|
428 |
+
$this->updateMeta((object)$metaArr, $fp, false);
|
429 |
+
} else {
|
430 |
+
$this->appendMeta((object)$metaArr, $fp, false);
|
431 |
+
}
|
432 |
+
|
433 |
+
if($toClose) {
|
434 |
+
$this->closeMetaFile(dirname($path));
|
435 |
+
}
|
436 |
+
return $meta->status;
|
437 |
+
}
|
438 |
+
|
439 |
+
protected function openMetaFileIfNeeded($path) {
|
440 |
+
if(isset($this->fp[$path])) {
|
441 |
+
fseek($this->fp[$path], 0);
|
442 |
+
return false;
|
443 |
+
}
|
444 |
+
$fp = $this->openMetaFile($path);
|
445 |
+
if(!$fp) {
|
446 |
+
throw new \Exception("Could not open meta file in folder " . $path . ". Please check permissions.", -14);
|
447 |
+
}
|
448 |
+
$this->fp[$path] = $fp;
|
449 |
+
return true;
|
450 |
+
}
|
451 |
+
|
452 |
+
protected function getMetaFile($path) {
|
453 |
+
return $this->fp[$path];
|
454 |
+
}
|
455 |
+
|
456 |
+
protected function closeMetaFile($path) {
|
457 |
+
if(isset($this->fp[$path])) {
|
458 |
+
$fp = $this->fp[$path];
|
459 |
+
unset($this->fp[$path]);
|
460 |
+
fclose($fp);
|
461 |
+
}
|
462 |
+
}
|
463 |
+
|
464 |
+
protected function readMetaFile($fp) {
|
465 |
+
$dataArr = array(); $err = false;
|
466 |
+
for ($i = 0; ($line = fgets($fp)) !== FALSE; $i++) {
|
467 |
+
$data = $this->parse($line);
|
468 |
+
if($data) {
|
469 |
+
$data->filePos = $i;
|
470 |
+
if(isset($dataArr[$data->file])) {
|
471 |
+
$err = true; //found situations where a line was duplicated, will rewrite but take only the first
|
472 |
+
} else {
|
473 |
+
$dataArr[$data->file] = $data;
|
474 |
+
}
|
475 |
+
} else {
|
476 |
+
$err = true;
|
477 |
+
}
|
478 |
+
}
|
479 |
+
if($err) { //at least one error found in the .shortpixel file, rewrite it
|
480 |
+
fseek($fp, 0);
|
481 |
+
ftruncate($fp, 0);
|
482 |
+
foreach($dataArr as $meta) {
|
483 |
+
fwrite($fp, $this->assemble($meta));
|
484 |
+
fwrite($fp, $line . "\r\n");
|
485 |
+
}
|
486 |
+
}
|
487 |
+
return $dataArr;
|
488 |
+
}
|
489 |
+
|
490 |
+
protected function openMetaFile($path, $type = 'update') {
|
491 |
+
$metaFile = $path . '/' . ShortPixel::opt("persist_name");
|
492 |
+
if(!is_dir($path) && !@mkdir($path, 0777, true)) { //create the folder
|
493 |
+
throw new ClientException("The metadata destination path cannot be found. Please check rights", -17);
|
494 |
+
}
|
495 |
+
$fp = @fopen($metaFile, $type == 'update' ? 'c+' : 'r');
|
496 |
+
if(!$fp) {
|
497 |
+
if(is_dir($metaFile)) { //saw this for a client
|
498 |
+
throw new ClientException("Could not open persistence file $metaFile. There's already a directory with this name.", -16);
|
499 |
+
} else {
|
500 |
+
throw new ClientException("Could not open persistence file $metaFile. Please check rights.", -16);
|
501 |
+
}
|
502 |
+
}
|
503 |
+
return $fp;
|
504 |
+
}
|
505 |
+
|
506 |
+
protected function findMeta($path) {
|
507 |
+
$fp = $this->openMetaFile(dirname($path));
|
508 |
+
fseek($fp, 0);
|
509 |
+
for ($i = 0; ($line = fgets($fp)) !== FALSE; $i++) {
|
510 |
+
$data = $this->parse($line);
|
511 |
+
if(!property_exists($data, 'file')) {
|
512 |
+
die(var_dump($line));
|
513 |
+
}
|
514 |
+
if($data->file === \ShortPixel\MB_basename($path)) {
|
515 |
+
$data->filePos = $i;
|
516 |
+
return $data;
|
517 |
+
}
|
518 |
+
}
|
519 |
+
return false;
|
520 |
+
}
|
521 |
+
|
522 |
+
/**
|
523 |
+
* @param $meta
|
524 |
+
* @param bool|false $returnPointer - set this to true if need to have the file pointer back afterwards, such as when updating while reading the file line by line
|
525 |
+
*/
|
526 |
+
protected function updateMeta($meta, $fp, $returnPointer = false) {
|
527 |
+
if($returnPointer) {
|
528 |
+
$crt = ftell($fp);
|
529 |
+
}
|
530 |
+
fseek($fp, self::LINE_LENGTH * $meta->filePos); // +2 for the \r\n
|
531 |
+
fwrite($fp, $this->assemble($meta));
|
532 |
+
fflush($fp);
|
533 |
+
if($returnPointer) {
|
534 |
+
fseek($fp, $crt);
|
535 |
+
}
|
536 |
+
}
|
537 |
+
|
538 |
+
/**
|
539 |
+
* @param $meta
|
540 |
+
* @param bool|false $returnPointer - set this to true if need to have the file pointer back afterwards, such as when updating while reading the file line by line
|
541 |
+
*/
|
542 |
+
protected function appendMeta($meta, $fp, $returnPointer = false) {
|
543 |
+
if($returnPointer) {
|
544 |
+
$crt = ftell($fp);
|
545 |
+
}
|
546 |
+
$fstat = fstat($fp);
|
547 |
+
fseek($fp, 0, SEEK_END);
|
548 |
+
$line = $this->assemble($meta);
|
549 |
+
//$ob = $this->parse($line);
|
550 |
+
fwrite($fp, $line . "\r\n");
|
551 |
+
fflush($fp);
|
552 |
+
if($returnPointer) {
|
553 |
+
fseek($fp, $crt);
|
554 |
+
}
|
555 |
+
return $fstat['size'] / self::LINE_LENGTH;
|
556 |
+
}
|
557 |
+
|
558 |
+
protected function newMeta($file) {
|
559 |
+
//$this->logger->log(SPLog::PRODUCER_PERSISTER, "newMeta: file $file exists? " . (file_exists($file) ? "Yes" : "No"));
|
560 |
+
return (object) array(
|
561 |
+
"type" => is_dir($file) ? 'D' : 'I',
|
562 |
+
"status" => 'pending',
|
563 |
+
"retries" => 0,
|
564 |
+
"compressionType" => $this->options['lossy'] == 1 ? 'lossy' : ($this->options['lossy'] == 2 ? 'glossy' : 'lossless'),
|
565 |
+
"keepExif" => $this->options['keep_exif'],
|
566 |
+
"cmyk2rgb" => $this->options['cmyk2rgb'],
|
567 |
+
"resize" => $this->options['resize_width'] ? 1 : 0,
|
568 |
+
"resizeWidth" => 0 + $this->options['resize_width'],
|
569 |
+
"resizeHeight" => 0 + $this->options['resize_height'],
|
570 |
+
"convertto" => $this->options['convertto'],
|
571 |
+
"percent" => null,
|
572 |
+
"optimizedSize" => null,
|
573 |
+
"changeDate" => time(),
|
574 |
+
"file" => \ShortPixel\MB_basename($file),
|
575 |
+
"message" => '',
|
576 |
+
//file does not exist if source is a WebFolder and the optimized images are saved to a different target
|
577 |
+
"originalSize" => is_dir($file) || !file_exists($file) ? 0 : filesize($file));
|
578 |
+
}
|
579 |
+
|
580 |
+
protected function parse($line) {
|
581 |
+
if(strlen(rtrim($line, "\r\n")) != (self::LINE_LENGTH - 2)) return false;
|
582 |
+
$percent = trim(substr($line, 52, 6));
|
583 |
+
$optimizedSize = trim(substr($line, 58, 9));
|
584 |
+
$originalSize = trim(substr($line, 454, 9));
|
585 |
+
|
586 |
+
$convertto = trim(substr($line, 42, 10));
|
587 |
+
if(is_numeric($convertto)) {
|
588 |
+
//convert to string representation
|
589 |
+
$conv = [];
|
590 |
+
if($convertto | self::FLAG_WEBP) $conv[] = '+webp';
|
591 |
+
if($convertto | self::FLAG_AVIF) $conv[] = '+avif';
|
592 |
+
$convertto = implode('|', $conv);
|
593 |
+
$this->logger->log(SPLog::PRODUCER_PERSISTER, "Convertto $convertto");
|
594 |
+
}
|
595 |
+
|
596 |
+
$ret = (object) array(
|
597 |
+
"type" => trim(substr($line, 0, 2)),
|
598 |
+
"status" => trim(substr($line, 2, 11)),
|
599 |
+
"retries" => trim(substr($line, 13, 2)),
|
600 |
+
"compressionType" => trim(substr($line, 15, 9)),
|
601 |
+
"keepExif" => trim(substr($line, 24, 2)),
|
602 |
+
"cmyk2rgb" => trim(substr($line, 26, 2)),
|
603 |
+
"resize" => trim(substr($line, 28, 2)),
|
604 |
+
"resizeWidth" => trim(substr($line, 30, 6)),
|
605 |
+
"resizeHeight" => trim(substr($line, 36, 6)),
|
606 |
+
"convertto" => $convertto,
|
607 |
+
"percent" => is_numeric($percent) ? floatval($percent) : 0.0,
|
608 |
+
"optimizedSize" => is_numeric($optimizedSize) ? intval($optimizedSize) : 0,
|
609 |
+
"changeDate" => strtotime(trim(substr($line, 67, 20))),
|
610 |
+
"file" => rtrim(substr($line, 87, 256)), //rtrim because there could be file names starting with a blank!! (had that)
|
611 |
+
"message" => trim(substr($line, 343, 111)),
|
612 |
+
"originalSize" => is_numeric($originalSize) ? intval($originalSize) : 0,
|
613 |
+
);
|
614 |
+
if(!in_array($ret->status, self::$ALLOWED_STATUSES) || !$ret->changeDate) {
|
615 |
+
return false;
|
616 |
+
}
|
617 |
+
return $ret;
|
618 |
+
}
|
619 |
+
|
620 |
+
protected function assemble($data) {
|
621 |
+
$convertto = 1;
|
622 |
+
if(strpos($data->convertto, '+webp') !== false) $convertto |= self::FLAG_WEBP;
|
623 |
+
if(strpos($data->convertto, '+avif') !== false) $convertto |= self::FLAG_AVIF;
|
624 |
+
|
625 |
+
return sprintf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
|
626 |
+
str_pad($data->type, 2),
|
627 |
+
str_pad($data->status, 11),
|
628 |
+
str_pad($data->retries % 100, 2), // for folders, retries can be > 100 so do a sanity check here - we're not actually interested in folder retries
|
629 |
+
str_pad($data->compressionType, 9),
|
630 |
+
str_pad($data->keepExif, 2),
|
631 |
+
str_pad($data->cmyk2rgb, 2),
|
632 |
+
str_pad($data->resize, 2),
|
633 |
+
str_pad(substr($data->resizeWidth, 0 , 5), 6),
|
634 |
+
str_pad(substr($data->resizeHeight, 0 , 5), 6),
|
635 |
+
str_pad($convertto, 10),
|
636 |
+
str_pad(substr(number_format($data->percent, 2, ".",""),0 , 5), 6),
|
637 |
+
str_pad(substr(number_format($data->optimizedSize, 0, ".", ""),0 , 8), 9),
|
638 |
+
str_pad(date("Y-m-d H:i:s", $data->changeDate), 20),
|
639 |
+
str_pad(substr($data->file, 0, 255), 256),
|
640 |
+
str_pad(substr($data->message, 0, 110), 111),
|
641 |
+
str_pad(substr(number_format($data->originalSize, 0, ".", ""),0 , 8), 9)
|
642 |
+
);
|
643 |
+
}
|
644 |
+
}
|
vendor/shortpixel/shortpixel-php/lib/cmdShortpixelOptimize.php
ADDED
@@ -0,0 +1,327 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Created by: simon
|
4 |
+
* Date: 15.11.2016
|
5 |
+
* Time: 14:59
|
6 |
+
* Usage: cmdShortpixelOptimize.php --apiKey=<your-api-key-here> --folder=/full/path/to/your/images
|
7 |
+
* - add --compression=x : 1 for lossy, 2 for glossy and 0 for lossless
|
8 |
+
* - add --resize=800x600/[type] where type can be 1 for outer resize (default) and 3 for inner resize
|
9 |
+
* - add --backupBase=/full/path/to/your/backup/basedir
|
10 |
+
* - add --targetFolder to specify a different destination for the optimized files.
|
11 |
+
* - add --webPath=http://yoursites.address/img/folder/ to map the folder to a web URL and have our servers download the images instead of posting them (less heavy on memory for large files)
|
12 |
+
* - add --keepExif to keep the EXIF data
|
13 |
+
* - add --speeed=x x between 1 and 10 - default is 10 but if you have large images it will eat up a lot of memory when creating the post messages so sometimes you might need to lower it. Not needed when using the webPath mapping.
|
14 |
+
* - add --verbose parameter for more info during optimization
|
15 |
+
* - add --clearLock to clear a lock that's already placed on the folder. BE SURE you know what you're doing, files might get corrupted if the previous script is still running. The locks expire in 6 min. anyway.
|
16 |
+
* - add --logLevel for different areas of logging - bitwise flags: 4 for metadata handling, 8 for server comm (add them up to log more areas)
|
17 |
+
* - add --cacheTime=[seconds] to cache the folders which have no new image to process. Useful for large folders for which checking at each pass is slowing down the optimization.
|
18 |
+
* - add --quiet for no output - TBD
|
19 |
+
* - the backup path will be used as parent directory to the backup folder which, if the backup path is outside the optimized folder, will be the basename of the folder, otherwise will be ShortPixelBackup
|
20 |
+
* The script will read the .sp-options configuration file and will honour the parameters set there, but the command line parameters take priority
|
21 |
+
*/
|
22 |
+
|
23 |
+
ini_set('memory_limit','256M');
|
24 |
+
//error_reporting(E_ALL);
|
25 |
+
//ini_set('display_errors', 1);
|
26 |
+
|
27 |
+
require_once("shortpixel-php-req.php");
|
28 |
+
|
29 |
+
use \ShortPixel\SPLog;
|
30 |
+
|
31 |
+
$processId = uniqid("CLI");
|
32 |
+
|
33 |
+
$options = getopt("", array("apiKey::", "folder::", "targetFolder::", "webPath::", "compression::", "resize::", "createWebP", "createAVIF", "keepExif", "speed::", "backupBase::", "verbose", "clearLock", "retrySkipped",
|
34 |
+
"exclude::", "recurseDepth::", "logLevel::", "cacheTime::"));
|
35 |
+
|
36 |
+
$verbose = isset($options["verbose"]) ? (isset($options["logLevel"]) ? $options["logLevel"] : 0) | SPLog::PRODUCER_CMD_VERBOSE : 0;
|
37 |
+
$logger = SPLog::Init($processId, $verbose | SPLog::PRODUCER_CMD, SPLog::TARGET_CONSOLE, false, ($verbose ? SPLog::FLAG_MEMORY : SPLog::FLAG_NONE));
|
38 |
+
$logger->log(SPLog::PRODUCER_CMD_VERBOSE, "ShortPixel CLI version " . \ShortPixel\ShortPixel::VERSION);
|
39 |
+
|
40 |
+
$logger->log(SPLog::PRODUCER_CMD_VERBOSE, "ShortPixel Logging VERBOSE" . ($verbose & SPLog::PRODUCER_PERSISTER ? ", PERSISTER" : "") . ($verbose & SPLog::PRODUCER_CLIENT ? ", CLIENT" : ""));
|
41 |
+
|
42 |
+
$apiKey = isset($options["apiKey"]) ? $options["apiKey"] : false;
|
43 |
+
$folder = isset($options["folder"]) ? verifyFolder($options["folder"]) : false;
|
44 |
+
$targetFolder = isset($options["targetFolder"]) ? verifyFolder($options["targetFolder"], true) : $folder;
|
45 |
+
$webPath = isset($options["webPath"]) ? filter_var($options["webPath"], FILTER_VALIDATE_URL) : false;
|
46 |
+
$compression = isset($options["compression"]) ? intval($options["compression"]) : false;
|
47 |
+
$resizeRaw = isset($options["resize"]) ? $options["resize"] : false;
|
48 |
+
$createWebP = isset($options["createWebP"]);
|
49 |
+
$createAVIF = isset($options["createAVIF"]);
|
50 |
+
$keepExif = isset($options["keepExif"]);
|
51 |
+
$speed = isset($options["speed"]) ? intval($options["speed"]) : false;
|
52 |
+
$bkBase = isset($options["backupBase"]) ? verifyFolder($options["backupBase"]) : false;
|
53 |
+
$clearLock = isset($options["clearLock"]);
|
54 |
+
$retrySkipped = isset($options["retrySkipped"]);
|
55 |
+
$exclude = isset($options["exclude"]) ? explode(",", $options["exclude"]) : array();
|
56 |
+
$recurseDepth = isset($options["recurseDepth"]) && is_numeric($options["recurseDepth"]) && $options["recurseDepth"] >= 0 ? $options["recurseDepth"] : PHP_INT_MAX;
|
57 |
+
$cacheTime = isset($options["cacheTime"]) && is_numeric($options["cacheTime"]) && $options["cacheTime"] >= 0 ? $options["cacheTime"] : 0;
|
58 |
+
|
59 |
+
if(!function_exists('curl_version')) {
|
60 |
+
$logger->bye(SPLog::PRODUCER_CMD, "cURL is not enabled. ShortPixel needs Curl to send the images to optimization and retrieve the results. Please enable cURL and retry.");
|
61 |
+
} elseif($verbose) {
|
62 |
+
$ver = curl_version();
|
63 |
+
$logger->log(SPLog::PRODUCER_CMD_VERBOSE, "cURL version: " . $ver['version']);
|
64 |
+
}
|
65 |
+
|
66 |
+
if($webPath === false && isset($options["webPath"])) {
|
67 |
+
$logger->bye(SPLog::PRODUCER_CMD, "The Web Path specified is invalid - " . $options["webPath"]);
|
68 |
+
}
|
69 |
+
|
70 |
+
$bkFolder = $bkFolderRel = false;
|
71 |
+
if($bkBase) {
|
72 |
+
if(is_dir($bkBase)) {
|
73 |
+
$bkBase = trailingslashit($bkBase);
|
74 |
+
$bkFolder = $bkBase . (strpos($bkBase, trailingslashit($folder)) === 0 ? 'ShortPixelBackups' : basename($folder) . (strpos($bkBase, trailingslashit(dirname($folder))) === 0 ? "_SP_BKP" : "" ));
|
75 |
+
$bkFolderRel = \ShortPixel\Settings::pathToRelative($bkFolder, $targetFolder);
|
76 |
+
} else {
|
77 |
+
$logger->bye(SPLog::PRODUCER_CMD, "Backup path does not exist ($bkFolder)");
|
78 |
+
}
|
79 |
+
}
|
80 |
+
|
81 |
+
//handle the ctrl+C
|
82 |
+
if (function_exists('pcntl_signal')) {
|
83 |
+
declare(ticks=1); // PHP internal, make signal handling work
|
84 |
+
pcntl_signal(SIGINT, 'spCmdSignalHandler');
|
85 |
+
}
|
86 |
+
|
87 |
+
//sanity checks
|
88 |
+
if(!$apiKey || strlen($apiKey) != 20 || !ctype_alnum($apiKey)) {
|
89 |
+
$logger->bye(SPLog::PRODUCER_CMD, "Please provide a valid API Key");
|
90 |
+
}
|
91 |
+
|
92 |
+
if(!$folder || strlen($folder) == 0) {
|
93 |
+
$logger->bye(SPLog::PRODUCER_CMD, "Please specify a folder to optimize");
|
94 |
+
}
|
95 |
+
|
96 |
+
if($targetFolder != $folder) {
|
97 |
+
if(strpos($targetFolder, trailingslashit($folder)) === 0) {
|
98 |
+
$logger->bye(SPLog::PRODUCER_CMD, "Target folder cannot be a subfolder of the source folder. ( $targetFolder $folder)");
|
99 |
+
} elseif (strpos($folder, trailingslashit($targetFolder)) === 0) {
|
100 |
+
$logger->bye(SPLog::PRODUCER_CMD, "Target folder cannot be a parent folder of the source folder.");
|
101 |
+
} else {
|
102 |
+
@mkdir($targetFolder, 0777, true);
|
103 |
+
}
|
104 |
+
}
|
105 |
+
|
106 |
+
$notifier = \ShortPixel\notify\ProgressNotifier::constructNotifier($folder);
|
107 |
+
$logger->log(SPLog::PRODUCER_CMD_VERBOSE, "Using notifier: " . get_class($notifier));
|
108 |
+
|
109 |
+
try {
|
110 |
+
//check if the folder is not locked by another ShortPixel process
|
111 |
+
$splock = new \ShortPixel\Lock($processId, $targetFolder, $clearLock);
|
112 |
+
try {
|
113 |
+
$splock->lock();
|
114 |
+
} catch(\Exception $ex) {
|
115 |
+
$logger->log(SPLog::PRODUCER_CMD_VERBOSE, "Waiting for lock...");
|
116 |
+
$splock->requestLock("CLI");
|
117 |
+
$logger->log(SPLog::PRODUCER_CMD_VERBOSE, "Lock aquired");
|
118 |
+
}
|
119 |
+
|
120 |
+
$logger->log(SPLog::PRODUCER_CMD, "Starting to optimize folder $folder using API Key $apiKey ...");
|
121 |
+
|
122 |
+
ShortPixel\setKey($apiKey);
|
123 |
+
|
124 |
+
//try to get optimization options from the folder .sp-options
|
125 |
+
$optionsHandler = new \ShortPixel\Settings();
|
126 |
+
$sourceOptions = $optionsHandler->readOptions($folder);
|
127 |
+
$targetOptions = $optionsHandler->readOptions($targetFolder);
|
128 |
+
$folderOptions = array_merge(is_array($sourceOptions) ? $sourceOptions : [], is_array($targetOptions) ? $targetOptions : []);
|
129 |
+
if(count($folderOptions)) {
|
130 |
+
$logger->log(SPLog::PRODUCER_CMD_VERBOSE, "Options from .sp-options file: ", $folderOptions);
|
131 |
+
}
|
132 |
+
|
133 |
+
if((!isset($webPath) || !$webPath) && isset($folderOptions["base_url"]) && strlen($folderOptions["base_url"])) {
|
134 |
+
$webPath = $folderOptions["base_url"];
|
135 |
+
$logger->log(SPLog::PRODUCER_CMD_VERBOSE, "Using Web Path from settings: $webPath");
|
136 |
+
}
|
137 |
+
|
138 |
+
// ********************* OPTIMIZATION OPTIONS FROM COMMAND LINE TAKE PRECEDENCE *********************
|
139 |
+
$overrides = array();
|
140 |
+
if($compression !== false) {
|
141 |
+
$overrides['lossy'] = $compression;
|
142 |
+
}
|
143 |
+
if($resizeRaw !== false) {
|
144 |
+
$tmp = explode("/", $resizeRaw);
|
145 |
+
$resizeType = (count($tmp) == 2) && ($tmp[1] == 3) ? 3 : 1;
|
146 |
+
$sizes = explode("x", $tmp[0]);
|
147 |
+
if(count($sizes) == 2 and is_numeric($sizes[0]) && is_numeric($sizes[1])) {
|
148 |
+
$overrides['resize'] = $resizeType;
|
149 |
+
$overrides['resize_width'] = $sizes[0];
|
150 |
+
$overrides['resize_height'] = $sizes[1];
|
151 |
+
$logger->log(SPLog::PRODUCER_CMD_VERBOSE, "Resize type: " . ($resizeType == 3 ? "inner" : "outer") . ", width: {$overrides['resize_width']}, height: {$overrides['resize_height']}");
|
152 |
+
} else {
|
153 |
+
$splock->unlock();
|
154 |
+
$logger->bye(SPLog::PRODUCER_CMD, "Malformed parameter --resize, should be --resize=[width]x[height]/[type] type being 1 for outer and 3 for inner");
|
155 |
+
}
|
156 |
+
}
|
157 |
+
if($createWebP !== false) {
|
158 |
+
$overrides['convertto'] = '+webp';
|
159 |
+
}
|
160 |
+
if($createAVIF !== false) {
|
161 |
+
$overrides['convertto'] = (strlen($overrides['convertto']) ? $overrides['convertto'] . '|' : '') . '+avif';
|
162 |
+
}
|
163 |
+
if($keepExif !== false) {
|
164 |
+
$overrides['keep_exif'] = 1;
|
165 |
+
}
|
166 |
+
|
167 |
+
if($bkFolderRel) {
|
168 |
+
$overrides['backup_path'] = $bkFolderRel;
|
169 |
+
}
|
170 |
+
if(!count($exclude) && isset($folderOptions["exclude"]) && strlen($folderOptions["exclude"])) {
|
171 |
+
$exclude = $folderOptions["exclude"];
|
172 |
+
}
|
173 |
+
$optimizationOptions = array_merge($folderOptions, $overrides, array("persist_type" => "text", "notify_progress" => true, "cache_time" => $cacheTime));
|
174 |
+
$logger->log(SPLog::PRODUCER_CMD_VERBOSE, "Using OPTIONS: ", $optimizationOptions);
|
175 |
+
\ShortPixel\ShortPixel::setOptions($optimizationOptions);
|
176 |
+
|
177 |
+
$imageCount = $failedImageCount = $sameImageCount = 0;
|
178 |
+
$tries = 0;
|
179 |
+
$consecutiveExceptions = 0;
|
180 |
+
$folderOptimized = false;
|
181 |
+
$targetFolderParam = ($targetFolder !== $folder ? $targetFolder : false);
|
182 |
+
|
183 |
+
$splock->setTimeout(7200);
|
184 |
+
$splock->lock();
|
185 |
+
$info = \ShortPixel\folderInfo($folder, true, false, $exclude, $targetFolderParam, $recurseDepth, $retrySkipped);
|
186 |
+
$splock->setTimeout(360);
|
187 |
+
$splock->lock();
|
188 |
+
$notifier->recordProgress($info, true);
|
189 |
+
|
190 |
+
if($info->status == 'error') {
|
191 |
+
$splock->unlock();
|
192 |
+
$logger->bye(SPLog::PRODUCER_CMD, "Error: " . $info->message . " (Code: " . $info->code . ")");
|
193 |
+
}
|
194 |
+
|
195 |
+
$logger->log(SPLog::PRODUCER_CMD, "Folder has " . $info->total . " files, " . $info->succeeded . " optimized, " . $info->pending . " pending, " . $info->same . " don't need optimization, " . $info->failed . " failed.");
|
196 |
+
|
197 |
+
if($info->status == "success") {
|
198 |
+
$logger->log(SPLog::PRODUCER_CMD, "Congratulations, the folder is optimized.");
|
199 |
+
}
|
200 |
+
else {
|
201 |
+
$lockTimeout = 360;
|
202 |
+
while ($tries < 100000) {
|
203 |
+
$crtImageCount = 0;
|
204 |
+
$tempus = time();
|
205 |
+
try {
|
206 |
+
if ($webPath) {
|
207 |
+
$result = \ShortPixel\fromWebFolder($folder, $webPath, $exclude, $targetFolderParam, $recurseDepth)->wait(300)->toFiles($targetFolder);
|
208 |
+
} else {
|
209 |
+
$speed = ($speed ? $speed : \ShortPixel\ShortPixel::MAX_ALLOWED_FILES_PER_CALL);
|
210 |
+
$result = \ShortPixel\fromFolder($folder, $speed, $exclude, $targetFolderParam, \ShortPixel\ShortPixel::CLIENT_MAX_BODY_SIZE, $recurseDepth)->wait(300)->toFiles($targetFolder);
|
211 |
+
}
|
212 |
+
if(time() - $tempus > $lockTimeout - 100) {
|
213 |
+
//increase the timeout of the lock file if a pass takes too long (for large folders)
|
214 |
+
$lockTimeout += time() - $tempus;
|
215 |
+
$logger->log(SPLog::PRODUCER_CMD_VERBOSE, "Increasing lock timeout to: $lockTimeout");
|
216 |
+
$splock->setTimeout($lockTimeout);
|
217 |
+
}
|
218 |
+
} catch (\ShortPixel\ClientException $ex) {
|
219 |
+
if ($ex->getCode() == \ShortPixel\ClientException::NO_FILE_FOUND || $ex->getCode() == 2) {
|
220 |
+
break;
|
221 |
+
} else {
|
222 |
+
$logger->log(SPLog::PRODUCER_CMD, "ClientException: " . $ex->getMessage() . " (CODE: " . $ex->getCode() . ")");
|
223 |
+
$tries++;
|
224 |
+
if(++$consecutiveExceptions > \ShortPixel\ShortPixel::MAX_RETRIES) {
|
225 |
+
$logger->log(SPLog::PRODUCER_CMD, "Too many exceptions. Exiting.");
|
226 |
+
break;
|
227 |
+
}
|
228 |
+
$splock->lock();
|
229 |
+
continue;
|
230 |
+
}
|
231 |
+
}
|
232 |
+
$tries++;
|
233 |
+
$consecutiveExceptions = 0;
|
234 |
+
|
235 |
+
if (count($result->succeeded) > 0) {
|
236 |
+
$crtImageCount += count($result->succeeded);
|
237 |
+
$imageCount += $crtImageCount;
|
238 |
+
} elseif (count($result->failed)) {
|
239 |
+
$crtImageCount += count($result->failed);
|
240 |
+
$failedImageCount += count($result->failed);
|
241 |
+
} elseif (count($result->same)) {
|
242 |
+
$crtImageCount += count($result->same);
|
243 |
+
$sameImageCount += count($result->same);
|
244 |
+
} elseif (count($result->pending)) {
|
245 |
+
$crtImageCount += count($result->pending);
|
246 |
+
}
|
247 |
+
if ($verbose) {
|
248 |
+
$msg = "\n" . date("Y-m-d H:i:s") . " PASS $tries : " . count($result->succeeded) . " succeeded, " . count($result->pending) . " pending, " . count($result->same) . " don't need optimization, " . count($result->failed) . " failed\n";
|
249 |
+
foreach ($result->succeeded as $item) {
|
250 |
+
$msg .= " - " . $item->SavedFile . " " . $item->Status->Message . " ("
|
251 |
+
. ($item->PercentImprovement > 0 ? "Reduced by " . $item->PercentImprovement . "%" : "") . ($item->PercentImprovement < 5 ? " - Bonus processing" : ""). ")\n";
|
252 |
+
}
|
253 |
+
foreach ($result->pending as $item) {
|
254 |
+
$msg .= " - " . $item->SavedFile . " " . $item->Status->Message . "\n";
|
255 |
+
}
|
256 |
+
foreach ($result->same as $item) {
|
257 |
+
$msg .= " - " . $item->SavedFile . " " . $item->Status->Message . " (Bonus processing)\n";
|
258 |
+
}
|
259 |
+
foreach ($result->failed as $item) {
|
260 |
+
$msg .= " - " . $item->SavedFile . " " . $item->Status->Message . "\n";
|
261 |
+
}
|
262 |
+
$logger->logRaw($msg . "\n");
|
263 |
+
} else {
|
264 |
+
$logger->logRaw(str_pad("", $crtImageCount, "#"));
|
265 |
+
}
|
266 |
+
//if no files were processed in this pass, the folder is done
|
267 |
+
if ($crtImageCount == 0) {
|
268 |
+
$folderOptimized = (!isset($item) || $item->Status->Code == 2);
|
269 |
+
break;
|
270 |
+
}
|
271 |
+
//check & refresh the lock file
|
272 |
+
$splock->lock();
|
273 |
+
}
|
274 |
+
|
275 |
+
$logger->log(SPLog::PRODUCER_CMD, "This pass: $imageCount images optimized, $sameImageCount don't need optimization, $failedImageCount failed to optimize." . ($folderOptimized ? " Congratulations, the folder is optimized.":""));
|
276 |
+
if ($crtImageCount > 0) $logger->log(SPLog::PRODUCER_CMD, "Images still pending, please relaunch the script to continue.");
|
277 |
+
echo("\n");
|
278 |
+
}
|
279 |
+
} catch(\Exception $e) {
|
280 |
+
//record progress only if it's not a lock exception.
|
281 |
+
if($e->getCode() != -19) {
|
282 |
+
$notifier->recordProgress((object)array("status" => (object)array("code" => $e->getCode(), "message" => $e->getMessage())), true);
|
283 |
+
}
|
284 |
+
$logger->log(SPLog::PRODUCER_CMD, "\n" . $e->getMessage() . "( code: " . $e->getCode() . " type: " . get_class($e) . " )" . "\n");
|
285 |
+
}
|
286 |
+
|
287 |
+
//cleanup the lock file
|
288 |
+
$splock->unlock();
|
289 |
+
|
290 |
+
function verifyFolder($folder, $create = false)
|
291 |
+
{
|
292 |
+
global $logger;
|
293 |
+
$folder = rtrim($folder, '/');
|
294 |
+
$suffix = '';
|
295 |
+
if($create) {
|
296 |
+
$suffix = '/' . basename($folder);
|
297 |
+
$folder = dirname($folder);
|
298 |
+
}
|
299 |
+
$folder = (realpath($folder) ? realpath($folder) : $folder);
|
300 |
+
if (!is_dir($folder)) {
|
301 |
+
if (substr($folder, 0, 2) == "./") {
|
302 |
+
$folder = str_replace(DIRECTORY_SEPARATOR, '/', getcwd()) . "/" . substr($folder, 2);
|
303 |
+
}
|
304 |
+
if (!is_dir($folder)) {
|
305 |
+
if ((strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' && preg_match('/^[a-zA-Z]:\//', $folder) === 0) //it's Windows and no drive letter X - relative path?
|
306 |
+
|| (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' && substr($folder, 0, 1) !== '/')
|
307 |
+
) { //linux and no / - relative path?
|
308 |
+
$folder = str_replace(DIRECTORY_SEPARATOR, '/', getcwd()) . "/" . $folder;
|
309 |
+
}
|
310 |
+
}
|
311 |
+
if (!is_dir($folder)) {
|
312 |
+
$logger->log(SPLog::PRODUCER_CMD, "The folder $folder does not exist.");
|
313 |
+
}
|
314 |
+
}
|
315 |
+
return str_replace(DIRECTORY_SEPARATOR, '/', $folder . $suffix);
|
316 |
+
}
|
317 |
+
|
318 |
+
function trailingslashit($path) {
|
319 |
+
return rtrim($path, '/') . '/';
|
320 |
+
}
|
321 |
+
|
322 |
+
function spCmdSignalHandler($signo)
|
323 |
+
{
|
324 |
+
global $splock, $logger;
|
325 |
+
$splock->unlock();
|
326 |
+
$logger->bye(SPLog::PRODUCER_CMD, "Caught interrupt signal, exiting.");
|
327 |
+
}
|
vendor/shortpixel/shortpixel-php/lib/data/shortpixel.crt
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
-----BEGIN CERTIFICATE-----
|
2 |
+
MIIE/DCCA+SgAwIBAgIQTOYGEQsUbdJjOrgYq+G/QjANBgkqhkiG9w0BAQsFADB1
|
3 |
+
MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjEpMCcGA1UECxMg
|
4 |
+
U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIzAhBgNVBAMTGlN0YXJ0
|
5 |
+
Q29tIENsYXNzIDEgQ2xpZW50IENBMB4XDTE2MDQwNzEwNTkzOVoXDTE3MDQwNzEw
|
6 |
+
NTkzOVowRDEdMBsGA1UEAwwUc2ltb25Ac2hvcnRwaXhlbC5jb20xIzAhBgkqhkiG
|
7 |
+
9w0BCQEWFHNpbW9uQHNob3J0cGl4ZWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOC
|
8 |
+
AQ8AMIIBCgKCAQEA6VymIV3QNQ67On0AgvkuntO2u20e7UIkk/WrG+jbf6mfQoyo
|
9 |
+
vd18t8VHfGnXAHMLB1SDzu/JDCAe+xgk4Y2uoMnNTA0NkPmqHg1rUKehVhXaAuMb
|
10 |
+
d4cKJhkmD3FtlUuAWZB//578nL+VZ2ufj3RON+vufNbePB8coexdkE0mMQPq9paK
|
11 |
+
399o8vQjULFuyRJPXXcVIkfiaS6hhfR2qbtG/0nivVIUi7GTjnqinBbsGUcOO53m
|
12 |
+
dSvPvreL5yqDPXORFX/bGa8ssVKxetFnpwyfv4e9+52kZqwWULbCW341uuYq0PfU
|
13 |
+
wZQ0HbXFUi08LExULq+5ZetWglcEdQSqSWFCEwIDAQABo4IBtzCCAbMwDgYDVR0P
|
14 |
+
AQH/BAQDAgSwMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDBDAJBgNVHRME
|
15 |
+
AjAAMB0GA1UdDgQWBBRWf5A1tltRHE/XkPbfyvqRQSUYLTAfBgNVHSMEGDAWgBQk
|
16 |
+
gWw5Yb5JD4+3G0YrySi1J0htaDBvBggrBgEFBQcBAQRjMGEwJAYIKwYBBQUHMAGG
|
17 |
+
GGh0dHA6Ly9vY3NwLnN0YXJ0c3NsLmNvbTA5BggrBgEFBQcwAoYtaHR0cDovL2Fp
|
18 |
+
YS5zdGFydHNzbC5jb20vY2VydHMvc2NhLmNsaWVudDEuY3J0MDgGA1UdHwQxMC8w
|
19 |
+
LaAroCmGJ2h0dHA6Ly9jcmwuc3RhcnRzc2wuY29tL3NjYS1jbGllbnQxLmNybDAf
|
20 |
+
BgNVHREEGDAWgRRzaW1vbkBzaG9ydHBpeGVsLmNvbTAjBgNVHRIEHDAahhhodHRw
|
21 |
+
Oi8vd3d3LnN0YXJ0c3NsLmNvbS8wRgYDVR0gBD8wPTA7BgsrBgEEAYG1NwECBTAs
|
22 |
+
MCoGCCsGAQUFBwIBFh5odHRwOi8vd3d3LnN0YXJ0c3NsLmNvbS9wb2xpY3kwDQYJ
|
23 |
+
KoZIhvcNAQELBQADggEBAFbFDY2edKRLAG0sDjOiiGob9F11ImhhIDQLy5/M2djn
|
24 |
+
1D3x9z1sc+U5kTfS7DNdK4K8XDY+TndAE7h+mEB8hKh0+UYkX00VqhdnBWtDopHM
|
25 |
+
2C/3PCyKeCr9YSUIvvSKrOnNsmm7tuiybmd63iQKTe8SWIMJOSZ9i0E8jm03RmKw
|
26 |
+
QYW+KzOXsq0hRy/CdOBuVxHvu97TeMqyeH5/S0ChxCPZozr8xxT8+sHgkFy4r11P
|
27 |
+
+YADBHflgqrtbfmbcEoQwx3ucRmiBOtSm8IVQhfiPeYcsOIgPfRFfpu55Qa4kvpk
|
28 |
+
lP6nlHlkln7XDJsg9i7jHIbyrh9aGGPSMU4mSiEd2Q4=
|
29 |
+
-----END CERTIFICATE-----
|
vendor/shortpixel/shortpixel-php/lib/no-composer.php
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
require_once("../lib/shortpixel-php-req.php");
|
3 |
+
|
4 |
+
ShortPixel\setKey("<YOUR API KEY HERE>");
|
5 |
+
$tmpFolder = tempnam(sys_get_temp_dir(), "shortpixel-php");
|
6 |
+
echo("Temp folder: " . $tmpFolder);
|
7 |
+
if(file_exists($tmpFolder)) unlink($tmpFolder);
|
8 |
+
mkdir($tmpFolder);
|
9 |
+
\ShortPixel\fromUrls("https://shortpixel.com/img/tests/wrapper/shortpixel.png")->refresh()->wait(300)->toFiles($tmpFolder);
|
10 |
+
echo("\nSuccessfully saved the optimized image from URL to temp folder.\n");
|
11 |
+
\ShortPixel\fromFile(__DIR__ . "/data/cc.jpg")->refresh()->wait(300)->toFiles($tmpFolder);
|
12 |
+
echo("\nSuccessfully saved the optimized image from path to temp folder.\n\n");
|
vendor/shortpixel/shortpixel-php/lib/shortpixel-php-req.php
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/*
|
3 |
+
function __autoload($class_name) {
|
4 |
+
require_once("./$class_name.php");
|
5 |
+
}
|
6 |
+
|
7 |
+
use ShortPixel\Persister;
|
8 |
+
use ShortPixel\Commander;
|
9 |
+
use ShortPixel\Client;
|
10 |
+
use ShortPixel\Exception;
|
11 |
+
use ShortPixel\Source;
|
12 |
+
use ShortPixel\persist\TextPersister;
|
13 |
+
use ShortPixel\persist\ExifPersister;
|
14 |
+
use ShortPixel\persist\PNGMetadataExtractor;
|
15 |
+
use ShortPixel\persist\PNGReader;
|
16 |
+
use ShortPixel\Result;
|
17 |
+
use ShortPixel;
|
18 |
+
*/
|
19 |
+
|
20 |
+
require_once("ShortPixel/Settings.php");
|
21 |
+
require_once("ShortPixel/Lock.php");
|
22 |
+
require_once("ShortPixel/SPLog.php");
|
23 |
+
require_once("ShortPixel/SPCache.php");
|
24 |
+
|
25 |
+
require_once("ShortPixel/Persister.php");
|
26 |
+
require_once("ShortPixel/persist/TextPersister.php");
|
27 |
+
require_once("ShortPixel/persist/ExifPersister.php");
|
28 |
+
require_once("ShortPixel/persist/PNGMetadataExtractor.php");
|
29 |
+
require_once("ShortPixel/persist/PNGReader.php");
|
30 |
+
|
31 |
+
require_once("ShortPixel/notify/ProgressNotifier.php");
|
32 |
+
require_once("ShortPixel/notify/ProgressNotifierMemcache.php");
|
33 |
+
require_once("ShortPixel/notify/ProgressNotifierFileQ.php");
|
34 |
+
|
35 |
+
require_once("ShortPixel/Commander.php");
|
36 |
+
require_once("ShortPixel/Client.php");
|
37 |
+
require_once("ShortPixel/Exception.php");
|
38 |
+
require_once("ShortPixel/Source.php");
|
39 |
+
require_once("ShortPixel/Result.php");
|
40 |
+
require_once("ShortPixel.php");
|
41 |
+
|
vendor/shortpixel/shortpixel-php/phpunit.xml
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!--<phpunit bootstrap="test/helper.php" colors="true">-->
|
2 |
+
<phpunit colors="true">
|
3 |
+
<testsuites>
|
4 |
+
<testsuite name="ShortPixel PHP Test Suite">
|
5 |
+
<directory suffix="Test.php">test</directory>
|
6 |
+
</testsuite>
|
7 |
+
</testsuites>
|
8 |
+
<filter>
|
9 |
+
<whitelist>
|
10 |
+
<directory>lib</directory>
|
11 |
+
</whitelist>
|
12 |
+
</filter>
|
13 |
+
</phpunit>
|
vendor/symfony/deprecation-contracts/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Copyright (c) 2020-2021 Fabien Potencier
|
2 |
+
|
3 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4 |
+
of this software and associated documentation files (the "Software"), to deal
|
5 |
+
in the Software without restriction, including without limitation the rights
|
6 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7 |
+
copies of the Software, and to permit persons to whom the Software is furnished
|
8 |
+
to do so, subject to the following conditions:
|
9 |
+
|
10 |
+
The above copyright notice and this permission notice shall be included in all
|
11 |
+
copies or substantial portions of the Software.
|
12 |
+
|
13 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19 |
+
THE SOFTWARE.
|
vendor/symfony/deprecation-contracts/function.php
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Symfony package.
|
5 |
+
*
|
6 |
+
* (c) Fabien Potencier <fabien@symfony.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
if (!function_exists('trigger_deprecation')) {
|
13 |
+
/**
|
14 |
+
* Triggers a silenced deprecation notice.
|
15 |
+
*
|
16 |
+
* @param string $package The name of the Composer package that is triggering the deprecation
|
17 |
+
* @param string $version The version of the package that introduced the deprecation
|
18 |
+
* @param string $message The message of the deprecation
|
19 |
+
* @param mixed ...$args Values to insert in the message using printf() formatting
|
20 |
+
*
|
21 |
+
* @author Nicolas Grekas <p@tchwork.com>
|
22 |
+
*/
|
23 |
+
function trigger_deprecation(string $package, string $version, string $message, ...$args): void
|
24 |
+
{
|
25 |
+
@trigger_error(($package || $version ? "Since $package $version: " : '').($args ? vsprintf($message, $args) : $message), \E_USER_DEPRECATED);
|
26 |
+
}
|
27 |
+
}
|
vendor/symfony/dotenv/.gitattributes
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
/Tests export-ignore
|
2 |
+
/phpunit.xml.dist export-ignore
|
3 |
+
/.gitattributes export-ignore
|
4 |
+
/.gitignore export-ignore
|
vendor/symfony/dotenv/Dotenv.php
ADDED
@@ -0,0 +1,571 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Symfony package.
|
5 |
+
*
|
6 |
+
* (c) Fabien Potencier <fabien@symfony.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
namespace Symfony\Component\Dotenv;
|
13 |
+
|
14 |
+
use Symfony\Component\Dotenv\Exception\FormatException;
|
15 |
+
use Symfony\Component\Dotenv\Exception\FormatExceptionContext;
|
16 |
+
use Symfony\Component\Dotenv\Exception\PathException;
|
17 |
+
use Symfony\Component\Process\Exception\ExceptionInterface as ProcessException;
|
18 |
+
use Symfony\Component\Process\Process;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Manages .env files.
|
22 |
+
*
|
23 |
+
* @author Fabien Potencier <fabien@symfony.com>
|
24 |
+
* @author Kévin Dunglas <dunglas@gmail.com>
|
25 |
+
*/
|
26 |
+
final class Dotenv
|
27 |
+
{
|
28 |
+
public const VARNAME_REGEX = '(?i:[A-Z][A-Z0-9_]*+)';
|
29 |
+
public const STATE_VARNAME = 0;
|
30 |
+
public const STATE_VALUE = 1;
|
31 |
+
|
32 |
+
private $path;
|
33 |
+
private $cursor;
|
34 |
+
private $lineno;
|
35 |
+
private $data;
|
36 |
+
private $end;
|
37 |
+
private $values;
|
38 |
+
private $envKey;
|
39 |
+
private $debugKey;
|
40 |
+
private $prodEnvs = ['prod'];
|
41 |
+
private $usePutenv = false;
|
42 |
+
|
43 |
+
/**
|
44 |
+
* @param string $envKey
|
45 |
+
*/
|
46 |
+
public function __construct($envKey = 'APP_ENV', string $debugKey = 'APP_DEBUG')
|
47 |
+
{
|
48 |
+
if (\in_array($envKey = (string) $envKey, ['1', ''], true)) {
|
49 |
+
trigger_deprecation('symfony/dotenv', '5.1', 'Passing a boolean to the constructor of "%s" is deprecated, use "Dotenv::usePutenv()".', __CLASS__);
|
50 |
+
$this->usePutenv = (bool) $envKey;
|
51 |
+
$envKey = 'APP_ENV';
|
52 |
+
}
|
53 |
+
|
54 |
+
$this->envKey = $envKey;
|
55 |
+
$this->debugKey = $debugKey;
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* @return $this
|
60 |
+
*/
|
61 |
+
public function setProdEnvs(array $prodEnvs): self
|
62 |
+
{
|
63 |
+
$this->prodEnvs = $prodEnvs;
|
64 |
+
|
65 |
+
return $this;
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* @param bool $usePutenv If `putenv()` should be used to define environment variables or not.
|
70 |
+
* Beware that `putenv()` is not thread safe, that's why this setting defaults to false
|
71 |
+
*
|
72 |
+
* @return $this
|
73 |
+
*/
|
74 |
+
public function usePutenv($usePutenv = true): self
|
75 |
+
{
|
76 |
+
$this->usePutenv = $usePutenv;
|
77 |
+
|
78 |
+
return $this;
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Loads one or several .env files.
|
83 |
+
*
|
84 |
+
* @param string $path A file to load
|
85 |
+
* @param ...string $extraPaths A list of additional files to load
|
86 |
+
*
|
87 |
+
* @throws FormatException when a file has a syntax error
|
88 |
+
* @throws PathException when a file does not exist or is not readable
|
89 |
+
*/
|
90 |
+
public function load(string $path, string ...$extraPaths): void
|
91 |
+
{
|
92 |
+
$this->doLoad(false, \func_get_args());
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Loads a .env file and the corresponding .env.local, .env.$env and .env.$env.local files if they exist.
|
97 |
+
*
|
98 |
+
* .env.local is always ignored in test env because tests should produce the same results for everyone.
|
99 |
+
* .env.dist is loaded when it exists and .env is not found.
|
100 |
+
*
|
101 |
+
* @param string $path A file to load
|
102 |
+
* @param string $envKey|null The name of the env vars that defines the app env
|
103 |
+
* @param string $defaultEnv The app env to use when none is defined
|
104 |
+
* @param array $testEnvs A list of app envs for which .env.local should be ignored
|
105 |
+
*
|
106 |
+
* @throws FormatException when a file has a syntax error
|
107 |
+
* @throws PathException when a file does not exist or is not readable
|
108 |
+
*/
|
109 |
+
public function loadEnv(string $path, string $envKey = null, string $defaultEnv = 'dev', array $testEnvs = ['test']): void
|
110 |
+
{
|
111 |
+
$k = $envKey ?? $this->envKey;
|
112 |
+
|
113 |
+
if (is_file($path) || !is_file($p = "$path.dist")) {
|
114 |
+
$this->load($path);
|
115 |
+
} else {
|
116 |
+
$this->load($p);
|
117 |
+
}
|
118 |
+
|
119 |
+
if (null === $env = $_SERVER[$k] ?? $_ENV[$k] ?? null) {
|
120 |
+
$this->populate([$k => $env = $defaultEnv]);
|
121 |
+
}
|
122 |
+
|
123 |
+
if (!\in_array($env, $testEnvs, true) && is_file($p = "$path.local")) {
|
124 |
+
$this->load($p);
|
125 |
+
$env = $_SERVER[$k] ?? $_ENV[$k] ?? $env;
|
126 |
+
}
|
127 |
+
|
128 |
+
if ('local' === $env) {
|
129 |
+
return;
|
130 |
+
}
|
131 |
+
|
132 |
+
if (is_file($p = "$path.$env")) {
|
133 |
+
$this->load($p);
|
134 |
+
}
|
135 |
+
|
136 |
+
if (is_file($p = "$path.$env.local")) {
|
137 |
+
$this->load($p);
|
138 |
+
}
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* Loads env vars from .env.local.php if the file exists or from the other .env files otherwise.
|
143 |
+
*
|
144 |
+
* This method also configures the APP_DEBUG env var according to the current APP_ENV.
|
145 |
+
*
|
146 |
+
* See method loadEnv() for rules related to .env files.
|
147 |
+
*/
|
148 |
+
public function bootEnv(string $path, string $defaultEnv = 'dev', array $testEnvs = ['test']): void
|
149 |
+
{
|
150 |
+
$p = $path.'.local.php';
|
151 |
+
$env = is_file($p) ? include $p : null;
|
152 |
+
$k = $this->envKey;
|
153 |
+
|
154 |
+
if (\is_array($env) && (!isset($env[$k]) || ($_SERVER[$k] ?? $_ENV[$k] ?? $env[$k]) === $env[$k])) {
|
155 |
+
$this->populate($env);
|
156 |
+
} else {
|
157 |
+
$this->loadEnv($path, $k, $defaultEnv, $testEnvs);
|
158 |
+
}
|
159 |
+
|
160 |
+
$_SERVER += $_ENV;
|
161 |
+
|
162 |
+
$k = $this->debugKey;
|
163 |
+
$debug = $_SERVER[$k] ?? !\in_array($_SERVER[$this->envKey], $this->prodEnvs, true);
|
164 |
+
$_SERVER[$k] = $_ENV[$k] = (int) $debug || (!\is_bool($debug) && filter_var($debug, \FILTER_VALIDATE_BOOLEAN)) ? '1' : '0';
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* Loads one or several .env files and enables override existing vars.
|
169 |
+
*
|
170 |
+
* @param string $path A file to load
|
171 |
+
* @param ...string $extraPaths A list of additional files to load
|
172 |
+
*
|
173 |
+
* @throws FormatException when a file has a syntax error
|
174 |
+
* @throws PathException when a file does not exist or is not readable
|
175 |
+
*/
|
176 |
+
public function overload(string $path, string ...$extraPaths): void
|
177 |
+
{
|
178 |
+
$this->doLoad(true, \func_get_args());
|
179 |
+
}
|
180 |
+
|
181 |
+
/**
|
182 |
+
* Sets values as environment variables (via putenv, $_ENV, and $_SERVER).
|
183 |
+
*
|
184 |
+
* @param array $values An array of env variables
|
185 |
+
* @param bool $overrideExistingVars true when existing environment variables must be overridden
|
186 |
+
*/
|
187 |
+
public function populate(array $values, bool $overrideExistingVars = false): void
|
188 |
+
{
|
189 |
+
$updateLoadedVars = false;
|
190 |
+
$loadedVars = array_flip(explode(',', $_SERVER['SYMFONY_DOTENV_VARS'] ?? $_ENV['SYMFONY_DOTENV_VARS'] ?? ''));
|
191 |
+
|
192 |
+
foreach ($values as $name => $value) {
|
193 |
+
$notHttpName = 0 !== strpos($name, 'HTTP_');
|
194 |
+
// don't check existence with getenv() because of thread safety issues
|
195 |
+
if (!isset($loadedVars[$name]) && (!$overrideExistingVars && (isset($_ENV[$name]) || (isset($_SERVER[$name]) && $notHttpName)))) {
|
196 |
+
continue;
|
197 |
+
}
|
198 |
+
|
199 |
+
if ($this->usePutenv) {
|
200 |
+
putenv("$name=$value");
|
201 |
+
}
|
202 |
+
|
203 |
+
$_ENV[$name] = $value;
|
204 |
+
if ($notHttpName) {
|
205 |
+
$_SERVER[$name] = $value;
|
206 |
+
}
|
207 |
+
|
208 |
+
if (!isset($loadedVars[$name])) {
|
209 |
+
$loadedVars[$name] = $updateLoadedVars = true;
|
210 |
+
}
|
211 |
+
}
|
212 |
+
|
213 |
+
if ($updateLoadedVars) {
|
214 |
+
unset($loadedVars['']);
|
215 |
+
$loadedVars = implode(',', array_keys($loadedVars));
|
216 |
+
$_ENV['SYMFONY_DOTENV_VARS'] = $_SERVER['SYMFONY_DOTENV_VARS'] = $loadedVars;
|
217 |
+
|
218 |
+
if ($this->usePutenv) {
|
219 |
+
putenv('SYMFONY_DOTENV_VARS='.$loadedVars);
|
220 |
+
}
|
221 |
+
}
|
222 |
+
}
|
223 |
+
|
224 |
+
/**
|
225 |
+
* Parses the contents of an .env file.
|
226 |
+
*
|
227 |
+
* @param string $data The data to be parsed
|
228 |
+
* @param string $path The original file name where data where stored (used for more meaningful error messages)
|
229 |
+
*
|
230 |
+
* @return array An array of env variables
|
231 |
+
*
|
232 |
+
* @throws FormatException when a file has a syntax error
|
233 |
+
*/
|
234 |
+
public function parse(string $data, string $path = '.env'): array
|
235 |
+
{
|
236 |
+
$this->path = $path;
|
237 |
+
$this->data = str_replace(["\r\n", "\r"], "\n", $data);
|
238 |
+
$this->lineno = 1;
|
239 |
+
$this->cursor = 0;
|
240 |
+
$this->end = \strlen($this->data);
|
241 |
+
$state = self::STATE_VARNAME;
|
242 |
+
$this->values = [];
|
243 |
+
$name = '';
|
244 |
+
|
245 |
+
$this->skipEmptyLines();
|
246 |
+
|
247 |
+
while ($this->cursor < $this->end) {
|
248 |
+
switch ($state) {
|
249 |
+
case self::STATE_VARNAME:
|
250 |
+
$name = $this->lexVarname();
|
251 |
+
$state = self::STATE_VALUE;
|
252 |
+
break;
|
253 |
+
|
254 |
+
case self::STATE_VALUE:
|
255 |
+
$this->values[$name] = $this->lexValue();
|
256 |
+
$state = self::STATE_VARNAME;
|
257 |
+
break;
|
258 |
+
}
|
259 |
+
}
|
260 |
+
|
261 |
+
if (self::STATE_VALUE === $state) {
|
262 |
+
$this->values[$name] = '';
|
263 |
+
}
|
264 |
+
|
265 |
+
try {
|
266 |
+
return $this->values;
|
267 |
+
} finally {
|
268 |
+
$this->values = [];
|
269 |
+
$this->data = null;
|
270 |
+
$this->path = null;
|
271 |
+
}
|
272 |
+
}
|
273 |
+
|
274 |
+
private function lexVarname(): string
|
275 |
+
{
|
276 |
+
// var name + optional export
|
277 |
+
if (!preg_match('/(export[ \t]++)?('.self::VARNAME_REGEX.')/A', $this->data, $matches, 0, $this->cursor)) {
|
278 |
+
throw $this->createFormatException('Invalid character in variable name');
|
279 |
+
}
|
280 |
+
$this->moveCursor($matches[0]);
|
281 |
+
|
282 |
+
if ($this->cursor === $this->end || "\n" === $this->data[$this->cursor] || '#' === $this->data[$this->cursor]) {
|
283 |
+
if ($matches[1]) {
|
284 |
+
throw $this->createFormatException('Unable to unset an environment variable');
|
285 |
+
}
|
286 |
+
|
287 |
+
throw $this->createFormatException('Missing = in the environment variable declaration');
|
288 |
+
}
|
289 |
+
|
290 |
+
if (' ' === $this->data[$this->cursor] || "\t" === $this->data[$this->cursor]) {
|
291 |
+
throw $this->createFormatException('Whitespace characters are not supported after the variable name');
|
292 |
+
}
|
293 |
+
|
294 |
+
if ('=' !== $this->data[$this->cursor]) {
|
295 |
+
throw $this->createFormatException('Missing = in the environment variable declaration');
|
296 |
+
}
|
297 |
+
++$this->cursor;
|
298 |
+
|
299 |
+
return $matches[2];
|
300 |
+
}
|
301 |
+
|
302 |
+
private function lexValue(): string
|
303 |
+
{
|
304 |
+
if (preg_match('/[ \t]*+(?:#.*)?$/Am', $this->data, $matches, 0, $this->cursor)) {
|
305 |
+
$this->moveCursor($matches[0]);
|
306 |
+
$this->skipEmptyLines();
|
307 |
+
|
308 |
+
return '';
|
309 |
+
}
|
310 |
+
|
311 |
+
if (' ' === $this->data[$this->cursor] || "\t" === $this->data[$this->cursor]) {
|
312 |
+
throw $this->createFormatException('Whitespace are not supported before the value');
|
313 |
+
}
|
314 |
+
|
315 |
+
$loadedVars = array_flip(explode(',', $_SERVER['SYMFONY_DOTENV_VARS'] ?? ($_ENV['SYMFONY_DOTENV_VARS'] ?? '')));
|
316 |
+
unset($loadedVars['']);
|
317 |
+
$v = '';
|
318 |
+
|
319 |
+
do {
|
320 |
+
if ("'" === $this->data[$this->cursor]) {
|
321 |
+
$len = 0;
|
322 |
+
|
323 |
+
do {
|
324 |
+
if ($this->cursor + ++$len === $this->end) {
|
325 |
+
$this->cursor += $len;
|
326 |
+
|
327 |
+
throw $this->createFormatException('Missing quote to end the value');
|
328 |
+
}
|
329 |
+
} while ("'" !== $this->data[$this->cursor + $len]);
|
330 |
+
|
331 |
+
$v .= substr($this->data, 1 + $this->cursor, $len - 1);
|
332 |
+
$this->cursor += 1 + $len;
|
333 |
+
} elseif ('"' === $this->data[$this->cursor]) {
|
334 |
+
$value = '';
|
335 |
+
|
336 |
+
if (++$this->cursor === $this->end) {
|
337 |
+
throw $this->createFormatException('Missing quote to end the value');
|
338 |
+
}
|
339 |
+
|
340 |
+
while ('"' !== $this->data[$this->cursor] || ('\\' === $this->data[$this->cursor - 1] && '\\' !== $this->data[$this->cursor - 2])) {
|
341 |
+
$value .= $this->data[$this->cursor];
|
342 |
+
++$this->cursor;
|
343 |
+
|
344 |
+
if ($this->cursor === $this->end) {
|
345 |
+
throw $this->createFormatException('Missing quote to end the value');
|
346 |
+
}
|
347 |
+
}
|
348 |
+
++$this->cursor;
|
349 |
+
$value = str_replace(['\\"', '\r', '\n'], ['"', "\r", "\n"], $value);
|
350 |
+
$resolvedValue = $value;
|
351 |
+
$resolvedValue = $this->resolveVariables($resolvedValue, $loadedVars);
|
352 |
+
$resolvedValue = $this->resolveCommands($resolvedValue, $loadedVars);
|
353 |
+
$resolvedValue = str_replace('\\\\', '\\', $resolvedValue);
|
354 |
+
$v .= $resolvedValue;
|
355 |
+
} else {
|
356 |
+
$value = '';
|
357 |
+
$prevChr = $this->data[$this->cursor - 1];
|
358 |
+
while ($this->cursor < $this->end && !\in_array($this->data[$this->cursor], ["\n", '"', "'"], true) && !((' ' === $prevChr || "\t" === $prevChr) && '#' === $this->data[$this->cursor])) {
|
359 |
+
if ('\\' === $this->data[$this->cursor] && isset($this->data[$this->cursor + 1]) && ('"' === $this->data[$this->cursor + 1] || "'" === $this->data[$this->cursor + 1])) {
|
360 |
+
++$this->cursor;
|
361 |
+
}
|
362 |
+
|
363 |
+
$value .= $prevChr = $this->data[$this->cursor];
|
364 |
+
|
365 |
+
if ('$' === $this->data[$this->cursor] && isset($this->data[$this->cursor + 1]) && '(' === $this->data[$this->cursor + 1]) {
|
366 |
+
++$this->cursor;
|
367 |
+
$value .= '('.$this->lexNestedExpression().')';
|
368 |
+
}
|
369 |
+
|
370 |
+
++$this->cursor;
|
371 |
+
}
|
372 |
+
$value = rtrim($value);
|
373 |
+
$resolvedValue = $value;
|
374 |
+
$resolvedValue = $this->resolveVariables($resolvedValue, $loadedVars);
|
375 |
+
$resolvedValue = $this->resolveCommands($resolvedValue, $loadedVars);
|
376 |
+
$resolvedValue = str_replace('\\\\', '\\', $resolvedValue);
|
377 |
+
|
378 |
+
if ($resolvedValue === $value && preg_match('/\s+/', $value)) {
|
379 |
+
throw $this->createFormatException('A value containing spaces must be surrounded by quotes');
|
380 |
+
}
|
381 |
+
|
382 |
+
$v .= $resolvedValue;
|
383 |
+
|
384 |
+
if ($this->cursor < $this->end && '#' === $this->data[$this->cursor]) {
|
385 |
+
break;
|
386 |
+
}
|
387 |
+
}
|
388 |
+
} while ($this->cursor < $this->end && "\n" !== $this->data[$this->cursor]);
|
389 |
+
|
390 |
+
$this->skipEmptyLines();
|
391 |
+
|
392 |
+
return $v;
|
393 |
+
}
|
394 |
+
|
395 |
+
private function lexNestedExpression(): string
|
396 |
+
{
|
397 |
+
++$this->cursor;
|
398 |
+
$value = '';
|
399 |
+
|
400 |
+
while ("\n" !== $this->data[$this->cursor] && ')' !== $this->data[$this->cursor]) {
|
401 |
+
$value .= $this->data[$this->cursor];
|
402 |
+
|
403 |
+
if ('(' === $this->data[$this->cursor]) {
|
404 |
+
$value .= $this->lexNestedExpression().')';
|
405 |
+
}
|
406 |
+
|
407 |
+
++$this->cursor;
|
408 |
+
|
409 |
+
if ($this->cursor === $this->end) {
|
410 |
+
throw $this->createFormatException('Missing closing parenthesis.');
|
411 |
+
}
|
412 |
+
}
|
413 |
+
|
414 |
+
if ("\n" === $this->data[$this->cursor]) {
|
415 |
+
throw $this->createFormatException('Missing closing parenthesis.');
|
416 |
+
}
|
417 |
+
|
418 |
+
return $value;
|
419 |
+
}
|
420 |
+
|
421 |
+
private function skipEmptyLines()
|
422 |
+
{
|
423 |
+
if (preg_match('/(?:\s*+(?:#[^\n]*+)?+)++/A', $this->data, $match, 0, $this->cursor)) {
|
424 |
+
$this->moveCursor($match[0]);
|
425 |
+
}
|
426 |
+
}
|
427 |
+
|
428 |
+
private function resolveCommands(string $value, array $loadedVars): string
|
429 |
+
{
|
430 |
+
if (false === strpos($value, '$')) {
|
431 |
+
return $value;
|
432 |
+
}
|
433 |
+
|
434 |
+
$regex = '/
|
435 |
+
(\\\\)? # escaped with a backslash?
|
436 |
+
\$
|
437 |
+
(?<cmd>
|
438 |
+
\( # require opening parenthesis
|
439 |
+
([^()]|\g<cmd>)+ # allow any number of non-parens, or balanced parens (by nesting the <cmd> expression recursively)
|
440 |
+
\) # require closing paren
|
441 |
+
)
|
442 |
+
/x';
|
443 |
+
|
444 |
+
return preg_replace_callback($regex, function ($matches) use ($loadedVars) {
|
445 |
+
if ('\\' === $matches[1]) {
|
446 |
+
return substr($matches[0], 1);
|
447 |
+
}
|
448 |
+
|
449 |
+
if ('\\' === \DIRECTORY_SEPARATOR) {
|
450 |
+
throw new \LogicException('Resolving commands is not supported on Windows.');
|
451 |
+
}
|
452 |
+
|
453 |
+
if (!class_exists(Process::class)) {
|
454 |
+
throw new \LogicException('Resolving commands requires the Symfony Process component.');
|
455 |
+
}
|
456 |
+
|
457 |
+
$process = method_exists(Process::class, 'fromShellCommandline') ? Process::fromShellCommandline('echo '.$matches[0]) : new Process('echo '.$matches[0]);
|
458 |
+
|
459 |
+
if (!method_exists(Process::class, 'fromShellCommandline') && method_exists(Process::class, 'inheritEnvironmentVariables')) {
|
460 |
+
// Symfony 3.4 does not inherit env vars by default:
|
461 |
+
$process->inheritEnvironmentVariables();
|
462 |
+
}
|
463 |
+
|
464 |
+
$env = [];
|
465 |
+
foreach ($this->values as $name => $value) {
|
466 |
+
if (isset($loadedVars[$name]) || (!isset($_ENV[$name]) && !(isset($_SERVER[$name]) && 0 !== strpos($name, 'HTTP_')))) {
|
467 |
+
$env[$name] = $value;
|
468 |
+
}
|
469 |
+
}
|
470 |
+
$process->setEnv($env);
|
471 |
+
|
472 |
+
try {
|
473 |
+
$process->mustRun();
|
474 |
+
} catch (ProcessException $e) {
|
475 |
+
throw $this->createFormatException(sprintf('Issue expanding a command (%s)', $process->getErrorOutput()));
|
476 |
+
}
|
477 |
+
|
478 |
+
return preg_replace('/[\r\n]+$/', '', $process->getOutput());
|
479 |
+
}, $value);
|
480 |
+
}
|
481 |
+
|
482 |
+
private function resolveVariables(string $value, array $loadedVars): string
|
483 |
+
{
|
484 |
+
if (false === strpos($value, '$')) {
|
485 |
+
return $value;
|
486 |
+
}
|
487 |
+
|
488 |
+
$regex = '/
|
489 |
+
(?<!\\\\)
|
490 |
+
(?P<backslashes>\\\\*) # escaped with a backslash?
|
491 |
+
\$
|
492 |
+
(?!\() # no opening parenthesis
|
493 |
+
(?P<opening_brace>\{)? # optional brace
|
494 |
+
(?P<name>'.self::VARNAME_REGEX.')? # var name
|
495 |
+
(?P<default_value>:[-=][^\}]++)? # optional default value
|
496 |
+
(?P<closing_brace>\})? # optional closing brace
|
497 |
+
/x';
|
498 |
+
|
499 |
+
$value = preg_replace_callback($regex, function ($matches) use ($loadedVars) {
|
500 |
+
// odd number of backslashes means the $ character is escaped
|
501 |
+
if (1 === \strlen($matches['backslashes']) % 2) {
|
502 |
+
return substr($matches[0], 1);
|
503 |
+
}
|
504 |
+
|
505 |
+
// unescaped $ not followed by variable name
|
506 |
+
if (!isset($matches['name'])) {
|
507 |
+
return $matches[0];
|
508 |
+
}
|
509 |
+
|
510 |
+
if ('{' === $matches['opening_brace'] && !isset($matches['closing_brace'])) {
|
511 |
+
throw $this->createFormatException('Unclosed braces on variable expansion');
|
512 |
+
}
|
513 |
+
|
514 |
+
$name = $matches['name'];
|
515 |
+
if (isset($loadedVars[$name]) && isset($this->values[$name])) {
|
516 |
+
$value = $this->values[$name];
|
517 |
+
} elseif (isset($_ENV[$name])) {
|
518 |
+
$value = $_ENV[$name];
|
519 |
+
} elseif (isset($_SERVER[$name]) && 0 !== strpos($name, 'HTTP_')) {
|
520 |
+
$value = $_SERVER[$name];
|
521 |
+
} elseif (isset($this->values[$name])) {
|
522 |
+
$value = $this->values[$name];
|
523 |
+
} else {
|
524 |
+
$value = (string) getenv($name);
|
525 |
+
}
|
526 |
+
|
527 |
+
if ('' === $value && isset($matches['default_value']) && '' !== $matches['default_value']) {
|
528 |
+
$unsupportedChars = strpbrk($matches['default_value'], '\'"{$');
|
529 |
+
if (false !== $unsupportedChars) {
|
530 |
+
throw $this->createFormatException(sprintf('Unsupported character "%s" found in the default value of variable "$%s".', $unsupportedChars[0], $name));
|
531 |
+
}
|
532 |
+
|
533 |
+
$value = substr($matches['default_value'], 2);
|
534 |
+
|
535 |
+
if ('=' === $matches['default_value'][1]) {
|
536 |
+
$this->values[$name] = $value;
|
537 |
+
}
|
538 |
+
}
|
539 |
+
|
540 |
+
if (!$matches['opening_brace'] && isset($matches['closing_brace'])) {
|
541 |
+
$value .= '}';
|
542 |
+
}
|
543 |
+
|
544 |
+
return $matches['backslashes'].$value;
|
545 |
+
}, $value);
|
546 |
+
|
547 |
+
return $value;
|
548 |
+
}
|
549 |
+
|
550 |
+
private function moveCursor(string $text)
|
551 |
+
{
|
552 |
+
$this->cursor += \strlen($text);
|
553 |
+
$this->lineno += substr_count($text, "\n");
|
554 |
+
}
|
555 |
+
|
556 |
+
private function createFormatException(string $message): FormatException
|
557 |
+
{
|
558 |
+
return new FormatException($message, new FormatExceptionContext($this->data, $this->path, $this->lineno, $this->cursor));
|
559 |
+
}
|
560 |
+
|
561 |
+
private function doLoad(bool $overrideExistingVars, array $paths): void
|
562 |
+
{
|
563 |
+
foreach ($paths as $path) {
|
564 |
+
if (!is_readable($path) || is_dir($path)) {
|
565 |
+
throw new PathException($path);
|
566 |
+
}
|
567 |
+
|
568 |
+
$this->populate($this->parse(file_get_contents($path), $path), $overrideExistingVars);
|
569 |
+
}
|
570 |
+
}
|
571 |
+
}
|
vendor/symfony/dotenv/Exception/ExceptionInterface.php
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Symfony package.
|
5 |
+
*
|
6 |
+
* (c) Fabien Potencier <fabien@symfony.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
namespace Symfony\Component\Dotenv\Exception;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Interface for exceptions.
|
16 |
+
*
|
17 |
+
* @author Fabien Potencier <fabien@symfony.com>
|
18 |
+
*/
|
19 |
+
interface ExceptionInterface extends \Throwable
|
20 |
+
{
|
21 |
+
}
|
vendor/symfony/dotenv/Exception/FormatException.php
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Symfony package.
|
5 |
+
*
|
6 |
+
* (c) Fabien Potencier <fabien@symfony.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
namespace Symfony\Component\Dotenv\Exception;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Thrown when a file has a syntax error.
|
16 |
+
*
|
17 |
+
* @author Fabien Potencier <fabien@symfony.com>
|
18 |
+
*/
|
19 |
+
final class FormatException extends \LogicException implements ExceptionInterface
|
20 |
+
{
|
21 |
+
private $context;
|
22 |
+
|
23 |
+
public function __construct(string $message, FormatExceptionContext $context, int $code = 0, \Throwable $previous = null)
|
24 |
+
{
|
25 |
+
$this->context = $context;
|
26 |
+
|
27 |
+
parent::__construct(sprintf("%s in \"%s\" at line %d.\n%s", $message, $context->getPath(), $context->getLineno(), $context->getDetails()), $code, $previous);
|
28 |
+
}
|
29 |
+
|
30 |
+
public function getContext(): FormatExceptionContext
|
31 |
+
{
|
32 |
+
return $this->context;
|
33 |
+
}
|
34 |
+
}
|
vendor/symfony/dotenv/Exception/FormatExceptionContext.php
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Symfony package.
|
5 |
+
*
|
6 |
+
* (c) Fabien Potencier <fabien@symfony.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
namespace Symfony\Component\Dotenv\Exception;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* @author Fabien Potencier <fabien@symfony.com>
|
16 |
+
*/
|
17 |
+
final class FormatExceptionContext
|
18 |
+
{
|
19 |
+
private $data;
|
20 |
+
private $path;
|
21 |
+
private $lineno;
|
22 |
+
private $cursor;
|
23 |
+
|
24 |
+
public function __construct(string $data, string $path, int $lineno, int $cursor)
|
25 |
+
{
|
26 |
+
$this->data = $data;
|
27 |
+
$this->path = $path;
|
28 |
+
$this->lineno = $lineno;
|
29 |
+
$this->cursor = $cursor;
|
30 |
+
}
|
31 |
+
|
32 |
+
public function getPath(): string
|
33 |
+
{
|
34 |
+
return $this->path;
|
35 |
+
}
|
36 |
+
|
37 |
+
public function getLineno(): int
|
38 |
+
{
|
39 |
+
return $this->lineno;
|
40 |
+
}
|
41 |
+
|
42 |
+
public function getDetails(): string
|
43 |
+
{
|
44 |
+
$before = str_replace("\n", '\n', substr($this->data, max(0, $this->cursor - 20), min(20, $this->cursor)));
|
45 |
+
$after = str_replace("\n", '\n', substr($this->data, $this->cursor, 20));
|
46 |
+
|
47 |
+
return '...'.$before.$after."...\n".str_repeat(' ', \strlen($before) + 2).'^ line '.$this->lineno.' offset '.$this->cursor;
|
48 |
+
}
|
49 |
+
}
|
vendor/symfony/dotenv/Exception/PathException.php
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Symfony package.
|
5 |
+
*
|
6 |
+
* (c) Fabien Potencier <fabien@symfony.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
namespace Symfony\Component\Dotenv\Exception;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Thrown when a file does not exist or is not readable.
|
16 |
+
*
|
17 |
+
* @author Fabien Potencier <fabien@symfony.com>
|
18 |
+
*/
|
19 |
+
final class PathException extends \RuntimeException implements ExceptionInterface
|
20 |
+
{
|
21 |
+
public function __construct(string $path, int $code = 0, \Throwable $previous = null)
|
22 |
+
{
|
23 |
+
parent::__construct(sprintf('Unable to read the "%s" environment file.', $path), $code, $previous);
|
24 |
+
}
|
25 |
+
}
|
vendor/symfony/dotenv/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Copyright (c) 2016-2021 Fabien Potencier
|
2 |
+
|
3 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4 |
+
of this software and associated documentation files (the "Software"), to deal
|
5 |
+
in the Software without restriction, including without limitation the rights
|
6 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7 |
+
copies of the Software, and to permit persons to whom the Software is furnished
|
8 |
+
to do so, subject to the following conditions:
|
9 |
+
|
10 |
+
The above copyright notice and this permission notice shall be included in all
|
11 |
+
copies or substantial portions of the Software.
|
12 |
+
|
13 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19 |
+
THE SOFTWARE.
|
vendor/symfony/dotenv/Tests/DotenvTest.php
ADDED
@@ -0,0 +1,499 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Symfony package.
|
5 |
+
*
|
6 |
+
* (c) Fabien Potencier <fabien@symfony.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
namespace Symfony\Component\Dotenv\Tests;
|
13 |
+
|
14 |
+
use PHPUnit\Framework\TestCase;
|
15 |
+
use Symfony\Component\Dotenv\Dotenv;
|
16 |
+
use Symfony\Component\Dotenv\Exception\FormatException;
|
17 |
+
|
18 |
+
class DotenvTest extends TestCase
|
19 |
+
{
|
20 |
+
/**
|
21 |
+
* @dataProvider getEnvDataWithFormatErrors
|
22 |
+
*/
|
23 |
+
public function testParseWithFormatError($data, $error)
|
24 |
+
{
|
25 |
+
$dotenv = new Dotenv();
|
26 |
+
|
27 |
+
try {
|
28 |
+
$dotenv->parse($data);
|
29 |
+
$this->fail('Should throw a FormatException');
|
30 |
+
} catch (FormatException $e) {
|
31 |
+
$this->assertStringMatchesFormat($error, $e->getMessage());
|
32 |
+
}
|
33 |
+
}
|
34 |
+
|
35 |
+
public function getEnvDataWithFormatErrors()
|
36 |
+
{
|
37 |
+
$tests = [
|
38 |
+
['FOO=BAR BAZ', "A value containing spaces must be surrounded by quotes in \".env\" at line 1.\n...FOO=BAR BAZ...\n ^ line 1 offset 11"],
|
39 |
+
['FOO BAR=BAR', "Whitespace characters are not supported after the variable name in \".env\" at line 1.\n...FOO BAR=BAR...\n ^ line 1 offset 3"],
|
40 |
+
['FOO', "Missing = in the environment variable declaration in \".env\" at line 1.\n...FOO...\n ^ line 1 offset 3"],
|
41 |
+
['FOO="foo', "Missing quote to end the value in \".env\" at line 1.\n...FOO=\"foo...\n ^ line 1 offset 8"],
|
42 |
+
['FOO=\'foo', "Missing quote to end the value in \".env\" at line 1.\n...FOO='foo...\n ^ line 1 offset 8"],
|
43 |
+
["FOO=\"foo\nBAR=\"bar\"", "Missing quote to end the value in \".env\" at line 1.\n...FOO=\"foo\\nBAR=\"bar\"...\n ^ line 1 offset 18"],
|
44 |
+
['FOO=\'foo'."\n", "Missing quote to end the value in \".env\" at line 1.\n...FOO='foo\\n...\n ^ line 1 offset 9"],
|
45 |
+
['export FOO', "Unable to unset an environment variable in \".env\" at line 1.\n...export FOO...\n ^ line 1 offset 10"],
|
46 |
+
['FOO=${FOO', "Unclosed braces on variable expansion in \".env\" at line 1.\n...FOO=\${FOO...\n ^ line 1 offset 9"],
|
47 |
+
['FOO= BAR', "Whitespace are not supported before the value in \".env\" at line 1.\n...FOO= BAR...\n ^ line 1 offset 4"],
|
48 |
+
['Стасян', "Invalid character in variable name in \".env\" at line 1.\n...Стасян...\n ^ line 1 offset 0"],
|
49 |
+
['FOO!', "Missing = in the environment variable declaration in \".env\" at line 1.\n...FOO!...\n ^ line 1 offset 3"],
|
50 |
+
['FOO=$(echo foo', "Missing closing parenthesis. in \".env\" at line 1.\n...FOO=$(echo foo...\n ^ line 1 offset 14"],
|
51 |
+
['FOO=$(echo foo'."\n", "Missing closing parenthesis. in \".env\" at line 1.\n...FOO=$(echo foo\\n...\n ^ line 1 offset 14"],
|
52 |
+
["FOO=\nBAR=\${FOO:-\'a{a}a}", "Unsupported character \"'\" found in the default value of variable \"\$FOO\". in \".env\" at line 2.\n...\\nBAR=\${FOO:-\'a{a}a}...\n ^ line 2 offset 24"],
|
53 |
+
["FOO=\nBAR=\${FOO:-a\$a}", "Unsupported character \"\$\" found in the default value of variable \"\$FOO\". in \".env\" at line 2.\n...FOO=\\nBAR=\${FOO:-a\$a}...\n ^ line 2 offset 20"],
|
54 |
+
["FOO=\nBAR=\${FOO:-a\"a}", "Unclosed braces on variable expansion in \".env\" at line 2.\n...FOO=\\nBAR=\${FOO:-a\"a}...\n ^ line 2 offset 17"],
|
55 |
+
];
|
56 |
+
|
57 |
+
if ('\\' !== \DIRECTORY_SEPARATOR) {
|
58 |
+
$tests[] = ['FOO=$((1dd2))', "Issue expanding a command (%s\n) in \".env\" at line 1.\n...FOO=$((1dd2))...\n ^ line 1 offset 13"];
|
59 |
+
}
|
60 |
+
|
61 |
+
return $tests;
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* @dataProvider getEnvData
|
66 |
+
*/
|
67 |
+
public function testParse($data, $expected)
|
68 |
+
{
|
69 |
+
$dotenv = new Dotenv();
|
70 |
+
$this->assertSame($expected, $dotenv->parse($data));
|
71 |
+
}
|
72 |
+
|
73 |
+
public function getEnvData()
|
74 |
+
{
|
75 |
+
putenv('LOCAL=local');
|
76 |
+
$_ENV['LOCAL'] = 'local';
|
77 |
+
$_ENV['REMOTE'] = 'remote';
|
78 |
+
$_SERVER['SERVERVAR'] = 'servervar';
|
79 |
+
|
80 |
+
$tests = [
|
81 |
+
// backslashes
|
82 |
+
['FOO=foo\\\\bar', ['FOO' => 'foo\\bar']],
|
83 |
+
["FOO='foo\\\\bar'", ['FOO' => 'foo\\\\bar']],
|
84 |
+
['FOO="foo\\\\bar"', ['FOO' => 'foo\\bar']],
|
85 |
+
|
86 |
+
// escaped backslash in front of variable
|
87 |
+
["BAR=bar\nFOO=foo\\\\\$BAR", ['BAR' => 'bar', 'FOO' => 'foo\\bar']],
|
88 |
+
["BAR=bar\nFOO='foo\\\\\$BAR'", ['BAR' => 'bar', 'FOO' => 'foo\\\\$BAR']],
|
89 |
+
["BAR=bar\nFOO=\"foo\\\\\$BAR\"", ['BAR' => 'bar', 'FOO' => 'foo\\bar']],
|
90 |
+
|
91 |
+
['FOO=foo\\\\\\$BAR', ['FOO' => 'foo\\$BAR']],
|
92 |
+
['FOO=\'foo\\\\\\$BAR\'', ['FOO' => 'foo\\\\\\$BAR']],
|
93 |
+
['FOO="foo\\\\\\$BAR"', ['FOO' => 'foo\\$BAR']],
|
94 |
+
|
95 |
+
// spaces
|
96 |
+
['FOO=bar', ['FOO' => 'bar']],
|
97 |
+
[' FOO=bar ', ['FOO' => 'bar']],
|
98 |
+
['FOO=', ['FOO' => '']],
|
99 |
+
["FOO=\n\n\nBAR=bar", ['FOO' => '', 'BAR' => 'bar']],
|
100 |
+
['FOO= ', ['FOO' => '']],
|
101 |
+
["FOO=\nBAR=bar", ['FOO' => '', 'BAR' => 'bar']],
|
102 |
+
|
103 |
+
// newlines
|
104 |
+
["\n\nFOO=bar\r\n\n", ['FOO' => 'bar']],
|
105 |
+
["FOO=bar\r\nBAR=foo", ['FOO' => 'bar', 'BAR' => 'foo']],
|
106 |
+
["FOO=bar\rBAR=foo", ['FOO' => 'bar', 'BAR' => 'foo']],
|
107 |
+
["FOO=bar\nBAR=foo", ['FOO' => 'bar', 'BAR' => 'foo']],
|
108 |
+
|
109 |
+
// quotes
|
110 |
+
["FOO=\"bar\"\n", ['FOO' => 'bar']],
|
111 |
+
["FOO=\"bar'foo\"\n", ['FOO' => 'bar\'foo']],
|
112 |
+
["FOO='bar'\n", ['FOO' => 'bar']],
|
113 |
+
["FOO='bar\"foo'\n", ['FOO' => 'bar"foo']],
|
114 |
+
["FOO=\"bar\\\"foo\"\n", ['FOO' => 'bar"foo']],
|
115 |
+
['FOO="bar\nfoo"', ['FOO' => "bar\nfoo"]],
|
116 |
+
['FOO="bar\rfoo"', ['FOO' => "bar\rfoo"]],
|
117 |
+
['FOO=\'bar\nfoo\'', ['FOO' => 'bar\nfoo']],
|
118 |
+
['FOO=\'bar\rfoo\'', ['FOO' => 'bar\rfoo']],
|
119 |
+
["FOO='bar\nfoo'", ['FOO' => "bar\nfoo"]],
|
120 |
+
['FOO=" FOO "', ['FOO' => ' FOO ']],
|
121 |
+
['FOO=" "', ['FOO' => ' ']],
|
122 |
+
['PATH="c:\\\\"', ['PATH' => 'c:\\']],
|
123 |
+
["FOO=\"bar\nfoo\"", ['FOO' => "bar\nfoo"]],
|
124 |
+
['FOO=BAR\\"', ['FOO' => 'BAR"']],
|
125 |
+
["FOO=BAR\\'BAZ", ['FOO' => "BAR'BAZ"]],
|
126 |
+
['FOO=\\"BAR', ['FOO' => '"BAR']],
|
127 |
+
|
128 |
+
// concatenated values
|
129 |
+
["FOO='bar''foo'\n", ['FOO' => 'barfoo']],
|
130 |
+
["FOO='bar '' baz'", ['FOO' => 'bar baz']],
|
131 |
+
["FOO=bar\nBAR='baz'\"\$FOO\"", ['FOO' => 'bar', 'BAR' => 'bazbar']],
|
132 |
+
["FOO='bar '\\'' baz'", ['FOO' => "bar ' baz"]],
|
133 |
+
|
134 |
+
// comments
|
135 |
+
["#FOO=bar\nBAR=foo", ['BAR' => 'foo']],
|
136 |
+
["#FOO=bar # Comment\nBAR=foo", ['BAR' => 'foo']],
|
137 |
+
["FOO='bar foo' # Comment", ['FOO' => 'bar foo']],
|
138 |
+
["FOO='bar#foo' # Comment", ['FOO' => 'bar#foo']],
|
139 |
+
["# Comment\r\nFOO=bar\n# Comment\nBAR=foo", ['FOO' => 'bar', 'BAR' => 'foo']],
|
140 |
+
["FOO=bar # Another comment\nBAR=foo", ['FOO' => 'bar', 'BAR' => 'foo']],
|
141 |
+
["FOO=\n\n# comment\nBAR=bar", ['FOO' => '', 'BAR' => 'bar']],
|
142 |
+
['FOO=NOT#COMMENT', ['FOO' => 'NOT#COMMENT']],
|
143 |
+
['FOO= # Comment', ['FOO' => '']],
|
144 |
+
|
145 |
+
// edge cases (no conversions, only strings as values)
|
146 |
+
['FOO=0', ['FOO' => '0']],
|
147 |
+
['FOO=false', ['FOO' => 'false']],
|
148 |
+
['FOO=null', ['FOO' => 'null']],
|
149 |
+
|
150 |
+
// export
|
151 |
+
['export FOO=bar', ['FOO' => 'bar']],
|
152 |
+
[' export FOO=bar', ['FOO' => 'bar']],
|
153 |
+
|
154 |
+
// variable expansion
|
155 |
+
["FOO=BAR\nBAR=\$FOO", ['FOO' => 'BAR', 'BAR' => 'BAR']],
|
156 |
+
["FOO=BAR\nBAR=\"\$FOO\"", ['FOO' => 'BAR', 'BAR' => 'BAR']],
|
157 |
+
["FOO=BAR\nBAR='\$FOO'", ['FOO' => 'BAR', 'BAR' => '$FOO']],
|
158 |
+
["FOO_BAR9=BAR\nBAR=\$FOO_BAR9", ['FOO_BAR9' => 'BAR', 'BAR' => 'BAR']],
|
159 |
+
["FOO=BAR\nBAR=\${FOO}Z", ['FOO' => 'BAR', 'BAR' => 'BARZ']],
|
160 |
+
["FOO=BAR\nBAR=\$FOO}", ['FOO' => 'BAR', 'BAR' => 'BAR}']],
|
161 |
+
["FOO=BAR\nBAR=\\\$FOO", ['FOO' => 'BAR', 'BAR' => '$FOO']],
|
162 |
+
['FOO=" \\$ "', ['FOO' => ' $ ']],
|
163 |
+
['FOO=" $ "', ['FOO' => ' $ ']],
|
164 |
+
['BAR=$LOCAL', ['BAR' => 'local']],
|
165 |
+
['BAR=$REMOTE', ['BAR' => 'remote']],
|
166 |
+
['BAR=$SERVERVAR', ['BAR' => 'servervar']],
|
167 |
+
['FOO=$NOTDEFINED', ['FOO' => '']],
|
168 |
+
["FOO=BAR\nBAR=\${FOO:-TEST}", ['FOO' => 'BAR', 'BAR' => 'BAR']],
|
169 |
+
["FOO=BAR\nBAR=\${NOTDEFINED:-TEST}", ['FOO' => 'BAR', 'BAR' => 'TEST']],
|
170 |
+
["FOO=\nBAR=\${FOO:-TEST}", ['FOO' => '', 'BAR' => 'TEST']],
|
171 |
+
["FOO=\nBAR=\$FOO:-TEST}", ['FOO' => '', 'BAR' => 'TEST}']],
|
172 |
+
["FOO=BAR\nBAR=\${FOO:=TEST}", ['FOO' => 'BAR', 'BAR' => 'BAR']],
|
173 |
+
["FOO=BAR\nBAR=\${NOTDEFINED:=TEST}", ['FOO' => 'BAR', 'NOTDEFINED' => 'TEST', 'BAR' => 'TEST']],
|
174 |
+
["FOO=\nBAR=\${FOO:=TEST}", ['FOO' => 'TEST', 'BAR' => 'TEST']],
|
175 |
+
["FOO=\nBAR=\$FOO:=TEST}", ['FOO' => 'TEST', 'BAR' => 'TEST}']],
|
176 |
+
["FOO=foo\nFOOBAR=\${FOO}\${BAR}", ['FOO' => 'foo', 'FOOBAR' => 'foo']],
|
177 |
+
];
|
178 |
+
|
179 |
+
if ('\\' !== \DIRECTORY_SEPARATOR) {
|
180 |
+
$tests = array_merge($tests, [
|
181 |
+
// command expansion
|
182 |
+
['FOO=$(echo foo)', ['FOO' => 'foo']],
|
183 |
+
['FOO=$((1+2))', ['FOO' => '3']],
|
184 |
+
['FOO=FOO$((1+2))BAR', ['FOO' => 'FOO3BAR']],
|
185 |
+
['FOO=$(echo "$(echo "$(echo "$(echo foo)")")")', ['FOO' => 'foo']],
|
186 |
+
["FOO=$(echo \"Quotes won't be a problem\")", ['FOO' => 'Quotes won\'t be a problem']],
|
187 |
+
["FOO=bar\nBAR=$(echo \"FOO is \$FOO\")", ['FOO' => 'bar', 'BAR' => 'FOO is bar']],
|
188 |
+
]);
|
189 |
+
}
|
190 |
+
|
191 |
+
return $tests;
|
192 |
+
}
|
193 |
+
|
194 |
+
public function testLoad()
|
195 |
+
{
|
196 |
+
unset($_ENV['FOO']);
|
197 |
+
unset($_ENV['BAR']);
|
198 |
+
unset($_SERVER['FOO']);
|
199 |
+
unset($_SERVER['BAR']);
|
200 |
+
putenv('FOO');
|
201 |
+
putenv('BAR');
|
202 |
+
|
203 |
+
@mkdir($tmpdir = sys_get_temp_dir().'/dotenv');
|
204 |
+
|
205 |
+
$path1 = tempnam($tmpdir, 'sf-');
|
206 |
+
$path2 = tempnam($tmpdir, 'sf-');
|
207 |
+
|
208 |
+
file_put_contents($path1, 'FOO=BAR');
|
209 |
+
file_put_contents($path2, 'BAR=BAZ');
|
210 |
+
|
211 |
+
(new Dotenv())->usePutenv()->load($path1, $path2);
|
212 |
+
|
213 |
+
$foo = getenv('FOO');
|
214 |
+
$bar = getenv('BAR');
|
215 |
+
|
216 |
+
putenv('FOO');
|
217 |
+
putenv('BAR');
|
218 |
+
unlink($path1);
|
219 |
+
unlink($path2);
|
220 |
+
rmdir($tmpdir);
|
221 |
+
|
222 |
+
$this->assertSame('BAR', $foo);
|
223 |
+
$this->assertSame('BAZ', $bar);
|
224 |
+
}
|
225 |
+
|
226 |
+
public function testLoadEnv()
|
227 |
+
{
|
228 |
+
unset($_ENV['FOO']);
|
229 |
+
unset($_ENV['BAR']);
|
230 |
+
unset($_SERVER['FOO']);
|
231 |
+
unset($_SERVER['BAR']);
|
232 |
+
putenv('FOO');
|
233 |
+
putenv('BAR');
|
234 |
+
|
235 |
+
@mkdir($tmpdir = sys_get_temp_dir().'/dotenv');
|
236 |
+
|
237 |
+
$path = tempnam($tmpdir, 'sf-');
|
238 |
+
|
239 |
+
// .env
|
240 |
+
|
241 |
+
file_put_contents($path, 'FOO=BAR');
|
242 |
+
(new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV');
|
243 |
+
$this->assertSame('BAR', getenv('FOO'));
|
244 |
+
$this->assertSame('dev', getenv('TEST_APP_ENV'));
|
245 |
+
|
246 |
+
// .env.local
|
247 |
+
|
248 |
+
$_SERVER['TEST_APP_ENV'] = 'local';
|
249 |
+
file_put_contents("$path.local", 'FOO=localBAR');
|
250 |
+
(new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV');
|
251 |
+
$this->assertSame('localBAR', getenv('FOO'));
|
252 |
+
|
253 |
+
// special case for test
|
254 |
+
|
255 |
+
$_SERVER['TEST_APP_ENV'] = 'test';
|
256 |
+
(new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV');
|
257 |
+
$this->assertSame('BAR', getenv('FOO'));
|
258 |
+
|
259 |
+
// .env.dev
|
260 |
+
|
261 |
+
unset($_SERVER['TEST_APP_ENV']);
|
262 |
+
file_put_contents("$path.dev", 'FOO=devBAR');
|
263 |
+
(new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV');
|
264 |
+
$this->assertSame('devBAR', getenv('FOO'));
|
265 |
+
|
266 |
+
// .env.dev.local
|
267 |
+
|
268 |
+
file_put_contents("$path.dev.local", 'FOO=devlocalBAR');
|
269 |
+
(new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV');
|
270 |
+
$this->assertSame('devlocalBAR', getenv('FOO'));
|
271 |
+
|
272 |
+
// .env.dist
|
273 |
+
|
274 |
+
unlink($path);
|
275 |
+
file_put_contents("$path.dist", 'BAR=distBAR');
|
276 |
+
(new Dotenv())->usePutenv()->loadEnv($path, 'TEST_APP_ENV');
|
277 |
+
$this->assertSame('distBAR', getenv('BAR'));
|
278 |
+
|
279 |
+
putenv('FOO');
|
280 |
+
putenv('BAR');
|
281 |
+
unlink("$path.dist");
|
282 |
+
unlink("$path.local");
|
283 |
+
unlink("$path.dev");
|
284 |
+
unlink("$path.dev.local");
|
285 |
+
rmdir($tmpdir);
|
286 |
+
}
|
287 |
+
|
288 |
+
public function testOverload()
|
289 |
+
{
|
290 |
+
unset($_ENV['FOO']);
|
291 |
+
unset($_ENV['BAR']);
|
292 |
+
unset($_SERVER['FOO']);
|
293 |
+
unset($_SERVER['BAR']);
|
294 |
+
|
295 |
+
putenv('FOO=initial_foo_value');
|
296 |
+
putenv('BAR=initial_bar_value');
|
297 |
+
$_ENV['FOO'] = 'initial_foo_value';
|
298 |
+
$_ENV['BAR'] = 'initial_bar_value';
|
299 |
+
|
300 |
+
@mkdir($tmpdir = sys_get_temp_dir().'/dotenv');
|
301 |
+
|
302 |
+
$path1 = tempnam($tmpdir, 'sf-');
|
303 |
+
$path2 = tempnam($tmpdir, 'sf-');
|
304 |
+
|
305 |
+
file_put_contents($path1, 'FOO=BAR');
|
306 |
+
file_put_contents($path2, 'BAR=BAZ');
|
307 |
+
|
308 |
+
(new Dotenv())->usePutenv()->overload($path1, $path2);
|
309 |
+
|
310 |
+
$foo = getenv('FOO');
|
311 |
+
$bar = getenv('BAR');
|
312 |
+
|
313 |
+
putenv('FOO');
|
314 |
+
putenv('BAR');
|
315 |
+
unlink($path1);
|
316 |
+
unlink($path2);
|
317 |
+
rmdir($tmpdir);
|
318 |
+
|
319 |
+
$this->assertSame('BAR', $foo);
|
320 |
+
$this->assertSame('BAZ', $bar);
|
321 |
+
}
|
322 |
+
|
323 |
+
public function testLoadDirectory()
|
324 |
+
{
|
325 |
+
$this->expectException(\Symfony\Component\Dotenv\Exception\PathException::class);
|
326 |
+
$dotenv = new Dotenv();
|
327 |
+
$dotenv->load(__DIR__);
|
328 |
+
}
|
329 |
+
|
330 |
+
public function testServerSuperglobalIsNotOverridden()
|
331 |
+
{
|
332 |
+
$originalValue = $_SERVER['argc'];
|
333 |
+
|
334 |
+
$dotenv = new Dotenv();
|
335 |
+
$dotenv->populate(['argc' => 'new_value']);
|
336 |
+
|
337 |
+
$this->assertSame($originalValue, $_SERVER['argc']);
|
338 |
+
}
|
339 |
+
|
340 |
+
public function testEnvVarIsNotOverridden()
|
341 |
+
{
|
342 |
+
putenv('TEST_ENV_VAR=original_value');
|
343 |
+
$_SERVER['TEST_ENV_VAR'] = 'original_value';
|
344 |
+
|
345 |
+
$dotenv = (new Dotenv())->usePutenv();
|
346 |
+
$dotenv->populate(['TEST_ENV_VAR' => 'new_value']);
|
347 |
+
|
348 |
+
$this->assertSame('original_value', getenv('TEST_ENV_VAR'));
|
349 |
+
}
|
350 |
+
|
351 |
+
public function testHttpVarIsPartiallyOverridden()
|
352 |
+
{
|
353 |
+
$_SERVER['HTTP_TEST_ENV_VAR'] = 'http_value';
|
354 |
+
|
355 |
+
$dotenv = (new Dotenv())->usePutenv();
|
356 |
+
$dotenv->populate(['HTTP_TEST_ENV_VAR' => 'env_value']);
|
357 |
+
|
358 |
+
$this->assertSame('env_value', getenv('HTTP_TEST_ENV_VAR'));
|
359 |
+
$this->assertSame('env_value', $_ENV['HTTP_TEST_ENV_VAR']);
|
360 |
+
$this->assertSame('http_value', $_SERVER['HTTP_TEST_ENV_VAR']);
|
361 |
+
}
|
362 |
+
|
363 |
+
public function testEnvVarIsOverriden()
|
364 |
+
{
|
365 |
+
putenv('TEST_ENV_VAR_OVERRIDEN=original_value');
|
366 |
+
|
367 |
+
$dotenv = (new Dotenv())->usePutenv();
|
368 |
+
$dotenv->populate(['TEST_ENV_VAR_OVERRIDEN' => 'new_value'], true);
|
369 |
+
|
370 |
+
$this->assertSame('new_value', getenv('TEST_ENV_VAR_OVERRIDEN'));
|
371 |
+
$this->assertSame('new_value', $_ENV['TEST_ENV_VAR_OVERRIDEN']);
|
372 |
+
$this->assertSame('new_value', $_SERVER['TEST_ENV_VAR_OVERRIDEN']);
|
373 |
+
}
|
374 |
+
|
375 |
+
public function testMemorizingLoadedVarsNamesInSpecialVar()
|
376 |
+
{
|
377 |
+
// Special variable not exists
|
378 |
+
unset($_ENV['SYMFONY_DOTENV_VARS']);
|
379 |
+
unset($_SERVER['SYMFONY_DOTENV_VARS']);
|
380 |
+
putenv('SYMFONY_DOTENV_VARS');
|
381 |
+
|
382 |
+
unset($_ENV['APP_DEBUG']);
|
383 |
+
unset($_SERVER['APP_DEBUG']);
|
384 |
+
putenv('APP_DEBUG');
|
385 |
+
unset($_ENV['DATABASE_URL']);
|
386 |
+
unset($_SERVER['DATABASE_URL']);
|
387 |
+
putenv('DATABASE_URL');
|
388 |
+
|
389 |
+
$dotenv = (new Dotenv())->usePutenv();
|
390 |
+
$dotenv->populate(['APP_DEBUG' => '1', 'DATABASE_URL' => 'mysql://root@localhost/db']);
|
391 |
+
|
392 |
+
$this->assertSame('APP_DEBUG,DATABASE_URL', getenv('SYMFONY_DOTENV_VARS'));
|
393 |
+
|
394 |
+
// Special variable has a value
|
395 |
+
$_ENV['SYMFONY_DOTENV_VARS'] = 'APP_ENV';
|
396 |
+
$_SERVER['SYMFONY_DOTENV_VARS'] = 'APP_ENV';
|
397 |
+
putenv('SYMFONY_DOTENV_VARS=APP_ENV');
|
398 |
+
|
399 |
+
$_ENV['APP_DEBUG'] = '1';
|
400 |
+
$_SERVER['APP_DEBUG'] = '1';
|
401 |
+
putenv('APP_DEBUG=1');
|
402 |
+
unset($_ENV['DATABASE_URL']);
|
403 |
+
unset($_SERVER['DATABASE_URL']);
|
404 |
+
putenv('DATABASE_URL');
|
405 |
+
|
406 |
+
$dotenv = (new Dotenv())->usePutenv();
|
407 |
+
$dotenv->populate(['APP_DEBUG' => '0', 'DATABASE_URL' => 'mysql://root@localhost/db']);
|
408 |
+
$dotenv->populate(['DATABASE_URL' => 'sqlite:///somedb.sqlite']);
|
409 |
+
|
410 |
+
$this->assertSame('APP_ENV,DATABASE_URL', getenv('SYMFONY_DOTENV_VARS'));
|
411 |
+
}
|
412 |
+
|
413 |
+
public function testOverridingEnvVarsWithNamesMemorizedInSpecialVar()
|
414 |
+
{
|
415 |
+
putenv('SYMFONY_DOTENV_VARS='.$_SERVER['SYMFONY_DOTENV_VARS'] = 'FOO,BAR,BAZ');
|
416 |
+
|
417 |
+
putenv('FOO=foo');
|
418 |
+
putenv('BAR=bar');
|
419 |
+
putenv('BAZ=baz');
|
420 |
+
putenv('DOCUMENT_ROOT=/var/www');
|
421 |
+
|
422 |
+
$dotenv = (new Dotenv())->usePutenv();
|
423 |
+
$dotenv->populate(['FOO' => 'foo1', 'BAR' => 'bar1', 'BAZ' => 'baz1', 'DOCUMENT_ROOT' => '/boot']);
|
424 |
+
|
425 |
+
$this->assertSame('foo1', getenv('FOO'));
|
426 |
+
$this->assertSame('bar1', getenv('BAR'));
|
427 |
+
$this->assertSame('baz1', getenv('BAZ'));
|
428 |
+
$this->assertSame('/var/www', getenv('DOCUMENT_ROOT'));
|
429 |
+
}
|
430 |
+
|
431 |
+
public function testGetVariablesValueFromEnvFirst()
|
432 |
+
{
|
433 |
+
$_ENV['APP_ENV'] = 'prod';
|
434 |
+
$dotenv = new Dotenv();
|
435 |
+
|
436 |
+
$test = "APP_ENV=dev\nTEST1=foo1_\${APP_ENV}";
|
437 |
+
$values = $dotenv->parse($test);
|
438 |
+
$this->assertSame('foo1_prod', $values['TEST1']);
|
439 |
+
|
440 |
+
if ('\\' !== \DIRECTORY_SEPARATOR) {
|
441 |
+
$test = "APP_ENV=dev\nTEST2=foo2_\$(php -r 'echo \$_SERVER[\"APP_ENV\"];')";
|
442 |
+
$values = $dotenv->parse($test);
|
443 |
+
$this->assertSame('foo2_prod', $values['TEST2']);
|
444 |
+
}
|
445 |
+
}
|
446 |
+
|
447 |
+
public function testGetVariablesValueFromGetenv()
|
448 |
+
{
|
449 |
+
putenv('Foo=Bar');
|
450 |
+
|
451 |
+
$dotenv = new Dotenv();
|
452 |
+
|
453 |
+
try {
|
454 |
+
$values = $dotenv->parse('Foo=${Foo}');
|
455 |
+
$this->assertSame('Bar', $values['Foo']);
|
456 |
+
} finally {
|
457 |
+
putenv('Foo');
|
458 |
+
}
|
459 |
+
}
|
460 |
+
|
461 |
+
public function testNoDeprecationWarning()
|
462 |
+
{
|
463 |
+
$dotenv = new Dotenv();
|
464 |
+
$this->assertInstanceOf(Dotenv::class, $dotenv);
|
465 |
+
}
|
466 |
+
|
467 |
+
public function testDoNotUsePutenv()
|
468 |
+
{
|
469 |
+
$dotenv = new Dotenv();
|
470 |
+
$dotenv->populate(['TEST_USE_PUTENV' => 'no']);
|
471 |
+
|
472 |
+
$this->assertSame('no', $_SERVER['TEST_USE_PUTENV']);
|
473 |
+
$this->assertSame('no', $_ENV['TEST_USE_PUTENV']);
|
474 |
+
$this->assertFalse(getenv('TEST_USE_PUTENV'));
|
475 |
+
}
|
476 |
+
|
477 |
+
public function testBootEnv()
|
478 |
+
{
|
479 |
+
@mkdir($tmpdir = sys_get_temp_dir().'/dotenv');
|
480 |
+
$path = tempnam($tmpdir, 'sf-');
|
481 |
+
|
482 |
+
file_put_contents($path, 'FOO=BAR');
|
483 |
+
(new Dotenv('TEST_APP_ENV', 'TEST_APP_DEBUG'))->bootEnv($path);
|
484 |
+
|
485 |
+
$this->assertSame('BAR', $_SERVER['FOO']);
|
486 |
+
|
487 |
+
unset($_SERVER['FOO'], $_ENV['FOO']);
|
488 |
+
unlink($path);
|
489 |
+
|
490 |
+
file_put_contents($path.'.local.php', '<?php return ["TEST_APP_ENV" => "dev", "FOO" => "BAR"];');
|
491 |
+
(new Dotenv('TEST_APP_ENV', 'TEST_APP_DEBUG'))->bootEnv($path);
|
492 |
+
$this->assertSame('BAR', $_SERVER['FOO']);
|
493 |
+
$this->assertSame('1', $_SERVER['TEST_APP_DEBUG']);
|
494 |
+
|
495 |
+
unset($_SERVER['FOO'], $_ENV['FOO']);
|
496 |
+
unlink($path.'.local.php');
|
497 |
+
rmdir($tmpdir);
|
498 |
+
}
|
499 |
+
}
|
vendor/symfony/polyfill-ctype/Ctype.php
ADDED
@@ -0,0 +1,227 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Symfony package.
|
5 |
+
*
|
6 |
+
* (c) Fabien Potencier <fabien@symfony.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
namespace Symfony\Polyfill\Ctype;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Ctype implementation through regex.
|
16 |
+
*
|
17 |
+
* @internal
|
18 |
+
*
|
19 |
+
* @author Gert de Pagter <BackEndTea@gmail.com>
|
20 |
+
*/
|
21 |
+
final class Ctype
|
22 |
+
{
|
23 |
+
/**
|
24 |
+
* Returns TRUE if every character in text is either a letter or a digit, FALSE otherwise.
|
25 |
+
*
|
26 |
+
* @see https://php.net/ctype-alnum
|
27 |
+
*
|
28 |
+
* @param string|int $text
|
29 |
+
*
|
30 |
+
* @return bool
|
31 |
+
*/
|
32 |
+
public static function ctype_alnum($text)
|
33 |
+
{
|
34 |
+
$text = self::convert_int_to_char_for_ctype($text);
|
35 |
+
|
36 |
+
return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z0-9]/', $text);
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Returns TRUE if every character in text is a letter, FALSE otherwise.
|
41 |
+
*
|
42 |
+
* @see https://php.net/ctype-alpha
|
43 |
+
*
|
44 |
+
* @param string|int $text
|
45 |
+
*
|
46 |
+
* @return bool
|
47 |
+
*/
|
48 |
+
public static function ctype_alpha($text)
|
49 |
+
{
|
50 |
+
$text = self::convert_int_to_char_for_ctype($text);
|
51 |
+
|
52 |
+
return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z]/', $text);
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Returns TRUE if every character in text is a control character from the current locale, FALSE otherwise.
|
57 |
+
*
|
58 |
+
* @see https://php.net/ctype-cntrl
|
59 |
+
*
|
60 |
+
* @param string|int $text
|
61 |
+
*
|
62 |
+
* @return bool
|
63 |
+
*/
|
64 |
+
public static function ctype_cntrl($text)
|
65 |
+
{
|
66 |
+
$text = self::convert_int_to_char_for_ctype($text);
|
67 |
+
|
68 |
+
return \is_string($text) && '' !== $text && !preg_match('/[^\x00-\x1f\x7f]/', $text);
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Returns TRUE if every character in the string text is a decimal digit, FALSE otherwise.
|
73 |
+
*
|
74 |
+
* @see https://php.net/ctype-digit
|
75 |
+
*
|
76 |
+
* @param string|int $text
|
77 |
+
*
|
78 |
+
* @return bool
|
79 |
+
*/
|
80 |
+
public static function ctype_digit($text)
|
81 |
+
{
|
82 |
+
$text = self::convert_int_to_char_for_ctype($text);
|
83 |
+
|
84 |
+
return \is_string($text) && '' !== $text && !preg_match('/[^0-9]/', $text);
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Returns TRUE if every character in text is printable and actually creates visible output (no white space), FALSE otherwise.
|
89 |
+
*
|
90 |
+
* @see https://php.net/ctype-graph
|
91 |
+
*
|
92 |
+
* @param string|int $text
|
93 |
+
*
|
94 |
+
* @return bool
|
95 |
+
*/
|
96 |
+
public static function ctype_graph($text)
|
97 |
+
{
|
98 |
+
$text = self::convert_int_to_char_for_ctype($text);
|
99 |
+
|
100 |
+
return \is_string($text) && '' !== $text && !preg_match('/[^!-~]/', $text);
|
101 |
+
}
|
102 |
+
|
103 |
+
/**
|
104 |
+
* Returns TRUE if every character in text is a lowercase letter.
|
105 |
+
*
|
106 |
+
* @see https://php.net/ctype-lower
|
107 |
+
*
|
108 |
+
* @param string|int $text
|
109 |
+
*
|
110 |
+
* @return bool
|
111 |
+
*/
|
112 |
+
public static function ctype_lower($text)
|
113 |
+
{
|
114 |
+
$text = self::convert_int_to_char_for_ctype($text);
|
115 |
+
|
116 |
+
return \is_string($text) && '' !== $text && !preg_match('/[^a-z]/', $text);
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Returns TRUE if every character in text will actually create output (including blanks). Returns FALSE if text contains control characters or characters that do not have any output or control function at all.
|
121 |
+
*
|
122 |
+
* @see https://php.net/ctype-print
|
123 |
+
*
|
124 |
+
* @param string|int $text
|
125 |
+
*
|
126 |
+
* @return bool
|
127 |
+
*/
|
128 |
+
public static function ctype_print($text)
|
129 |
+
{
|
130 |
+
$text = self::convert_int_to_char_for_ctype($text);
|
131 |
+
|
132 |
+
return \is_string($text) && '' !== $text && !preg_match('/[^ -~]/', $text);
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Returns TRUE if every character in text is printable, but neither letter, digit or blank, FALSE otherwise.
|
137 |
+
*
|
138 |
+
* @see https://php.net/ctype-punct
|
139 |
+
*
|
140 |
+
* @param string|int $text
|
141 |
+
*
|
142 |
+
* @return bool
|
143 |
+
*/
|
144 |
+
public static function ctype_punct($text)
|
145 |
+
{
|
146 |
+
$text = self::convert_int_to_char_for_ctype($text);
|
147 |
+
|
148 |
+
return \is_string($text) && '' !== $text && !preg_match('/[^!-\/\:-@\[-`\{-~]/', $text);
|
149 |
+
}
|
150 |
+
|
151 |
+
/**
|
152 |
+
* Returns TRUE if every character in text creates some sort of white space, FALSE otherwise. Besides the blank character this also includes tab, vertical tab, line feed, carriage return and form feed characters.
|
153 |
+
*
|
154 |
+
* @see https://php.net/ctype-space
|
155 |
+
*
|
156 |
+
* @param string|int $text
|
157 |
+
*
|
158 |
+
* @return bool
|
159 |
+
*/
|
160 |
+
public static function ctype_space($text)
|
161 |
+
{
|
162 |
+
$text = self::convert_int_to_char_for_ctype($text);
|
163 |
+
|
164 |
+
return \is_string($text) && '' !== $text && !preg_match('/[^\s]/', $text);
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* Returns TRUE if every character in text is an uppercase letter.
|
169 |
+
*
|
170 |
+
* @see https://php.net/ctype-upper
|
171 |
+
*
|
172 |
+
* @param string|int $text
|
173 |
+
*
|
174 |
+
* @return bool
|
175 |
+
*/
|
176 |
+
public static function ctype_upper($text)
|
177 |
+
{
|
178 |
+
$text = self::convert_int_to_char_for_ctype($text);
|
179 |
+
|
180 |
+
return \is_string($text) && '' !== $text && !preg_match('/[^A-Z]/', $text);
|
181 |
+
}
|
182 |
+
|
183 |
+
/**
|
184 |
+
* Returns TRUE if every character in text is a hexadecimal 'digit', that is a decimal digit or a character from [A-Fa-f] , FALSE otherwise.
|
185 |
+
*
|
186 |
+
* @see https://php.net/ctype-xdigit
|
187 |
+
*
|
188 |
+
* @param string|int $text
|
189 |
+
*
|
190 |
+
* @return bool
|
191 |
+
*/
|
192 |
+
public static function ctype_xdigit($text)
|
193 |
+
{
|
194 |
+
$text = self::convert_int_to_char_for_ctype($text);
|
195 |
+
|
196 |
+
return \is_string($text) && '' !== $text && !preg_match('/[^A-Fa-f0-9]/', $text);
|
197 |
+
}
|
198 |
+
|
199 |
+
/**
|
200 |
+
* Converts integers to their char versions according to normal ctype behaviour, if needed.
|
201 |
+
*
|
202 |
+
* If an integer between -128 and 255 inclusive is provided,
|
203 |
+
* it is interpreted as the ASCII value of a single character
|
204 |
+
* (negative values have 256 added in order to allow characters in the Extended ASCII range).
|
205 |
+
* Any other integer is interpreted as a string containing the decimal digits of the integer.
|
206 |
+
*
|
207 |
+
* @param string|int $int
|
208 |
+
*
|
209 |
+
* @return mixed
|
210 |
+
*/
|
211 |
+
private static function convert_int_to_char_for_ctype($int)
|
212 |
+
{
|
213 |
+
if (!\is_int($int)) {
|
214 |
+
return $int;
|
215 |
+
}
|
216 |
+
|
217 |
+
if ($int < -128 || $int > 255) {
|
218 |
+
return (string) $int;
|
219 |
+
}
|
220 |
+
|
221 |
+
if ($int < 0) {
|
222 |
+
$int += 256;
|
223 |
+
}
|
224 |
+
|
225 |
+
return \chr($int);
|
226 |
+
}
|
227 |
+
}
|
vendor/symfony/polyfill-ctype/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Copyright (c) 2018-2019 Fabien Potencier
|
2 |
+
|
3 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4 |
+
of this software and associated documentation files (the "Software"), to deal
|
5 |
+
in the Software without restriction, including without limitation the rights
|
6 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7 |
+
copies of the Software, and to permit persons to whom the Software is furnished
|
8 |
+
to do so, subject to the following conditions:
|
9 |
+
|
10 |
+
The above copyright notice and this permission notice shall be included in all
|
11 |
+
copies or substantial portions of the Software.
|
12 |
+
|
13 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19 |
+
THE SOFTWARE.
|
vendor/symfony/polyfill-ctype/bootstrap.php
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Symfony package.
|
5 |
+
*
|
6 |
+
* (c) Fabien Potencier <fabien@symfony.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
use Symfony\Polyfill\Ctype as p;
|
13 |
+
|
14 |
+
if (\PHP_VERSION_ID >= 80000) {
|
15 |
+
return require __DIR__.'/bootstrap80.php';
|
16 |
+
}
|
17 |
+
|
18 |
+
if (!function_exists('ctype_alnum')) {
|
19 |
+
function ctype_alnum($text) { return p\Ctype::ctype_alnum($text); }
|
20 |
+
}
|
21 |
+
if (!function_exists('ctype_alpha')) {
|
22 |
+
function ctype_alpha($text) { return p\Ctype::ctype_alpha($text); }
|
23 |
+
}
|
24 |
+
if (!function_exists('ctype_cntrl')) {
|
25 |
+
function ctype_cntrl($text) { return p\Ctype::ctype_cntrl($text); }
|
26 |
+
}
|
27 |
+
if (!function_exists('ctype_digit')) {
|
28 |
+
function ctype_digit($text) { return p\Ctype::ctype_digit($text); }
|
29 |
+
}
|
30 |
+
if (!function_exists('ctype_graph')) {
|
31 |
+
function ctype_graph($text) { return p\Ctype::ctype_graph($text); }
|
32 |
+
}
|
33 |
+
if (!function_exists('ctype_lower')) {
|
34 |
+
function ctype_lower($text) { return p\Ctype::ctype_lower($text); }
|
35 |
+
}
|
36 |
+
if (!function_exists('ctype_print')) {
|
37 |
+
function ctype_print($text) { return p\Ctype::ctype_print($text); }
|
38 |
+
}
|
39 |
+
if (!function_exists('ctype_punct')) {
|
40 |
+
function ctype_punct($text) { return p\Ctype::ctype_punct($text); }
|
41 |
+
}
|
42 |
+
if (!function_exists('ctype_space')) {
|
43 |
+
function ctype_space($text) { return p\Ctype::ctype_space($text); }
|
44 |
+
}
|
45 |
+
if (!function_exists('ctype_upper')) {
|
46 |
+
function ctype_upper($text) { return p\Ctype::ctype_upper($text); }
|
47 |
+
}
|
48 |
+
if (!function_exists('ctype_xdigit')) {
|
49 |
+
function ctype_xdigit($text) { return p\Ctype::ctype_xdigit($text); }
|
50 |
+
}
|
vendor/symfony/polyfill-ctype/bootstrap80.php
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
* This file is part of the Symfony package.
|
5 |
+
*
|
6 |
+
* (c) Fabien Potencier <fabien@symfony.com>
|
7 |
+
*
|
8 |
+
* For the full copyright and license information, please view the LICENSE
|
9 |
+
* file that was distributed with this source code.
|
10 |
+
*/
|
11 |
+
|
12 |
+
use Symfony\Polyfill\Ctype as p;
|
13 |
+
|
14 |
+
if (!function_exists('ctype_alnum')) {
|
15 |
+
function ctype_alnum(mixed $text): bool { return p\Ctype::ctype_alnum($text); }
|
16 |
+
}
|
17 |
+
if (!function_exists('ctype_alpha')) {
|
18 |
+
function ctype_alpha(mixed $text): bool { return p\Ctype::ctype_alpha($text); }
|
19 |
+
}
|
20 |
+
if (!function_exists('ctype_cntrl')) {
|
21 |
+
function ctype_cntrl(mixed $text): bool { return p\Ctype::ctype_cntrl($text); }
|
22 |
+
}
|
23 |
+
if (!function_exists('ctype_digit')) {
|
24 |
+
function ctype_digit(mixed $text): bool { return p\Ctype::ctype_digit($text); }
|
25 |
+
}
|
26 |
+
if (!function_exists('ctype_graph')) {
|
27 |
+
function ctype_graph(mixed $text): bool { return p\Ctype::ctype_graph($text); }
|
28 |
+
}
|
29 |
+
if (!function_exists('ctype_lower')) {
|
30 |
+
function ctype_lower(mixed $text): bool { return p\Ctype::ctype_lower($text); }
|
31 |
+
}
|
32 |
+
if (!function_exists('ctype_print')) {
|
33 |
+
function ctype_print(mixed $text): bool { return p\Ctype::ctype_print($text); }
|
34 |
+
}
|
35 |
+
if (!function_exists('ctype_punct')) {
|
36 |
+
function ctype_punct(mixed $text): bool { return p\Ctype::ctype_punct($text); }
|
37 |
+
}
|
38 |
+
if (!function_exists('ctype_space')) {
|
39 |
+
function ctype_space(mixed $text): bool { return p\Ctype::ctype_space($text); }
|
40 |
+
}
|
41 |
+
if (!function_exists('ctype_upper')) {
|
42 |
+
function ctype_upper(mixed $text): bool { return p\Ctype::ctype_upper($text); }
|
43 |
+
}
|
44 |
+
if (!function_exists('ctype_xdigit')) {
|
45 |
+
function ctype_xdigit(mixed $text): bool { return p\Ctype::ctype_xdigit($text); }
|
46 |
+
}
|