├── .gitignore ├── .travis.yml ├── Gruntfile.js ├── LICENSE ├── README.md ├── bin └── install-wp-tests.sh ├── composer.json ├── composer.lock ├── lightweight-term-count-update.php ├── multisite.xml ├── package.json ├── phpcs.ruleset.xml ├── phpunit.xml.dist ├── readme.txt └── tests ├── bootstrap.php └── test-term-counting.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | /.phpunit.result.cache 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: php 4 | 5 | notifications: 6 | email: 7 | on_success: never 8 | on_failure: change 9 | 10 | branches: 11 | only: 12 | - master 13 | - develop 14 | 15 | matrix: 16 | include: 17 | - php: 5.6 18 | env: WP_VERSION=latest PHP_LINT=1 19 | - php: 7.0 20 | env: WP_VERSION=latest PHP_LINT=1 21 | - php: 7.1 22 | env: WP_VERSION=latest PHP_LINT=1 23 | - php: 7.2 24 | env: WP_VERSION=latest PHP_LINT=1 WP_PHPCS=1 25 | - php: 7.2 26 | env: WP_VERSION=nightly WP_TEST_OBJECT_CACHE=1 27 | - php: 7.3 28 | env: WP_VERSION=nightly PHP_LINT=1 29 | fast_finish: true 30 | 31 | cache: 32 | - composer 33 | - $HOME/.composer/cache 34 | 35 | before_script: 36 | - | 37 | if [[ ! -z "$WP_VERSION" ]] ; then 38 | bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION 39 | fi 40 | - | 41 | if [[ "$WP_TEST_OBJECT_CACHE" == "1" ]]; then 42 | WP_CORE_DIR=${WP_CORE_DIR-/tmp/wordpress/} 43 | curl https://raw.githubusercontent.com/tollmanz/wordpress-pecl-memcached-object-cache/584392b56dc4adbe52bd2c7b86f875e23a3e5f75/object-cache.php > $WP_CORE_DIR/wp-content/object-cache.php 44 | ls -la $WP_CORE_DIR/wp-content/ 45 | echo "extension = memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini 46 | fi 47 | - export PATH="$HOME/.composer/vendor/bin:$PATH" 48 | - | 49 | if [[ ${TRAVIS_PHP_VERSION:0:2} == "7." ]]; then 50 | composer global require "phpunit/phpunit=5.7.*" 51 | else 52 | composer global require "phpunit/phpunit=4.8.*" 53 | fi 54 | - | 55 | if [[ "$WP_PHPCS" == "1" ]]; then 56 | composer global require wp-coding-standards/wpcs 57 | phpcs --config-set installed_paths $HOME/.composer/vendor/wp-coding-standards/wpcs 58 | fi 59 | - phpunit --version 60 | 61 | script: 62 | - if [[ "$PHP_LINT" == "1" ]]; then find . -type "f" -iname "*.php" | xargs -L "1" php -l; fi 63 | - if [[ "$WP_PHPCS" == "1" ]]; then phpcs -n --standard=phpcs.ruleset.xml lightweight-term-count-update.php; fi 64 | - phpunit 65 | - phpunit -c multisite.xml 66 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function( grunt ) { 2 | 3 | 'use strict'; 4 | var banner = '/**\n * <%= pkg.homepage %>\n * Copyright (c) <%= grunt.template.today("yyyy") %>\n * This file is generated automatically. Do not edit.\n */\n'; 5 | // Project configuration 6 | grunt.initConfig( { 7 | 8 | pkg: grunt.file.readJSON( 'package.json' ), 9 | 10 | addtextdomain: { 11 | options: { 12 | textdomain: 'lightweight-term-count-update', 13 | }, 14 | target: { 15 | files: { 16 | src: [ '*.php', '**/*.php', '!node_modules/**', '!php-tests/**', '!bin/**' ] 17 | } 18 | } 19 | }, 20 | 21 | wp_readme_to_markdown: { 22 | your_target: { 23 | files: { 24 | 'README.md': 'readme.txt' 25 | } 26 | }, 27 | }, 28 | 29 | makepot: { 30 | target: { 31 | options: { 32 | domainPath: '/languages', 33 | mainFile: 'lightweight-term-count-update.php', 34 | potFilename: 'lightweight-term-count-update.pot', 35 | potHeaders: { 36 | poedit: true, 37 | 'x-poedit-keywordslist': true 38 | }, 39 | type: 'wp-plugin', 40 | updateTimestamp: true 41 | } 42 | } 43 | }, 44 | } ); 45 | 46 | grunt.loadNpmTasks( 'grunt-wp-i18n' ); 47 | grunt.loadNpmTasks( 'grunt-wp-readme-to-markdown' ); 48 | grunt.registerTask( 'i18n', ['addtextdomain', 'makepot'] ); 49 | grunt.registerTask( 'readme', ['wp_readme_to_markdown'] ); 50 | 51 | grunt.util.linefeed = '\n'; 52 | 53 | }; 54 | -------------------------------------------------------------------------------- /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. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lightweight Term Count Update 2 | 3 | This plugin prevents WordPress from running term count queries. 4 | 5 | Rather, it will instead perform a simple increment/decrement on the post count that does not require a recalculation. -------------------------------------------------------------------------------- /bin/install-wp-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ $# -lt 3 ]; then 4 | echo "usage: $0 [db-host] [wp-version] [skip-database-creation]" 5 | exit 1 6 | fi 7 | 8 | DB_NAME=$1 9 | DB_USER=$2 10 | DB_PASS=$3 11 | DB_HOST=${4-localhost} 12 | WP_VERSION=${5-latest} 13 | SKIP_DB_CREATE=${6-false} 14 | 15 | WP_TESTS_DIR=${WP_TESTS_DIR-/tmp/wordpress-tests-lib} 16 | WP_CORE_DIR=${WP_CORE_DIR-/tmp/wordpress/} 17 | 18 | download() { 19 | if [ `which curl` ]; then 20 | curl -s "$1" > "$2"; 21 | elif [ `which wget` ]; then 22 | wget -nv -O "$2" "$1" 23 | fi 24 | } 25 | 26 | if [[ $WP_VERSION =~ [0-9]+\.[0-9]+(\.[0-9]+)? ]]; then 27 | WP_TESTS_TAG="tags/$WP_VERSION" 28 | elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then 29 | WP_TESTS_TAG="trunk" 30 | else 31 | # http serves a single offer, whereas https serves multiple. we only want one 32 | download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json 33 | grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json 34 | LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//') 35 | if [[ -z "$LATEST_VERSION" ]]; then 36 | echo "Latest WordPress version could not be found" 37 | exit 1 38 | fi 39 | WP_TESTS_TAG="tags/$LATEST_VERSION" 40 | fi 41 | 42 | set -ex 43 | 44 | install_wp() { 45 | 46 | if [ -d $WP_CORE_DIR ]; then 47 | return; 48 | fi 49 | 50 | mkdir -p $WP_CORE_DIR 51 | 52 | if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then 53 | mkdir -p /tmp/wordpress-nightly 54 | download https://wordpress.org/nightly-builds/wordpress-latest.zip /tmp/wordpress-nightly/wordpress-nightly.zip 55 | unzip -q /tmp/wordpress-nightly/wordpress-nightly.zip -d /tmp/wordpress-nightly/ 56 | mv /tmp/wordpress-nightly/wordpress/* $WP_CORE_DIR 57 | else 58 | if [ $WP_VERSION == 'latest' ]; then 59 | local ARCHIVE_NAME='latest' 60 | else 61 | local ARCHIVE_NAME="wordpress-$WP_VERSION" 62 | fi 63 | download https://wordpress.org/${ARCHIVE_NAME}.tar.gz /tmp/wordpress.tar.gz 64 | tar --strip-components=1 -zxmf /tmp/wordpress.tar.gz -C $WP_CORE_DIR 65 | fi 66 | 67 | download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php 68 | } 69 | 70 | install_test_suite() { 71 | # portable in-place argument for both GNU sed and Mac OSX sed 72 | if [[ $(uname -s) == 'Darwin' ]]; then 73 | local ioption='-i .bak' 74 | else 75 | local ioption='-i' 76 | fi 77 | 78 | # set up testing suite if it doesn't yet exist 79 | if [ ! -d $WP_TESTS_DIR ]; then 80 | # set up testing suite 81 | mkdir -p $WP_TESTS_DIR 82 | svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes 83 | svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data 84 | fi 85 | 86 | if [ ! -f wp-tests-config.php ]; then 87 | download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php 88 | # remove all forward slashes in the end 89 | WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::") 90 | sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php 91 | sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php 92 | sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php 93 | sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php 94 | sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php 95 | fi 96 | 97 | } 98 | 99 | install_db() { 100 | 101 | if [ ${SKIP_DB_CREATE} = "true" ]; then 102 | return 0 103 | fi 104 | 105 | # parse DB_HOST for port or socket references 106 | local PARTS=(${DB_HOST//\:/ }) 107 | local DB_HOSTNAME=${PARTS[0]}; 108 | local DB_SOCK_OR_PORT=${PARTS[1]}; 109 | local EXTRA="" 110 | 111 | if ! [ -z $DB_HOSTNAME ] ; then 112 | if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then 113 | EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp" 114 | elif ! [ -z $DB_SOCK_OR_PORT ] ; then 115 | EXTRA=" --socket=$DB_SOCK_OR_PORT" 116 | elif ! [ -z $DB_HOSTNAME ] ; then 117 | EXTRA=" --host=$DB_HOSTNAME --protocol=tcp" 118 | fi 119 | fi 120 | 121 | # create database 122 | mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA 123 | } 124 | 125 | install_wp 126 | install_test_suite 127 | install_db 128 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "automattic/lightweight-term-count-update", 3 | "description": "This plugin prevents WordPress from running term count queries, instead performing a simple increment/decrement on the post count.", 4 | "type": "wordpress-plugin", 5 | "license": "GPL-2.0-or-later", 6 | "require-dev": { 7 | "phpunit/phpunit": "^5.7.21 || ^7.5", 8 | "yoast/phpunit-polyfills": "^1.0" 9 | }, 10 | "scripts": { 11 | "phpunit": "vendor/bin/phpunit" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /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": "ffabef1e39af78b54aeb044e9df073b3", 8 | "packages": [], 9 | "packages-dev": [ 10 | { 11 | "name": "doctrine/instantiator", 12 | "version": "1.4.1", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/doctrine/instantiator.git", 16 | "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", 21 | "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "php": "^7.1 || ^8.0" 26 | }, 27 | "require-dev": { 28 | "doctrine/coding-standard": "^9", 29 | "ext-pdo": "*", 30 | "ext-phar": "*", 31 | "phpbench/phpbench": "^0.16 || ^1", 32 | "phpstan/phpstan": "^1.4", 33 | "phpstan/phpstan-phpunit": "^1", 34 | "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", 35 | "vimeo/psalm": "^4.22" 36 | }, 37 | "type": "library", 38 | "autoload": { 39 | "psr-4": { 40 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 41 | } 42 | }, 43 | "notification-url": "https://packagist.org/downloads/", 44 | "license": [ 45 | "MIT" 46 | ], 47 | "authors": [ 48 | { 49 | "name": "Marco Pivetta", 50 | "email": "ocramius@gmail.com", 51 | "homepage": "https://ocramius.github.io/" 52 | } 53 | ], 54 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 55 | "homepage": "https://www.doctrine-project.org/projects/instantiator.html", 56 | "keywords": [ 57 | "constructor", 58 | "instantiate" 59 | ], 60 | "support": { 61 | "issues": "https://github.com/doctrine/instantiator/issues", 62 | "source": "https://github.com/doctrine/instantiator/tree/1.4.1" 63 | }, 64 | "funding": [ 65 | { 66 | "url": "https://www.doctrine-project.org/sponsorship.html", 67 | "type": "custom" 68 | }, 69 | { 70 | "url": "https://www.patreon.com/phpdoctrine", 71 | "type": "patreon" 72 | }, 73 | { 74 | "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", 75 | "type": "tidelift" 76 | } 77 | ], 78 | "time": "2022-03-03T08:28:38+00:00" 79 | }, 80 | { 81 | "name": "myclabs/deep-copy", 82 | "version": "1.11.0", 83 | "source": { 84 | "type": "git", 85 | "url": "https://github.com/myclabs/DeepCopy.git", 86 | "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" 87 | }, 88 | "dist": { 89 | "type": "zip", 90 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", 91 | "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", 92 | "shasum": "" 93 | }, 94 | "require": { 95 | "php": "^7.1 || ^8.0" 96 | }, 97 | "conflict": { 98 | "doctrine/collections": "<1.6.8", 99 | "doctrine/common": "<2.13.3 || >=3,<3.2.2" 100 | }, 101 | "require-dev": { 102 | "doctrine/collections": "^1.6.8", 103 | "doctrine/common": "^2.13.3 || ^3.2.2", 104 | "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" 105 | }, 106 | "type": "library", 107 | "autoload": { 108 | "files": [ 109 | "src/DeepCopy/deep_copy.php" 110 | ], 111 | "psr-4": { 112 | "DeepCopy\\": "src/DeepCopy/" 113 | } 114 | }, 115 | "notification-url": "https://packagist.org/downloads/", 116 | "license": [ 117 | "MIT" 118 | ], 119 | "description": "Create deep copies (clones) of your objects", 120 | "keywords": [ 121 | "clone", 122 | "copy", 123 | "duplicate", 124 | "object", 125 | "object graph" 126 | ], 127 | "support": { 128 | "issues": "https://github.com/myclabs/DeepCopy/issues", 129 | "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" 130 | }, 131 | "funding": [ 132 | { 133 | "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", 134 | "type": "tidelift" 135 | } 136 | ], 137 | "time": "2022-03-03T13:19:32+00:00" 138 | }, 139 | { 140 | "name": "nikic/php-parser", 141 | "version": "v4.15.2", 142 | "source": { 143 | "type": "git", 144 | "url": "https://github.com/nikic/PHP-Parser.git", 145 | "reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc" 146 | }, 147 | "dist": { 148 | "type": "zip", 149 | "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc", 150 | "reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc", 151 | "shasum": "" 152 | }, 153 | "require": { 154 | "ext-tokenizer": "*", 155 | "php": ">=7.0" 156 | }, 157 | "require-dev": { 158 | "ircmaxell/php-yacc": "^0.0.7", 159 | "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" 160 | }, 161 | "bin": [ 162 | "bin/php-parse" 163 | ], 164 | "type": "library", 165 | "extra": { 166 | "branch-alias": { 167 | "dev-master": "4.9-dev" 168 | } 169 | }, 170 | "autoload": { 171 | "psr-4": { 172 | "PhpParser\\": "lib/PhpParser" 173 | } 174 | }, 175 | "notification-url": "https://packagist.org/downloads/", 176 | "license": [ 177 | "BSD-3-Clause" 178 | ], 179 | "authors": [ 180 | { 181 | "name": "Nikita Popov" 182 | } 183 | ], 184 | "description": "A PHP parser written in PHP", 185 | "keywords": [ 186 | "parser", 187 | "php" 188 | ], 189 | "support": { 190 | "issues": "https://github.com/nikic/PHP-Parser/issues", 191 | "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.2" 192 | }, 193 | "time": "2022-11-12T15:38:23+00:00" 194 | }, 195 | { 196 | "name": "phar-io/manifest", 197 | "version": "2.0.3", 198 | "source": { 199 | "type": "git", 200 | "url": "https://github.com/phar-io/manifest.git", 201 | "reference": "97803eca37d319dfa7826cc2437fc020857acb53" 202 | }, 203 | "dist": { 204 | "type": "zip", 205 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", 206 | "reference": "97803eca37d319dfa7826cc2437fc020857acb53", 207 | "shasum": "" 208 | }, 209 | "require": { 210 | "ext-dom": "*", 211 | "ext-phar": "*", 212 | "ext-xmlwriter": "*", 213 | "phar-io/version": "^3.0.1", 214 | "php": "^7.2 || ^8.0" 215 | }, 216 | "type": "library", 217 | "extra": { 218 | "branch-alias": { 219 | "dev-master": "2.0.x-dev" 220 | } 221 | }, 222 | "autoload": { 223 | "classmap": [ 224 | "src/" 225 | ] 226 | }, 227 | "notification-url": "https://packagist.org/downloads/", 228 | "license": [ 229 | "BSD-3-Clause" 230 | ], 231 | "authors": [ 232 | { 233 | "name": "Arne Blankerts", 234 | "email": "arne@blankerts.de", 235 | "role": "Developer" 236 | }, 237 | { 238 | "name": "Sebastian Heuer", 239 | "email": "sebastian@phpeople.de", 240 | "role": "Developer" 241 | }, 242 | { 243 | "name": "Sebastian Bergmann", 244 | "email": "sebastian@phpunit.de", 245 | "role": "Developer" 246 | } 247 | ], 248 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", 249 | "support": { 250 | "issues": "https://github.com/phar-io/manifest/issues", 251 | "source": "https://github.com/phar-io/manifest/tree/2.0.3" 252 | }, 253 | "time": "2021-07-20T11:28:43+00:00" 254 | }, 255 | { 256 | "name": "phar-io/version", 257 | "version": "3.2.1", 258 | "source": { 259 | "type": "git", 260 | "url": "https://github.com/phar-io/version.git", 261 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" 262 | }, 263 | "dist": { 264 | "type": "zip", 265 | "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 266 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", 267 | "shasum": "" 268 | }, 269 | "require": { 270 | "php": "^7.2 || ^8.0" 271 | }, 272 | "type": "library", 273 | "autoload": { 274 | "classmap": [ 275 | "src/" 276 | ] 277 | }, 278 | "notification-url": "https://packagist.org/downloads/", 279 | "license": [ 280 | "BSD-3-Clause" 281 | ], 282 | "authors": [ 283 | { 284 | "name": "Arne Blankerts", 285 | "email": "arne@blankerts.de", 286 | "role": "Developer" 287 | }, 288 | { 289 | "name": "Sebastian Heuer", 290 | "email": "sebastian@phpeople.de", 291 | "role": "Developer" 292 | }, 293 | { 294 | "name": "Sebastian Bergmann", 295 | "email": "sebastian@phpunit.de", 296 | "role": "Developer" 297 | } 298 | ], 299 | "description": "Library for handling version information and constraints", 300 | "support": { 301 | "issues": "https://github.com/phar-io/version/issues", 302 | "source": "https://github.com/phar-io/version/tree/3.2.1" 303 | }, 304 | "time": "2022-02-21T01:04:05+00:00" 305 | }, 306 | { 307 | "name": "phpunit/php-code-coverage", 308 | "version": "9.2.19", 309 | "source": { 310 | "type": "git", 311 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 312 | "reference": "c77b56b63e3d2031bd8997fcec43c1925ae46559" 313 | }, 314 | "dist": { 315 | "type": "zip", 316 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c77b56b63e3d2031bd8997fcec43c1925ae46559", 317 | "reference": "c77b56b63e3d2031bd8997fcec43c1925ae46559", 318 | "shasum": "" 319 | }, 320 | "require": { 321 | "ext-dom": "*", 322 | "ext-libxml": "*", 323 | "ext-xmlwriter": "*", 324 | "nikic/php-parser": "^4.14", 325 | "php": ">=7.3", 326 | "phpunit/php-file-iterator": "^3.0.3", 327 | "phpunit/php-text-template": "^2.0.2", 328 | "sebastian/code-unit-reverse-lookup": "^2.0.2", 329 | "sebastian/complexity": "^2.0", 330 | "sebastian/environment": "^5.1.2", 331 | "sebastian/lines-of-code": "^1.0.3", 332 | "sebastian/version": "^3.0.1", 333 | "theseer/tokenizer": "^1.2.0" 334 | }, 335 | "require-dev": { 336 | "phpunit/phpunit": "^9.3" 337 | }, 338 | "suggest": { 339 | "ext-pcov": "*", 340 | "ext-xdebug": "*" 341 | }, 342 | "type": "library", 343 | "extra": { 344 | "branch-alias": { 345 | "dev-master": "9.2-dev" 346 | } 347 | }, 348 | "autoload": { 349 | "classmap": [ 350 | "src/" 351 | ] 352 | }, 353 | "notification-url": "https://packagist.org/downloads/", 354 | "license": [ 355 | "BSD-3-Clause" 356 | ], 357 | "authors": [ 358 | { 359 | "name": "Sebastian Bergmann", 360 | "email": "sebastian@phpunit.de", 361 | "role": "lead" 362 | } 363 | ], 364 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 365 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 366 | "keywords": [ 367 | "coverage", 368 | "testing", 369 | "xunit" 370 | ], 371 | "support": { 372 | "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", 373 | "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.19" 374 | }, 375 | "funding": [ 376 | { 377 | "url": "https://github.com/sebastianbergmann", 378 | "type": "github" 379 | } 380 | ], 381 | "time": "2022-11-18T07:47:47+00:00" 382 | }, 383 | { 384 | "name": "phpunit/php-file-iterator", 385 | "version": "3.0.6", 386 | "source": { 387 | "type": "git", 388 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 389 | "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" 390 | }, 391 | "dist": { 392 | "type": "zip", 393 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", 394 | "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", 395 | "shasum": "" 396 | }, 397 | "require": { 398 | "php": ">=7.3" 399 | }, 400 | "require-dev": { 401 | "phpunit/phpunit": "^9.3" 402 | }, 403 | "type": "library", 404 | "extra": { 405 | "branch-alias": { 406 | "dev-master": "3.0-dev" 407 | } 408 | }, 409 | "autoload": { 410 | "classmap": [ 411 | "src/" 412 | ] 413 | }, 414 | "notification-url": "https://packagist.org/downloads/", 415 | "license": [ 416 | "BSD-3-Clause" 417 | ], 418 | "authors": [ 419 | { 420 | "name": "Sebastian Bergmann", 421 | "email": "sebastian@phpunit.de", 422 | "role": "lead" 423 | } 424 | ], 425 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 426 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 427 | "keywords": [ 428 | "filesystem", 429 | "iterator" 430 | ], 431 | "support": { 432 | "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", 433 | "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" 434 | }, 435 | "funding": [ 436 | { 437 | "url": "https://github.com/sebastianbergmann", 438 | "type": "github" 439 | } 440 | ], 441 | "time": "2021-12-02T12:48:52+00:00" 442 | }, 443 | { 444 | "name": "phpunit/php-invoker", 445 | "version": "3.1.1", 446 | "source": { 447 | "type": "git", 448 | "url": "https://github.com/sebastianbergmann/php-invoker.git", 449 | "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" 450 | }, 451 | "dist": { 452 | "type": "zip", 453 | "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", 454 | "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", 455 | "shasum": "" 456 | }, 457 | "require": { 458 | "php": ">=7.3" 459 | }, 460 | "require-dev": { 461 | "ext-pcntl": "*", 462 | "phpunit/phpunit": "^9.3" 463 | }, 464 | "suggest": { 465 | "ext-pcntl": "*" 466 | }, 467 | "type": "library", 468 | "extra": { 469 | "branch-alias": { 470 | "dev-master": "3.1-dev" 471 | } 472 | }, 473 | "autoload": { 474 | "classmap": [ 475 | "src/" 476 | ] 477 | }, 478 | "notification-url": "https://packagist.org/downloads/", 479 | "license": [ 480 | "BSD-3-Clause" 481 | ], 482 | "authors": [ 483 | { 484 | "name": "Sebastian Bergmann", 485 | "email": "sebastian@phpunit.de", 486 | "role": "lead" 487 | } 488 | ], 489 | "description": "Invoke callables with a timeout", 490 | "homepage": "https://github.com/sebastianbergmann/php-invoker/", 491 | "keywords": [ 492 | "process" 493 | ], 494 | "support": { 495 | "issues": "https://github.com/sebastianbergmann/php-invoker/issues", 496 | "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" 497 | }, 498 | "funding": [ 499 | { 500 | "url": "https://github.com/sebastianbergmann", 501 | "type": "github" 502 | } 503 | ], 504 | "time": "2020-09-28T05:58:55+00:00" 505 | }, 506 | { 507 | "name": "phpunit/php-text-template", 508 | "version": "2.0.4", 509 | "source": { 510 | "type": "git", 511 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 512 | "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" 513 | }, 514 | "dist": { 515 | "type": "zip", 516 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", 517 | "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", 518 | "shasum": "" 519 | }, 520 | "require": { 521 | "php": ">=7.3" 522 | }, 523 | "require-dev": { 524 | "phpunit/phpunit": "^9.3" 525 | }, 526 | "type": "library", 527 | "extra": { 528 | "branch-alias": { 529 | "dev-master": "2.0-dev" 530 | } 531 | }, 532 | "autoload": { 533 | "classmap": [ 534 | "src/" 535 | ] 536 | }, 537 | "notification-url": "https://packagist.org/downloads/", 538 | "license": [ 539 | "BSD-3-Clause" 540 | ], 541 | "authors": [ 542 | { 543 | "name": "Sebastian Bergmann", 544 | "email": "sebastian@phpunit.de", 545 | "role": "lead" 546 | } 547 | ], 548 | "description": "Simple template engine.", 549 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 550 | "keywords": [ 551 | "template" 552 | ], 553 | "support": { 554 | "issues": "https://github.com/sebastianbergmann/php-text-template/issues", 555 | "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" 556 | }, 557 | "funding": [ 558 | { 559 | "url": "https://github.com/sebastianbergmann", 560 | "type": "github" 561 | } 562 | ], 563 | "time": "2020-10-26T05:33:50+00:00" 564 | }, 565 | { 566 | "name": "phpunit/php-timer", 567 | "version": "5.0.3", 568 | "source": { 569 | "type": "git", 570 | "url": "https://github.com/sebastianbergmann/php-timer.git", 571 | "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" 572 | }, 573 | "dist": { 574 | "type": "zip", 575 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", 576 | "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", 577 | "shasum": "" 578 | }, 579 | "require": { 580 | "php": ">=7.3" 581 | }, 582 | "require-dev": { 583 | "phpunit/phpunit": "^9.3" 584 | }, 585 | "type": "library", 586 | "extra": { 587 | "branch-alias": { 588 | "dev-master": "5.0-dev" 589 | } 590 | }, 591 | "autoload": { 592 | "classmap": [ 593 | "src/" 594 | ] 595 | }, 596 | "notification-url": "https://packagist.org/downloads/", 597 | "license": [ 598 | "BSD-3-Clause" 599 | ], 600 | "authors": [ 601 | { 602 | "name": "Sebastian Bergmann", 603 | "email": "sebastian@phpunit.de", 604 | "role": "lead" 605 | } 606 | ], 607 | "description": "Utility class for timing", 608 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 609 | "keywords": [ 610 | "timer" 611 | ], 612 | "support": { 613 | "issues": "https://github.com/sebastianbergmann/php-timer/issues", 614 | "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" 615 | }, 616 | "funding": [ 617 | { 618 | "url": "https://github.com/sebastianbergmann", 619 | "type": "github" 620 | } 621 | ], 622 | "time": "2020-10-26T13:16:10+00:00" 623 | }, 624 | { 625 | "name": "phpunit/phpunit", 626 | "version": "9.5.26", 627 | "source": { 628 | "type": "git", 629 | "url": "https://github.com/sebastianbergmann/phpunit.git", 630 | "reference": "851867efcbb6a1b992ec515c71cdcf20d895e9d2" 631 | }, 632 | "dist": { 633 | "type": "zip", 634 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/851867efcbb6a1b992ec515c71cdcf20d895e9d2", 635 | "reference": "851867efcbb6a1b992ec515c71cdcf20d895e9d2", 636 | "shasum": "" 637 | }, 638 | "require": { 639 | "doctrine/instantiator": "^1.3.1", 640 | "ext-dom": "*", 641 | "ext-json": "*", 642 | "ext-libxml": "*", 643 | "ext-mbstring": "*", 644 | "ext-xml": "*", 645 | "ext-xmlwriter": "*", 646 | "myclabs/deep-copy": "^1.10.1", 647 | "phar-io/manifest": "^2.0.3", 648 | "phar-io/version": "^3.0.2", 649 | "php": ">=7.3", 650 | "phpunit/php-code-coverage": "^9.2.13", 651 | "phpunit/php-file-iterator": "^3.0.5", 652 | "phpunit/php-invoker": "^3.1.1", 653 | "phpunit/php-text-template": "^2.0.3", 654 | "phpunit/php-timer": "^5.0.2", 655 | "sebastian/cli-parser": "^1.0.1", 656 | "sebastian/code-unit": "^1.0.6", 657 | "sebastian/comparator": "^4.0.8", 658 | "sebastian/diff": "^4.0.3", 659 | "sebastian/environment": "^5.1.3", 660 | "sebastian/exporter": "^4.0.5", 661 | "sebastian/global-state": "^5.0.1", 662 | "sebastian/object-enumerator": "^4.0.3", 663 | "sebastian/resource-operations": "^3.0.3", 664 | "sebastian/type": "^3.2", 665 | "sebastian/version": "^3.0.2" 666 | }, 667 | "suggest": { 668 | "ext-soap": "*", 669 | "ext-xdebug": "*" 670 | }, 671 | "bin": [ 672 | "phpunit" 673 | ], 674 | "type": "library", 675 | "extra": { 676 | "branch-alias": { 677 | "dev-master": "9.5-dev" 678 | } 679 | }, 680 | "autoload": { 681 | "files": [ 682 | "src/Framework/Assert/Functions.php" 683 | ], 684 | "classmap": [ 685 | "src/" 686 | ] 687 | }, 688 | "notification-url": "https://packagist.org/downloads/", 689 | "license": [ 690 | "BSD-3-Clause" 691 | ], 692 | "authors": [ 693 | { 694 | "name": "Sebastian Bergmann", 695 | "email": "sebastian@phpunit.de", 696 | "role": "lead" 697 | } 698 | ], 699 | "description": "The PHP Unit Testing framework.", 700 | "homepage": "https://phpunit.de/", 701 | "keywords": [ 702 | "phpunit", 703 | "testing", 704 | "xunit" 705 | ], 706 | "support": { 707 | "issues": "https://github.com/sebastianbergmann/phpunit/issues", 708 | "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.26" 709 | }, 710 | "funding": [ 711 | { 712 | "url": "https://phpunit.de/sponsors.html", 713 | "type": "custom" 714 | }, 715 | { 716 | "url": "https://github.com/sebastianbergmann", 717 | "type": "github" 718 | }, 719 | { 720 | "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", 721 | "type": "tidelift" 722 | } 723 | ], 724 | "time": "2022-10-28T06:00:21+00:00" 725 | }, 726 | { 727 | "name": "sebastian/cli-parser", 728 | "version": "1.0.1", 729 | "source": { 730 | "type": "git", 731 | "url": "https://github.com/sebastianbergmann/cli-parser.git", 732 | "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" 733 | }, 734 | "dist": { 735 | "type": "zip", 736 | "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", 737 | "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", 738 | "shasum": "" 739 | }, 740 | "require": { 741 | "php": ">=7.3" 742 | }, 743 | "require-dev": { 744 | "phpunit/phpunit": "^9.3" 745 | }, 746 | "type": "library", 747 | "extra": { 748 | "branch-alias": { 749 | "dev-master": "1.0-dev" 750 | } 751 | }, 752 | "autoload": { 753 | "classmap": [ 754 | "src/" 755 | ] 756 | }, 757 | "notification-url": "https://packagist.org/downloads/", 758 | "license": [ 759 | "BSD-3-Clause" 760 | ], 761 | "authors": [ 762 | { 763 | "name": "Sebastian Bergmann", 764 | "email": "sebastian@phpunit.de", 765 | "role": "lead" 766 | } 767 | ], 768 | "description": "Library for parsing CLI options", 769 | "homepage": "https://github.com/sebastianbergmann/cli-parser", 770 | "support": { 771 | "issues": "https://github.com/sebastianbergmann/cli-parser/issues", 772 | "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" 773 | }, 774 | "funding": [ 775 | { 776 | "url": "https://github.com/sebastianbergmann", 777 | "type": "github" 778 | } 779 | ], 780 | "time": "2020-09-28T06:08:49+00:00" 781 | }, 782 | { 783 | "name": "sebastian/code-unit", 784 | "version": "1.0.8", 785 | "source": { 786 | "type": "git", 787 | "url": "https://github.com/sebastianbergmann/code-unit.git", 788 | "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" 789 | }, 790 | "dist": { 791 | "type": "zip", 792 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", 793 | "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", 794 | "shasum": "" 795 | }, 796 | "require": { 797 | "php": ">=7.3" 798 | }, 799 | "require-dev": { 800 | "phpunit/phpunit": "^9.3" 801 | }, 802 | "type": "library", 803 | "extra": { 804 | "branch-alias": { 805 | "dev-master": "1.0-dev" 806 | } 807 | }, 808 | "autoload": { 809 | "classmap": [ 810 | "src/" 811 | ] 812 | }, 813 | "notification-url": "https://packagist.org/downloads/", 814 | "license": [ 815 | "BSD-3-Clause" 816 | ], 817 | "authors": [ 818 | { 819 | "name": "Sebastian Bergmann", 820 | "email": "sebastian@phpunit.de", 821 | "role": "lead" 822 | } 823 | ], 824 | "description": "Collection of value objects that represent the PHP code units", 825 | "homepage": "https://github.com/sebastianbergmann/code-unit", 826 | "support": { 827 | "issues": "https://github.com/sebastianbergmann/code-unit/issues", 828 | "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" 829 | }, 830 | "funding": [ 831 | { 832 | "url": "https://github.com/sebastianbergmann", 833 | "type": "github" 834 | } 835 | ], 836 | "time": "2020-10-26T13:08:54+00:00" 837 | }, 838 | { 839 | "name": "sebastian/code-unit-reverse-lookup", 840 | "version": "2.0.3", 841 | "source": { 842 | "type": "git", 843 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 844 | "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" 845 | }, 846 | "dist": { 847 | "type": "zip", 848 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", 849 | "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", 850 | "shasum": "" 851 | }, 852 | "require": { 853 | "php": ">=7.3" 854 | }, 855 | "require-dev": { 856 | "phpunit/phpunit": "^9.3" 857 | }, 858 | "type": "library", 859 | "extra": { 860 | "branch-alias": { 861 | "dev-master": "2.0-dev" 862 | } 863 | }, 864 | "autoload": { 865 | "classmap": [ 866 | "src/" 867 | ] 868 | }, 869 | "notification-url": "https://packagist.org/downloads/", 870 | "license": [ 871 | "BSD-3-Clause" 872 | ], 873 | "authors": [ 874 | { 875 | "name": "Sebastian Bergmann", 876 | "email": "sebastian@phpunit.de" 877 | } 878 | ], 879 | "description": "Looks up which function or method a line of code belongs to", 880 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 881 | "support": { 882 | "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", 883 | "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" 884 | }, 885 | "funding": [ 886 | { 887 | "url": "https://github.com/sebastianbergmann", 888 | "type": "github" 889 | } 890 | ], 891 | "time": "2020-09-28T05:30:19+00:00" 892 | }, 893 | { 894 | "name": "sebastian/comparator", 895 | "version": "4.0.8", 896 | "source": { 897 | "type": "git", 898 | "url": "https://github.com/sebastianbergmann/comparator.git", 899 | "reference": "fa0f136dd2334583309d32b62544682ee972b51a" 900 | }, 901 | "dist": { 902 | "type": "zip", 903 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", 904 | "reference": "fa0f136dd2334583309d32b62544682ee972b51a", 905 | "shasum": "" 906 | }, 907 | "require": { 908 | "php": ">=7.3", 909 | "sebastian/diff": "^4.0", 910 | "sebastian/exporter": "^4.0" 911 | }, 912 | "require-dev": { 913 | "phpunit/phpunit": "^9.3" 914 | }, 915 | "type": "library", 916 | "extra": { 917 | "branch-alias": { 918 | "dev-master": "4.0-dev" 919 | } 920 | }, 921 | "autoload": { 922 | "classmap": [ 923 | "src/" 924 | ] 925 | }, 926 | "notification-url": "https://packagist.org/downloads/", 927 | "license": [ 928 | "BSD-3-Clause" 929 | ], 930 | "authors": [ 931 | { 932 | "name": "Sebastian Bergmann", 933 | "email": "sebastian@phpunit.de" 934 | }, 935 | { 936 | "name": "Jeff Welch", 937 | "email": "whatthejeff@gmail.com" 938 | }, 939 | { 940 | "name": "Volker Dusch", 941 | "email": "github@wallbash.com" 942 | }, 943 | { 944 | "name": "Bernhard Schussek", 945 | "email": "bschussek@2bepublished.at" 946 | } 947 | ], 948 | "description": "Provides the functionality to compare PHP values for equality", 949 | "homepage": "https://github.com/sebastianbergmann/comparator", 950 | "keywords": [ 951 | "comparator", 952 | "compare", 953 | "equality" 954 | ], 955 | "support": { 956 | "issues": "https://github.com/sebastianbergmann/comparator/issues", 957 | "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" 958 | }, 959 | "funding": [ 960 | { 961 | "url": "https://github.com/sebastianbergmann", 962 | "type": "github" 963 | } 964 | ], 965 | "time": "2022-09-14T12:41:17+00:00" 966 | }, 967 | { 968 | "name": "sebastian/complexity", 969 | "version": "2.0.2", 970 | "source": { 971 | "type": "git", 972 | "url": "https://github.com/sebastianbergmann/complexity.git", 973 | "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" 974 | }, 975 | "dist": { 976 | "type": "zip", 977 | "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", 978 | "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", 979 | "shasum": "" 980 | }, 981 | "require": { 982 | "nikic/php-parser": "^4.7", 983 | "php": ">=7.3" 984 | }, 985 | "require-dev": { 986 | "phpunit/phpunit": "^9.3" 987 | }, 988 | "type": "library", 989 | "extra": { 990 | "branch-alias": { 991 | "dev-master": "2.0-dev" 992 | } 993 | }, 994 | "autoload": { 995 | "classmap": [ 996 | "src/" 997 | ] 998 | }, 999 | "notification-url": "https://packagist.org/downloads/", 1000 | "license": [ 1001 | "BSD-3-Clause" 1002 | ], 1003 | "authors": [ 1004 | { 1005 | "name": "Sebastian Bergmann", 1006 | "email": "sebastian@phpunit.de", 1007 | "role": "lead" 1008 | } 1009 | ], 1010 | "description": "Library for calculating the complexity of PHP code units", 1011 | "homepage": "https://github.com/sebastianbergmann/complexity", 1012 | "support": { 1013 | "issues": "https://github.com/sebastianbergmann/complexity/issues", 1014 | "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" 1015 | }, 1016 | "funding": [ 1017 | { 1018 | "url": "https://github.com/sebastianbergmann", 1019 | "type": "github" 1020 | } 1021 | ], 1022 | "time": "2020-10-26T15:52:27+00:00" 1023 | }, 1024 | { 1025 | "name": "sebastian/diff", 1026 | "version": "4.0.4", 1027 | "source": { 1028 | "type": "git", 1029 | "url": "https://github.com/sebastianbergmann/diff.git", 1030 | "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" 1031 | }, 1032 | "dist": { 1033 | "type": "zip", 1034 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", 1035 | "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", 1036 | "shasum": "" 1037 | }, 1038 | "require": { 1039 | "php": ">=7.3" 1040 | }, 1041 | "require-dev": { 1042 | "phpunit/phpunit": "^9.3", 1043 | "symfony/process": "^4.2 || ^5" 1044 | }, 1045 | "type": "library", 1046 | "extra": { 1047 | "branch-alias": { 1048 | "dev-master": "4.0-dev" 1049 | } 1050 | }, 1051 | "autoload": { 1052 | "classmap": [ 1053 | "src/" 1054 | ] 1055 | }, 1056 | "notification-url": "https://packagist.org/downloads/", 1057 | "license": [ 1058 | "BSD-3-Clause" 1059 | ], 1060 | "authors": [ 1061 | { 1062 | "name": "Sebastian Bergmann", 1063 | "email": "sebastian@phpunit.de" 1064 | }, 1065 | { 1066 | "name": "Kore Nordmann", 1067 | "email": "mail@kore-nordmann.de" 1068 | } 1069 | ], 1070 | "description": "Diff implementation", 1071 | "homepage": "https://github.com/sebastianbergmann/diff", 1072 | "keywords": [ 1073 | "diff", 1074 | "udiff", 1075 | "unidiff", 1076 | "unified diff" 1077 | ], 1078 | "support": { 1079 | "issues": "https://github.com/sebastianbergmann/diff/issues", 1080 | "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" 1081 | }, 1082 | "funding": [ 1083 | { 1084 | "url": "https://github.com/sebastianbergmann", 1085 | "type": "github" 1086 | } 1087 | ], 1088 | "time": "2020-10-26T13:10:38+00:00" 1089 | }, 1090 | { 1091 | "name": "sebastian/environment", 1092 | "version": "5.1.4", 1093 | "source": { 1094 | "type": "git", 1095 | "url": "https://github.com/sebastianbergmann/environment.git", 1096 | "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7" 1097 | }, 1098 | "dist": { 1099 | "type": "zip", 1100 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7", 1101 | "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7", 1102 | "shasum": "" 1103 | }, 1104 | "require": { 1105 | "php": ">=7.3" 1106 | }, 1107 | "require-dev": { 1108 | "phpunit/phpunit": "^9.3" 1109 | }, 1110 | "suggest": { 1111 | "ext-posix": "*" 1112 | }, 1113 | "type": "library", 1114 | "extra": { 1115 | "branch-alias": { 1116 | "dev-master": "5.1-dev" 1117 | } 1118 | }, 1119 | "autoload": { 1120 | "classmap": [ 1121 | "src/" 1122 | ] 1123 | }, 1124 | "notification-url": "https://packagist.org/downloads/", 1125 | "license": [ 1126 | "BSD-3-Clause" 1127 | ], 1128 | "authors": [ 1129 | { 1130 | "name": "Sebastian Bergmann", 1131 | "email": "sebastian@phpunit.de" 1132 | } 1133 | ], 1134 | "description": "Provides functionality to handle HHVM/PHP environments", 1135 | "homepage": "http://www.github.com/sebastianbergmann/environment", 1136 | "keywords": [ 1137 | "Xdebug", 1138 | "environment", 1139 | "hhvm" 1140 | ], 1141 | "support": { 1142 | "issues": "https://github.com/sebastianbergmann/environment/issues", 1143 | "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4" 1144 | }, 1145 | "funding": [ 1146 | { 1147 | "url": "https://github.com/sebastianbergmann", 1148 | "type": "github" 1149 | } 1150 | ], 1151 | "time": "2022-04-03T09:37:03+00:00" 1152 | }, 1153 | { 1154 | "name": "sebastian/exporter", 1155 | "version": "4.0.5", 1156 | "source": { 1157 | "type": "git", 1158 | "url": "https://github.com/sebastianbergmann/exporter.git", 1159 | "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" 1160 | }, 1161 | "dist": { 1162 | "type": "zip", 1163 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", 1164 | "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", 1165 | "shasum": "" 1166 | }, 1167 | "require": { 1168 | "php": ">=7.3", 1169 | "sebastian/recursion-context": "^4.0" 1170 | }, 1171 | "require-dev": { 1172 | "ext-mbstring": "*", 1173 | "phpunit/phpunit": "^9.3" 1174 | }, 1175 | "type": "library", 1176 | "extra": { 1177 | "branch-alias": { 1178 | "dev-master": "4.0-dev" 1179 | } 1180 | }, 1181 | "autoload": { 1182 | "classmap": [ 1183 | "src/" 1184 | ] 1185 | }, 1186 | "notification-url": "https://packagist.org/downloads/", 1187 | "license": [ 1188 | "BSD-3-Clause" 1189 | ], 1190 | "authors": [ 1191 | { 1192 | "name": "Sebastian Bergmann", 1193 | "email": "sebastian@phpunit.de" 1194 | }, 1195 | { 1196 | "name": "Jeff Welch", 1197 | "email": "whatthejeff@gmail.com" 1198 | }, 1199 | { 1200 | "name": "Volker Dusch", 1201 | "email": "github@wallbash.com" 1202 | }, 1203 | { 1204 | "name": "Adam Harvey", 1205 | "email": "aharvey@php.net" 1206 | }, 1207 | { 1208 | "name": "Bernhard Schussek", 1209 | "email": "bschussek@gmail.com" 1210 | } 1211 | ], 1212 | "description": "Provides the functionality to export PHP variables for visualization", 1213 | "homepage": "https://www.github.com/sebastianbergmann/exporter", 1214 | "keywords": [ 1215 | "export", 1216 | "exporter" 1217 | ], 1218 | "support": { 1219 | "issues": "https://github.com/sebastianbergmann/exporter/issues", 1220 | "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" 1221 | }, 1222 | "funding": [ 1223 | { 1224 | "url": "https://github.com/sebastianbergmann", 1225 | "type": "github" 1226 | } 1227 | ], 1228 | "time": "2022-09-14T06:03:37+00:00" 1229 | }, 1230 | { 1231 | "name": "sebastian/global-state", 1232 | "version": "5.0.5", 1233 | "source": { 1234 | "type": "git", 1235 | "url": "https://github.com/sebastianbergmann/global-state.git", 1236 | "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2" 1237 | }, 1238 | "dist": { 1239 | "type": "zip", 1240 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2", 1241 | "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2", 1242 | "shasum": "" 1243 | }, 1244 | "require": { 1245 | "php": ">=7.3", 1246 | "sebastian/object-reflector": "^2.0", 1247 | "sebastian/recursion-context": "^4.0" 1248 | }, 1249 | "require-dev": { 1250 | "ext-dom": "*", 1251 | "phpunit/phpunit": "^9.3" 1252 | }, 1253 | "suggest": { 1254 | "ext-uopz": "*" 1255 | }, 1256 | "type": "library", 1257 | "extra": { 1258 | "branch-alias": { 1259 | "dev-master": "5.0-dev" 1260 | } 1261 | }, 1262 | "autoload": { 1263 | "classmap": [ 1264 | "src/" 1265 | ] 1266 | }, 1267 | "notification-url": "https://packagist.org/downloads/", 1268 | "license": [ 1269 | "BSD-3-Clause" 1270 | ], 1271 | "authors": [ 1272 | { 1273 | "name": "Sebastian Bergmann", 1274 | "email": "sebastian@phpunit.de" 1275 | } 1276 | ], 1277 | "description": "Snapshotting of global state", 1278 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1279 | "keywords": [ 1280 | "global state" 1281 | ], 1282 | "support": { 1283 | "issues": "https://github.com/sebastianbergmann/global-state/issues", 1284 | "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5" 1285 | }, 1286 | "funding": [ 1287 | { 1288 | "url": "https://github.com/sebastianbergmann", 1289 | "type": "github" 1290 | } 1291 | ], 1292 | "time": "2022-02-14T08:28:10+00:00" 1293 | }, 1294 | { 1295 | "name": "sebastian/lines-of-code", 1296 | "version": "1.0.3", 1297 | "source": { 1298 | "type": "git", 1299 | "url": "https://github.com/sebastianbergmann/lines-of-code.git", 1300 | "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" 1301 | }, 1302 | "dist": { 1303 | "type": "zip", 1304 | "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", 1305 | "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", 1306 | "shasum": "" 1307 | }, 1308 | "require": { 1309 | "nikic/php-parser": "^4.6", 1310 | "php": ">=7.3" 1311 | }, 1312 | "require-dev": { 1313 | "phpunit/phpunit": "^9.3" 1314 | }, 1315 | "type": "library", 1316 | "extra": { 1317 | "branch-alias": { 1318 | "dev-master": "1.0-dev" 1319 | } 1320 | }, 1321 | "autoload": { 1322 | "classmap": [ 1323 | "src/" 1324 | ] 1325 | }, 1326 | "notification-url": "https://packagist.org/downloads/", 1327 | "license": [ 1328 | "BSD-3-Clause" 1329 | ], 1330 | "authors": [ 1331 | { 1332 | "name": "Sebastian Bergmann", 1333 | "email": "sebastian@phpunit.de", 1334 | "role": "lead" 1335 | } 1336 | ], 1337 | "description": "Library for counting the lines of code in PHP source code", 1338 | "homepage": "https://github.com/sebastianbergmann/lines-of-code", 1339 | "support": { 1340 | "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", 1341 | "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" 1342 | }, 1343 | "funding": [ 1344 | { 1345 | "url": "https://github.com/sebastianbergmann", 1346 | "type": "github" 1347 | } 1348 | ], 1349 | "time": "2020-11-28T06:42:11+00:00" 1350 | }, 1351 | { 1352 | "name": "sebastian/object-enumerator", 1353 | "version": "4.0.4", 1354 | "source": { 1355 | "type": "git", 1356 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 1357 | "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" 1358 | }, 1359 | "dist": { 1360 | "type": "zip", 1361 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", 1362 | "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", 1363 | "shasum": "" 1364 | }, 1365 | "require": { 1366 | "php": ">=7.3", 1367 | "sebastian/object-reflector": "^2.0", 1368 | "sebastian/recursion-context": "^4.0" 1369 | }, 1370 | "require-dev": { 1371 | "phpunit/phpunit": "^9.3" 1372 | }, 1373 | "type": "library", 1374 | "extra": { 1375 | "branch-alias": { 1376 | "dev-master": "4.0-dev" 1377 | } 1378 | }, 1379 | "autoload": { 1380 | "classmap": [ 1381 | "src/" 1382 | ] 1383 | }, 1384 | "notification-url": "https://packagist.org/downloads/", 1385 | "license": [ 1386 | "BSD-3-Clause" 1387 | ], 1388 | "authors": [ 1389 | { 1390 | "name": "Sebastian Bergmann", 1391 | "email": "sebastian@phpunit.de" 1392 | } 1393 | ], 1394 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 1395 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 1396 | "support": { 1397 | "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", 1398 | "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" 1399 | }, 1400 | "funding": [ 1401 | { 1402 | "url": "https://github.com/sebastianbergmann", 1403 | "type": "github" 1404 | } 1405 | ], 1406 | "time": "2020-10-26T13:12:34+00:00" 1407 | }, 1408 | { 1409 | "name": "sebastian/object-reflector", 1410 | "version": "2.0.4", 1411 | "source": { 1412 | "type": "git", 1413 | "url": "https://github.com/sebastianbergmann/object-reflector.git", 1414 | "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" 1415 | }, 1416 | "dist": { 1417 | "type": "zip", 1418 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", 1419 | "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", 1420 | "shasum": "" 1421 | }, 1422 | "require": { 1423 | "php": ">=7.3" 1424 | }, 1425 | "require-dev": { 1426 | "phpunit/phpunit": "^9.3" 1427 | }, 1428 | "type": "library", 1429 | "extra": { 1430 | "branch-alias": { 1431 | "dev-master": "2.0-dev" 1432 | } 1433 | }, 1434 | "autoload": { 1435 | "classmap": [ 1436 | "src/" 1437 | ] 1438 | }, 1439 | "notification-url": "https://packagist.org/downloads/", 1440 | "license": [ 1441 | "BSD-3-Clause" 1442 | ], 1443 | "authors": [ 1444 | { 1445 | "name": "Sebastian Bergmann", 1446 | "email": "sebastian@phpunit.de" 1447 | } 1448 | ], 1449 | "description": "Allows reflection of object attributes, including inherited and non-public ones", 1450 | "homepage": "https://github.com/sebastianbergmann/object-reflector/", 1451 | "support": { 1452 | "issues": "https://github.com/sebastianbergmann/object-reflector/issues", 1453 | "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" 1454 | }, 1455 | "funding": [ 1456 | { 1457 | "url": "https://github.com/sebastianbergmann", 1458 | "type": "github" 1459 | } 1460 | ], 1461 | "time": "2020-10-26T13:14:26+00:00" 1462 | }, 1463 | { 1464 | "name": "sebastian/recursion-context", 1465 | "version": "4.0.4", 1466 | "source": { 1467 | "type": "git", 1468 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1469 | "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" 1470 | }, 1471 | "dist": { 1472 | "type": "zip", 1473 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", 1474 | "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", 1475 | "shasum": "" 1476 | }, 1477 | "require": { 1478 | "php": ">=7.3" 1479 | }, 1480 | "require-dev": { 1481 | "phpunit/phpunit": "^9.3" 1482 | }, 1483 | "type": "library", 1484 | "extra": { 1485 | "branch-alias": { 1486 | "dev-master": "4.0-dev" 1487 | } 1488 | }, 1489 | "autoload": { 1490 | "classmap": [ 1491 | "src/" 1492 | ] 1493 | }, 1494 | "notification-url": "https://packagist.org/downloads/", 1495 | "license": [ 1496 | "BSD-3-Clause" 1497 | ], 1498 | "authors": [ 1499 | { 1500 | "name": "Sebastian Bergmann", 1501 | "email": "sebastian@phpunit.de" 1502 | }, 1503 | { 1504 | "name": "Jeff Welch", 1505 | "email": "whatthejeff@gmail.com" 1506 | }, 1507 | { 1508 | "name": "Adam Harvey", 1509 | "email": "aharvey@php.net" 1510 | } 1511 | ], 1512 | "description": "Provides functionality to recursively process PHP variables", 1513 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 1514 | "support": { 1515 | "issues": "https://github.com/sebastianbergmann/recursion-context/issues", 1516 | "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" 1517 | }, 1518 | "funding": [ 1519 | { 1520 | "url": "https://github.com/sebastianbergmann", 1521 | "type": "github" 1522 | } 1523 | ], 1524 | "time": "2020-10-26T13:17:30+00:00" 1525 | }, 1526 | { 1527 | "name": "sebastian/resource-operations", 1528 | "version": "3.0.3", 1529 | "source": { 1530 | "type": "git", 1531 | "url": "https://github.com/sebastianbergmann/resource-operations.git", 1532 | "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" 1533 | }, 1534 | "dist": { 1535 | "type": "zip", 1536 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", 1537 | "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", 1538 | "shasum": "" 1539 | }, 1540 | "require": { 1541 | "php": ">=7.3" 1542 | }, 1543 | "require-dev": { 1544 | "phpunit/phpunit": "^9.0" 1545 | }, 1546 | "type": "library", 1547 | "extra": { 1548 | "branch-alias": { 1549 | "dev-master": "3.0-dev" 1550 | } 1551 | }, 1552 | "autoload": { 1553 | "classmap": [ 1554 | "src/" 1555 | ] 1556 | }, 1557 | "notification-url": "https://packagist.org/downloads/", 1558 | "license": [ 1559 | "BSD-3-Clause" 1560 | ], 1561 | "authors": [ 1562 | { 1563 | "name": "Sebastian Bergmann", 1564 | "email": "sebastian@phpunit.de" 1565 | } 1566 | ], 1567 | "description": "Provides a list of PHP built-in functions that operate on resources", 1568 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations", 1569 | "support": { 1570 | "issues": "https://github.com/sebastianbergmann/resource-operations/issues", 1571 | "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" 1572 | }, 1573 | "funding": [ 1574 | { 1575 | "url": "https://github.com/sebastianbergmann", 1576 | "type": "github" 1577 | } 1578 | ], 1579 | "time": "2020-09-28T06:45:17+00:00" 1580 | }, 1581 | { 1582 | "name": "sebastian/type", 1583 | "version": "3.2.0", 1584 | "source": { 1585 | "type": "git", 1586 | "url": "https://github.com/sebastianbergmann/type.git", 1587 | "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e" 1588 | }, 1589 | "dist": { 1590 | "type": "zip", 1591 | "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", 1592 | "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", 1593 | "shasum": "" 1594 | }, 1595 | "require": { 1596 | "php": ">=7.3" 1597 | }, 1598 | "require-dev": { 1599 | "phpunit/phpunit": "^9.5" 1600 | }, 1601 | "type": "library", 1602 | "extra": { 1603 | "branch-alias": { 1604 | "dev-master": "3.2-dev" 1605 | } 1606 | }, 1607 | "autoload": { 1608 | "classmap": [ 1609 | "src/" 1610 | ] 1611 | }, 1612 | "notification-url": "https://packagist.org/downloads/", 1613 | "license": [ 1614 | "BSD-3-Clause" 1615 | ], 1616 | "authors": [ 1617 | { 1618 | "name": "Sebastian Bergmann", 1619 | "email": "sebastian@phpunit.de", 1620 | "role": "lead" 1621 | } 1622 | ], 1623 | "description": "Collection of value objects that represent the types of the PHP type system", 1624 | "homepage": "https://github.com/sebastianbergmann/type", 1625 | "support": { 1626 | "issues": "https://github.com/sebastianbergmann/type/issues", 1627 | "source": "https://github.com/sebastianbergmann/type/tree/3.2.0" 1628 | }, 1629 | "funding": [ 1630 | { 1631 | "url": "https://github.com/sebastianbergmann", 1632 | "type": "github" 1633 | } 1634 | ], 1635 | "time": "2022-09-12T14:47:03+00:00" 1636 | }, 1637 | { 1638 | "name": "sebastian/version", 1639 | "version": "3.0.2", 1640 | "source": { 1641 | "type": "git", 1642 | "url": "https://github.com/sebastianbergmann/version.git", 1643 | "reference": "c6c1022351a901512170118436c764e473f6de8c" 1644 | }, 1645 | "dist": { 1646 | "type": "zip", 1647 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", 1648 | "reference": "c6c1022351a901512170118436c764e473f6de8c", 1649 | "shasum": "" 1650 | }, 1651 | "require": { 1652 | "php": ">=7.3" 1653 | }, 1654 | "type": "library", 1655 | "extra": { 1656 | "branch-alias": { 1657 | "dev-master": "3.0-dev" 1658 | } 1659 | }, 1660 | "autoload": { 1661 | "classmap": [ 1662 | "src/" 1663 | ] 1664 | }, 1665 | "notification-url": "https://packagist.org/downloads/", 1666 | "license": [ 1667 | "BSD-3-Clause" 1668 | ], 1669 | "authors": [ 1670 | { 1671 | "name": "Sebastian Bergmann", 1672 | "email": "sebastian@phpunit.de", 1673 | "role": "lead" 1674 | } 1675 | ], 1676 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1677 | "homepage": "https://github.com/sebastianbergmann/version", 1678 | "support": { 1679 | "issues": "https://github.com/sebastianbergmann/version/issues", 1680 | "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" 1681 | }, 1682 | "funding": [ 1683 | { 1684 | "url": "https://github.com/sebastianbergmann", 1685 | "type": "github" 1686 | } 1687 | ], 1688 | "time": "2020-09-28T06:39:44+00:00" 1689 | }, 1690 | { 1691 | "name": "theseer/tokenizer", 1692 | "version": "1.2.1", 1693 | "source": { 1694 | "type": "git", 1695 | "url": "https://github.com/theseer/tokenizer.git", 1696 | "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" 1697 | }, 1698 | "dist": { 1699 | "type": "zip", 1700 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", 1701 | "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", 1702 | "shasum": "" 1703 | }, 1704 | "require": { 1705 | "ext-dom": "*", 1706 | "ext-tokenizer": "*", 1707 | "ext-xmlwriter": "*", 1708 | "php": "^7.2 || ^8.0" 1709 | }, 1710 | "type": "library", 1711 | "autoload": { 1712 | "classmap": [ 1713 | "src/" 1714 | ] 1715 | }, 1716 | "notification-url": "https://packagist.org/downloads/", 1717 | "license": [ 1718 | "BSD-3-Clause" 1719 | ], 1720 | "authors": [ 1721 | { 1722 | "name": "Arne Blankerts", 1723 | "email": "arne@blankerts.de", 1724 | "role": "Developer" 1725 | } 1726 | ], 1727 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", 1728 | "support": { 1729 | "issues": "https://github.com/theseer/tokenizer/issues", 1730 | "source": "https://github.com/theseer/tokenizer/tree/1.2.1" 1731 | }, 1732 | "funding": [ 1733 | { 1734 | "url": "https://github.com/theseer", 1735 | "type": "github" 1736 | } 1737 | ], 1738 | "time": "2021-07-28T10:34:58+00:00" 1739 | }, 1740 | { 1741 | "name": "yoast/phpunit-polyfills", 1742 | "version": "1.0.4", 1743 | "source": { 1744 | "type": "git", 1745 | "url": "https://github.com/Yoast/PHPUnit-Polyfills.git", 1746 | "reference": "3c621ff5429d2b1ff96dc5808ad6cde99d31ea4c" 1747 | }, 1748 | "dist": { 1749 | "type": "zip", 1750 | "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/3c621ff5429d2b1ff96dc5808ad6cde99d31ea4c", 1751 | "reference": "3c621ff5429d2b1ff96dc5808ad6cde99d31ea4c", 1752 | "shasum": "" 1753 | }, 1754 | "require": { 1755 | "php": ">=5.4", 1756 | "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.0" 1757 | }, 1758 | "require-dev": { 1759 | "yoast/yoastcs": "^2.2.1" 1760 | }, 1761 | "type": "library", 1762 | "extra": { 1763 | "branch-alias": { 1764 | "dev-main": "1.x-dev", 1765 | "dev-develop": "1.x-dev" 1766 | } 1767 | }, 1768 | "autoload": { 1769 | "files": [ 1770 | "phpunitpolyfills-autoload.php" 1771 | ] 1772 | }, 1773 | "notification-url": "https://packagist.org/downloads/", 1774 | "license": [ 1775 | "BSD-3-Clause" 1776 | ], 1777 | "authors": [ 1778 | { 1779 | "name": "Team Yoast", 1780 | "email": "support@yoast.com", 1781 | "homepage": "https://yoast.com" 1782 | }, 1783 | { 1784 | "name": "Contributors", 1785 | "homepage": "https://github.com/Yoast/PHPUnit-Polyfills/graphs/contributors" 1786 | } 1787 | ], 1788 | "description": "Set of polyfills for changed PHPUnit functionality to allow for creating PHPUnit cross-version compatible tests", 1789 | "homepage": "https://github.com/Yoast/PHPUnit-Polyfills", 1790 | "keywords": [ 1791 | "phpunit", 1792 | "polyfill", 1793 | "testing" 1794 | ], 1795 | "support": { 1796 | "issues": "https://github.com/Yoast/PHPUnit-Polyfills/issues", 1797 | "source": "https://github.com/Yoast/PHPUnit-Polyfills" 1798 | }, 1799 | "time": "2022-11-16T09:07:52+00:00" 1800 | } 1801 | ], 1802 | "aliases": [], 1803 | "minimum-stability": "stable", 1804 | "stability-flags": [], 1805 | "prefer-stable": false, 1806 | "prefer-lowest": false, 1807 | "platform": [], 1808 | "platform-dev": [], 1809 | "plugin-api-version": "2.1.0" 1810 | } 1811 | -------------------------------------------------------------------------------- /lightweight-term-count-update.php: -------------------------------------------------------------------------------- 1 | setup(); 62 | } 63 | return self::$instance; 64 | } 65 | 66 | /** 67 | * Setup the singleton. 68 | */ 69 | public function setup() { 70 | // Prevent core from counting terms. 71 | wp_defer_term_counting( true ); 72 | remove_action( 'transition_post_status', '_update_term_count_on_transition_post_status' ); 73 | 74 | /** 75 | * Filter the statuses that should be counted, to allow for custom post 76 | * statuses that are otherwise equivalent to 'publish'. 77 | * 78 | * @param array $counted_statuses The statuses that should be counted. 79 | * Defaults to ['publish']. 80 | */ 81 | $this->counted_statuses = apply_filters( 'ltcu_counted_statuses', $this->counted_statuses ); 82 | add_action( 'transition_post_status', array( $this, 'transition_post_status' ), 10, 3 ); 83 | add_action( 'added_term_relationship', array( $this, 'added_term_relationship' ), 10, 3 ); 84 | add_action( 'deleted_term_relationships', array( $this, 'deleted_term_relationships' ), 10, 3 ); 85 | 86 | /** 87 | * Possibly recount posts for a term once it's been edited. 88 | */ 89 | add_action( 'edit_term', array( $this, 'maybe_recount_posts_for_term' ), 10, 3 ); 90 | } 91 | 92 | /** 93 | * When a term relationship is added, increment the term count. 94 | * 95 | * @see {LTCU_Plugin::handle_term_relationship_change()} 96 | * 97 | * @param int $object_id Object ID. 98 | * @param int $tt_id Single term taxonomy ID. 99 | * @param string $taxonomy Taxonomy slug. 100 | */ 101 | public function added_term_relationship( $object_id, $tt_id, $taxonomy ) { 102 | $this->handle_term_relationship_change( $object_id, (array) $tt_id, $taxonomy, 'increment' ); 103 | } 104 | 105 | /** 106 | * When a term relationship is deleted, decrement the term count. 107 | * 108 | * @see {LTCU_Plugin::handle_term_relationship_change()} 109 | * 110 | * @param int $object_id Object ID. 111 | * @param array $tt_ids Array of term taxonomy IDs. 112 | * @param string $taxonomy Taxonomy slug. 113 | */ 114 | public function deleted_term_relationships( $object_id, $tt_ids, $taxonomy ) { 115 | $this->handle_term_relationship_change( $object_id, $tt_ids, $taxonomy, 'decrement' ); 116 | } 117 | 118 | /** 119 | * Update term counts when term relationships are added or deleted. 120 | * 121 | * @see {LTCU_Plugin::added_term_relationship()} 122 | * @see {LTCU_Plugin::deleted_term_relationships()} 123 | * 124 | * @param int $object_id Object ID. 125 | * @param array $tt_ids Array of term taxonomy IDs. 126 | * @param string $taxonomy Taxonomy slug. 127 | * @param string $transition_type Transition type (increment or decrement). 128 | */ 129 | protected function handle_term_relationship_change( $object_id, $tt_ids, $taxonomy, $transition_type ) { 130 | $post = get_post( $object_id ); 131 | 132 | if ( ! $post || ! is_object_in_taxonomy( $post->post_type, $taxonomy ) ) { 133 | // If this object isn't a post, we can jump right into counting it. 134 | $this->quick_update_terms_count( $object_id, $tt_ids, $taxonomy, $transition_type ); 135 | } elseif ( in_array( get_post_status( $post ), $this->counted_statuses, true ) ) { 136 | // If this is a post, we only count it if it's in a counted status. 137 | // If the status changed, that will be caught by 138 | // `LTCU_Plugin::transition_post_status()`. Also note that we used 139 | // `get_post_status()` above because that checks the parent status 140 | // if the status is inherit. 141 | $this->quick_update_terms_count( $object_id, $tt_ids, $taxonomy, $transition_type ); 142 | } else { 143 | clean_term_cache( $tt_ids, '', false ); 144 | } 145 | } 146 | 147 | /** 148 | * When a post transitions, increment or decrement term counts as 149 | * appropriate. 150 | * 151 | * @param string $new_status New post status. 152 | * @param string $old_status Old post status. 153 | * @param object $post { 154 | * Post being transitioned. This not always a \WP_Post. 155 | * 156 | * @type int $ID Post ID. 157 | * @type string $post_type Post type. 158 | * } 159 | */ 160 | public function transition_post_status( $new_status, $old_status, $post ) { 161 | foreach ( (array) get_object_taxonomies( $post->post_type ) as $taxonomy ) { 162 | $tt_ids = wp_get_object_terms( $post->ID, $taxonomy, array( 163 | 'fields' => 'tt_ids', 164 | ) ); 165 | 166 | if ( ! empty( $tt_ids ) && ! is_wp_error( $tt_ids ) ) { 167 | $this->quick_update_terms_count( 168 | $post->ID, 169 | $tt_ids, 170 | $taxonomy, 171 | $this->transition_type( $new_status, $old_status ) 172 | ); 173 | } 174 | } 175 | 176 | // For non-attachments, let's check if there are any attachment children 177 | // with inherited post status -- if so those will need to be re-counted. 178 | if ( 'attachment' !== $post->post_type ) { 179 | $attachments = new WP_Query( array( 180 | 'post_type' => 'attachment', 181 | 'post_parent' => $post->ID, 182 | 'post_status' => 'inherit', 183 | 'ignore_sticky_posts' => true, 184 | 'no_found_rows' => true, 185 | 'posts_per_page' => -1, 186 | 'fields' => 'ids', 187 | 'orderby' => 'ID', 188 | 'order' => 'ASC', 189 | ) ); 190 | 191 | if ( $attachments->have_posts() ) { 192 | foreach ( $attachments->posts as $attachment_id ) { 193 | $this->transition_post_status( $new_status, $old_status, (object) array( 194 | 'ID' => $attachment_id, 195 | 'post_type' => 'attachment', 196 | ) ); 197 | } 198 | } 199 | } 200 | } 201 | 202 | /** 203 | * Update term counts using a very light SQL query. 204 | * 205 | * @param int $object_id Object ID with the term relationship. 206 | * @param array $tt_ids Term taxonomy IDs. 207 | * @param string $taxonomy Taxonomy slug. 208 | * @param string $transition_type 'increment' or 'decrement'. 209 | */ 210 | public function quick_update_terms_count( $object_id, $tt_ids, $taxonomy, $transition_type ) { 211 | global $wpdb; 212 | 213 | if ( ! $transition_type ) { 214 | return false; 215 | } 216 | 217 | $tax_obj = get_taxonomy( $taxonomy ); 218 | if ( $tax_obj ) { 219 | $tt_ids = array_filter( array_map( 'intval', (array) $tt_ids ) ); 220 | 221 | // Respect if a taxonomy has a callback override. 222 | if ( ! empty( $tax_obj->update_count_callback ) ) { 223 | call_user_func( $tax_obj->update_count_callback, $tt_ids, $tax_obj ); 224 | } elseif ( ! empty( $tt_ids ) ) { 225 | if ( ! isset( $this->counted_terms[ $object_id ][ $taxonomy ][ $transition_type ] ) ) { 226 | $this->counted_terms[ $object_id ][ $taxonomy ][ $transition_type ] = array(); 227 | } 228 | 229 | // Ensure that these terms haven't already been counted. 230 | $tt_ids = array_diff( $tt_ids, $this->counted_terms[ $object_id ][ $taxonomy ][ $transition_type ] ); 231 | 232 | if ( empty( $tt_ids ) ) { 233 | // No term to process. So return. 234 | return; 235 | } 236 | 237 | $this->counted_terms[ $object_id ][ $taxonomy ][ $transition_type ] = array_merge( 238 | $this->counted_terms[ $object_id ][ $taxonomy ][ $transition_type ], 239 | $tt_ids 240 | ); 241 | 242 | $tt_ids = array_map( 'absint', $tt_ids ); 243 | $tt_ids_string = '(' . implode( ',', $tt_ids ) . ')'; 244 | 245 | if ( 'increment' === $transition_type ) { 246 | // Incrementing. 247 | $update_query = "UPDATE {$wpdb->term_taxonomy} AS tt SET tt.count = tt.count + 1 WHERE tt.term_taxonomy_id IN $tt_ids_string"; 248 | } else { 249 | // Decrementing. 250 | $update_query = "UPDATE {$wpdb->term_taxonomy} AS tt SET tt.count = tt.count - 1 WHERE tt.term_taxonomy_id IN $tt_ids_string AND tt.count > 0"; 251 | } 252 | 253 | foreach ( $tt_ids as $tt_id ) { 254 | /** This action is documented in wp-includes/taxonomy.php */ 255 | do_action( 'edit_term_taxonomy', $tt_id, $taxonomy ); 256 | } 257 | $wpdb->query( $update_query ); // WPCS: unprepared SQL ok. 258 | foreach ( $tt_ids as $tt_id ) { 259 | /** This action is documented in wp-includes/taxonomy.php */ 260 | do_action( 'edited_term_taxonomy', $tt_id, $taxonomy ); 261 | } 262 | } // End if(). 263 | 264 | clean_term_cache( $tt_ids, '', false ); 265 | } // End if(). 266 | } 267 | 268 | /** 269 | * Determine if a term count should be incremented or decremented. 270 | * 271 | * @param string $new New post status. 272 | * @param string $old Old post status. 273 | * @return string|bool 'increment', 'decrement', or false. 274 | */ 275 | public function transition_type( $new, $old ) { 276 | if ( ! is_array( $this->counted_statuses ) || ! $this->counted_statuses ) { 277 | return false; 278 | } 279 | 280 | $new_is_counted = in_array( $new, $this->counted_statuses, true ); 281 | $old_is_counted = in_array( $old, $this->counted_statuses, true ); 282 | 283 | if ( $new_is_counted && ! $old_is_counted ) { 284 | return 'increment'; 285 | } elseif ( $old_is_counted && ! $new_is_counted ) { 286 | return 'decrement'; 287 | } else { 288 | return false; 289 | } 290 | } 291 | 292 | /** 293 | * Force-recount posts for a term. Do this only when the update originates from the edit term screen. 294 | * 295 | * @param int $term_id the term id. 296 | * @param int $tt_id the term taxonomy id. 297 | * @param string $taxonomy the taxonomy. 298 | * 299 | * @return bool false if the screen check fails, true otherwise 300 | */ 301 | public function maybe_recount_posts_for_term( $term_id, $tt_id, $taxonomy ) { 302 | $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : ''; 303 | if ( ! ( $screen instanceof WP_Screen ) ) { 304 | return false; 305 | } 306 | if ( "edit-$taxonomy" === $screen->id ) { 307 | wp_update_term_count_now( array( $tt_id ), $taxonomy ); 308 | } 309 | return true; 310 | } 311 | } 312 | 313 | // Only run this plugin if we're not running a cron task or wp-cli task. 314 | if ( 315 | ! ( defined( 'DOING_CRON' ) && DOING_CRON ) 316 | && ! ( defined( 'WP_CLI' ) && WP_CLI ) 317 | && ! ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) 318 | ) { 319 | add_action( 'init', array( 'LTCU_Plugin', 'instance' ) ); 320 | } 321 | -------------------------------------------------------------------------------- /multisite.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | tests 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "name": "lightweight-term-count-update", 4 | "version": "0.1.0", 5 | "main": "Gruntfile.js", 6 | "author": "YOUR NAME HERE", 7 | "devDependencies": { 8 | "grunt": "~0.4.5", 9 | "grunt-wp-i18n": "~0.5.0", 10 | "grunt-wp-readme-to-markdown": "~1.0.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /phpcs.ruleset.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Generally-applicable sniffs for WordPress plugins 4 | 5 | 6 | 7 | vendor/* 8 | node_modules/* 9 | 10 | 11 | warning 12 | 13 | 14 | warning 15 | 16 | 17 | 18 | 19 | warning 20 | 21 | 22 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | ./tests/ 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === Cronify Term Count Update === 2 | Contributors: Automattic, alleyinteractive 3 | Tags: performance, post-save 4 | Requires at least: 4.7 5 | Tested up to: 4.7.3 6 | Stable tag: 0.1.0 7 | License: GPLv2 or later 8 | License URI: http://www.gnu.org/licenses/gpl-2.0.html 9 | 10 | This plugin deferres the _update_term_count_on_transition_post_status action to cron 11 | 12 | == Description == 13 | 14 | Sometimes the term count update hooked to the transition_post_status action may slow down the post_save action. This plugin offloads the action to cron, so it does no block the post_save action. 15 | 16 | == Installation == 17 | 18 | 1. Upload this plugin to the `/wp-content/plugins/` directory 19 | 1. Activate the plugin through the 'Plugins' menu in WordPress 20 | 21 | == Changelog == 22 | 23 | = 0.1.0 = 24 | Initial version 25 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | counted_terms = array(); 18 | } 19 | 20 | /** 21 | * Create and return a test category. 22 | * 23 | * @return \WP_Term 24 | */ 25 | protected function make_category() { 26 | return self::factory()->category->create_and_get( 27 | array( 28 | 'slug' => 'mytestcat', 29 | 'name' => 'My Test Category 1', 30 | ) 31 | ); 32 | } 33 | 34 | /** 35 | * Test a single post publish action with a single category 36 | */ 37 | function test_single_post_publish_with_a_single_category() { 38 | 39 | // Create Test Category. 40 | $testcat = $this->make_category(); 41 | 42 | $post_id = self::factory()->post->create( array( 43 | 'post_type' => 'post', 44 | 'post_title' => 'Test post 1', 45 | 'post_status' => 'publish', 46 | 'post_category' => array( $testcat->term_id ), 47 | ) ); 48 | 49 | $category = get_term( $testcat->term_id, 'category' ); 50 | 51 | $this->assertEquals( $testcat->count + 1, $category->count ); 52 | } 53 | 54 | /** 55 | * Test multiple posts counting 56 | */ 57 | function test_multiple_posts_counting() { 58 | // Create a test category. 59 | $testcat = $this->make_category(); 60 | 61 | self::factory()->post->create_many( 3, array( 62 | 'post_type' => 'post', 63 | 'post_status' => 'publish', 64 | 'post_category' => array( $testcat->term_id ), 65 | ) ); 66 | 67 | $category = get_term( $testcat->term_id, 'category' ); 68 | 69 | $this->assertEquals( $testcat->count + 3, $category->count ); 70 | } 71 | 72 | /** 73 | * Test post unpublish action 74 | */ 75 | function test_post_unpublish() { 76 | $testcat = $this->make_category(); 77 | 78 | $post_id = self::factory()->post->create( array( 79 | 'post_type' => 'post', 80 | 'post_status' => 'publish', 81 | 'post_category' => array( $testcat->term_id ), 82 | ) ); 83 | 84 | wp_update_post( array( 'ID' => $post_id, 'post_status' => 'draft' ) ); 85 | 86 | $category = get_term( $testcat->term_id, 'category' ); 87 | 88 | $this->assertEquals( $testcat->count, $category->count ); 89 | } 90 | 91 | function test_set_object_terms() { 92 | // Create a test category. 93 | $testcat = $this->make_category(); 94 | 95 | $post_ids = self::factory()->post->create_many( 3, array( 96 | 'post_type' => 'post', 97 | 'post_status' => 'publish', 98 | ) ); 99 | 100 | wp_set_object_terms( $post_ids[0], $testcat->term_id, 'category' ); 101 | 102 | $category = get_term( $testcat->term_id, 'category' ); 103 | 104 | $this->assertEquals( $testcat->count + 1, $category->count ); 105 | } 106 | 107 | function test_remove_object_terms() { 108 | // Create a test category. 109 | $testcat = $this->make_category(); 110 | 111 | $post_ids = self::factory()->post->create_many( 3, array( 112 | 'post_type' => 'post', 113 | 'post_status' => 'publish', 114 | 'post_category' => array( $testcat->term_id ), 115 | ) ); 116 | 117 | wp_remove_object_terms( $post_ids[0], $testcat->term_id, 'category' ); 118 | 119 | $category = get_term( $testcat->term_id, 'category' ); 120 | 121 | $this->assertEquals( $testcat->count + 2, $category->count ); 122 | } 123 | 124 | /** 125 | * Data provider to test status changes. 126 | * 127 | * @return array 128 | */ 129 | public function term_counts_data() { 130 | return array( 131 | array( 'publish', 'publish' ), 132 | array( 'draft', 'publish' ), 133 | array( 'publish', 'draft' ), 134 | array( 'draft', 'draft' ), 135 | ); 136 | } 137 | 138 | /** 139 | * @dataProvider term_counts_data 140 | * @param string $old_status Post status to transition from. 141 | * @param string $new_status Post status to transition to. 142 | */ 143 | function test_set_object_terms_with_status_changes( $old_status, $new_status ) { 144 | // Create a test category. 145 | $testcat = $this->make_category(); 146 | 147 | $post_ids = self::factory()->post->create_many( 3, array( 148 | 'post_type' => 'post', 149 | 'post_status' => $old_status, 150 | ) ); 151 | 152 | // Reset the counted terms cache to mimic pre-existing posts. 153 | LTCU_Plugin::instance()->counted_terms = array(); 154 | 155 | wp_update_post( array( 156 | 'ID' => $post_ids[0], 157 | 'post_status' => $new_status, 158 | ) ); 159 | wp_set_object_terms( $post_ids[0], $testcat->term_id, 'category' ); 160 | 161 | $category = get_term( $testcat->term_id, 'category' ); 162 | 163 | $change = ( 'publish' === $new_status ) ? 1 : 0; 164 | $this->assertEquals( $testcat->count + $change, $category->count ); 165 | } 166 | 167 | /** 168 | * @dataProvider term_counts_data 169 | * @param string $old_status Post status to transition from. 170 | * @param string $new_status Post status to transition to. 171 | */ 172 | function test_remove_object_terms_with_status_changes( $old_status, $new_status ) { 173 | // Create a test category. 174 | $testcat = $this->make_category(); 175 | 176 | $post_ids = self::factory()->post->create_many( 3, array( 177 | 'post_type' => 'post', 178 | 'post_status' => $old_status, 179 | 'post_category' => array( $testcat->term_id ), 180 | ) ); 181 | 182 | // Reset the counted terms cache to mimic pre-existing posts. 183 | LTCU_Plugin::instance()->counted_terms = array(); 184 | 185 | wp_update_post( array( 186 | 'ID' => $post_ids[0], 187 | 'post_status' => $new_status, 188 | 'post_date' => '2017-01-01 12:34:56', 189 | ) ); 190 | wp_remove_object_terms( $post_ids[0], $testcat->term_id, 'category' ); 191 | 192 | $category = get_term( $testcat->term_id, 'category' ); 193 | 194 | $change = ( 'draft' === $old_status ) ? 0 : 2; 195 | $this->assertEquals( $testcat->count + $change, $category->count ); 196 | } 197 | 198 | function test_post_update_removing_categories() { 199 | // Create a test category. 200 | $testcat = $this->make_category(); 201 | 202 | $post_id = self::factory()->post->create( array( 203 | 'post_type' => 'post', 204 | 'post_status' => 'publish', 205 | 'post_category' => array( $testcat->term_id ), 206 | ) ); 207 | 208 | // Reset the counted terms cache to mimic pre-existing posts. 209 | LTCU_Plugin::instance()->counted_terms = array(); 210 | 211 | $testcat2 = self::factory()->category->create_and_get(); 212 | 213 | wp_update_post( array( 214 | 'ID' => $post_id, 215 | 'post_category' => array( $testcat2->term_id ), 216 | ) ); 217 | 218 | $category = get_term( $testcat->term_id, 'category' ); 219 | 220 | $this->assertEquals( 0, $category->count ); 221 | 222 | $category2 = get_term( $testcat2->term_id, 'category' ); 223 | 224 | $this->assertEquals( $testcat2->count + 1, $category2->count ); 225 | } 226 | 227 | /** 228 | * Categories with a parent are being reflected in category count 229 | * in case the parent post has publish post status 230 | */ 231 | function test_attachments_with_inherit_post_status_categories() { 232 | // Create a test category. 233 | $testcat = $this->make_category(); 234 | 235 | register_taxonomy_for_object_type( 'category', 'attachment' ); 236 | 237 | $post_id = self::factory()->post->create( array( 238 | 'post_type' => 'post', 239 | 'post_status' => 'publish', 240 | ) ); 241 | 242 | $attachment_id = self::factory()->attachment->create( array( 243 | 'post_parent' => $post_id, 244 | ) ); 245 | 246 | // Reset the counted terms cache to mimic pre-existing posts. 247 | LTCU_Plugin::instance()->counted_terms = array(); 248 | 249 | wp_update_post( array( 250 | 'ID' => $attachment_id, 251 | 'post_category' => array( $testcat->term_id ), 252 | ) ); 253 | 254 | $category = get_term( $testcat->term_id, 'category' ); 255 | 256 | $this->assertEquals( $testcat->count + 1, $category->count ); 257 | } 258 | 259 | /** 260 | * Categories w/o parent have, by default, post_status set by inherit 261 | * and WordPress does not currently reflect them in category counts 262 | * {@see https://core.trac.wordpress.org/ticket/22558} and 263 | * {@see https://core.trac.wordpress.org/ticket/23530}. However, they should 264 | * be included and so this plugin does count them. If this behavior is left 265 | * as a "won't fix" in core, this plugin might change how it works to remain 266 | * inline with core. 267 | */ 268 | function test_non_attached_attachments_categories() { 269 | // Create a test category. 270 | $testcat = $this->make_category(); 271 | 272 | register_taxonomy_for_object_type( 'category', 'attachment' ); 273 | 274 | $attachment_id = self::factory()->attachment->create( array() ); 275 | 276 | // Reset the counted terms cache to mimic pre-existing posts. 277 | LTCU_Plugin::instance()->counted_terms = array(); 278 | 279 | wp_update_post( array( 280 | 'ID' => $attachment_id, 281 | 'post_category' => array( $testcat->term_id ), 282 | ) ); 283 | 284 | $category = get_term( $testcat->term_id, 'category' ); 285 | 286 | $this->assertEquals( $testcat->count + 1, $category->count ); 287 | } 288 | 289 | /** 290 | * Attachments with post_status inherit should be counted in the category 291 | * count in case their parent is published 292 | */ 293 | function test_post_publish_of_post_with_attachments() { 294 | // Create a test category. 295 | $testcat = $this->make_category(); 296 | 297 | register_taxonomy_for_object_type( 'category', 'attachment' ); 298 | 299 | $post_id = self::factory()->post->create( array( 300 | 'post_type' => 'post', 301 | 'post_status' => 'draft', 302 | 'post_category' => array( $testcat->term_id ), 303 | ) ); 304 | 305 | self::factory()->attachment->create_many( 11, array( 306 | 'post_parent' => $post_id, 307 | 'post_category' => array( $testcat->term_id ), 308 | ) ); 309 | 310 | // Reset the counted terms cache to mimic pre-existing posts. 311 | LTCU_Plugin::instance()->counted_terms = array(); 312 | 313 | wp_update_post( array( 314 | 'ID' => $post_id, 315 | 'post_status' => 'publish', 316 | ) ); 317 | 318 | $category = get_term( $testcat->term_id, 'category' ); 319 | 320 | $this->assertEquals( $testcat->count + 12, $category->count ); 321 | } 322 | 323 | /** 324 | * Test non-post taxonomy 325 | * @see: http://justintadlock.com/archives/2011/10/20/custom-user-taxonomies-in-wordpress 326 | */ 327 | function test_non_post_taxonomy() { 328 | register_taxonomy( 329 | 'profession', 330 | 'user', 331 | array( 332 | 'public' => true, 333 | ) 334 | ); 335 | $testterm_array = wp_create_term( 'Developer', 'profession' ); 336 | 337 | $testterm = get_term( $testterm_array['term_id'], 'profession' ); 338 | 339 | $user_id = self::factory()->user->create(); 340 | 341 | wp_set_object_terms( $user_id, $testterm->term_id, 'profession' ); 342 | 343 | $term = get_term( $testterm->term_id, 'profession' ); 344 | 345 | $this->assertEquals( $testterm->count + 1, $term->count ); 346 | } 347 | 348 | /** 349 | * Test custom count callback of a custom taxonomy 350 | */ 351 | function test_custom_count_callback() { 352 | register_taxonomy( 353 | 'test-taxonomy', 354 | 'post', 355 | array( 356 | 'public' => true, 357 | 'update_count_callback' => function( $terms, $taxonomy ) { 358 | global $wpdb; 359 | 360 | //confirm parameters are as expected 361 | $this->assertTrue( is_int($terms) || is_array( $terms ) ); 362 | $this->assertInstanceOf( 'WP_Taxonomy', $taxonomy ); 363 | 364 | foreach ( (array) $terms as $term ) { 365 | 366 | $count = 10; 367 | 368 | do_action( 'edit_term_taxonomy', $term, $taxonomy ); 369 | $wpdb->update( $wpdb->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) ); 370 | do_action( 'edited_term_taxonomy', $term, $taxonomy ); 371 | } 372 | }, 373 | ) 374 | ); 375 | $testterm_array = wp_create_term( 'Test term', 'test-taxonomy' ); 376 | 377 | $testterm = get_term( $testterm_array['term_id'], 'test-taxonomy' ); 378 | 379 | $post_id = self::factory()->post->create( array( 380 | 'post_type' => 'post', 381 | 'post_status' => 'publish', 382 | ) ); 383 | 384 | // Reset the counted terms cache to mimic pre-existing posts. 385 | LTCU_Plugin::instance()->counted_terms = array(); 386 | 387 | wp_set_object_terms( $post_id, $testterm->term_id, 'test-taxonomy' ); 388 | 389 | $term = get_term( $testterm->term_id, 'test-taxonomy' ); 390 | 391 | $this->assertEquals( 10, $term->count ); 392 | } 393 | 394 | 395 | /** 396 | * Data provider to test actions fired on update. 397 | * 398 | * @return array 399 | */ 400 | public function actions_data() { 401 | return array( 402 | array( 'edit_term_taxonomy' ), 403 | array( 'edited_term_taxonomy' ), 404 | ); 405 | } 406 | 407 | /** 408 | * @dataProvider actions_data 409 | * @param string $action Action to be fired. 410 | */ 411 | function test_actins_being_fired( $action ) { 412 | // Create a test category. 413 | $testcats = self::factory()->category->create_many( 3 ); 414 | 415 | // Reset the counted terms cache to mimic pre-existing posts. 416 | LTCU_Plugin::instance()->counted_terms = array(); 417 | 418 | // Hook test action. 419 | $GLOBALS['ltcu_fired_action_tt_ids'] = array(); 420 | $GLOBALS['ltcu_fired_action_taxonomy'] = ''; 421 | add_action( $action, function( $tt_id, $taxonomy ) { 422 | $GLOBALS['ltcu_fired_action_tt_ids'][] = $tt_id; 423 | $GLOBALS['ltcu_fired_action_taxonomy'] = $taxonomy; 424 | }, 10, 2 ); 425 | 426 | $post_id = self::factory()->post->create( array( 427 | 'post_type' => 'post', 428 | 'post_status' => 'publish', 429 | 'post_category' => $testcats, 430 | ) ); 431 | 432 | wp_set_object_terms( $post_id, $testcats, 'category' ); 433 | 434 | // Prepare the comparison. 435 | $tt_ids = array(); 436 | foreach ( $testcats as $term_id ) { 437 | $term = get_term( $term_id, 'category' ); 438 | $tt_ids[] = $term->term_taxonomy_id; 439 | } 440 | 441 | $this->assertEquals( sort( $GLOBALS['ltcu_fired_action_tt_ids'] ) , sort( $tt_ids ) ); 442 | $this->assertEquals( 'category', $GLOBALS['ltcu_fired_action_taxonomy'] ); 443 | } 444 | 445 | /** 446 | * Test that the first draft of a post gets terms set properly and the term 447 | * cache is cleared. 448 | * 449 | * {@see https://github.com/Automattic/lightweight-term-count-update/pull/8} 450 | */ 451 | function test_first_draft_save() { 452 | // Create Test Category. 453 | $testcat = $this->make_category(); 454 | 455 | // Create the blank post object to mimic the "Write" screen. 456 | $post = get_default_post_to_edit( 'post', true ); 457 | $post_id = $post->ID; 458 | 459 | // Save a draft of the post. 460 | wp_update_post( array( 461 | 'ID' => $post_id, 462 | 'post_status' => 'draft', 463 | 'post_title' => 'some-post', 464 | 'post_type' => 'post', 465 | 'post_content' => 'some_content', 466 | 'post_category' => array( $testcat->term_id ), 467 | ) ); 468 | 469 | // Ensure that caches were cleared. 470 | $terms = get_the_terms( $post_id, 'category' ); 471 | 472 | $this->assertTrue( is_array( $terms ) ); 473 | $this->assertSame( array( $testcat->term_id ), wp_list_pluck( $terms, 'term_id' ) ); 474 | } 475 | } 476 | --------------------------------------------------------------------------------