├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── composer.lock ├── concat-utils.php ├── cssconcat.php ├── cssmin ├── Colors.php ├── Minifier.php ├── Utils.php └── cssmin.php ├── jsconcat.php ├── ngx-http-concat.php ├── phpunit.xml.dist └── tests ├── bootstrap.php └── test-concat-utils.php /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | workflow_dispatch: 6 | 7 | permissions: 8 | contents: read 9 | 10 | jobs: 11 | test: 12 | name: 'Run tests (PHP ${{ matrix.php }}, WordPress ${{ matrix.wp }}, multisite: ${{ matrix.multisite }})' 13 | runs-on: ubuntu-latest 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | php: 18 | - '8.0' 19 | - '8.1' 20 | - '8.2' 21 | - '8.3' 22 | wp: 23 | - latest 24 | multisite: 25 | - 'no' 26 | include: 27 | - php: '8.1' 28 | wp: latest 29 | multisite: yes 30 | services: 31 | mysql: 32 | image: ghcr.io/automattic/vip-container-images/mariadb-lite:10.3 33 | ports: 34 | - "3306:3306" 35 | env: 36 | MYSQL_ROOT_PASSWORD: wordpress 37 | MARIADB_INITDB_SKIP_TZINFO: 1 38 | MYSQL_USER: wordpress 39 | MYSQL_PASSWORD: wordpress 40 | MYSQL_DATABASE: wordpress_test 41 | steps: 42 | - name: Check out source code 43 | uses: actions/checkout@v3 44 | 45 | - name: Install svn 46 | run: sudo apt-get update && sudo apt-get install -y subversion 47 | shell: bash 48 | 49 | - name: Set up PHP 50 | uses: shivammathur/setup-php@2.32.0 51 | with: 52 | php-version: ${{ matrix.php }} 53 | env: 54 | fail-fast: 'true' 55 | 56 | - name: Install PHP Dependencies 57 | uses: ramsey/composer-install@3.0.0 58 | 59 | - name: Set up WordPress and WordPress Test Library 60 | uses: sjinks/setup-wordpress-test-library@v2.1.3 61 | with: 62 | version: ${{ matrix.wp }} 63 | 64 | - name: Set up multisite mode 65 | run: echo "WP_MULTISITE=1" >> $GITHUB_ENV 66 | if: matrix.multisite == 'yes' 67 | 68 | - name: Verify MariaDB connection 69 | run: | 70 | echo Waiting for MySQL to come online... 71 | while ! mysqladmin ping -h 127.0.0.1 -P 3306 --silent; do 72 | sleep 1 73 | done 74 | timeout-minutes: 1 75 | 76 | - name: Run tests 77 | run: vendor/bin/phpunit 78 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | nginx-http-concat 2 | ================= 3 | 4 | Overview 5 | -------- 6 | 7 | WP.com plugin to perform CSS and Javascript concatenation of individual scripts into a single script. 8 | 9 | Installation & Configuration 10 | ---------------------------- 11 | 12 | 1. Copy the ‘http-concat’ directory and its contents to your WordPress plugins directory. 13 | 14 | 1. Configure the NGINX server to perform the concatenation step in the process by adding the following to your WordPress installations NGINX configuration: 15 | 16 | ```nginx 17 | location /_static/ { 18 | fastcgi_pass unix:/var/run/fastcgi.sock; 19 | include /etc/nginx/fastcgi_params; 20 | fastcgi_param SCRIPT_FILENAME $document_root/wp-content/plugins/http-concat/ngx-http-concat.php; 21 | } 22 | ``` 23 | 24 | 1. Once this is done the installation is ready for use and you can enable/disable the JS and/or CSS concatenation via the plugins interface of your WordPress installation. 25 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "automattic/nginx-http-concat", 3 | "description": "WP.com plugin to perform CSS and Javascript concatenation of individual scripts into a single script.", 4 | "homepage" : "https://github.com/Automattic/nginx-http-concat", 5 | "type" : "wordpress-plugin", 6 | "license" : "GPL-2.0+", 7 | "require" : { 8 | "php": ">=7.4", 9 | "composer/installers": "^1.0 || ^2.0" 10 | }, 11 | "require-dev": { 12 | "yoast/phpunit-polyfills": "^1.1.0" 13 | }, 14 | "config": { 15 | "platform": { 16 | "php": "7.4" 17 | }, 18 | "allow-plugins": { 19 | "composer/installers": true 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "06367dcc62b511a0b31736c78a3494f1", 8 | "packages": [ 9 | { 10 | "name": "composer/installers", 11 | "version": "v2.2.0", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/composer/installers.git", 15 | "reference": "c29dc4b93137acb82734f672c37e029dfbd95b35" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/composer/installers/zipball/c29dc4b93137acb82734f672c37e029dfbd95b35", 20 | "reference": "c29dc4b93137acb82734f672c37e029dfbd95b35", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "composer-plugin-api": "^1.0 || ^2.0", 25 | "php": "^7.2 || ^8.0" 26 | }, 27 | "require-dev": { 28 | "composer/composer": "1.6.* || ^2.0", 29 | "composer/semver": "^1 || ^3", 30 | "phpstan/phpstan": "^0.12.55", 31 | "phpstan/phpstan-phpunit": "^0.12.16", 32 | "symfony/phpunit-bridge": "^5.3", 33 | "symfony/process": "^5" 34 | }, 35 | "type": "composer-plugin", 36 | "extra": { 37 | "class": "Composer\\Installers\\Plugin", 38 | "branch-alias": { 39 | "dev-main": "2.x-dev" 40 | }, 41 | "plugin-modifies-install-path": true 42 | }, 43 | "autoload": { 44 | "psr-4": { 45 | "Composer\\Installers\\": "src/Composer/Installers" 46 | } 47 | }, 48 | "notification-url": "https://packagist.org/downloads/", 49 | "license": [ 50 | "MIT" 51 | ], 52 | "authors": [ 53 | { 54 | "name": "Kyle Robinson Young", 55 | "email": "kyle@dontkry.com", 56 | "homepage": "https://github.com/shama" 57 | } 58 | ], 59 | "description": "A multi-framework Composer library installer", 60 | "homepage": "https://composer.github.io/installers/", 61 | "keywords": [ 62 | "Dolibarr", 63 | "Eliasis", 64 | "Hurad", 65 | "ImageCMS", 66 | "Kanboard", 67 | "Lan Management System", 68 | "MODX Evo", 69 | "MantisBT", 70 | "Mautic", 71 | "Maya", 72 | "OXID", 73 | "Plentymarkets", 74 | "Porto", 75 | "RadPHP", 76 | "SMF", 77 | "Starbug", 78 | "Thelia", 79 | "Whmcs", 80 | "WolfCMS", 81 | "agl", 82 | "annotatecms", 83 | "attogram", 84 | "bitrix", 85 | "cakephp", 86 | "chef", 87 | "cockpit", 88 | "codeigniter", 89 | "concrete5", 90 | "croogo", 91 | "dokuwiki", 92 | "drupal", 93 | "eZ Platform", 94 | "elgg", 95 | "expressionengine", 96 | "fuelphp", 97 | "grav", 98 | "installer", 99 | "itop", 100 | "known", 101 | "kohana", 102 | "laravel", 103 | "lavalite", 104 | "lithium", 105 | "magento", 106 | "majima", 107 | "mako", 108 | "matomo", 109 | "mediawiki", 110 | "miaoxing", 111 | "modulework", 112 | "modx", 113 | "moodle", 114 | "osclass", 115 | "pantheon", 116 | "phpbb", 117 | "piwik", 118 | "ppi", 119 | "processwire", 120 | "puppet", 121 | "pxcms", 122 | "reindex", 123 | "roundcube", 124 | "shopware", 125 | "silverstripe", 126 | "sydes", 127 | "sylius", 128 | "tastyigniter", 129 | "wordpress", 130 | "yawik", 131 | "zend", 132 | "zikula" 133 | ], 134 | "support": { 135 | "issues": "https://github.com/composer/installers/issues", 136 | "source": "https://github.com/composer/installers/tree/v2.2.0" 137 | }, 138 | "funding": [ 139 | { 140 | "url": "https://packagist.com", 141 | "type": "custom" 142 | }, 143 | { 144 | "url": "https://github.com/composer", 145 | "type": "github" 146 | }, 147 | { 148 | "url": "https://tidelift.com/funding/github/packagist/composer/composer", 149 | "type": "tidelift" 150 | } 151 | ], 152 | "time": "2022-08-20T06:45:11+00:00" 153 | } 154 | ], 155 | "packages-dev": [ 156 | { 157 | "name": "doctrine/instantiator", 158 | "version": "1.5.0", 159 | "source": { 160 | "type": "git", 161 | "url": "https://github.com/doctrine/instantiator.git", 162 | "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" 163 | }, 164 | "dist": { 165 | "type": "zip", 166 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", 167 | "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", 168 | "shasum": "" 169 | }, 170 | "require": { 171 | "php": "^7.1 || ^8.0" 172 | }, 173 | "require-dev": { 174 | "doctrine/coding-standard": "^9 || ^11", 175 | "ext-pdo": "*", 176 | "ext-phar": "*", 177 | "phpbench/phpbench": "^0.16 || ^1", 178 | "phpstan/phpstan": "^1.4", 179 | "phpstan/phpstan-phpunit": "^1", 180 | "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", 181 | "vimeo/psalm": "^4.30 || ^5.4" 182 | }, 183 | "type": "library", 184 | "autoload": { 185 | "psr-4": { 186 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 187 | } 188 | }, 189 | "notification-url": "https://packagist.org/downloads/", 190 | "license": [ 191 | "MIT" 192 | ], 193 | "authors": [ 194 | { 195 | "name": "Marco Pivetta", 196 | "email": "ocramius@gmail.com", 197 | "homepage": "https://ocramius.github.io/" 198 | } 199 | ], 200 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 201 | "homepage": "https://www.doctrine-project.org/projects/instantiator.html", 202 | "keywords": [ 203 | "constructor", 204 | "instantiate" 205 | ], 206 | "support": { 207 | "issues": "https://github.com/doctrine/instantiator/issues", 208 | "source": "https://github.com/doctrine/instantiator/tree/1.5.0" 209 | }, 210 | "funding": [ 211 | { 212 | "url": "https://www.doctrine-project.org/sponsorship.html", 213 | "type": "custom" 214 | }, 215 | { 216 | "url": "https://www.patreon.com/phpdoctrine", 217 | "type": "patreon" 218 | }, 219 | { 220 | "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", 221 | "type": "tidelift" 222 | } 223 | ], 224 | "time": "2022-12-30T00:15:36+00:00" 225 | }, 226 | { 227 | "name": "myclabs/deep-copy", 228 | "version": "1.11.1", 229 | "source": { 230 | "type": "git", 231 | "url": "https://github.com/myclabs/DeepCopy.git", 232 | "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" 233 | }, 234 | "dist": { 235 | "type": "zip", 236 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", 237 | "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", 238 | "shasum": "" 239 | }, 240 | "require": { 241 | "php": "^7.1 || ^8.0" 242 | }, 243 | "conflict": { 244 | "doctrine/collections": "<1.6.8", 245 | "doctrine/common": "<2.13.3 || >=3,<3.2.2" 246 | }, 247 | "require-dev": { 248 | "doctrine/collections": "^1.6.8", 249 | "doctrine/common": "^2.13.3 || ^3.2.2", 250 | "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" 251 | }, 252 | "type": "library", 253 | "autoload": { 254 | "files": [ 255 | "src/DeepCopy/deep_copy.php" 256 | ], 257 | "psr-4": { 258 | "DeepCopy\\": "src/DeepCopy/" 259 | } 260 | }, 261 | "notification-url": "https://packagist.org/downloads/", 262 | "license": [ 263 | "MIT" 264 | ], 265 | "description": "Create deep copies (clones) of your objects", 266 | "keywords": [ 267 | "clone", 268 | "copy", 269 | "duplicate", 270 | "object", 271 | "object graph" 272 | ], 273 | "support": { 274 | "issues": "https://github.com/myclabs/DeepCopy/issues", 275 | "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" 276 | }, 277 | "funding": [ 278 | { 279 | "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", 280 | "type": "tidelift" 281 | } 282 | ], 283 | "time": "2023-03-08T13:26:56+00:00" 284 | }, 285 | { 286 | "name": "nikic/php-parser", 287 | "version": "v4.18.0", 288 | "source": { 289 | "type": "git", 290 | "url": "https://github.com/nikic/PHP-Parser.git", 291 | "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999" 292 | }, 293 | "dist": { 294 | "type": "zip", 295 | "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/1bcbb2179f97633e98bbbc87044ee2611c7d7999", 296 | "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999", 297 | "shasum": "" 298 | }, 299 | "require": { 300 | "ext-tokenizer": "*", 301 | "php": ">=7.0" 302 | }, 303 | "require-dev": { 304 | "ircmaxell/php-yacc": "^0.0.7", 305 | "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" 306 | }, 307 | "bin": [ 308 | "bin/php-parse" 309 | ], 310 | "type": "library", 311 | "extra": { 312 | "branch-alias": { 313 | "dev-master": "4.9-dev" 314 | } 315 | }, 316 | "autoload": { 317 | "psr-4": { 318 | "PhpParser\\": "lib/PhpParser" 319 | } 320 | }, 321 | "notification-url": "https://packagist.org/downloads/", 322 | "license": [ 323 | "BSD-3-Clause" 324 | ], 325 | "authors": [ 326 | { 327 | "name": "Nikita Popov" 328 | } 329 | ], 330 | "description": "A PHP parser written in PHP", 331 | "keywords": [ 332 | "parser", 333 | "php" 334 | ], 335 | "support": { 336 | "issues": "https://github.com/nikic/PHP-Parser/issues", 337 | "source": "https://github.com/nikic/PHP-Parser/tree/v4.18.0" 338 | }, 339 | "time": "2023-12-10T21:03:43+00:00" 340 | }, 341 | { 342 | "name": "phar-io/manifest", 343 | "version": "2.0.3", 344 | "source": { 345 | "type": "git", 346 | "url": "https://github.com/phar-io/manifest.git", 347 | "reference": "97803eca37d319dfa7826cc2437fc020857acb53" 348 | }, 349 | "dist": { 350 | "type": "zip", 351 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", 352 | "reference": "97803eca37d319dfa7826cc2437fc020857acb53", 353 | "shasum": "" 354 | }, 355 | "require": { 356 | "ext-dom": "*", 357 | "ext-phar": "*", 358 | "ext-xmlwriter": "*", 359 | "phar-io/version": "^3.0.1", 360 | "php": "^7.2 || ^8.0" 361 | }, 362 | "type": "library", 363 | "extra": { 364 | "branch-alias": { 365 | "dev-master": "2.0.x-dev" 366 | } 367 | }, 368 | "autoload": { 369 | "classmap": [ 370 | "src/" 371 | ] 372 | }, 373 | "notification-url": "https://packagist.org/downloads/", 374 | "license": [ 375 | "BSD-3-Clause" 376 | ], 377 | "authors": [ 378 | { 379 | "name": "Arne Blankerts", 380 | "email": "arne@blankerts.de", 381 | "role": "Developer" 382 | }, 383 | { 384 | "name": "Sebastian Heuer", 385 | "email": "sebastian@phpeople.de", 386 | "role": "Developer" 387 | }, 388 | { 389 | "name": "Sebastian Bergmann", 390 | "email": "sebastian@phpunit.de", 391 | "role": "Developer" 392 | } 393 | ], 394 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", 395 | "support": { 396 | "issues": "https://github.com/phar-io/manifest/issues", 397 | "source": "https://github.com/phar-io/manifest/tree/2.0.3" 398 | }, 399 | "time": "2021-07-20T11:28:43+00:00" 400 | }, 401 | { 402 | "name": "phar-io/version", 403 | "version": "3.2.1", 404 | "source": { 405 | "type": "git", 406 | "url": "https://github.com/phar-io/version.git", 407 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" 408 | }, 409 | "dist": { 410 | "type": "zip", 411 | "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 412 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 413 | "shasum": "" 414 | }, 415 | "require": { 416 | "php": "^7.2 || ^8.0" 417 | }, 418 | "type": "library", 419 | "autoload": { 420 | "classmap": [ 421 | "src/" 422 | ] 423 | }, 424 | "notification-url": "https://packagist.org/downloads/", 425 | "license": [ 426 | "BSD-3-Clause" 427 | ], 428 | "authors": [ 429 | { 430 | "name": "Arne Blankerts", 431 | "email": "arne@blankerts.de", 432 | "role": "Developer" 433 | }, 434 | { 435 | "name": "Sebastian Heuer", 436 | "email": "sebastian@phpeople.de", 437 | "role": "Developer" 438 | }, 439 | { 440 | "name": "Sebastian Bergmann", 441 | "email": "sebastian@phpunit.de", 442 | "role": "Developer" 443 | } 444 | ], 445 | "description": "Library for handling version information and constraints", 446 | "support": { 447 | "issues": "https://github.com/phar-io/version/issues", 448 | "source": "https://github.com/phar-io/version/tree/3.2.1" 449 | }, 450 | "time": "2022-02-21T01:04:05+00:00" 451 | }, 452 | { 453 | "name": "phpunit/php-code-coverage", 454 | "version": "9.2.29", 455 | "source": { 456 | "type": "git", 457 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 458 | "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76" 459 | }, 460 | "dist": { 461 | "type": "zip", 462 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6a3a87ac2bbe33b25042753df8195ba4aa534c76", 463 | "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76", 464 | "shasum": "" 465 | }, 466 | "require": { 467 | "ext-dom": "*", 468 | "ext-libxml": "*", 469 | "ext-xmlwriter": "*", 470 | "nikic/php-parser": "^4.15", 471 | "php": ">=7.3", 472 | "phpunit/php-file-iterator": "^3.0.3", 473 | "phpunit/php-text-template": "^2.0.2", 474 | "sebastian/code-unit-reverse-lookup": "^2.0.2", 475 | "sebastian/complexity": "^2.0", 476 | "sebastian/environment": "^5.1.2", 477 | "sebastian/lines-of-code": "^1.0.3", 478 | "sebastian/version": "^3.0.1", 479 | "theseer/tokenizer": "^1.2.0" 480 | }, 481 | "require-dev": { 482 | "phpunit/phpunit": "^9.3" 483 | }, 484 | "suggest": { 485 | "ext-pcov": "PHP extension that provides line coverage", 486 | "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" 487 | }, 488 | "type": "library", 489 | "extra": { 490 | "branch-alias": { 491 | "dev-master": "9.2-dev" 492 | } 493 | }, 494 | "autoload": { 495 | "classmap": [ 496 | "src/" 497 | ] 498 | }, 499 | "notification-url": "https://packagist.org/downloads/", 500 | "license": [ 501 | "BSD-3-Clause" 502 | ], 503 | "authors": [ 504 | { 505 | "name": "Sebastian Bergmann", 506 | "email": "sebastian@phpunit.de", 507 | "role": "lead" 508 | } 509 | ], 510 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 511 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 512 | "keywords": [ 513 | "coverage", 514 | "testing", 515 | "xunit" 516 | ], 517 | "support": { 518 | "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", 519 | "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", 520 | "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.29" 521 | }, 522 | "funding": [ 523 | { 524 | "url": "https://github.com/sebastianbergmann", 525 | "type": "github" 526 | } 527 | ], 528 | "time": "2023-09-19T04:57:46+00:00" 529 | }, 530 | { 531 | "name": "phpunit/php-file-iterator", 532 | "version": "3.0.6", 533 | "source": { 534 | "type": "git", 535 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 536 | "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" 537 | }, 538 | "dist": { 539 | "type": "zip", 540 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", 541 | "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", 542 | "shasum": "" 543 | }, 544 | "require": { 545 | "php": ">=7.3" 546 | }, 547 | "require-dev": { 548 | "phpunit/phpunit": "^9.3" 549 | }, 550 | "type": "library", 551 | "extra": { 552 | "branch-alias": { 553 | "dev-master": "3.0-dev" 554 | } 555 | }, 556 | "autoload": { 557 | "classmap": [ 558 | "src/" 559 | ] 560 | }, 561 | "notification-url": "https://packagist.org/downloads/", 562 | "license": [ 563 | "BSD-3-Clause" 564 | ], 565 | "authors": [ 566 | { 567 | "name": "Sebastian Bergmann", 568 | "email": "sebastian@phpunit.de", 569 | "role": "lead" 570 | } 571 | ], 572 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 573 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 574 | "keywords": [ 575 | "filesystem", 576 | "iterator" 577 | ], 578 | "support": { 579 | "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", 580 | "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" 581 | }, 582 | "funding": [ 583 | { 584 | "url": "https://github.com/sebastianbergmann", 585 | "type": "github" 586 | } 587 | ], 588 | "time": "2021-12-02T12:48:52+00:00" 589 | }, 590 | { 591 | "name": "phpunit/php-invoker", 592 | "version": "3.1.1", 593 | "source": { 594 | "type": "git", 595 | "url": "https://github.com/sebastianbergmann/php-invoker.git", 596 | "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" 597 | }, 598 | "dist": { 599 | "type": "zip", 600 | "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", 601 | "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", 602 | "shasum": "" 603 | }, 604 | "require": { 605 | "php": ">=7.3" 606 | }, 607 | "require-dev": { 608 | "ext-pcntl": "*", 609 | "phpunit/phpunit": "^9.3" 610 | }, 611 | "suggest": { 612 | "ext-pcntl": "*" 613 | }, 614 | "type": "library", 615 | "extra": { 616 | "branch-alias": { 617 | "dev-master": "3.1-dev" 618 | } 619 | }, 620 | "autoload": { 621 | "classmap": [ 622 | "src/" 623 | ] 624 | }, 625 | "notification-url": "https://packagist.org/downloads/", 626 | "license": [ 627 | "BSD-3-Clause" 628 | ], 629 | "authors": [ 630 | { 631 | "name": "Sebastian Bergmann", 632 | "email": "sebastian@phpunit.de", 633 | "role": "lead" 634 | } 635 | ], 636 | "description": "Invoke callables with a timeout", 637 | "homepage": "https://github.com/sebastianbergmann/php-invoker/", 638 | "keywords": [ 639 | "process" 640 | ], 641 | "support": { 642 | "issues": "https://github.com/sebastianbergmann/php-invoker/issues", 643 | "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" 644 | }, 645 | "funding": [ 646 | { 647 | "url": "https://github.com/sebastianbergmann", 648 | "type": "github" 649 | } 650 | ], 651 | "time": "2020-09-28T05:58:55+00:00" 652 | }, 653 | { 654 | "name": "phpunit/php-text-template", 655 | "version": "2.0.4", 656 | "source": { 657 | "type": "git", 658 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 659 | "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" 660 | }, 661 | "dist": { 662 | "type": "zip", 663 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", 664 | "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", 665 | "shasum": "" 666 | }, 667 | "require": { 668 | "php": ">=7.3" 669 | }, 670 | "require-dev": { 671 | "phpunit/phpunit": "^9.3" 672 | }, 673 | "type": "library", 674 | "extra": { 675 | "branch-alias": { 676 | "dev-master": "2.0-dev" 677 | } 678 | }, 679 | "autoload": { 680 | "classmap": [ 681 | "src/" 682 | ] 683 | }, 684 | "notification-url": "https://packagist.org/downloads/", 685 | "license": [ 686 | "BSD-3-Clause" 687 | ], 688 | "authors": [ 689 | { 690 | "name": "Sebastian Bergmann", 691 | "email": "sebastian@phpunit.de", 692 | "role": "lead" 693 | } 694 | ], 695 | "description": "Simple template engine.", 696 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 697 | "keywords": [ 698 | "template" 699 | ], 700 | "support": { 701 | "issues": "https://github.com/sebastianbergmann/php-text-template/issues", 702 | "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" 703 | }, 704 | "funding": [ 705 | { 706 | "url": "https://github.com/sebastianbergmann", 707 | "type": "github" 708 | } 709 | ], 710 | "time": "2020-10-26T05:33:50+00:00" 711 | }, 712 | { 713 | "name": "phpunit/php-timer", 714 | "version": "5.0.3", 715 | "source": { 716 | "type": "git", 717 | "url": "https://github.com/sebastianbergmann/php-timer.git", 718 | "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" 719 | }, 720 | "dist": { 721 | "type": "zip", 722 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", 723 | "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", 724 | "shasum": "" 725 | }, 726 | "require": { 727 | "php": ">=7.3" 728 | }, 729 | "require-dev": { 730 | "phpunit/phpunit": "^9.3" 731 | }, 732 | "type": "library", 733 | "extra": { 734 | "branch-alias": { 735 | "dev-master": "5.0-dev" 736 | } 737 | }, 738 | "autoload": { 739 | "classmap": [ 740 | "src/" 741 | ] 742 | }, 743 | "notification-url": "https://packagist.org/downloads/", 744 | "license": [ 745 | "BSD-3-Clause" 746 | ], 747 | "authors": [ 748 | { 749 | "name": "Sebastian Bergmann", 750 | "email": "sebastian@phpunit.de", 751 | "role": "lead" 752 | } 753 | ], 754 | "description": "Utility class for timing", 755 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 756 | "keywords": [ 757 | "timer" 758 | ], 759 | "support": { 760 | "issues": "https://github.com/sebastianbergmann/php-timer/issues", 761 | "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" 762 | }, 763 | "funding": [ 764 | { 765 | "url": "https://github.com/sebastianbergmann", 766 | "type": "github" 767 | } 768 | ], 769 | "time": "2020-10-26T13:16:10+00:00" 770 | }, 771 | { 772 | "name": "phpunit/phpunit", 773 | "version": "9.6.15", 774 | "source": { 775 | "type": "git", 776 | "url": "https://github.com/sebastianbergmann/phpunit.git", 777 | "reference": "05017b80304e0eb3f31d90194a563fd53a6021f1" 778 | }, 779 | "dist": { 780 | "type": "zip", 781 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/05017b80304e0eb3f31d90194a563fd53a6021f1", 782 | "reference": "05017b80304e0eb3f31d90194a563fd53a6021f1", 783 | "shasum": "" 784 | }, 785 | "require": { 786 | "doctrine/instantiator": "^1.3.1 || ^2", 787 | "ext-dom": "*", 788 | "ext-json": "*", 789 | "ext-libxml": "*", 790 | "ext-mbstring": "*", 791 | "ext-xml": "*", 792 | "ext-xmlwriter": "*", 793 | "myclabs/deep-copy": "^1.10.1", 794 | "phar-io/manifest": "^2.0.3", 795 | "phar-io/version": "^3.0.2", 796 | "php": ">=7.3", 797 | "phpunit/php-code-coverage": "^9.2.28", 798 | "phpunit/php-file-iterator": "^3.0.5", 799 | "phpunit/php-invoker": "^3.1.1", 800 | "phpunit/php-text-template": "^2.0.3", 801 | "phpunit/php-timer": "^5.0.2", 802 | "sebastian/cli-parser": "^1.0.1", 803 | "sebastian/code-unit": "^1.0.6", 804 | "sebastian/comparator": "^4.0.8", 805 | "sebastian/diff": "^4.0.3", 806 | "sebastian/environment": "^5.1.3", 807 | "sebastian/exporter": "^4.0.5", 808 | "sebastian/global-state": "^5.0.1", 809 | "sebastian/object-enumerator": "^4.0.3", 810 | "sebastian/resource-operations": "^3.0.3", 811 | "sebastian/type": "^3.2", 812 | "sebastian/version": "^3.0.2" 813 | }, 814 | "suggest": { 815 | "ext-soap": "To be able to generate mocks based on WSDL files", 816 | "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" 817 | }, 818 | "bin": [ 819 | "phpunit" 820 | ], 821 | "type": "library", 822 | "extra": { 823 | "branch-alias": { 824 | "dev-master": "9.6-dev" 825 | } 826 | }, 827 | "autoload": { 828 | "files": [ 829 | "src/Framework/Assert/Functions.php" 830 | ], 831 | "classmap": [ 832 | "src/" 833 | ] 834 | }, 835 | "notification-url": "https://packagist.org/downloads/", 836 | "license": [ 837 | "BSD-3-Clause" 838 | ], 839 | "authors": [ 840 | { 841 | "name": "Sebastian Bergmann", 842 | "email": "sebastian@phpunit.de", 843 | "role": "lead" 844 | } 845 | ], 846 | "description": "The PHP Unit Testing framework.", 847 | "homepage": "https://phpunit.de/", 848 | "keywords": [ 849 | "phpunit", 850 | "testing", 851 | "xunit" 852 | ], 853 | "support": { 854 | "issues": "https://github.com/sebastianbergmann/phpunit/issues", 855 | "security": "https://github.com/sebastianbergmann/phpunit/security/policy", 856 | "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.15" 857 | }, 858 | "funding": [ 859 | { 860 | "url": "https://phpunit.de/sponsors.html", 861 | "type": "custom" 862 | }, 863 | { 864 | "url": "https://github.com/sebastianbergmann", 865 | "type": "github" 866 | }, 867 | { 868 | "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", 869 | "type": "tidelift" 870 | } 871 | ], 872 | "time": "2023-12-01T16:55:19+00:00" 873 | }, 874 | { 875 | "name": "sebastian/cli-parser", 876 | "version": "1.0.1", 877 | "source": { 878 | "type": "git", 879 | "url": "https://github.com/sebastianbergmann/cli-parser.git", 880 | "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" 881 | }, 882 | "dist": { 883 | "type": "zip", 884 | "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", 885 | "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", 886 | "shasum": "" 887 | }, 888 | "require": { 889 | "php": ">=7.3" 890 | }, 891 | "require-dev": { 892 | "phpunit/phpunit": "^9.3" 893 | }, 894 | "type": "library", 895 | "extra": { 896 | "branch-alias": { 897 | "dev-master": "1.0-dev" 898 | } 899 | }, 900 | "autoload": { 901 | "classmap": [ 902 | "src/" 903 | ] 904 | }, 905 | "notification-url": "https://packagist.org/downloads/", 906 | "license": [ 907 | "BSD-3-Clause" 908 | ], 909 | "authors": [ 910 | { 911 | "name": "Sebastian Bergmann", 912 | "email": "sebastian@phpunit.de", 913 | "role": "lead" 914 | } 915 | ], 916 | "description": "Library for parsing CLI options", 917 | "homepage": "https://github.com/sebastianbergmann/cli-parser", 918 | "support": { 919 | "issues": "https://github.com/sebastianbergmann/cli-parser/issues", 920 | "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" 921 | }, 922 | "funding": [ 923 | { 924 | "url": "https://github.com/sebastianbergmann", 925 | "type": "github" 926 | } 927 | ], 928 | "time": "2020-09-28T06:08:49+00:00" 929 | }, 930 | { 931 | "name": "sebastian/code-unit", 932 | "version": "1.0.8", 933 | "source": { 934 | "type": "git", 935 | "url": "https://github.com/sebastianbergmann/code-unit.git", 936 | "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" 937 | }, 938 | "dist": { 939 | "type": "zip", 940 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", 941 | "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", 942 | "shasum": "" 943 | }, 944 | "require": { 945 | "php": ">=7.3" 946 | }, 947 | "require-dev": { 948 | "phpunit/phpunit": "^9.3" 949 | }, 950 | "type": "library", 951 | "extra": { 952 | "branch-alias": { 953 | "dev-master": "1.0-dev" 954 | } 955 | }, 956 | "autoload": { 957 | "classmap": [ 958 | "src/" 959 | ] 960 | }, 961 | "notification-url": "https://packagist.org/downloads/", 962 | "license": [ 963 | "BSD-3-Clause" 964 | ], 965 | "authors": [ 966 | { 967 | "name": "Sebastian Bergmann", 968 | "email": "sebastian@phpunit.de", 969 | "role": "lead" 970 | } 971 | ], 972 | "description": "Collection of value objects that represent the PHP code units", 973 | "homepage": "https://github.com/sebastianbergmann/code-unit", 974 | "support": { 975 | "issues": "https://github.com/sebastianbergmann/code-unit/issues", 976 | "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" 977 | }, 978 | "funding": [ 979 | { 980 | "url": "https://github.com/sebastianbergmann", 981 | "type": "github" 982 | } 983 | ], 984 | "time": "2020-10-26T13:08:54+00:00" 985 | }, 986 | { 987 | "name": "sebastian/code-unit-reverse-lookup", 988 | "version": "2.0.3", 989 | "source": { 990 | "type": "git", 991 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 992 | "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" 993 | }, 994 | "dist": { 995 | "type": "zip", 996 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", 997 | "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", 998 | "shasum": "" 999 | }, 1000 | "require": { 1001 | "php": ">=7.3" 1002 | }, 1003 | "require-dev": { 1004 | "phpunit/phpunit": "^9.3" 1005 | }, 1006 | "type": "library", 1007 | "extra": { 1008 | "branch-alias": { 1009 | "dev-master": "2.0-dev" 1010 | } 1011 | }, 1012 | "autoload": { 1013 | "classmap": [ 1014 | "src/" 1015 | ] 1016 | }, 1017 | "notification-url": "https://packagist.org/downloads/", 1018 | "license": [ 1019 | "BSD-3-Clause" 1020 | ], 1021 | "authors": [ 1022 | { 1023 | "name": "Sebastian Bergmann", 1024 | "email": "sebastian@phpunit.de" 1025 | } 1026 | ], 1027 | "description": "Looks up which function or method a line of code belongs to", 1028 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 1029 | "support": { 1030 | "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", 1031 | "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" 1032 | }, 1033 | "funding": [ 1034 | { 1035 | "url": "https://github.com/sebastianbergmann", 1036 | "type": "github" 1037 | } 1038 | ], 1039 | "time": "2020-09-28T05:30:19+00:00" 1040 | }, 1041 | { 1042 | "name": "sebastian/comparator", 1043 | "version": "4.0.8", 1044 | "source": { 1045 | "type": "git", 1046 | "url": "https://github.com/sebastianbergmann/comparator.git", 1047 | "reference": "fa0f136dd2334583309d32b62544682ee972b51a" 1048 | }, 1049 | "dist": { 1050 | "type": "zip", 1051 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", 1052 | "reference": "fa0f136dd2334583309d32b62544682ee972b51a", 1053 | "shasum": "" 1054 | }, 1055 | "require": { 1056 | "php": ">=7.3", 1057 | "sebastian/diff": "^4.0", 1058 | "sebastian/exporter": "^4.0" 1059 | }, 1060 | "require-dev": { 1061 | "phpunit/phpunit": "^9.3" 1062 | }, 1063 | "type": "library", 1064 | "extra": { 1065 | "branch-alias": { 1066 | "dev-master": "4.0-dev" 1067 | } 1068 | }, 1069 | "autoload": { 1070 | "classmap": [ 1071 | "src/" 1072 | ] 1073 | }, 1074 | "notification-url": "https://packagist.org/downloads/", 1075 | "license": [ 1076 | "BSD-3-Clause" 1077 | ], 1078 | "authors": [ 1079 | { 1080 | "name": "Sebastian Bergmann", 1081 | "email": "sebastian@phpunit.de" 1082 | }, 1083 | { 1084 | "name": "Jeff Welch", 1085 | "email": "whatthejeff@gmail.com" 1086 | }, 1087 | { 1088 | "name": "Volker Dusch", 1089 | "email": "github@wallbash.com" 1090 | }, 1091 | { 1092 | "name": "Bernhard Schussek", 1093 | "email": "bschussek@2bepublished.at" 1094 | } 1095 | ], 1096 | "description": "Provides the functionality to compare PHP values for equality", 1097 | "homepage": "https://github.com/sebastianbergmann/comparator", 1098 | "keywords": [ 1099 | "comparator", 1100 | "compare", 1101 | "equality" 1102 | ], 1103 | "support": { 1104 | "issues": "https://github.com/sebastianbergmann/comparator/issues", 1105 | "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" 1106 | }, 1107 | "funding": [ 1108 | { 1109 | "url": "https://github.com/sebastianbergmann", 1110 | "type": "github" 1111 | } 1112 | ], 1113 | "time": "2022-09-14T12:41:17+00:00" 1114 | }, 1115 | { 1116 | "name": "sebastian/complexity", 1117 | "version": "2.0.2", 1118 | "source": { 1119 | "type": "git", 1120 | "url": "https://github.com/sebastianbergmann/complexity.git", 1121 | "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" 1122 | }, 1123 | "dist": { 1124 | "type": "zip", 1125 | "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", 1126 | "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", 1127 | "shasum": "" 1128 | }, 1129 | "require": { 1130 | "nikic/php-parser": "^4.7", 1131 | "php": ">=7.3" 1132 | }, 1133 | "require-dev": { 1134 | "phpunit/phpunit": "^9.3" 1135 | }, 1136 | "type": "library", 1137 | "extra": { 1138 | "branch-alias": { 1139 | "dev-master": "2.0-dev" 1140 | } 1141 | }, 1142 | "autoload": { 1143 | "classmap": [ 1144 | "src/" 1145 | ] 1146 | }, 1147 | "notification-url": "https://packagist.org/downloads/", 1148 | "license": [ 1149 | "BSD-3-Clause" 1150 | ], 1151 | "authors": [ 1152 | { 1153 | "name": "Sebastian Bergmann", 1154 | "email": "sebastian@phpunit.de", 1155 | "role": "lead" 1156 | } 1157 | ], 1158 | "description": "Library for calculating the complexity of PHP code units", 1159 | "homepage": "https://github.com/sebastianbergmann/complexity", 1160 | "support": { 1161 | "issues": "https://github.com/sebastianbergmann/complexity/issues", 1162 | "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" 1163 | }, 1164 | "funding": [ 1165 | { 1166 | "url": "https://github.com/sebastianbergmann", 1167 | "type": "github" 1168 | } 1169 | ], 1170 | "time": "2020-10-26T15:52:27+00:00" 1171 | }, 1172 | { 1173 | "name": "sebastian/diff", 1174 | "version": "4.0.5", 1175 | "source": { 1176 | "type": "git", 1177 | "url": "https://github.com/sebastianbergmann/diff.git", 1178 | "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" 1179 | }, 1180 | "dist": { 1181 | "type": "zip", 1182 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", 1183 | "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", 1184 | "shasum": "" 1185 | }, 1186 | "require": { 1187 | "php": ">=7.3" 1188 | }, 1189 | "require-dev": { 1190 | "phpunit/phpunit": "^9.3", 1191 | "symfony/process": "^4.2 || ^5" 1192 | }, 1193 | "type": "library", 1194 | "extra": { 1195 | "branch-alias": { 1196 | "dev-master": "4.0-dev" 1197 | } 1198 | }, 1199 | "autoload": { 1200 | "classmap": [ 1201 | "src/" 1202 | ] 1203 | }, 1204 | "notification-url": "https://packagist.org/downloads/", 1205 | "license": [ 1206 | "BSD-3-Clause" 1207 | ], 1208 | "authors": [ 1209 | { 1210 | "name": "Sebastian Bergmann", 1211 | "email": "sebastian@phpunit.de" 1212 | }, 1213 | { 1214 | "name": "Kore Nordmann", 1215 | "email": "mail@kore-nordmann.de" 1216 | } 1217 | ], 1218 | "description": "Diff implementation", 1219 | "homepage": "https://github.com/sebastianbergmann/diff", 1220 | "keywords": [ 1221 | "diff", 1222 | "udiff", 1223 | "unidiff", 1224 | "unified diff" 1225 | ], 1226 | "support": { 1227 | "issues": "https://github.com/sebastianbergmann/diff/issues", 1228 | "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" 1229 | }, 1230 | "funding": [ 1231 | { 1232 | "url": "https://github.com/sebastianbergmann", 1233 | "type": "github" 1234 | } 1235 | ], 1236 | "time": "2023-05-07T05:35:17+00:00" 1237 | }, 1238 | { 1239 | "name": "sebastian/environment", 1240 | "version": "5.1.5", 1241 | "source": { 1242 | "type": "git", 1243 | "url": "https://github.com/sebastianbergmann/environment.git", 1244 | "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" 1245 | }, 1246 | "dist": { 1247 | "type": "zip", 1248 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", 1249 | "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", 1250 | "shasum": "" 1251 | }, 1252 | "require": { 1253 | "php": ">=7.3" 1254 | }, 1255 | "require-dev": { 1256 | "phpunit/phpunit": "^9.3" 1257 | }, 1258 | "suggest": { 1259 | "ext-posix": "*" 1260 | }, 1261 | "type": "library", 1262 | "extra": { 1263 | "branch-alias": { 1264 | "dev-master": "5.1-dev" 1265 | } 1266 | }, 1267 | "autoload": { 1268 | "classmap": [ 1269 | "src/" 1270 | ] 1271 | }, 1272 | "notification-url": "https://packagist.org/downloads/", 1273 | "license": [ 1274 | "BSD-3-Clause" 1275 | ], 1276 | "authors": [ 1277 | { 1278 | "name": "Sebastian Bergmann", 1279 | "email": "sebastian@phpunit.de" 1280 | } 1281 | ], 1282 | "description": "Provides functionality to handle HHVM/PHP environments", 1283 | "homepage": "http://www.github.com/sebastianbergmann/environment", 1284 | "keywords": [ 1285 | "Xdebug", 1286 | "environment", 1287 | "hhvm" 1288 | ], 1289 | "support": { 1290 | "issues": "https://github.com/sebastianbergmann/environment/issues", 1291 | "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" 1292 | }, 1293 | "funding": [ 1294 | { 1295 | "url": "https://github.com/sebastianbergmann", 1296 | "type": "github" 1297 | } 1298 | ], 1299 | "time": "2023-02-03T06:03:51+00:00" 1300 | }, 1301 | { 1302 | "name": "sebastian/exporter", 1303 | "version": "4.0.5", 1304 | "source": { 1305 | "type": "git", 1306 | "url": "https://github.com/sebastianbergmann/exporter.git", 1307 | "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" 1308 | }, 1309 | "dist": { 1310 | "type": "zip", 1311 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", 1312 | "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", 1313 | "shasum": "" 1314 | }, 1315 | "require": { 1316 | "php": ">=7.3", 1317 | "sebastian/recursion-context": "^4.0" 1318 | }, 1319 | "require-dev": { 1320 | "ext-mbstring": "*", 1321 | "phpunit/phpunit": "^9.3" 1322 | }, 1323 | "type": "library", 1324 | "extra": { 1325 | "branch-alias": { 1326 | "dev-master": "4.0-dev" 1327 | } 1328 | }, 1329 | "autoload": { 1330 | "classmap": [ 1331 | "src/" 1332 | ] 1333 | }, 1334 | "notification-url": "https://packagist.org/downloads/", 1335 | "license": [ 1336 | "BSD-3-Clause" 1337 | ], 1338 | "authors": [ 1339 | { 1340 | "name": "Sebastian Bergmann", 1341 | "email": "sebastian@phpunit.de" 1342 | }, 1343 | { 1344 | "name": "Jeff Welch", 1345 | "email": "whatthejeff@gmail.com" 1346 | }, 1347 | { 1348 | "name": "Volker Dusch", 1349 | "email": "github@wallbash.com" 1350 | }, 1351 | { 1352 | "name": "Adam Harvey", 1353 | "email": "aharvey@php.net" 1354 | }, 1355 | { 1356 | "name": "Bernhard Schussek", 1357 | "email": "bschussek@gmail.com" 1358 | } 1359 | ], 1360 | "description": "Provides the functionality to export PHP variables for visualization", 1361 | "homepage": "https://www.github.com/sebastianbergmann/exporter", 1362 | "keywords": [ 1363 | "export", 1364 | "exporter" 1365 | ], 1366 | "support": { 1367 | "issues": "https://github.com/sebastianbergmann/exporter/issues", 1368 | "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" 1369 | }, 1370 | "funding": [ 1371 | { 1372 | "url": "https://github.com/sebastianbergmann", 1373 | "type": "github" 1374 | } 1375 | ], 1376 | "time": "2022-09-14T06:03:37+00:00" 1377 | }, 1378 | { 1379 | "name": "sebastian/global-state", 1380 | "version": "5.0.6", 1381 | "source": { 1382 | "type": "git", 1383 | "url": "https://github.com/sebastianbergmann/global-state.git", 1384 | "reference": "bde739e7565280bda77be70044ac1047bc007e34" 1385 | }, 1386 | "dist": { 1387 | "type": "zip", 1388 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", 1389 | "reference": "bde739e7565280bda77be70044ac1047bc007e34", 1390 | "shasum": "" 1391 | }, 1392 | "require": { 1393 | "php": ">=7.3", 1394 | "sebastian/object-reflector": "^2.0", 1395 | "sebastian/recursion-context": "^4.0" 1396 | }, 1397 | "require-dev": { 1398 | "ext-dom": "*", 1399 | "phpunit/phpunit": "^9.3" 1400 | }, 1401 | "suggest": { 1402 | "ext-uopz": "*" 1403 | }, 1404 | "type": "library", 1405 | "extra": { 1406 | "branch-alias": { 1407 | "dev-master": "5.0-dev" 1408 | } 1409 | }, 1410 | "autoload": { 1411 | "classmap": [ 1412 | "src/" 1413 | ] 1414 | }, 1415 | "notification-url": "https://packagist.org/downloads/", 1416 | "license": [ 1417 | "BSD-3-Clause" 1418 | ], 1419 | "authors": [ 1420 | { 1421 | "name": "Sebastian Bergmann", 1422 | "email": "sebastian@phpunit.de" 1423 | } 1424 | ], 1425 | "description": "Snapshotting of global state", 1426 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1427 | "keywords": [ 1428 | "global state" 1429 | ], 1430 | "support": { 1431 | "issues": "https://github.com/sebastianbergmann/global-state/issues", 1432 | "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" 1433 | }, 1434 | "funding": [ 1435 | { 1436 | "url": "https://github.com/sebastianbergmann", 1437 | "type": "github" 1438 | } 1439 | ], 1440 | "time": "2023-08-02T09:26:13+00:00" 1441 | }, 1442 | { 1443 | "name": "sebastian/lines-of-code", 1444 | "version": "1.0.3", 1445 | "source": { 1446 | "type": "git", 1447 | "url": "https://github.com/sebastianbergmann/lines-of-code.git", 1448 | "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" 1449 | }, 1450 | "dist": { 1451 | "type": "zip", 1452 | "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", 1453 | "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", 1454 | "shasum": "" 1455 | }, 1456 | "require": { 1457 | "nikic/php-parser": "^4.6", 1458 | "php": ">=7.3" 1459 | }, 1460 | "require-dev": { 1461 | "phpunit/phpunit": "^9.3" 1462 | }, 1463 | "type": "library", 1464 | "extra": { 1465 | "branch-alias": { 1466 | "dev-master": "1.0-dev" 1467 | } 1468 | }, 1469 | "autoload": { 1470 | "classmap": [ 1471 | "src/" 1472 | ] 1473 | }, 1474 | "notification-url": "https://packagist.org/downloads/", 1475 | "license": [ 1476 | "BSD-3-Clause" 1477 | ], 1478 | "authors": [ 1479 | { 1480 | "name": "Sebastian Bergmann", 1481 | "email": "sebastian@phpunit.de", 1482 | "role": "lead" 1483 | } 1484 | ], 1485 | "description": "Library for counting the lines of code in PHP source code", 1486 | "homepage": "https://github.com/sebastianbergmann/lines-of-code", 1487 | "support": { 1488 | "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", 1489 | "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" 1490 | }, 1491 | "funding": [ 1492 | { 1493 | "url": "https://github.com/sebastianbergmann", 1494 | "type": "github" 1495 | } 1496 | ], 1497 | "time": "2020-11-28T06:42:11+00:00" 1498 | }, 1499 | { 1500 | "name": "sebastian/object-enumerator", 1501 | "version": "4.0.4", 1502 | "source": { 1503 | "type": "git", 1504 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 1505 | "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" 1506 | }, 1507 | "dist": { 1508 | "type": "zip", 1509 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", 1510 | "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", 1511 | "shasum": "" 1512 | }, 1513 | "require": { 1514 | "php": ">=7.3", 1515 | "sebastian/object-reflector": "^2.0", 1516 | "sebastian/recursion-context": "^4.0" 1517 | }, 1518 | "require-dev": { 1519 | "phpunit/phpunit": "^9.3" 1520 | }, 1521 | "type": "library", 1522 | "extra": { 1523 | "branch-alias": { 1524 | "dev-master": "4.0-dev" 1525 | } 1526 | }, 1527 | "autoload": { 1528 | "classmap": [ 1529 | "src/" 1530 | ] 1531 | }, 1532 | "notification-url": "https://packagist.org/downloads/", 1533 | "license": [ 1534 | "BSD-3-Clause" 1535 | ], 1536 | "authors": [ 1537 | { 1538 | "name": "Sebastian Bergmann", 1539 | "email": "sebastian@phpunit.de" 1540 | } 1541 | ], 1542 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 1543 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 1544 | "support": { 1545 | "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", 1546 | "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" 1547 | }, 1548 | "funding": [ 1549 | { 1550 | "url": "https://github.com/sebastianbergmann", 1551 | "type": "github" 1552 | } 1553 | ], 1554 | "time": "2020-10-26T13:12:34+00:00" 1555 | }, 1556 | { 1557 | "name": "sebastian/object-reflector", 1558 | "version": "2.0.4", 1559 | "source": { 1560 | "type": "git", 1561 | "url": "https://github.com/sebastianbergmann/object-reflector.git", 1562 | "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" 1563 | }, 1564 | "dist": { 1565 | "type": "zip", 1566 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", 1567 | "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", 1568 | "shasum": "" 1569 | }, 1570 | "require": { 1571 | "php": ">=7.3" 1572 | }, 1573 | "require-dev": { 1574 | "phpunit/phpunit": "^9.3" 1575 | }, 1576 | "type": "library", 1577 | "extra": { 1578 | "branch-alias": { 1579 | "dev-master": "2.0-dev" 1580 | } 1581 | }, 1582 | "autoload": { 1583 | "classmap": [ 1584 | "src/" 1585 | ] 1586 | }, 1587 | "notification-url": "https://packagist.org/downloads/", 1588 | "license": [ 1589 | "BSD-3-Clause" 1590 | ], 1591 | "authors": [ 1592 | { 1593 | "name": "Sebastian Bergmann", 1594 | "email": "sebastian@phpunit.de" 1595 | } 1596 | ], 1597 | "description": "Allows reflection of object attributes, including inherited and non-public ones", 1598 | "homepage": "https://github.com/sebastianbergmann/object-reflector/", 1599 | "support": { 1600 | "issues": "https://github.com/sebastianbergmann/object-reflector/issues", 1601 | "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" 1602 | }, 1603 | "funding": [ 1604 | { 1605 | "url": "https://github.com/sebastianbergmann", 1606 | "type": "github" 1607 | } 1608 | ], 1609 | "time": "2020-10-26T13:14:26+00:00" 1610 | }, 1611 | { 1612 | "name": "sebastian/recursion-context", 1613 | "version": "4.0.5", 1614 | "source": { 1615 | "type": "git", 1616 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1617 | "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" 1618 | }, 1619 | "dist": { 1620 | "type": "zip", 1621 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", 1622 | "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", 1623 | "shasum": "" 1624 | }, 1625 | "require": { 1626 | "php": ">=7.3" 1627 | }, 1628 | "require-dev": { 1629 | "phpunit/phpunit": "^9.3" 1630 | }, 1631 | "type": "library", 1632 | "extra": { 1633 | "branch-alias": { 1634 | "dev-master": "4.0-dev" 1635 | } 1636 | }, 1637 | "autoload": { 1638 | "classmap": [ 1639 | "src/" 1640 | ] 1641 | }, 1642 | "notification-url": "https://packagist.org/downloads/", 1643 | "license": [ 1644 | "BSD-3-Clause" 1645 | ], 1646 | "authors": [ 1647 | { 1648 | "name": "Sebastian Bergmann", 1649 | "email": "sebastian@phpunit.de" 1650 | }, 1651 | { 1652 | "name": "Jeff Welch", 1653 | "email": "whatthejeff@gmail.com" 1654 | }, 1655 | { 1656 | "name": "Adam Harvey", 1657 | "email": "aharvey@php.net" 1658 | } 1659 | ], 1660 | "description": "Provides functionality to recursively process PHP variables", 1661 | "homepage": "https://github.com/sebastianbergmann/recursion-context", 1662 | "support": { 1663 | "issues": "https://github.com/sebastianbergmann/recursion-context/issues", 1664 | "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" 1665 | }, 1666 | "funding": [ 1667 | { 1668 | "url": "https://github.com/sebastianbergmann", 1669 | "type": "github" 1670 | } 1671 | ], 1672 | "time": "2023-02-03T06:07:39+00:00" 1673 | }, 1674 | { 1675 | "name": "sebastian/resource-operations", 1676 | "version": "3.0.3", 1677 | "source": { 1678 | "type": "git", 1679 | "url": "https://github.com/sebastianbergmann/resource-operations.git", 1680 | "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" 1681 | }, 1682 | "dist": { 1683 | "type": "zip", 1684 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", 1685 | "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", 1686 | "shasum": "" 1687 | }, 1688 | "require": { 1689 | "php": ">=7.3" 1690 | }, 1691 | "require-dev": { 1692 | "phpunit/phpunit": "^9.0" 1693 | }, 1694 | "type": "library", 1695 | "extra": { 1696 | "branch-alias": { 1697 | "dev-master": "3.0-dev" 1698 | } 1699 | }, 1700 | "autoload": { 1701 | "classmap": [ 1702 | "src/" 1703 | ] 1704 | }, 1705 | "notification-url": "https://packagist.org/downloads/", 1706 | "license": [ 1707 | "BSD-3-Clause" 1708 | ], 1709 | "authors": [ 1710 | { 1711 | "name": "Sebastian Bergmann", 1712 | "email": "sebastian@phpunit.de" 1713 | } 1714 | ], 1715 | "description": "Provides a list of PHP built-in functions that operate on resources", 1716 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations", 1717 | "support": { 1718 | "issues": "https://github.com/sebastianbergmann/resource-operations/issues", 1719 | "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" 1720 | }, 1721 | "funding": [ 1722 | { 1723 | "url": "https://github.com/sebastianbergmann", 1724 | "type": "github" 1725 | } 1726 | ], 1727 | "time": "2020-09-28T06:45:17+00:00" 1728 | }, 1729 | { 1730 | "name": "sebastian/type", 1731 | "version": "3.2.1", 1732 | "source": { 1733 | "type": "git", 1734 | "url": "https://github.com/sebastianbergmann/type.git", 1735 | "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" 1736 | }, 1737 | "dist": { 1738 | "type": "zip", 1739 | "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", 1740 | "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", 1741 | "shasum": "" 1742 | }, 1743 | "require": { 1744 | "php": ">=7.3" 1745 | }, 1746 | "require-dev": { 1747 | "phpunit/phpunit": "^9.5" 1748 | }, 1749 | "type": "library", 1750 | "extra": { 1751 | "branch-alias": { 1752 | "dev-master": "3.2-dev" 1753 | } 1754 | }, 1755 | "autoload": { 1756 | "classmap": [ 1757 | "src/" 1758 | ] 1759 | }, 1760 | "notification-url": "https://packagist.org/downloads/", 1761 | "license": [ 1762 | "BSD-3-Clause" 1763 | ], 1764 | "authors": [ 1765 | { 1766 | "name": "Sebastian Bergmann", 1767 | "email": "sebastian@phpunit.de", 1768 | "role": "lead" 1769 | } 1770 | ], 1771 | "description": "Collection of value objects that represent the types of the PHP type system", 1772 | "homepage": "https://github.com/sebastianbergmann/type", 1773 | "support": { 1774 | "issues": "https://github.com/sebastianbergmann/type/issues", 1775 | "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" 1776 | }, 1777 | "funding": [ 1778 | { 1779 | "url": "https://github.com/sebastianbergmann", 1780 | "type": "github" 1781 | } 1782 | ], 1783 | "time": "2023-02-03T06:13:03+00:00" 1784 | }, 1785 | { 1786 | "name": "sebastian/version", 1787 | "version": "3.0.2", 1788 | "source": { 1789 | "type": "git", 1790 | "url": "https://github.com/sebastianbergmann/version.git", 1791 | "reference": "c6c1022351a901512170118436c764e473f6de8c" 1792 | }, 1793 | "dist": { 1794 | "type": "zip", 1795 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", 1796 | "reference": "c6c1022351a901512170118436c764e473f6de8c", 1797 | "shasum": "" 1798 | }, 1799 | "require": { 1800 | "php": ">=7.3" 1801 | }, 1802 | "type": "library", 1803 | "extra": { 1804 | "branch-alias": { 1805 | "dev-master": "3.0-dev" 1806 | } 1807 | }, 1808 | "autoload": { 1809 | "classmap": [ 1810 | "src/" 1811 | ] 1812 | }, 1813 | "notification-url": "https://packagist.org/downloads/", 1814 | "license": [ 1815 | "BSD-3-Clause" 1816 | ], 1817 | "authors": [ 1818 | { 1819 | "name": "Sebastian Bergmann", 1820 | "email": "sebastian@phpunit.de", 1821 | "role": "lead" 1822 | } 1823 | ], 1824 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1825 | "homepage": "https://github.com/sebastianbergmann/version", 1826 | "support": { 1827 | "issues": "https://github.com/sebastianbergmann/version/issues", 1828 | "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" 1829 | }, 1830 | "funding": [ 1831 | { 1832 | "url": "https://github.com/sebastianbergmann", 1833 | "type": "github" 1834 | } 1835 | ], 1836 | "time": "2020-09-28T06:39:44+00:00" 1837 | }, 1838 | { 1839 | "name": "theseer/tokenizer", 1840 | "version": "1.2.2", 1841 | "source": { 1842 | "type": "git", 1843 | "url": "https://github.com/theseer/tokenizer.git", 1844 | "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" 1845 | }, 1846 | "dist": { 1847 | "type": "zip", 1848 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", 1849 | "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", 1850 | "shasum": "" 1851 | }, 1852 | "require": { 1853 | "ext-dom": "*", 1854 | "ext-tokenizer": "*", 1855 | "ext-xmlwriter": "*", 1856 | "php": "^7.2 || ^8.0" 1857 | }, 1858 | "type": "library", 1859 | "autoload": { 1860 | "classmap": [ 1861 | "src/" 1862 | ] 1863 | }, 1864 | "notification-url": "https://packagist.org/downloads/", 1865 | "license": [ 1866 | "BSD-3-Clause" 1867 | ], 1868 | "authors": [ 1869 | { 1870 | "name": "Arne Blankerts", 1871 | "email": "arne@blankerts.de", 1872 | "role": "Developer" 1873 | } 1874 | ], 1875 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", 1876 | "support": { 1877 | "issues": "https://github.com/theseer/tokenizer/issues", 1878 | "source": "https://github.com/theseer/tokenizer/tree/1.2.2" 1879 | }, 1880 | "funding": [ 1881 | { 1882 | "url": "https://github.com/theseer", 1883 | "type": "github" 1884 | } 1885 | ], 1886 | "time": "2023-11-20T00:12:19+00:00" 1887 | }, 1888 | { 1889 | "name": "yoast/phpunit-polyfills", 1890 | "version": "1.1.0", 1891 | "source": { 1892 | "type": "git", 1893 | "url": "https://github.com/Yoast/PHPUnit-Polyfills.git", 1894 | "reference": "224e4a1329c03d8bad520e3fc4ec980034a4b212" 1895 | }, 1896 | "dist": { 1897 | "type": "zip", 1898 | "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/224e4a1329c03d8bad520e3fc4ec980034a4b212", 1899 | "reference": "224e4a1329c03d8bad520e3fc4ec980034a4b212", 1900 | "shasum": "" 1901 | }, 1902 | "require": { 1903 | "php": ">=5.4", 1904 | "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.0" 1905 | }, 1906 | "require-dev": { 1907 | "yoast/yoastcs": "^2.3.0" 1908 | }, 1909 | "type": "library", 1910 | "extra": { 1911 | "branch-alias": { 1912 | "dev-main": "2.x-dev" 1913 | } 1914 | }, 1915 | "autoload": { 1916 | "files": [ 1917 | "phpunitpolyfills-autoload.php" 1918 | ] 1919 | }, 1920 | "notification-url": "https://packagist.org/downloads/", 1921 | "license": [ 1922 | "BSD-3-Clause" 1923 | ], 1924 | "authors": [ 1925 | { 1926 | "name": "Team Yoast", 1927 | "email": "support@yoast.com", 1928 | "homepage": "https://yoast.com" 1929 | }, 1930 | { 1931 | "name": "Contributors", 1932 | "homepage": "https://github.com/Yoast/PHPUnit-Polyfills/graphs/contributors" 1933 | } 1934 | ], 1935 | "description": "Set of polyfills for changed PHPUnit functionality to allow for creating PHPUnit cross-version compatible tests", 1936 | "homepage": "https://github.com/Yoast/PHPUnit-Polyfills", 1937 | "keywords": [ 1938 | "phpunit", 1939 | "polyfill", 1940 | "testing" 1941 | ], 1942 | "support": { 1943 | "issues": "https://github.com/Yoast/PHPUnit-Polyfills/issues", 1944 | "source": "https://github.com/Yoast/PHPUnit-Polyfills" 1945 | }, 1946 | "time": "2023-08-19T14:25:08+00:00" 1947 | } 1948 | ], 1949 | "aliases": [], 1950 | "minimum-stability": "stable", 1951 | "stability-flags": [], 1952 | "prefer-stable": false, 1953 | "prefer-lowest": false, 1954 | "platform": { 1955 | "php": ">=7.4" 1956 | }, 1957 | "platform-dev": [], 1958 | "platform-overrides": { 1959 | "php": "7.4" 1960 | }, 1961 | "plugin-api-version": "2.6.0" 1962 | } 1963 | -------------------------------------------------------------------------------- /concat-utils.php: -------------------------------------------------------------------------------- 1 | url(/absolute/and/not/relative/path/to/file) 49 | $buf = preg_replace( 50 | '/(:?\s*url\s*\()\s*(?:\'|")?\s*([^\/\'"\s\)](?:(?old_styles = new WP_Styles(); 23 | } else { 24 | $this->old_styles = $styles; 25 | } 26 | 27 | // Unset all the object properties except our private copy of the styles object. 28 | // We have to unset everything so that the overload methods talk to $this->old_styles->whatever 29 | // instead of $this->whatever. 30 | foreach ( array_keys( get_object_vars( $this ) ) as $key ) { 31 | if ( 'old_styles' === $key ) { 32 | continue; 33 | } 34 | unset( $this->$key ); 35 | } 36 | } 37 | 38 | function do_items( $handles = false, $group = false ) { 39 | $handles = false === $handles ? $this->queue : (array) $handles; 40 | $stylesheets = array(); 41 | $siteurl = apply_filters( 'ngx_http_concat_site_url', $this->base_url ); 42 | 43 | $this->all_deps( $handles ); 44 | 45 | $stylesheet_group_index = 0; 46 | foreach( $this->to_do as $key => $handle ) { 47 | $obj = $this->registered[$handle]; 48 | $obj->src = apply_filters( 'style_loader_src', $obj->src, $obj->handle ); 49 | 50 | // Core is kind of broken and returns "true" for src of "colors" handle 51 | // http://core.trac.wordpress.org/attachment/ticket/16827/colors-hacked-fixed.diff 52 | // http://core.trac.wordpress.org/ticket/20729 53 | $css_url = $obj->src; 54 | if ( 'colors' == $obj->handle && true === $css_url ) { 55 | $css_url = wp_style_loader_src( $css_url, $obj->handle ); 56 | } 57 | 58 | $css_url_parsed = parse_url( is_string( $obj->src ) ? $obj->src : '' ); 59 | $extra = $obj->extra; 60 | 61 | // Don't concat by default 62 | $do_concat = false; 63 | 64 | // Only try to concat static css files 65 | if ( false !== strpos( $css_url_parsed['path'], '.css' ) ) 66 | $do_concat = true; 67 | 68 | // Don't try to concat styles which are loaded conditionally (like IE stuff) 69 | if ( isset( $extra['conditional'] ) ) 70 | $do_concat = false; 71 | 72 | // Don't concat rtl stuff for now until concat supports it correctly 73 | if ( 'rtl' === $this->text_direction && ! empty( $extra['rtl'] ) ) 74 | $do_concat = false; 75 | 76 | // Don't try to concat externally hosted scripts 77 | $is_internal_url = WPCOM_Concat_Utils::is_internal_url( $css_url, $siteurl ); 78 | if ( ! $is_internal_url ) { 79 | $do_concat = false; 80 | } 81 | 82 | // Concat and canonicalize the paths only for 83 | // existing scripts that aren't outside ABSPATH 84 | $css_realpath = WPCOM_Concat_Utils::realpath( $css_url, $siteurl ); 85 | if ( ! $css_realpath || 0 !== strpos( $css_realpath, ABSPATH ) ) 86 | $do_concat = false; 87 | else 88 | $css_url_parsed['path'] = substr( $css_realpath, strlen( ABSPATH ) - 1 ); 89 | 90 | // Only allow filter for anything that is currently concat'ing 91 | // Stylesheets which are not concat'able should stay that way 92 | if ( true === $do_concat ) { 93 | $do_concat = apply_filters( 'css_do_concat', $do_concat, $handle ); 94 | } 95 | 96 | if ( true === $do_concat ) { 97 | $media = $obj->args; 98 | if( empty( $media ) ) 99 | $media = 'all'; 100 | if ( ! isset( $stylesheets[ $stylesheet_group_index ] ) || ( isset( $stylesheets[ $stylesheet_group_index ] ) && ! is_array( $stylesheets[ $stylesheet_group_index ] ) ) ) 101 | $stylesheets[ $stylesheet_group_index ] = array(); 102 | 103 | $stylesheets[ $stylesheet_group_index ][ $media ][ $handle ] = $css_url_parsed['path']; 104 | 105 | if ( count( $stylesheets[ $stylesheet_group_index ][ $media ] ) >= WPCOM_Concat_Utils::get_concat_max() ) { 106 | $stylesheet_group_index++; 107 | } 108 | $this->done[] = $handle; 109 | } else { 110 | $stylesheet_group_index++; 111 | $stylesheets[ $stylesheet_group_index ][ 'noconcat' ][] = $handle; 112 | $stylesheet_group_index++; 113 | } 114 | unset( $this->to_do[$key] ); 115 | } 116 | 117 | foreach( $stylesheets as $idx => $stylesheets_group ) { 118 | foreach( $stylesheets_group as $media => $css ) { 119 | if ( 'noconcat' == $media ) { 120 | 121 | foreach( $css as $handle ) { 122 | if ( $this->do_item( $handle, $group ) ) 123 | $this->done[] = $handle; 124 | } 125 | continue; 126 | } elseif ( count( $css ) > 1) { 127 | $paths = array_map( function( $url ) { return ABSPATH . $url; }, $css ); 128 | $mtime = max( array_map( 'filemtime', $paths ) ); 129 | $path_str = implode( ',', $css ) . "?m={$mtime}"; 130 | 131 | if ( $this->allow_gzip_compression ) { 132 | $path_64 = base64_encode( gzcompress( $path_str ) ); 133 | if ( strlen( $path_str ) > ( strlen( $path_64 ) + 1 ) ) 134 | $path_str = '-' . $path_64; 135 | } 136 | 137 | $href = $siteurl . "/_static/??" . $path_str; 138 | } else { 139 | $href = $this->cache_bust_mtime( $siteurl . current( $css ), $siteurl ); 140 | } 141 | 142 | $handles = array_keys( $css ); 143 | echo apply_filters( 'ngx_http_concat_style_loader_tag', "\n", $handles, $href, $media ); 144 | array_map( array( $this, 'print_inline_style' ), array_keys( $css ) ); 145 | } 146 | } 147 | return $this->done; 148 | } 149 | 150 | function cache_bust_mtime( $url, $siteurl ) { 151 | if ( strpos( $url, '?m=' ) ) 152 | return $url; 153 | 154 | $parts = parse_url( $url ); 155 | if ( ! isset( $parts['path'] ) || empty( $parts['path'] ) ) 156 | return $url; 157 | 158 | $file = WPCOM_Concat_Utils::realpath( $url, $siteurl ); 159 | 160 | $mtime = false; 161 | if ( file_exists( $file ) ) 162 | $mtime = filemtime( $file ); 163 | 164 | if ( ! $mtime ) 165 | return $url; 166 | 167 | if ( false === strpos( $url, '?' ) ) { 168 | $q = ''; 169 | } else { 170 | list( $url, $q ) = explode( '?', $url, 2 ); 171 | if ( strlen( $q ) ) 172 | $q = '&' . $q; 173 | } 174 | 175 | return "$url?m={$mtime}g{$q}"; 176 | } 177 | 178 | function __isset( $key ) { 179 | return isset( $this->old_styles->$key ); 180 | } 181 | 182 | function __unset( $key ) { 183 | unset( $this->old_styles->$key ); 184 | } 185 | 186 | function &__get( $key ) { 187 | return $this->old_styles->$key; 188 | } 189 | 190 | function __set( $key, $value ) { 191 | $this->old_styles->$key = $value; 192 | } 193 | } 194 | 195 | function css_concat_init() { 196 | global $wp_styles; 197 | 198 | $wp_styles = new WPcom_CSS_Concat( $wp_styles ); 199 | $wp_styles->allow_gzip_compression = ALLOW_GZIP_COMPRESSION; 200 | } 201 | 202 | add_action( 'init', 'css_concat_init' ); 203 | -------------------------------------------------------------------------------- /cssmin/Colors.php: -------------------------------------------------------------------------------- 1 | 'azure', 12 | '#f5f5dc' => 'beige', 13 | '#ffe4c4' => 'bisque', 14 | '#a52a2a' => 'brown', 15 | '#ff7f50' => 'coral', 16 | '#ffd700' => 'gold', 17 | '#808080' => 'gray', 18 | '#008000' => 'green', 19 | '#4b0082' => 'indigo', 20 | '#fffff0' => 'ivory', 21 | '#f0e68c' => 'khaki', 22 | '#faf0e6' => 'linen', 23 | '#800000' => 'maroon', 24 | '#000080' => 'navy', 25 | '#fdf5e6' => 'oldlace', 26 | '#808000' => 'olive', 27 | '#ffa500' => 'orange', 28 | '#da70d6' => 'orchid', 29 | '#cd853f' => 'peru', 30 | '#ffc0cb' => 'pink', 31 | '#dda0dd' => 'plum', 32 | '#800080' => 'purple', 33 | '#f00' => 'red', 34 | '#fa8072' => 'salmon', 35 | '#a0522d' => 'sienna', 36 | '#c0c0c0' => 'silver', 37 | '#fffafa' => 'snow', 38 | '#d2b48c' => 'tan', 39 | '#008080' => 'teal', 40 | '#ff6347' => 'tomato', 41 | '#ee82ee' => 'violet', 42 | '#f5deb3' => 'wheat' 43 | ); 44 | } 45 | 46 | public static function getNamedToHexMap() 47 | { 48 | // Named colors longer than hex counterpart 49 | return array( 50 | 'aliceblue' => '#f0f8ff', 51 | 'antiquewhite' => '#faebd7', 52 | 'aquamarine' => '#7fffd4', 53 | 'black' => '#000', 54 | 'blanchedalmond' => '#ffebcd', 55 | 'blueviolet' => '#8a2be2', 56 | 'burlywood' => '#deb887', 57 | 'cadetblue' => '#5f9ea0', 58 | 'chartreuse' => '#7fff00', 59 | 'chocolate' => '#d2691e', 60 | 'cornflowerblue' => '#6495ed', 61 | 'cornsilk' => '#fff8dc', 62 | 'darkblue' => '#00008b', 63 | 'darkcyan' => '#008b8b', 64 | 'darkgoldenrod' => '#b8860b', 65 | 'darkgray' => '#a9a9a9', 66 | 'darkgreen' => '#006400', 67 | 'darkgrey' => '#a9a9a9', 68 | 'darkkhaki' => '#bdb76b', 69 | 'darkmagenta' => '#8b008b', 70 | 'darkolivegreen' => '#556b2f', 71 | 'darkorange' => '#ff8c00', 72 | 'darkorchid' => '#9932cc', 73 | 'darksalmon' => '#e9967a', 74 | 'darkseagreen' => '#8fbc8f', 75 | 'darkslateblue' => '#483d8b', 76 | 'darkslategray' => '#2f4f4f', 77 | 'darkslategrey' => '#2f4f4f', 78 | 'darkturquoise' => '#00ced1', 79 | 'darkviolet' => '#9400d3', 80 | 'deeppink' => '#ff1493', 81 | 'deepskyblue' => '#00bfff', 82 | 'dodgerblue' => '#1e90ff', 83 | 'firebrick' => '#b22222', 84 | 'floralwhite' => '#fffaf0', 85 | 'forestgreen' => '#228b22', 86 | 'fuchsia' => '#f0f', 87 | 'gainsboro' => '#dcdcdc', 88 | 'ghostwhite' => '#f8f8ff', 89 | 'goldenrod' => '#daa520', 90 | 'greenyellow' => '#adff2f', 91 | 'honeydew' => '#f0fff0', 92 | 'indianred' => '#cd5c5c', 93 | 'lavender' => '#e6e6fa', 94 | 'lavenderblush' => '#fff0f5', 95 | 'lawngreen' => '#7cfc00', 96 | 'lemonchiffon' => '#fffacd', 97 | 'lightblue' => '#add8e6', 98 | 'lightcoral' => '#f08080', 99 | 'lightcyan' => '#e0ffff', 100 | 'lightgoldenrodyellow' => '#fafad2', 101 | 'lightgray' => '#d3d3d3', 102 | 'lightgreen' => '#90ee90', 103 | 'lightgrey' => '#d3d3d3', 104 | 'lightpink' => '#ffb6c1', 105 | 'lightsalmon' => '#ffa07a', 106 | 'lightseagreen' => '#20b2aa', 107 | 'lightskyblue' => '#87cefa', 108 | 'lightslategray' => '#778899', 109 | 'lightslategrey' => '#778899', 110 | 'lightsteelblue' => '#b0c4de', 111 | 'lightyellow' => '#ffffe0', 112 | 'limegreen' => '#32cd32', 113 | 'mediumaquamarine' => '#66cdaa', 114 | 'mediumblue' => '#0000cd', 115 | 'mediumorchid' => '#ba55d3', 116 | 'mediumpurple' => '#9370db', 117 | 'mediumseagreen' => '#3cb371', 118 | 'mediumslateblue' => '#7b68ee', 119 | 'mediumspringgreen' => '#00fa9a', 120 | 'mediumturquoise' => '#48d1cc', 121 | 'mediumvioletred' => '#c71585', 122 | 'midnightblue' => '#191970', 123 | 'mintcream' => '#f5fffa', 124 | 'mistyrose' => '#ffe4e1', 125 | 'moccasin' => '#ffe4b5', 126 | 'navajowhite' => '#ffdead', 127 | 'olivedrab' => '#6b8e23', 128 | 'orangered' => '#ff4500', 129 | 'palegoldenrod' => '#eee8aa', 130 | 'palegreen' => '#98fb98', 131 | 'paleturquoise' => '#afeeee', 132 | 'palevioletred' => '#db7093', 133 | 'papayawhip' => '#ffefd5', 134 | 'peachpuff' => '#ffdab9', 135 | 'powderblue' => '#b0e0e6', 136 | 'rebeccapurple' => '#663399', 137 | 'rosybrown' => '#bc8f8f', 138 | 'royalblue' => '#4169e1', 139 | 'saddlebrown' => '#8b4513', 140 | 'sandybrown' => '#f4a460', 141 | 'seagreen' => '#2e8b57', 142 | 'seashell' => '#fff5ee', 143 | 'slateblue' => '#6a5acd', 144 | 'slategray' => '#708090', 145 | 'slategrey' => '#708090', 146 | 'springgreen' => '#00ff7f', 147 | 'steelblue' => '#4682b4', 148 | 'turquoise' => '#40e0d0', 149 | 'white' => '#fff', 150 | 'whitesmoke' => '#f5f5f5', 151 | 'yellow' => '#ff0', 152 | 'yellowgreen' => '#9acd32' 153 | ); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /cssmin/Minifier.php: -------------------------------------------------------------------------------- 1 | raisePhpLimits = (bool) $raisePhpLimits; 72 | $this->memoryLimit = 128 * 1048576; // 128MB in bytes 73 | $this->pcreBacktrackLimit = 1000 * 1000; 74 | $this->pcreRecursionLimit = 500 * 1000; 75 | $this->hexToNamedColorsMap = Colors::getHexToNamedMap(); 76 | $this->namedToHexColorsMap = Colors::getNamedToHexMap(); 77 | $this->namedToHexColorsRegex = sprintf( 78 | '/([:,( ])(%s)( |,|\)|;|$)/Si', 79 | implode('|', array_keys($this->namedToHexColorsMap)) 80 | ); 81 | $this->numRegex = sprintf('-?\d*\.?\d+%s?', $this->unitsGroupRegex); 82 | $this->setShortenZeroValuesRegexes(); 83 | } 84 | 85 | /** 86 | * Parses & minifies the given input CSS string 87 | * @param string $css 88 | * @return string 89 | */ 90 | public function run($css = '') 91 | { 92 | if (empty($css) || !is_string($css)) { 93 | return ''; 94 | } 95 | 96 | $this->resetRunProperties(); 97 | 98 | if ($this->raisePhpLimits) { 99 | $this->doRaisePhpLimits(); 100 | } 101 | 102 | return $this->minify($css); 103 | } 104 | 105 | /** 106 | * Sets whether to keep or remove sourcemap special comment. 107 | * Sourcemap comments are removed by default. 108 | * @param bool $keepSourceMapComment 109 | */ 110 | public function keepSourceMapComment($keepSourceMapComment = true) 111 | { 112 | $this->keepSourceMapComment = (bool) $keepSourceMapComment; 113 | } 114 | 115 | /** 116 | * Sets whether to keep or remove important comments. 117 | * Important comments outside of a declaration block are kept by default. 118 | * @param bool $removeImportantComments 119 | */ 120 | public function removeImportantComments($removeImportantComments = true) 121 | { 122 | $this->keepImportantComments = !(bool) $removeImportantComments; 123 | } 124 | 125 | /** 126 | * Sets the approximate column after which long lines will be splitted in the output 127 | * with a linebreak. 128 | * @param int $position 129 | */ 130 | public function setLineBreakPosition($position) 131 | { 132 | $this->linebreakPosition = (int) $position; 133 | } 134 | 135 | /** 136 | * Sets the memory limit for this script 137 | * @param int|string $limit 138 | */ 139 | public function setMemoryLimit($limit) 140 | { 141 | $this->memoryLimit = Utils::normalizeInt($limit); 142 | } 143 | 144 | /** 145 | * Sets the maximum execution time for this script 146 | * @param int|string $seconds 147 | */ 148 | public function setMaxExecutionTime($seconds) 149 | { 150 | $this->maxExecutionTime = (int) $seconds; 151 | } 152 | 153 | /** 154 | * Sets the PCRE backtrack limit for this script 155 | * @param int $limit 156 | */ 157 | public function setPcreBacktrackLimit($limit) 158 | { 159 | $this->pcreBacktrackLimit = (int) $limit; 160 | } 161 | 162 | /** 163 | * Sets the PCRE recursion limit for this script 164 | * @param int $limit 165 | */ 166 | public function setPcreRecursionLimit($limit) 167 | { 168 | $this->pcreRecursionLimit = (int) $limit; 169 | } 170 | 171 | /** 172 | * Builds regular expressions needed for shortening zero values 173 | */ 174 | private function setShortenZeroValuesRegexes() 175 | { 176 | $zeroRegex = '0'. $this->unitsGroupRegex; 177 | $numOrPosRegex = '('. $this->numRegex .'|top|left|bottom|right|center) '; 178 | $oneZeroSafeProperties = array( 179 | '(?:line-)?height', 180 | '(?:(?:min|max)-)?width', 181 | 'top', 182 | 'left', 183 | 'background-position', 184 | 'bottom', 185 | 'right', 186 | 'border(?:-(?:top|left|bottom|right))?(?:-width)?', 187 | 'border-(?:(?:top|bottom)-(?:left|right)-)?radius', 188 | 'column-(?:gap|width)', 189 | 'margin(?:-(?:top|left|bottom|right))?', 190 | 'outline-width', 191 | 'padding(?:-(?:top|left|bottom|right))?' 192 | ); 193 | 194 | // First zero regex 195 | $regex = '/(^|;)('. implode('|', $oneZeroSafeProperties) .'):%s/Si'; 196 | $this->shortenOneZeroesRegex = sprintf($regex, $zeroRegex); 197 | 198 | // Multiple zeroes regexes 199 | $regex = '/(^|;)(margin|padding|border-(?:width|radius)|background-position):%s/Si'; 200 | $this->shortenTwoZeroesRegex = sprintf($regex, $numOrPosRegex . $zeroRegex); 201 | $this->shortenThreeZeroesRegex = sprintf($regex, $numOrPosRegex . $numOrPosRegex . $zeroRegex); 202 | $this->shortenFourZeroesRegex = sprintf($regex, $numOrPosRegex . $numOrPosRegex . $numOrPosRegex . $zeroRegex); 203 | } 204 | 205 | /** 206 | * Resets properties whose value may change between runs 207 | */ 208 | private function resetRunProperties() 209 | { 210 | $this->comments = array(); 211 | $this->ruleBodies = array(); 212 | $this->preservedTokens = array(); 213 | } 214 | 215 | /** 216 | * Tries to configure PHP to use at least the suggested minimum settings 217 | * @return void 218 | */ 219 | private function doRaisePhpLimits() 220 | { 221 | $phpLimits = array( 222 | 'memory_limit' => $this->memoryLimit, 223 | 'max_execution_time' => $this->maxExecutionTime, 224 | 'pcre.backtrack_limit' => $this->pcreBacktrackLimit, 225 | 'pcre.recursion_limit' => $this->pcreRecursionLimit 226 | ); 227 | 228 | // If current settings are higher respect them. 229 | foreach ($phpLimits as $name => $suggested) { 230 | $current = Utils::normalizeInt(ini_get($name)); 231 | 232 | if ($current >= $suggested) { 233 | continue; 234 | } 235 | 236 | // memoryLimit exception: allow -1 for "no memory limit". 237 | if ($name === 'memory_limit' && $current === -1) { 238 | continue; 239 | } 240 | 241 | // maxExecutionTime exception: allow 0 for "no memory limit". 242 | if ($name === 'max_execution_time' && $current === 0) { 243 | continue; 244 | } 245 | 246 | ini_set($name, $suggested); 247 | } 248 | } 249 | 250 | /** 251 | * Registers a preserved token 252 | * @param string $token 253 | * @return string The token ID string 254 | */ 255 | private function registerPreservedToken($token) 256 | { 257 | $tokenId = sprintf(self::PRESERVED_TOKEN, count($this->preservedTokens)); 258 | $this->preservedTokens[$tokenId] = $token; 259 | return $tokenId; 260 | } 261 | 262 | /** 263 | * Registers a candidate comment token 264 | * @param string $comment 265 | * @return string The comment token ID string 266 | */ 267 | private function registerCommentToken($comment) 268 | { 269 | $tokenId = sprintf(self::COMMENT_TOKEN, count($this->comments)); 270 | $this->comments[$tokenId] = $comment; 271 | return $tokenId; 272 | } 273 | 274 | /** 275 | * Registers a rule body token 276 | * @param string $body the minified rule body 277 | * @return string The rule body token ID string 278 | */ 279 | private function registerRuleBodyToken($body) 280 | { 281 | if (empty($body)) { 282 | return ''; 283 | } 284 | 285 | $tokenId = sprintf(self::RULE_BODY_TOKEN, count($this->ruleBodies)); 286 | $this->ruleBodies[$tokenId] = $body; 287 | return $tokenId; 288 | } 289 | 290 | /** 291 | * Parses & minifies the given input CSS string 292 | * @param string $css 293 | * @return string 294 | */ 295 | private function minify($css) 296 | { 297 | // Process data urls 298 | $css = $this->processDataUrls($css); 299 | 300 | // Process comments 301 | $css = preg_replace_callback( 302 | '/(?processComments($css); 334 | 335 | // Process rule bodies 336 | $css = $this->processRuleBodies($css); 337 | 338 | // Process at-rules and selectors 339 | $css = $this->processAtRulesAndSelectors($css); 340 | 341 | // Restore preserved rule bodies before splitting 342 | $css = strtr($css, $this->ruleBodies); 343 | 344 | // Some source control tools don't like it when files containing lines longer 345 | // than, say 8000 characters, are checked in. The linebreak option is used in 346 | // that case to split long lines after a specific column. 347 | if ($this->linebreakPosition > 0) { 348 | $l = strlen($css); 349 | $offset = $this->linebreakPosition; 350 | while (preg_match('/(?linebreakPosition; 354 | $l += 1; 355 | if ($offset > $l) { 356 | break; 357 | } 358 | } 359 | } 360 | 361 | // Restore preserved comments and strings 362 | $css = strtr($css, $this->preservedTokens); 363 | 364 | return trim($css); 365 | } 366 | 367 | /** 368 | * Searches & replaces all data urls with tokens before we start compressing, 369 | * to avoid performance issues running some of the subsequent regexes against large string chunks. 370 | * @param string $css 371 | * @return string 372 | */ 373 | private function processDataUrls($css) 374 | { 375 | $ret = ''; 376 | $searchOffset = $substrOffset = 0; 377 | 378 | // Since we need to account for non-base64 data urls, we need to handle 379 | // ' and ) being part of the data string. 380 | while (preg_match('/url\(\s*(["\']?)data:/Si', $css, $m, PREG_OFFSET_CAPTURE, $searchOffset)) { 381 | $matchStartIndex = $m[0][1]; 382 | $dataStartIndex = $matchStartIndex + 4; // url( length 383 | $searchOffset = $matchStartIndex + strlen($m[0][0]); 384 | $terminator = $m[1][0]; // ', " or empty (not quoted) 385 | $terminatorRegex = '/(?registerPreservedToken(trim($token)) .')'; 401 | // No end terminator found, re-add the whole match. Should we throw/warn here? 402 | } else { 403 | $ret .= substr($css, $matchStartIndex, $searchOffset - $matchStartIndex); 404 | } 405 | 406 | $substrOffset = $searchOffset; 407 | } 408 | 409 | $ret .= substr($css, $substrOffset); 410 | 411 | return $ret; 412 | } 413 | 414 | /** 415 | * Registers all comments found as candidates to be preserved. 416 | * @param array $matches 417 | * @return string 418 | */ 419 | private function processCommentsCallback($matches) 420 | { 421 | return '/*'. $this->registerCommentToken($matches[1]) .'*/'; 422 | } 423 | 424 | /** 425 | * Preserves old IE Matrix string definition 426 | * @param array $matches 427 | * @return string 428 | */ 429 | private function processOldIeSpecificMatrixDefinitionCallback($matches) 430 | { 431 | return 'filter:progid:DXImageTransform.Microsoft.Matrix('. $this->registerPreservedToken($matches[1]) .')'; 432 | } 433 | 434 | /** 435 | * Preserves strings found 436 | * @param array $matches 437 | * @return string 438 | */ 439 | private function processStringsCallback($matches) 440 | { 441 | $match = $matches[0]; 442 | $quote = substr($match, 0, 1); 443 | $match = substr($match, 1, -1); 444 | 445 | // maybe the string contains a comment-like substring? 446 | // one, maybe more? put'em back then 447 | if (strpos($match, self::COMMENT_TOKEN_START) !== false) { 448 | $match = strtr($match, $this->comments); 449 | } 450 | 451 | // minify alpha opacity in filter strings 452 | $match = str_ireplace('progid:DXImageTransform.Microsoft.Alpha(Opacity=', 'alpha(opacity=', $match); 453 | 454 | return $quote . $this->registerPreservedToken($match) . $quote; 455 | } 456 | 457 | /** 458 | * Preserves or removes comments found. 459 | * @param string $css 460 | * @return string 461 | */ 462 | private function processComments($css) 463 | { 464 | foreach ($this->comments as $commentId => $comment) { 465 | $commentIdString = '/*'. $commentId .'*/'; 466 | 467 | // ! in the first position of the comment means preserve 468 | // so push to the preserved tokens keeping the ! 469 | if ($this->keepImportantComments && strpos($comment, '!') === 0) { 470 | $preservedTokenId = $this->registerPreservedToken($comment); 471 | // Put new lines before and after /*! important comments 472 | $css = str_replace($commentIdString, "\n/*$preservedTokenId*/\n", $css); 473 | continue; 474 | } 475 | 476 | // # sourceMappingURL= in the first position of the comment means sourcemap 477 | // so push to the preserved tokens if {$this->keepSourceMapComment} is truthy. 478 | if ($this->keepSourceMapComment && strpos($comment, '# sourceMappingURL=') === 0) { 479 | $preservedTokenId = $this->registerPreservedToken($comment); 480 | // Add new line before the sourcemap comment 481 | $css = str_replace($commentIdString, "\n/*$preservedTokenId*/", $css); 482 | continue; 483 | } 484 | 485 | // Keep empty comments after child selectors (IE7 hack) 486 | // e.g. html >/**/ body 487 | if (strlen($comment) === 0 && strpos($css, '>/*'.$commentId) !== false) { 488 | $css = str_replace($commentId, $this->registerPreservedToken(''), $css); 489 | continue; 490 | } 491 | 492 | // in all other cases kill the comment 493 | $css = str_replace($commentIdString, '', $css); 494 | } 495 | 496 | // Normalize whitespace again 497 | $css = preg_replace('/ +/S', ' ', $css); 498 | 499 | return $css; 500 | } 501 | 502 | /** 503 | * Finds, minifies & preserves all rule bodies. 504 | * @param string $css the whole stylesheet. 505 | * @return string 506 | */ 507 | private function processRuleBodies($css) 508 | { 509 | $ret = ''; 510 | $searchOffset = $substrOffset = 0; 511 | 512 | while (($blockStartPos = strpos($css, '{', $searchOffset)) !== false) { 513 | $blockEndPos = strpos($css, '}', $blockStartPos); 514 | $nextBlockStartPos = strpos($css, '{', $blockStartPos + 1); 515 | $ret .= substr($css, $substrOffset, $blockStartPos - $substrOffset); 516 | 517 | if ($nextBlockStartPos !== false && $nextBlockStartPos < $blockEndPos) { 518 | $ret .= substr($css, $blockStartPos, $nextBlockStartPos - $blockStartPos); 519 | $searchOffset = $nextBlockStartPos; 520 | } else { 521 | $ruleBody = substr($css, $blockStartPos + 1, $blockEndPos - $blockStartPos - 1); 522 | $ruleBodyToken = $this->registerRuleBodyToken($this->processRuleBody($ruleBody)); 523 | $ret .= '{'. $ruleBodyToken .'}'; 524 | $searchOffset = $blockEndPos + 1; 525 | } 526 | 527 | $substrOffset = $searchOffset; 528 | } 529 | 530 | $ret .= substr($css, $substrOffset); 531 | 532 | return $ret; 533 | } 534 | 535 | /** 536 | * Compresses non-group rule bodies. 537 | * @param string $body The rule body without curly braces 538 | * @return string 539 | */ 540 | private function processRuleBody($body) 541 | { 542 | $body = trim($body); 543 | 544 | // Remove spaces before the things that should not have spaces before them. 545 | $body = preg_replace('/ ([:=,)*\/;\n])/S', '$1', $body); 546 | 547 | // Remove the spaces after the things that should not have spaces after them. 548 | $body = preg_replace('/([:=,(*\/!;\n]) /S', '$1', $body); 549 | 550 | // Replace multiple semi-colons in a row by a single one 551 | $body = preg_replace('/;;+/S', ';', $body); 552 | 553 | // Remove semicolon before closing brace except when: 554 | // - The last property is prefixed with a `*` (lte IE7 hack) to avoid issues on Symbian S60 3.x browsers. 555 | if (!preg_match('/\*[a-z0-9-]+:[^;]+;$/Si', $body)) { 556 | $body = rtrim($body, ';'); 557 | } 558 | 559 | // Remove important comments inside a rule body (because they make no sense here). 560 | if (strpos($body, '/*') !== false) { 561 | $body = preg_replace('/\n?\/\*[A-Z0-9_]+\*\/\n?/S', '', $body); 562 | } 563 | 564 | // Empty rule body? Exit :) 565 | if (empty($body)) { 566 | return ''; 567 | } 568 | 569 | // Shorten font-weight values 570 | $body = preg_replace( 571 | array('/(font-weight:)bold\b/Si', '/(font-weight:)normal\b/Si'), 572 | array('${1}700', '${1}400'), 573 | $body 574 | ); 575 | 576 | // Shorten background property 577 | $body = preg_replace('/(background:)(?:none|transparent)( !|;|$)/Si', '${1}0 0$2', $body); 578 | 579 | // Shorten opacity IE filter 580 | $body = str_ireplace('progid:DXImageTransform.Microsoft.Alpha(Opacity=', 'alpha(opacity=', $body); 581 | 582 | // Shorten colors from rgb(51,102,153) to #336699, rgb(100%,0%,0%) to #ff0000 (sRGB color space) 583 | // Shorten colors from hsl(0, 100%, 50%) to #ff0000 (sRGB color space) 584 | // This makes it more likely that it'll get further compressed in the next step. 585 | $body = preg_replace_callback( 586 | '/(rgb|hsl)\(([0-9,.% -]+)\)(.|$)/Si', 587 | array($this, 'shortenHslAndRgbToHexCallback'), 588 | $body 589 | ); 590 | 591 | // Shorten colors from #AABBCC to #ABC or shorter color name: 592 | // - Look for hex colors which don't have a "=" in front of them (to avoid MSIE filters) 593 | $body = preg_replace_callback( 594 | '/(? #fff. 600 | // Run at least 2 times to cover most cases 601 | $body = preg_replace_callback( 602 | array($this->namedToHexColorsRegex, $this->namedToHexColorsRegex), 603 | array($this, 'shortenNamedColorsCallback'), 604 | $body 605 | ); 606 | 607 | // Replace positive sign from numbers before the leading space is removed. 608 | // +1.2em to 1.2em, +.8px to .8px, +2% to 2% 609 | $body = preg_replace('/([ :,(])\+(\.?\d+)/S', '$1$2', $body); 610 | 611 | // shorten ms to s 612 | $body = preg_replace_callback('/([ :,(])(-?)(\d{3,})ms/Si', function ($matches) { 613 | return $matches[1] . $matches[2] . ((int) $matches[3] / 1000) .'s'; 614 | }, $body); 615 | 616 | // Remove leading zeros from integer and float numbers. 617 | // 000.6 to .6, -0.8 to -.8, 0050 to 50, -01.05 to -1.05 618 | $body = preg_replace('/([ :,(])(-?)0+([1-9]?\.?\d+)/S', '$1$2$3', $body); 619 | 620 | // Remove trailing zeros from float numbers. 621 | // -6.0100em to -6.01em, .0100 to .01, 1.200px to 1.2px 622 | $body = preg_replace('/([ :,(])(-?\d?\.\d+?)0+([^\d])/S', '$1$2$3', $body); 623 | 624 | // Remove trailing .0 -> -9.0 to -9 625 | $body = preg_replace('/([ :,(])(-?\d+)\.0([^\d])/S', '$1$2$3', $body); 626 | 627 | // Replace 0 length numbers with 0 628 | $body = preg_replace('/([ :,(])-?\.?0+([^\d])/S', '${1}0$2', $body); 629 | 630 | // Shorten zero values for safe properties only 631 | $body = preg_replace( 632 | array( 633 | $this->shortenOneZeroesRegex, 634 | $this->shortenTwoZeroesRegex, 635 | $this->shortenThreeZeroesRegex, 636 | $this->shortenFourZeroesRegex 637 | ), 638 | array( 639 | '$1$2:0', 640 | '$1$2:$3 0', 641 | '$1$2:$3 $4 0', 642 | '$1$2:$3 $4 $5 0' 643 | ), 644 | $body 645 | ); 646 | 647 | // Replace 0 0 0; or 0 0 0 0; with 0 0 for background-position property. 648 | $body = preg_replace('/(background-position):0(?: 0){2,3}( !|;|$)/Si', '$1:0 0$2', $body); 649 | 650 | // Shorten suitable shorthand properties with repeated values 651 | $body = preg_replace( 652 | array( 653 | '/(margin|padding|border-(?:width|radius)):('.$this->numRegex.')(?: \2)+( !|;|$)/Si', 654 | '/(border-(?:style|color)):([#a-z0-9]+)(?: \2)+( !|;|$)/Si' 655 | ), 656 | '$1:$2$3', 657 | $body 658 | ); 659 | $body = preg_replace( 660 | array( 661 | '/(margin|padding|border-(?:width|radius)):'. 662 | '('.$this->numRegex.') ('.$this->numRegex.') \2 \3( !|;|$)/Si', 663 | '/(border-(?:style|color)):([#a-z0-9]+) ([#a-z0-9]+) \2 \3( !|;|$)/Si' 664 | ), 665 | '$1:$2 $3$4', 666 | $body 667 | ); 668 | $body = preg_replace( 669 | array( 670 | '/(margin|padding|border-(?:width|radius)):'. 671 | '('.$this->numRegex.') ('.$this->numRegex.') ('.$this->numRegex.') \3( !|;|$)/Si', 672 | '/(border-(?:style|color)):([#a-z0-9]+) ([#a-z0-9]+) ([#a-z0-9]+) \3( !|;|$)/Si' 673 | ), 674 | '$1:$2 $3 $4$5', 675 | $body 676 | ); 677 | 678 | // Lowercase some common functions that can be values 679 | $body = preg_replace_callback( 680 | '/(?:attr|blur|brightness|circle|contrast|cubic-bezier|drop-shadow|ellipse|from|grayscale|'. 681 | 'hsla?|hue-rotate|inset|invert|local|minmax|opacity|perspective|polygon|rgba?|rect|repeat|saturate|sepia|'. 682 | 'steps|to|url|var|-webkit-gradient|'. 683 | '(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|(?:repeating-)?(?:linear|radial)-gradient))\(/Si', 684 | array($this, 'strtolowerCallback'), 685 | $body 686 | ); 687 | 688 | // Lowercase all uppercase properties 689 | $body = preg_replace_callback('/(?:^|;)[A-Z-]+:/S', array($this, 'strtolowerCallback'), $body); 690 | 691 | return $body; 692 | } 693 | 694 | /** 695 | * Compresses At-rules and selectors. 696 | * @param string $css the whole stylesheet with rule bodies tokenized. 697 | * @return string 698 | */ 699 | private function processAtRulesAndSelectors($css) 700 | { 701 | $charset = ''; 702 | $imports = ''; 703 | $namespaces = ''; 704 | 705 | // Remove spaces before the things that should not have spaces before them. 706 | $css = preg_replace('/ ([@{};>+)\]~=,\/\n])/S', '$1', $css); 707 | 708 | // Remove the spaces after the things that should not have spaces after them. 709 | $css = preg_replace('/([{}:;>+(\[~=,\/\n]) /S', '$1', $css); 710 | 711 | // Shorten shortable double colon (CSS3) pseudo-elements to single colon (CSS2) 712 | $css = preg_replace('/::(before|after|first-(?:line|letter))(\{|,)/Si', ':$1$2', $css); 713 | 714 | // Retain space for special IE6 cases 715 | $css = preg_replace_callback('/:first-(line|letter)(\{|,)/Si', function ($matches) { 716 | return ':first-'. strtolower($matches[1]) .' '. $matches[2]; 717 | }, $css); 718 | 719 | // Find a fraction that may used in some @media queries such as: (min-aspect-ratio: 1/1) 720 | // Add token to add the "/" back in later 721 | $css = preg_replace('/\(([a-z-]+):([0-9]+)\/([0-9]+)\)/Si', '($1:$2'. self::QUERY_FRACTION .'$3)', $css); 722 | 723 | // Remove empty rule blocks up to 2 levels deep. 724 | $css = preg_replace(array_fill(0, 2, '/(\{)[^{};\/\n]+\{\}/S'), '$1', $css); 725 | $css = preg_replace('/[^{};\/\n]+\{\}/S', '', $css); 726 | 727 | // Two important comments next to each other? Remove extra newline. 728 | if ($this->keepImportantComments) { 729 | $css = str_replace("\n\n", "\n", $css); 730 | } 731 | 732 | // Restore fraction 733 | $css = str_replace(self::QUERY_FRACTION, '/', $css); 734 | 735 | // Lowercase some popular @directives 736 | $css = preg_replace_callback( 737 | '/(?charsetRegex, $css, $matches)) { 762 | // Keep the first @charset at-rule found 763 | $charset = $matches[0]; 764 | // Delete all @charset at-rules 765 | $css = preg_replace($this->charsetRegex, '', $css); 766 | } 767 | 768 | // @import handling 769 | $css = preg_replace_callback($this->importRegex, function ($matches) use (&$imports) { 770 | // Keep all @import at-rules found for later 771 | $imports .= $matches[0]; 772 | // Delete all @import at-rules 773 | return ''; 774 | }, $css); 775 | 776 | // @namespace handling 777 | $css = preg_replace_callback($this->namespaceRegex, function ($matches) use (&$namespaces) { 778 | // Keep all @namespace at-rules found for later 779 | $namespaces .= $matches[0]; 780 | // Delete all @namespace at-rules 781 | return ''; 782 | }, $css); 783 | 784 | // Order critical at-rules: 785 | // 1. @charset first 786 | // 2. @imports below @charset 787 | // 3. @namespaces below @imports 788 | $css = $charset . $imports . $namespaces . $css; 789 | 790 | return $css; 791 | } 792 | 793 | /** 794 | * Converts hsl() & rgb() colors to HEX format. 795 | * @param $matches 796 | * @return string 797 | */ 798 | private function shortenHslAndRgbToHexCallback($matches) 799 | { 800 | $type = $matches[1]; 801 | $values = explode(',', $matches[2]); 802 | $terminator = $matches[3]; 803 | 804 | if ($type === 'hsl') { 805 | $values = Utils::hslToRgb($values); 806 | } 807 | 808 | $hexColors = Utils::rgbToHex($values); 809 | 810 | // Restore space after rgb() or hsl() function in some cases such as: 811 | // background-image: linear-gradient(to bottom, rgb(210,180,140) 10%, rgb(255,0,0) 90%); 812 | if (!empty($terminator) && !preg_match('/[ ,);]/S', $terminator)) { 813 | $terminator = ' '. $terminator; 814 | } 815 | 816 | return '#'. implode('', $hexColors) . $terminator; 817 | } 818 | 819 | /** 820 | * Compresses HEX color values of the form #AABBCC to #ABC or short color name. 821 | * @param $matches 822 | * @return string 823 | */ 824 | private function shortenHexColorsCallback($matches) 825 | { 826 | $hex = $matches[1]; 827 | 828 | // Shorten suitable 6 chars HEX colors 829 | if (strlen($hex) === 6 && preg_match('/^([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3$/Si', $hex, $m)) { 830 | $hex = $m[1] . $m[2] . $m[3]; 831 | } 832 | 833 | // Lowercase 834 | $hex = '#'. strtolower($hex); 835 | 836 | // Replace Hex colors with shorter color names 837 | $color = array_key_exists($hex, $this->hexToNamedColorsMap) ? $this->hexToNamedColorsMap[$hex] : $hex; 838 | 839 | return $color . $matches[2]; 840 | } 841 | 842 | /** 843 | * Shortens all named colors with a shorter HEX counterpart for a set of safe properties 844 | * e.g. white -> #fff 845 | * @param array $matches 846 | * @return string 847 | */ 848 | private function shortenNamedColorsCallback($matches) 849 | { 850 | return $matches[1] . $this->namedToHexColorsMap[strtolower($matches[2])] . $matches[3]; 851 | } 852 | 853 | /** 854 | * Makes a string lowercase 855 | * @param array $matches 856 | * @return string 857 | */ 858 | private function strtolowerCallback($matches) 859 | { 860 | return strtolower($matches[0]); 861 | } 862 | } 863 | -------------------------------------------------------------------------------- /cssmin/Utils.php: -------------------------------------------------------------------------------- 1 | 1 ? $vh - 1 : $vh); 68 | 69 | if ($vh * 6 < 1) { 70 | return $v1 + ($v2 - $v1) * 6 * $vh; 71 | } 72 | 73 | if ($vh * 2 < 1) { 74 | return $v2; 75 | } 76 | 77 | if ($vh * 3 < 2) { 78 | return $v1 + ($v2 - $v1) * ((2 / 3) - $vh) * 6; 79 | } 80 | 81 | return $v1; 82 | } 83 | 84 | /** 85 | * Convert strings like "64M" or "30" to int values 86 | * @param mixed $size 87 | * @return int 88 | */ 89 | public static function normalizeInt($size) 90 | { 91 | if (is_string($size)) { 92 | $letter = substr($size, -1); 93 | $size = intval($size); 94 | switch ($letter) { 95 | case 'M': 96 | case 'm': 97 | return (int) $size * 1048576; 98 | case 'K': 99 | case 'k': 100 | return (int) $size * 1024; 101 | case 'G': 102 | case 'g': 103 | return (int) $size * 1073741824; 104 | } 105 | } 106 | return (int) $size; 107 | } 108 | 109 | /** 110 | * Converts a string containing and RGB percentage value into a RGB integer value i.e. '90%' -> 229.5 111 | * @param $rgbPercentage 112 | * @return int 113 | */ 114 | public static function rgbPercentageToRgbInteger($rgbPercentage) 115 | { 116 | if (strpos($rgbPercentage, '%') !== false) { 117 | $rgbPercentage = self::roundNumber(floatval(str_replace('%', '', $rgbPercentage)) * 2.55); 118 | } 119 | 120 | return intval($rgbPercentage, 10); 121 | } 122 | 123 | /** 124 | * Converts a RGB color into a HEX color 125 | * @param array $rgbColors 126 | * @return array 127 | */ 128 | public static function rgbToHex($rgbColors) 129 | { 130 | $hexColors = array(); 131 | 132 | // Values outside the sRGB color space should be clipped (0-255) 133 | for ($i = 0, $l = count($rgbColors); $i < $l; $i++) { 134 | $hexColors[$i] = sprintf("%02x", self::clampNumberSrgb(self::rgbPercentageToRgbInteger($rgbColors[$i]))); 135 | } 136 | 137 | return $hexColors; 138 | } 139 | 140 | /** 141 | * Rounds a number to its closest integer 142 | * @param $n 143 | * @return int 144 | */ 145 | public static function roundNumber($n) 146 | { 147 | return intval(round(floatval($n)), 10); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /cssmin/cssmin.php: -------------------------------------------------------------------------------- 1 | old_scripts = new WP_Scripts(); 23 | } else { 24 | $this->old_scripts = $scripts; 25 | } 26 | 27 | // Unset all the object properties except our private copy of the scripts object. 28 | // We have to unset everything so that the overload methods talk to $this->old_scripts->whatever 29 | // instead of $this->whatever. 30 | foreach ( array_keys( get_object_vars( $this ) ) as $key ) { 31 | if ( 'old_scripts' === $key ) { 32 | continue; 33 | } 34 | unset( $this->$key ); 35 | } 36 | } 37 | 38 | protected function has_inline_content( $handle ) { 39 | $before_output = $this->get_data( $handle, 'before' ); 40 | if ( ! empty( $before_output ) ) { 41 | return true; 42 | } 43 | 44 | $after_output = $this->get_data( $handle, 'after' ); 45 | if ( ! empty( $after_output ) ) { 46 | return true; 47 | } 48 | 49 | // JavaScript translations 50 | $has_translations = ! empty( $this->registered[ $handle ]->textdomain ); 51 | if ( $has_translations ) { 52 | return true; 53 | } 54 | 55 | return false; 56 | } 57 | 58 | function do_items( $handles = false, $group = false ) { 59 | $handles = false === $handles ? $this->queue : (array) $handles; 60 | $javascripts= array(); 61 | $siteurl = apply_filters( 'ngx_http_concat_site_url', $this->base_url ); 62 | 63 | $this->all_deps( $handles ); 64 | $level = 0; 65 | 66 | foreach( $this->to_do as $key => $handle ) { 67 | if ( in_array( $handle, $this->done ) || !isset( $this->registered[$handle] ) ) 68 | continue; 69 | 70 | if ( 0 === $group && $this->groups[$handle] > 0 ) { 71 | $this->in_footer[] = $handle; 72 | unset( $this->to_do[$key] ); 73 | continue; 74 | } 75 | 76 | if ( ! $this->registered[$handle]->src ) { // Defines a group. 77 | if ( $this->do_item( $handle, $group ) ) { 78 | $this->done[] = $handle; 79 | } 80 | 81 | continue; 82 | } 83 | 84 | if ( false === $group && in_array( $handle, $this->in_footer, true ) ) 85 | $this->in_footer = array_diff( $this->in_footer, (array) $handle ); 86 | 87 | $obj = $this->registered[$handle]; 88 | $js_url = $obj->src; 89 | $js_url_parsed = parse_url( $js_url ); 90 | $extra = $obj->extra; 91 | 92 | // Don't concat by default 93 | $do_concat = false; 94 | 95 | // Only try to concat static js files 96 | if ( false !== strpos( $js_url_parsed['path'], '.js' ) ) 97 | $do_concat = true; 98 | 99 | // Don't try to concat externally hosted scripts 100 | $is_internal_url = WPCOM_Concat_Utils::is_internal_url( $js_url, $siteurl ); 101 | if ( ! $is_internal_url ) { 102 | $do_concat = false; 103 | } 104 | 105 | // Concat and canonicalize the paths only for 106 | // existing scripts that aren't outside ABSPATH 107 | $js_realpath = WPCOM_Concat_Utils::realpath( $js_url, $siteurl ); 108 | if ( ! $js_realpath || 0 !== strpos( $js_realpath, ABSPATH ) ) 109 | $do_concat = false; 110 | else 111 | $js_url_parsed['path'] = substr( $js_realpath, strlen( ABSPATH ) - 1 ); 112 | 113 | if ( $this->has_inline_content( $handle ) ) { 114 | $do_concat = false; 115 | } 116 | 117 | // Skip core scripts that use Strict Mode 118 | if ( 'react' === $handle || 'react-dom' === $handle ) { 119 | $do_concat = false; 120 | } 121 | 122 | // Only allow filter for anything that is currently concat'ing 123 | // Scripts which are not concat'able should stay that way 124 | if ( true === $do_concat ) { 125 | $do_concat = apply_filters( 'js_do_concat', $do_concat, $handle ); 126 | } 127 | 128 | if ( true === $do_concat ) { 129 | if ( !isset( $javascripts[$level] ) ) 130 | $javascripts[$level]['type'] = 'concat'; 131 | 132 | $javascripts[$level]['paths'][] = $js_url_parsed['path']; 133 | $javascripts[$level]['handles'][] = $handle; 134 | 135 | if ( count( $javascripts[$level]['paths'] ) >= WPCOM_Concat_Utils::get_concat_max() ) { 136 | $level++; 137 | } 138 | 139 | } else { 140 | $level++; 141 | $javascripts[$level]['type'] = 'do_item'; 142 | $javascripts[$level]['handle'] = $handle; 143 | $level++; 144 | } 145 | unset( $this->to_do[$key] ); 146 | } 147 | 148 | if ( empty( $javascripts ) ) 149 | return $this->done; 150 | 151 | foreach ( $javascripts as $js_array ) { 152 | if ( 'do_item' == $js_array['type'] ) { 153 | if ( $this->do_item( $js_array['handle'], $group ) ) 154 | $this->done[] = $js_array['handle']; 155 | } else if ( 'concat' == $js_array['type'] ) { 156 | array_map( array( $this, 'print_extra_script' ), $js_array['handles'] ); 157 | 158 | if ( isset( $js_array['paths'] ) && count( $js_array['paths'] ) > 1) { 159 | $paths = array_map( function( $url ) { return ABSPATH . $url; }, $js_array['paths'] ); 160 | $mtime = max( array_map( 'filemtime', $paths ) ); 161 | $path_str = implode( ',', $js_array['paths'] ) . "?m={$mtime}j"; 162 | 163 | if ( $this->allow_gzip_compression ) { 164 | $path_64 = base64_encode( gzcompress( $path_str ) ); 165 | if ( strlen( $path_str ) > ( strlen( $path_64 ) + 1 ) ) 166 | $path_str = '-' . $path_64; 167 | } 168 | 169 | $href = $siteurl . "/_static/??" . $path_str; 170 | } elseif ( isset( $js_array['paths'] ) && is_array( $js_array['paths'] ) ) { 171 | $href = $this->cache_bust_mtime( $siteurl . $js_array['paths'][0], $siteurl ); 172 | } 173 | 174 | $this->done = array_merge( $this->done, $js_array['handles'] ); 175 | 176 | // Print before/after scripts from wp_inline_scripts() and concatenated script tag 177 | if ( isset( $js_array['extras']['before'] ) ) { 178 | foreach ( $js_array['extras']['before'] as $inline_before ) { 179 | echo $inline_before; 180 | } 181 | } 182 | 183 | // Allowed attributes taken from: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script 184 | $allowed_attributes = array( 185 | 'async', 186 | 'defer', 187 | 'nomodule', 188 | 'crossorigin', 189 | 'integrity', 190 | 'type', 191 | 'nonce', 192 | 'referrerpolicy' 193 | ); 194 | $attr_string = ''; 195 | /** 196 | * Allow adding extra arguments for the script tag. 197 | * Either associative array or regular array. 198 | * E.g. 199 | * [ 'async', 'defer', 'nonce' => '$random_generated_number' ] 200 | * 201 | * @param string $href URL for the script. 202 | * @param array $js_array array that contains the type, path, and handle for the scripts being processed. 203 | * @param WPcom_JS_Concat this instance of WPcom_JS_Concat. 204 | */ 205 | foreach ( (array) apply_filters( 'js_concat_script_attributes', [], $href, $js_array, $this ) as $k => $v ) { 206 | if ( is_int( $k ) && in_array( $v, $allowed_attributes ) ) { 207 | $attr_string .= sprintf( ' %s', esc_attr( $v ) ); 208 | } else if ( array_search( $k, $allowed_attributes ) ){ 209 | $attr_string .= sprintf( ' %s="%s"', sanitize_key( is_int( $k ) ? $v : $k ), esc_attr( $v ) ); 210 | } 211 | } 212 | if ( isset( $href ) ) { 213 | $script_html = sprintf( '', $href, $attr_string ); 214 | echo apply_filters( "ngx_http_concat_script_loader_tag", $script_html, $href, $attr_string ); 215 | } 216 | if ( isset( $js_array['extras']['after'] ) ) { 217 | foreach ( $js_array['extras']['after'] as $inline_after ) { 218 | echo $inline_after; 219 | } 220 | } 221 | } 222 | } 223 | 224 | do_action( 'js_concat_did_items', $javascripts ); 225 | return $this->done; 226 | } 227 | 228 | function cache_bust_mtime( $url, $siteurl ) { 229 | if ( strpos( $url, '?m=' ) ) 230 | return $url; 231 | 232 | $parts = parse_url( $url ); 233 | if ( ! isset( $parts['path'] ) || empty( $parts['path'] ) ) 234 | return $url; 235 | 236 | $file = WPCOM_Concat_Utils::realpath( $url, $siteurl ); 237 | 238 | $mtime = false; 239 | if ( file_exists( $file ) ) 240 | $mtime = filemtime( $file ); 241 | 242 | if ( ! $mtime ) 243 | return $url; 244 | 245 | if ( false === strpos( $url, '?' ) ) { 246 | $q = ''; 247 | } else { 248 | list( $url, $q ) = explode( '?', $url, 2 ); 249 | if ( strlen( $q ) ) 250 | $q = '&' . $q; 251 | } 252 | 253 | return "$url?m={$mtime}g{$q}"; 254 | } 255 | 256 | function __isset( $key ) { 257 | return isset( $this->old_scripts->$key ); 258 | } 259 | 260 | function __unset( $key ) { 261 | unset( $this->old_scripts->$key ); 262 | } 263 | 264 | function &__get( $key ) { 265 | return $this->old_scripts->$key; 266 | } 267 | 268 | function __set( $key, $value ) { 269 | $this->old_scripts->$key = $value; 270 | } 271 | } 272 | 273 | function js_concat_init() { 274 | global $wp_scripts; 275 | 276 | $wp_scripts = new WPcom_JS_Concat( $wp_scripts ); 277 | $wp_scripts->allow_gzip_compression = ALLOW_GZIP_COMPRESSION; 278 | } 279 | 280 | add_action( 'init', 'js_concat_init' ); 281 | -------------------------------------------------------------------------------- /ngx-http-concat.php: -------------------------------------------------------------------------------- 1 | 'text/css', 23 | 'js' => 'application/javascript' 24 | ); 25 | 26 | /* Constants */ 27 | // By default determine the document root from this scripts path in the plugins dir (you can hardcode this define) 28 | define( 'CONCAT_FILES_ROOT', substr( dirname( __DIR__ ), 0, strpos( dirname( __DIR__ ), '/wp-content' ) ) ); 29 | 30 | function concat_http_status_exit( $status ) { 31 | switch ( $status ) { 32 | case 200: 33 | $text = 'OK'; 34 | break; 35 | case 400: 36 | $text = 'Bad Request'; 37 | break; 38 | case 403: 39 | $text = 'Forbidden'; 40 | break; 41 | case 404: 42 | $text = 'Not found'; 43 | break; 44 | case 500: 45 | $text = 'Internal Server Error'; 46 | break; 47 | default: 48 | $text = ''; 49 | } 50 | 51 | $protocol = $_SERVER['SERVER_PROTOCOL']; 52 | if ( 'HTTP/1.1' != $protocol && 'HTTP/1.0' != $protocol ) 53 | $protocol = 'HTTP/1.0'; 54 | 55 | @header( "$protocol $status $text", true, $status ); 56 | exit(); 57 | } 58 | 59 | function concat_get_mtype( $file ) { 60 | global $concat_types; 61 | 62 | $lastdot_pos = strrpos( $file, '.' ); 63 | if ( false === $lastdot_pos ) 64 | return false; 65 | 66 | $ext = substr( $file, $lastdot_pos + 1 ); 67 | 68 | return isset( $concat_types[$ext] ) ? $concat_types[$ext] : false; 69 | } 70 | 71 | function concat_get_path( $uri ) { 72 | if ( ! strlen( $uri ) ) 73 | concat_http_status_exit( 400 ); 74 | 75 | if ( false !== strpos( $uri, '..' ) || false !== strpos( $uri, "\0" ) ) 76 | concat_http_status_exit( 400 ); 77 | 78 | return CONCAT_FILES_ROOT . ( '/' != $uri[0] ? '/' : '' ) . $uri; 79 | } 80 | 81 | /* Main() */ 82 | if ( !in_array( $_SERVER['REQUEST_METHOD'], array( 'GET', 'HEAD' ) ) ) 83 | concat_http_status_exit( 400 ); 84 | 85 | // /_static/??/foo/bar.css,/foo1/bar/baz.css?m=293847g 86 | // or 87 | // /_static/??-eJzTT8vP109KLNJLLi7W0QdyDEE8IK4CiVjn2hpZGluYmKcDABRMDPM= 88 | // or url-encoded 89 | // /_static/?%3F%2Ffoo%2Fbar.css%2Cfoo1%2Fbar%2Fbaz.css%3Fm%3D293847g 90 | $args = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_QUERY ); 91 | if ( ! $args ) { 92 | concat_http_status_exit( 400 ); 93 | } 94 | 95 | $args = rawurldecode( $args ); 96 | 97 | if ( false === strpos( $args, '?' ) ) { 98 | concat_http_status_exit( 400 ); 99 | } 100 | 101 | $args = substr( $args, strpos( $args, '?' ) + 1 ); 102 | 103 | // /foo/bar.css,/foo1/bar/baz.css?m=293847g 104 | // or 105 | // -eJzTT8vP109KLNJLLi7W0QdyDEE8IK4CiVjn2hpZGluYmKcDABRMDPM= 106 | if ( '-' == $args[0] ) { 107 | $args = @gzuncompress( base64_decode( substr( $args, 1 ) ) ); 108 | 109 | // Invalid data, abort! 110 | if ( false === $args ) { 111 | concat_http_status_exit( 400 ); 112 | } 113 | } 114 | 115 | // /foo/bar.css,/foo1/bar/baz.css?m=293847g 116 | $version_string_pos = strpos( $args, '?' ); 117 | if ( false !== $version_string_pos ) 118 | $args = substr( $args, 0, $version_string_pos ); 119 | 120 | // /foo/bar.css,/foo1/bar/baz.css 121 | $args = explode( ',', $args ); 122 | if ( ! $args ) 123 | concat_http_status_exit( 400 ); 124 | 125 | // array( '/foo/bar.css', '/foo1/bar/baz.css' ) 126 | if ( 0 == count( $args ) || count( $args ) > WPCOM_Concat_Utils::get_concat_max() ) 127 | concat_http_status_exit( 400 ); 128 | 129 | // If we're in a subdirectory context, use that as the root. 130 | // We can't assume that the root serves the same content as the subdir. 131 | $subdir_path_prefix = ''; 132 | $request_path = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH ); 133 | $_static_index = strpos( $request_path, '/_static/' ); 134 | if ( $_static_index > 0 ) { 135 | $subdir_path_prefix = substr( $request_path, 0, $_static_index ); 136 | } 137 | unset( $request_path, $_static_index ); 138 | 139 | $last_modified = 0; 140 | $pre_output = ''; 141 | $output = ''; 142 | 143 | $css_minify = new tubalmartin\CssMin\Minifier; 144 | 145 | foreach ( $args as $uri ) { 146 | $fullpath = concat_get_path( $uri ); 147 | 148 | if ( ! file_exists( $fullpath ) ) 149 | concat_http_status_exit( 404 ); 150 | 151 | $mime_type = concat_get_mtype( $fullpath ); 152 | if ( ! in_array( $mime_type, $concat_types ) ) 153 | concat_http_status_exit( 400 ); 154 | 155 | if ( $concat_unique ) { 156 | if ( ! isset( $last_mime_type ) ) 157 | $last_mime_type = $mime_type; 158 | 159 | if ( $last_mime_type != $mime_type ) 160 | concat_http_status_exit( 400 ); 161 | } 162 | 163 | $stat = stat( $fullpath ); 164 | if ( false === $stat ) 165 | concat_http_status_exit( 500 ); 166 | 167 | if ( $stat['mtime'] > $last_modified ) 168 | $last_modified = $stat['mtime']; 169 | 170 | $buf = file_get_contents( $fullpath ); 171 | if ( false === $buf ) 172 | concat_http_status_exit( 500 ); 173 | 174 | if ( 'text/css' == $mime_type ) { 175 | $dirpath = $subdir_path_prefix . dirname( $uri ); 176 | 177 | // url(relative/path/to/file) -> url(/absolute/and/not/relative/path/to/file) 178 | $buf = WPCOM_Concat_Utils::relative_path_replace( $buf, $dirpath ); 179 | 180 | // The @charset rules must be on top of the output 181 | if ( 0 === strpos( $buf, '@charset' ) ) { 182 | preg_replace_callback( 183 | '/(?P@charset\s+[\'"][^\'"]+[\'"];)/i', 184 | function ( $match ) { 185 | global $pre_output; 186 | 187 | if ( 0 === strpos( $pre_output, '@charset' ) ) 188 | return ''; 189 | 190 | $pre_output = $match[0] . "\n" . $pre_output; 191 | 192 | return ''; 193 | }, 194 | $buf 195 | ); 196 | } 197 | 198 | // Move the @import rules on top of the concatenated output. 199 | // Only @charset rule are allowed before them. 200 | if ( false !== strpos( $buf, '@import' ) ) { 201 | $buf = preg_replace_callback( 202 | '/(?P@import\b\s{0,}(?:url\s*\()?[\'"\s]*)(?P[^\'"\s](?:https?:\/\/.+\/?)?.+?)(?P[\'"\s\)]*(?:\W|screen|print|all);)/i', 203 | function ( $match ) use ( $dirpath ) { 204 | global $pre_output; 205 | 206 | if ( 0 !== strpos( $match['path'], 'http' ) && '/' != $match['path'][0] ) 207 | $pre_output .= $match['pre_path'] . ( $dirpath == '/' ? '/' : $dirpath . '/' ) . 208 | $match['path'] . $match['post_path'] . "\n"; 209 | else 210 | $pre_output .= $match[0] . "\n"; 211 | 212 | return ''; 213 | }, 214 | $buf 215 | ); 216 | } 217 | 218 | $buf = $css_minify->run( $buf ); 219 | } 220 | 221 | if ( 'application/javascript' == $mime_type ) 222 | $output .= "$buf;\n"; 223 | else 224 | $output .= "$buf"; 225 | } 226 | 227 | header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $last_modified ) . ' GMT' ); 228 | header( 'Content-Length: ' . ( strlen( $pre_output ) + strlen( $output ) ) ); 229 | header( "Content-Type: $mime_type" ); 230 | 231 | echo $pre_output . $output; 232 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | ./tests/ 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | array( 8 | 'https://fonts.googleapis.com/path/to/file.css', 9 | 'https://example.com', 10 | false, 11 | ), 12 | 13 | 'subdir_site__external_url' => array( 14 | 'https://fonts.googleapis.com/path/to/file.css', 15 | 'https://example.com/mysite', 16 | false, 17 | ), 18 | 19 | // Interal: Full URL 20 | 'single_site__internal_url' => array( 21 | 'https://example.com/wp-content/plugins/plugin/style.css', 22 | 'https://example.com', 23 | true, 24 | ), 25 | 26 | 'single_site_trailingslash__internal_url' => array( 27 | 'https://example.com/wp-content/plugins/plugin/style.css', 28 | 'https://example.com/', 29 | true, 30 | ), 31 | 32 | 33 | 'subdir_site__internal_subdir_url' => array( 34 | 'https://example.com/mysite/wp-content/plugins/plugin/style.css', 35 | 'https://example.com/mysite', 36 | true, 37 | ), 38 | 39 | 'subdir_site__internal_root_url' => array( 40 | 'https://example.com/wp-content/plugins/plugin/style.css', 41 | 'https://example.com/mysite', 42 | false, 43 | ), 44 | 45 | // Internal: absolute (i.e. no host, /wp-content/path/to/file.css) 46 | 'single_site__internal_absolute_url' => array( 47 | '/wp-content/plugins/plugin/style.css', 48 | 'https://example.com', 49 | true, 50 | ), 51 | 52 | 'subdir_site__internal_absolute_url' => array( 53 | '/wp-content/plugins/plugin/style.css', 54 | 'https://example.com/mysite', 55 | true, 56 | ), 57 | ); 58 | } 59 | 60 | /** 61 | * @dataProvider get_test_data 62 | */ 63 | function test__function( $test_url, $site_url, $expected ) { 64 | $actual = WPCOM_Concat_Utils::is_internal_url( $test_url, $site_url ); 65 | 66 | $this->assertSame( $expected, $actual ); 67 | } 68 | } 69 | 70 | class WPCOM_Concat_Utils__Replace_Relative_Url__TestCase extends WP_UnitTestCase { 71 | function get_test_data() { 72 | return array( 73 | // Relative URL with double quotes. 74 | 'relative_url_double_quotes' => array( 75 | 'src:url("social-logos.eot?51b607ee5b5cb2a0e4517176475a424c");', 76 | 'src:url(/social-logos.eot?51b607ee5b5cb2a0e4517176475a424c);', 77 | ), 78 | // Relative URL with double quotes subdir. 79 | 'relative_url_double_quotes_subdir' => array( 80 | 'src:url("social-logos.eot?51b607ee5b5cb2a0e4517176475a424c");', 81 | 'src:url(/mysite/social-logos.eot?51b607ee5b5cb2a0e4517176475a424c);', 82 | '/mysite', 83 | ), 84 | // Relative URL with single quotes. 85 | 'relative_url_single_quotes' => array( 86 | 'src:url(\'social-logos.eot?51b607ee5b5cb2a0e4517176475a424c\');', 87 | 'src:url(/social-logos.eot?51b607ee5b5cb2a0e4517176475a424c);' 88 | ), 89 | // Relative URL with single quotes subdir. 90 | 'relative_url_single_quotes_subdir' => array( 91 | 'src:url(\'social-logos.eot?51b607ee5b5cb2a0e4517176475a424c\');', 92 | 'src:url(/mysite/social-logos.eot?51b607ee5b5cb2a0e4517176475a424c);', 93 | '/mysite', 94 | ), 95 | // Relative URL no quotation marks. 96 | 'relative_url_no_quotes' => array( 97 | 'src:url(social-logos.eot?51b607ee5b5cb2a0e4517176475a424c);', 98 | 'src:url(/social-logos.eot?51b607ee5b5cb2a0e4517176475a424c);', 99 | ), 100 | // Relative URL no quotation marks subdir. 101 | 'relative_url_no_quotes_subdir' => array( 102 | 'src:url(social-logos.eot?51b607ee5b5cb2a0e4517176475a424c);', 103 | 'src:url(/mysite/social-logos.eot?51b607ee5b5cb2a0e4517176475a424c);', 104 | '/mysite', 105 | ), 106 | // Absolute URL double quotes. 107 | 'absolute_url_double_quotes' => array( 108 | 'src:url("https://fonts.googleapis.com/path/to/file.css");', 109 | 'src:url("https://fonts.googleapis.com/path/to/file.css");', 110 | ), 111 | // Absolute URL single quotes. 112 | 'absolute_url_single_quotes' => array( 113 | 'src:url(\'https://fonts.googleapis.com/path/to/file.css\');', 114 | 'src:url(\'https://fonts.googleapis.com/path/to/file.css\');', 115 | ), 116 | // Absolute URL no quotes. 117 | 'absolute_url_no_quotes' => array( 118 | 'src:url(https://fonts.googleapis.com/path/to/file.css);', 119 | 'src:url(https://fonts.googleapis.com/path/to/file.css);', 120 | ), 121 | // Relative URL containing hashtag in double quotes. 122 | 'relative_url_with_hashtag_double_quotes' => array( 123 | 'src:url("social-logos.eot?#iefix");', 124 | 'src:url(/social-logos.eot?#iefix);', 125 | ), 126 | // Relative URL containing hashtag in double quotes subdir. 127 | 'relative_url_with_hashtag_double_quotes_subdir' => array( 128 | 'src:url("social-logos.eot?#iefix");', 129 | 'src:url(/mysite/social-logos.eot?#iefix);', 130 | '/mysite', 131 | ), 132 | // Relative URL contaiing hashtag in single quotes. 133 | 'relative_url_with_hashtag_single_quotes' => array( 134 | 'src:url(\'social-logos.eot?#iefix\');', 135 | 'src:url(/social-logos.eot?#iefix);', 136 | ), 137 | // Relative URL contaiing hashtag in single quotes subdir. 138 | 'relative_url_with_hashtag_single_quotes_subdir' => array( 139 | 'src:url(\'social-logos.eot?#iefix\');', 140 | 'src:url(/mysite/social-logos.eot?#iefix);', 141 | '/mysite', 142 | ), 143 | // Relative URL containig hashtag w/o quotes. 144 | 'relative_url_with_hashtag_no_quotes' => array( 145 | 'src:url(social-logos.eot?#iefix);', 146 | 'src:url(/social-logos.eot?#iefix);', 147 | ), 148 | // Relative URL containig hashtag w/o quotes subdir. 149 | 'relative_url_with_hashtag_no_quotes_subdir' => array( 150 | 'src:url(social-logos.eot?#iefix);', 151 | 'src:url(/mysite/social-logos.eot?#iefix);', 152 | '/mysite', 153 | ), 154 | // Relative URL starting with hashtag double quotes. 155 | 'relative_url_starting_with_hashtag_double_quotes' => array( 156 | 'src:url("#social-logos.eot")', 157 | 'src:url("#social-logos.eot")', 158 | ), 159 | // Relative URL starting with hashtag single quotes. 160 | 'relative_url_starting_with_hashtag_single_quotes' => array( 161 | 'src:url(\'#social-logos.eot\')', 162 | 'src:url(\'#social-logos.eot\')', 163 | ), 164 | // Relative URL starting with hashtag no quotes. 165 | 'relative_url_starting_with_hashtag_no_quotes' => array( 166 | 'src:url(#social-logos.eot)', 167 | 'src:url(#social-logos.eot)', 168 | ), 169 | // data: URL double quotes. 170 | 'data_url_double_quotes' => array( 171 | 'src:url(url("data:application/x-font-woff;charset=utf-8;base64,d09GRk9UVE8AAEZAAAoAAAAAfBAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABDRkYgAAAA9AAAQsUAAHZfa1y5A0ZGVE0AAEO8AAAAGQAAABx4Dt9ZT1MvMgAAQ9gAAABKAAAAYEC7Yj5jbWFwAABEJAAAAIUAAAG6pEWcoGhlYWQAAESsAAAALwAAADYHEbeJaGhlYQAARNwAAAAdAAAAJAOvAd5obXR4AABE/AAAADgAAABeCDEE521heHAAAEU0AAAABgAAAAYALVAAbmFtZQAARTwAAADrAAAB5koHYmpwb3N0AABGKAAAABYAAAAg/8MAGnicrZ13mJXFFfDn3XbvVnb37i596SC9d8uLDQyKFXtD7F0RYzSGay9LDCpqjB1REaPR2CXCFbGB2ACR3otLWdje5zu/M++9QGK+fH98Dzyz804vZ86cOtczKSnG87zsKTdMvmrStX2vveGKG6YYL8l45qTaFqZ2rFc7Lqn2hOTaVinTs7zibW36ZyUXd3vVZqUUZ5jWp+fbkpJEJCv04mW102p7pbZLat2inTG57ZJuzmtn+rSb2jLf9KfJsGlhikx709X0NoPMSOObsWaCmWjON5PN1eYmc5uZZu43fzYzzTNmlnndvG0+NPPNF+Zbs8ysNpvMTlNmqk2Tl+Jlevlea6+j18Pr5w2Vf2O8k6Zef9XxAwcM5M+gAQPcn+BrkPsz2P0Z6v4Mc3+Guz8j9c9AV2+gqzf")', 172 | 'src:url(url("data:application/x-font-woff;charset=utf-8;base64,d09GRk9UVE8AAEZAAAoAAAAAfBAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABDRkYgAAAA9AAAQsUAAHZfa1y5A0ZGVE0AAEO8AAAAGQAAABx4Dt9ZT1MvMgAAQ9gAAABKAAAAYEC7Yj5jbWFwAABEJAAAAIUAAAG6pEWcoGhlYWQAAESsAAAALwAAADYHEbeJaGhlYQAARNwAAAAdAAAAJAOvAd5obXR4AABE/AAAADgAAABeCDEE521heHAAAEU0AAAABgAAAAYALVAAbmFtZQAARTwAAADrAAAB5koHYmpwb3N0AABGKAAAABYAAAAg/8MAGnicrZ13mJXFFfDn3XbvVnb37i596SC9d8uLDQyKFXtD7F0RYzSGay9LDCpqjB1REaPR2CXCFbGB2ACR3otLWdje5zu/M++9QGK+fH98Dzyz804vZ86cOtczKSnG87zsKTdMvmrStX2vveGKG6YYL8l45qTaFqZ2rFc7Lqn2hOTaVinTs7zibW36ZyUXd3vVZqUUZ5jWp+fbkpJEJCv04mW102p7pbZLat2inTG57ZJuzmtn+rSb2jLf9KfJsGlhikx709X0NoPMSOObsWaCmWjON5PN1eYmc5uZZu43fzYzzTNmlnndvG0+NPPNF+Zbs8ysNpvMTlNmqk2Tl+Jlevlea6+j18Pr5w2Vf2O8k6Zef9XxAwcM5M+gAQPcn+BrkPsz2P0Z6v4Mc3+Guz8j9c9AV2+gqzf")', 173 | ), 174 | 'data_url_single_quotes' => array( 175 | 'src:url(url(\'data:application/x-font-woff;charset=utf-8;base64,d09GRk9UVE8AAEZAAAoAAAAAfBAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABDRkYgAAAA9AAAQsUAAHZfa1y5A0ZGVE0AAEO8AAAAGQAAABx4Dt9ZT1MvMgAAQ9gAAABKAAAAYEC7Yj5jbWFwAABEJAAAAIUAAAG6pEWcoGhlYWQAAESsAAAALwAAADYHEbeJaGhlYQAARNwAAAAdAAAAJAOvAd5obXR4AABE/AAAADgAAABeCDEE521heHAAAEU0AAAABgAAAAYALVAAbmFtZQAARTwAAADrAAAB5koHYmpwb3N0AABGKAAAABYAAAAg/8MAGnicrZ13mJXFFfDn3XbvVnb37i596SC9d8uLDQyKFXtD7F0RYzSGay9LDCpqjB1REaPR2CXCFbGB2ACR3otLWdje5zu/M++9QGK+fH98Dzyz804vZ86cOtczKSnG87zsKTdMvmrStX2vveGKG6YYL8l45qTaFqZ2rFc7Lqn2hOTaVinTs7zibW36ZyUXd3vVZqUUZ5jWp+fbkpJEJCv04mW102p7pbZLat2inTG57ZJuzmtn+rSb2jLf9KfJsGlhikx709X0NoPMSOObsWaCmWjON5PN1eYmc5uZZu43fzYzzTNmlnndvG0+NPPNF+Zbs8ysNpvMTlNmqk2Tl+Jlevlea6+j18Pr5w2Vf2O8k6Zef9XxAwcM5M+gAQPcn+BrkPsz2P0Z6v4Mc3+Guz8j9c9AV2+gqzf\')', 176 | 'src:url(url(\'data:application/x-font-woff;charset=utf-8;base64,d09GRk9UVE8AAEZAAAoAAAAAfBAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABDRkYgAAAA9AAAQsUAAHZfa1y5A0ZGVE0AAEO8AAAAGQAAABx4Dt9ZT1MvMgAAQ9gAAABKAAAAYEC7Yj5jbWFwAABEJAAAAIUAAAG6pEWcoGhlYWQAAESsAAAALwAAADYHEbeJaGhlYQAARNwAAAAdAAAAJAOvAd5obXR4AABE/AAAADgAAABeCDEE521heHAAAEU0AAAABgAAAAYALVAAbmFtZQAARTwAAADrAAAB5koHYmpwb3N0AABGKAAAABYAAAAg/8MAGnicrZ13mJXFFfDn3XbvVnb37i596SC9d8uLDQyKFXtD7F0RYzSGay9LDCpqjB1REaPR2CXCFbGB2ACR3otLWdje5zu/M++9QGK+fH98Dzyz804vZ86cOtczKSnG87zsKTdMvmrStX2vveGKG6YYL8l45qTaFqZ2rFc7Lqn2hOTaVinTs7zibW36ZyUXd3vVZqUUZ5jWp+fbkpJEJCv04mW102p7pbZLat2inTG57ZJuzmtn+rSb2jLf9KfJsGlhikx709X0NoPMSOObsWaCmWjON5PN1eYmc5uZZu43fzYzzTNmlnndvG0+NPPNF+Zbs8ysNpvMTlNmqk2Tl+Jlevlea6+j18Pr5w2Vf2O8k6Zef9XxAwcM5M+gAQPcn+BrkPsz2P0Z6v4Mc3+Guz8j9c9AV2+gqzf\')', 177 | ), 178 | // Absolute URL non-secure double quotes. 179 | 'absolute_url_http_double_quotes' => array( 180 | 'src:url("http://fonts.googleapis.com/path/to/file.css")', 181 | 'src:url("http://fonts.googleapis.com/path/to/file.css")', 182 | ), 183 | // Absolute URL non-secure single quotes. 184 | 'absolute_url_http_single_quotes' => array( 185 | 'src:url(\'http://fonts.googleapis.com/path/to/file.css\')', 186 | 'src:url(\'http://fonts.googleapis.com/path/to/file.css\')', 187 | ), 188 | // Absolute URL non-secure no quotes. 189 | 'absolute_url_http_no_quotes' => array( 190 | 'src:url(http://fonts.googleapis.com/path/to/file.css)', 191 | 'src:url(http://fonts.googleapis.com/path/to/file.css)', 192 | ), 193 | // Absolute URL containing hashtag 194 | 'absolute_url_containig_hashtag' => array( 195 | 'src:url(http://fonts.googleapis.com/path/to/file.css#iefix)', 196 | 'src:url(http://fonts.googleapis.com/path/to/file.css#iefix)', 197 | ), 198 | ); 199 | } 200 | 201 | /** 202 | * @dataProvider get_test_data 203 | */ 204 | function test__function( $test_string, $expected, $dirpath = '/' ) { 205 | $actual = WPCOM_Concat_Utils::relative_path_replace( $test_string, $dirpath ); 206 | $this->assertSame( $expected, $actual ); 207 | } 208 | } 209 | --------------------------------------------------------------------------------