├── .editorconfig ├── .github ├── CODEOWNERS └── workflows │ ├── close-stale-issues.yml │ ├── dependency-review.yml │ └── wordpress-version-checker.yml ├── .gitignore ├── .travis.yml ├── .wordpress-org ├── banner-1544x500.jpg ├── banner-1544x500.png ├── banner-772x250.png ├── icon-128x128.png ├── icon-256x256.jpg ├── icon-256x256.png └── icon.svg ├── 10up-sitemaps.php ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CREDITS.md ├── LICENSE.md ├── README.md ├── bin └── install-wp-tests.sh ├── composer.json ├── composer.lock ├── includes ├── classes │ ├── Command.php │ └── Sitemap.php ├── core.php ├── templates │ ├── sitemap-page.php │ └── sitemap-root.php └── utils.php ├── phpcs.xml ├── phpunit.xml.dist ├── readme.txt └── tests ├── TestSitemap.php └── bootstrap.php /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | indent_style = tab 9 | 10 | [{*.json,*.yml,.babelrc,.bowerrc,.browserslistrc,.postcssrc}] 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [*.txt,wp-config-sample.php] 15 | end_of_line = crlf -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # These owners will be the default owners for everything in the repo. Unless a later match takes precedence, @tlovett1, as primary maintainer will be requested for review when someone opens a Pull Request. 2 | * @tlovett1 @10up/open-source-practice 3 | 4 | # GitHub and WordPress.org specifics 5 | /.github/ @jeffpaul 6 | /.wordpress-org/ @jeffpaul 7 | CODE_OF_CONDUCT.md @jeffpaul 8 | LICENSE.md @jeffpaul 9 | -------------------------------------------------------------------------------- /.github/workflows/close-stale-issues.yml: -------------------------------------------------------------------------------- 1 | name: 'Close stale issues' 2 | 3 | # **What it does**: Closes issues where the original author doesn't respond to a request for information. 4 | # **Why we have it**: To remove the need for maintainers to remember to check back on issues periodically to see if contributors have responded. 5 | 6 | on: 7 | schedule: 8 | # Schedule for every day at 1:30am UTC 9 | - cron: '30 1 * * *' 10 | 11 | permissions: 12 | issues: write 13 | 14 | jobs: 15 | stale: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/stale@v9 19 | with: 20 | days-before-stale: 7 21 | days-before-close: 7 22 | stale-issue-message: > 23 | It has been 7 days since more information was requested from you in this issue and we have not heard back. This issue is now marked as stale and will be closed in 7 days, but if you have more information to add then please comment and the issue will stay open. 24 | close-issue-message: > 25 | This issue has been automatically closed because there has been no response 26 | to our request for more information. With only the 27 | information that is currently in the issue, we don't have enough information 28 | to take action. Please reach out if you have or find the answers we need so 29 | that we can investigate further. See [this blog post on bug reports and the 30 | importance of repro steps](https://www.lee-dohm.com/2015/01/04/writing-good-bug-reports/) 31 | for more information about the kind of information that may be helpful. 32 | stale-issue-label: 'stale' 33 | close-issue-reason: 'not_planned' 34 | any-of-labels: 'needs:feedback' 35 | remove-stale-when-updated: true 36 | 37 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | # Dependency Review Action 2 | # 3 | # This Action will scan dependency manifest files that change as part of a Pull Reqest, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging. 4 | # 5 | # Source repository: https://github.com/actions/dependency-review-action 6 | # Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement 7 | name: 'Dependency Review' 8 | on: [pull_request] 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | dependency-review: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: 'Checkout Repository' 18 | uses: actions/checkout@v3 19 | - name: Dependency Review 20 | uses: actions/dependency-review-action@v3 21 | with: 22 | license-check: true 23 | vulnerability-check: false 24 | config-file: 10up/.github/.github/dependency-review-config.yml@trunk 25 | -------------------------------------------------------------------------------- /.github/workflows/wordpress-version-checker.yml: -------------------------------------------------------------------------------- 1 | name: "WordPress version checker" 2 | on: 3 | push: 4 | branches: 5 | - develop 6 | - trunk 7 | pull_request: 8 | branches: 9 | - develop 10 | schedule: 11 | - cron: '0 0 * * 1' 12 | 13 | permissions: 14 | issues: write 15 | 16 | jobs: 17 | wordpress-version-checker: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: WordPress version checker 21 | uses: skaut/wordpress-version-checker@master 22 | with: 23 | repo-token: ${{ secrets.GITHUB_TOKEN }} 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | node_modules 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - 7.2 4 | notifications: 5 | email: false 6 | services: 7 | - mysql 8 | env: 9 | global: 10 | - WP_VERSION=latest WP_MULTISITE=1" 11 | before_script: 12 | - composer install 13 | - bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION 14 | script: 15 | - composer run-script lint 16 | - composer run-script test 17 | -------------------------------------------------------------------------------- /.wordpress-org/banner-1544x500.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/10up-sitemaps/9cb41fefa2d74d8e6cff1b210fc696489acc49db/.wordpress-org/banner-1544x500.jpg -------------------------------------------------------------------------------- /.wordpress-org/banner-1544x500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/10up-sitemaps/9cb41fefa2d74d8e6cff1b210fc696489acc49db/.wordpress-org/banner-1544x500.png -------------------------------------------------------------------------------- /.wordpress-org/banner-772x250.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/10up-sitemaps/9cb41fefa2d74d8e6cff1b210fc696489acc49db/.wordpress-org/banner-772x250.png -------------------------------------------------------------------------------- /.wordpress-org/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/10up-sitemaps/9cb41fefa2d74d8e6cff1b210fc696489acc49db/.wordpress-org/icon-128x128.png -------------------------------------------------------------------------------- /.wordpress-org/icon-256x256.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/10up-sitemaps/9cb41fefa2d74d8e6cff1b210fc696489acc49db/.wordpress-org/icon-256x256.jpg -------------------------------------------------------------------------------- /.wordpress-org/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/10up/10up-sitemaps/9cb41fefa2d74d8e6cff1b210fc696489acc49db/.wordpress-org/icon-256x256.png -------------------------------------------------------------------------------- /.wordpress-org/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /10up-sitemaps.php: -------------------------------------------------------------------------------- 1 | 294 | Copyright (C) 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 | , 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 | # 10up Sitemaps 2 | 3 | ![10up Sitemaps](https://github.com/10up/10up-sitemaps/blob/develop/.wordpress-org/banner-1544x500.png) 4 | 5 | [![Support Level](https://img.shields.io/badge/support-stable-blue.svg)](#support-level) [![Release Version](https://img.shields.io/github/release/10up/10up-sitemaps.svg)](https://github.com/10up/10up-sitemaps/releases/latest) ![WordPress tested up to version](https://img.shields.io/badge/WordPress-v6.7%20tested-success.svg) [![GPL-2.0-or-later License](https://img.shields.io/github/license/10up/10up-sitemaps.svg)](https://github.com/10up/10up-sitemaps/blob/develop/LICENSE.md) [![Dependency Review](https://github.com/10up/10up-sitemaps/actions/workflows/dependency-review.yml/badge.svg)](https://github.com/10up/10up-sitemaps/actions/workflows/dependency-review.yml) 6 | 7 | > Simple sitemaps plugin that performs at scale. 8 | 9 | ## Overview 10 | 11 | This is a simple sitemap plugin meant to run at scale. Sitemaps are only updated via WP-CLI. Output is saved in an option for fast reading/displaying on the front end. 12 | 13 | ## Usage 14 | 15 | 1. Install the plugin. 16 | 2. Run the WP-CLI command: `wp tenup-sitemaps generate` 17 | 3. Add WP-CLI command to cron job. For multisite, add a command for each site in the network. 18 | 19 | You can pass `--range` to the `generate` command to only index content within a certain age range. `wp tenup-sitemaps generate --range=12` would only index content created/updated within the last 12 months. 20 | 21 | The plugin indexes all public posts, post type archives, and public taxonomy term archives. For posts, images are parsed and included. Translated content needs to be manually filtered in via `tenup_sitemaps_term_translations`. 22 | 23 | ## Support Level 24 | 25 | **Stable:** 10up is not planning to develop any new features for this, but will still respond to bug reports and security concerns. We welcome PRs, but any that include new features should be small and easy to integrate and should not include breaking changes. We otherwise intend to keep this tested up to the most recent version of WordPress. 26 | 27 | ## Changelog 28 | 29 | A complete listing of all notable changes to 10up Sitemaps is documented in [CHANGELOG.md](https://github.com/10up/10up-sitemaps/blob/develop/CHANGELOG.md). 30 | 31 | ## Contributing 32 | 33 | Please read [CODE_OF_CONDUCT.md](https://github.com/10up/10up-sitemaps/blob/develop/CODE_OF_CONDUCT.md) for details on our code of conduct, [CONTRIBUTING.md](https://github.com/10up/10up-sitemaps/blob/develop/CONTRIBUTING.md) for details on the process for submitting pull requests to us, and [CREDITS.md](https://github.com/10up/10up-sitemaps/blob/develop/CREDITS.md) for a listing of maintainers of, contributors to, and libraries used by 10up Sitemaps. 34 | 35 | ## Like what you see? 36 | 37 | Work with us at 10up 38 | -------------------------------------------------------------------------------- /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 | CREATE_DB_IF_EXISTS=${6-false} 14 | 15 | TMPDIR=${TMPDIR-/tmp} 16 | TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//") 17 | WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib} 18 | WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress/} 19 | 20 | download() { 21 | if [ `which curl` ]; then 22 | curl -s "$1" > "$2"; 23 | elif [ `which wget` ]; then 24 | wget -nv -O "$2" "$1" 25 | fi 26 | } 27 | 28 | if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+\-(beta|RC)[0-9]+$ ]]; then 29 | WP_BRANCH=${WP_VERSION%\-*} 30 | WP_TESTS_TAG="branches/$WP_BRANCH" 31 | 32 | elif [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+$ ]]; then 33 | WP_TESTS_TAG="branches/$WP_VERSION" 34 | elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then 35 | if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then 36 | # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x 37 | WP_TESTS_TAG="tags/${WP_VERSION%??}" 38 | else 39 | WP_TESTS_TAG="tags/$WP_VERSION" 40 | fi 41 | elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then 42 | WP_TESTS_TAG="trunk" 43 | else 44 | # http serves a single offer, whereas https serves multiple. we only want one 45 | download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json 46 | grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json 47 | LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//') 48 | if [[ -z "$LATEST_VERSION" ]]; then 49 | echo "Latest WordPress version could not be found" 50 | exit 1 51 | fi 52 | WP_TESTS_TAG="tags/$LATEST_VERSION" 53 | fi 54 | set -ex 55 | 56 | install_wp() { 57 | 58 | if [ -d $WP_CORE_DIR ]; then 59 | return; 60 | fi 61 | 62 | mkdir -p $WP_CORE_DIR 63 | 64 | if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then 65 | mkdir -p $TMPDIR/wordpress-nightly 66 | download https://wordpress.org/nightly-builds/wordpress-latest.zip $TMPDIR/wordpress-nightly/wordpress-nightly.zip 67 | unzip -q $TMPDIR/wordpress-nightly/wordpress-nightly.zip -d $TMPDIR/wordpress-nightly/ 68 | mv $TMPDIR/wordpress-nightly/wordpress/* $WP_CORE_DIR 69 | else 70 | if [ $WP_VERSION == 'latest' ]; then 71 | local ARCHIVE_NAME='latest' 72 | elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then 73 | # https serves multiple offers, whereas http serves single. 74 | download https://api.wordpress.org/core/version-check/1.7/ $TMPDIR/wp-latest.json 75 | if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then 76 | # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x 77 | LATEST_VERSION=${WP_VERSION%??} 78 | else 79 | # otherwise, scan the releases and get the most up to date minor version of the major release 80 | local VERSION_ESCAPED=`echo $WP_VERSION | sed 's/\./\\\\./g'` 81 | LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1) 82 | fi 83 | if [[ -z "$LATEST_VERSION" ]]; then 84 | local ARCHIVE_NAME="wordpress-$WP_VERSION" 85 | else 86 | local ARCHIVE_NAME="wordpress-$LATEST_VERSION" 87 | fi 88 | else 89 | local ARCHIVE_NAME="wordpress-$WP_VERSION" 90 | fi 91 | download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz 92 | tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR 93 | fi 94 | 95 | download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php 96 | } 97 | 98 | install_test_suite() { 99 | # portable in-place argument for both GNU sed and Mac OSX sed 100 | if [[ $(uname -s) == 'Darwin' ]]; then 101 | local ioption='-i.bak' 102 | else 103 | local ioption='-i' 104 | fi 105 | 106 | # set up testing suite if it doesn't yet exist 107 | if [ ! -d $WP_TESTS_DIR ]; then 108 | # set up testing suite 109 | mkdir -p $WP_TESTS_DIR 110 | svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes 111 | svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data 112 | fi 113 | 114 | if [ ! -f wp-tests-config.php ]; then 115 | download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php 116 | # remove all forward slashes in the end 117 | WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::") 118 | sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php 119 | sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php 120 | sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php 121 | sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php 122 | sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php 123 | fi 124 | 125 | } 126 | 127 | install_db() { 128 | 129 | # parse DB_HOST for port or socket references 130 | local PARTS=(${DB_HOST//\:/ }) 131 | local DB_HOSTNAME=${PARTS[0]}; 132 | local DB_SOCK_OR_PORT=${PARTS[1]}; 133 | local EXTRA="" 134 | 135 | if ! [ -z $DB_HOSTNAME ] ; then 136 | if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then 137 | EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp" 138 | elif ! [ -z $DB_SOCK_OR_PORT ] ; then 139 | EXTRA=" --socket=$DB_SOCK_OR_PORT" 140 | elif ! [ -z $DB_HOSTNAME ] ; then 141 | EXTRA=" --host=$DB_HOSTNAME --protocol=tcp" 142 | fi 143 | fi 144 | 145 | if [ ${CREATE_DB_IF_EXISTS} = "true" ]; then 146 | mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA || echo "Database already exists." 147 | else 148 | mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA 149 | fi 150 | } 151 | 152 | install_wp 153 | install_test_suite 154 | install_db 155 | 156 | echo "Done!" 157 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "10up/tenup-sitemaps", 3 | "description": "10up Sitemaps", 4 | "type": "wordpress-plugin", 5 | "license": "GPL-2.0-or-later", 6 | "authors": [ 7 | { 8 | "name": "Taylor Lovett", 9 | "email": "taylor.lovett@10up.com" 10 | } 11 | ], 12 | "require": { 13 | "php": ">=7.0" 14 | }, 15 | "autoload": { 16 | "psr-4": { 17 | "TenupSitemaps\\": "includes/classes/" 18 | } 19 | }, 20 | "require-dev": { 21 | "10up/phpcs-composer": "dev-master", 22 | "phpunit/phpunit": "^7" 23 | }, 24 | "scripts": { 25 | "lint": "phpcs .", 26 | "lint-fix": "phpcbf .", 27 | "test": "phpunit", 28 | "setup-local-tests": "bash bin/install-wp-tests.sh tsm_wp_test root password mysql latest true" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /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": "a6b77d6e4beb787c4896f2e1a831b4af", 8 | "packages": [], 9 | "packages-dev": [ 10 | { 11 | "name": "10up/phpcs-composer", 12 | "version": "dev-master", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/10up/phpcs-composer.git", 16 | "reference": "00940fa9732fb0069d7b4624f10bb46fdf7e1a28" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/10up/phpcs-composer/zipball/00940fa9732fb0069d7b4624f10bb46fdf7e1a28", 21 | "reference": "00940fa9732fb0069d7b4624f10bb46fdf7e1a28", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "dealerdirect/phpcodesniffer-composer-installer": "*", 26 | "phpcompatibility/phpcompatibility-wp": "^2", 27 | "squizlabs/php_codesniffer": "^3.4.0", 28 | "wp-coding-standards/wpcs": "*" 29 | }, 30 | "type": "phpcodesniffer-standard", 31 | "notification-url": "https://packagist.org/downloads/", 32 | "license": [ 33 | "MIT" 34 | ], 35 | "authors": [ 36 | { 37 | "name": "Ephraim Gregor", 38 | "email": "ephraim.gregor@10up.com" 39 | } 40 | ], 41 | "time": "2019-03-04T14:40:41+00:00" 42 | }, 43 | { 44 | "name": "dealerdirect/phpcodesniffer-composer-installer", 45 | "version": "v0.5.0", 46 | "source": { 47 | "type": "git", 48 | "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", 49 | "reference": "e749410375ff6fb7a040a68878c656c2e610b132" 50 | }, 51 | "dist": { 52 | "type": "zip", 53 | "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/e749410375ff6fb7a040a68878c656c2e610b132", 54 | "reference": "e749410375ff6fb7a040a68878c656c2e610b132", 55 | "shasum": "" 56 | }, 57 | "require": { 58 | "composer-plugin-api": "^1.0", 59 | "php": "^5.3|^7", 60 | "squizlabs/php_codesniffer": "^2|^3" 61 | }, 62 | "require-dev": { 63 | "composer/composer": "*", 64 | "phpcompatibility/php-compatibility": "^9.0", 65 | "sensiolabs/security-checker": "^4.1.0" 66 | }, 67 | "type": "composer-plugin", 68 | "extra": { 69 | "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" 70 | }, 71 | "autoload": { 72 | "psr-4": { 73 | "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" 74 | } 75 | }, 76 | "notification-url": "https://packagist.org/downloads/", 77 | "license": [ 78 | "MIT" 79 | ], 80 | "authors": [ 81 | { 82 | "name": "Franck Nijhof", 83 | "email": "franck.nijhof@dealerdirect.com", 84 | "homepage": "http://www.frenck.nl", 85 | "role": "Developer / IT Manager" 86 | } 87 | ], 88 | "description": "PHP_CodeSniffer Standards Composer Installer Plugin", 89 | "homepage": "http://www.dealerdirect.com", 90 | "keywords": [ 91 | "PHPCodeSniffer", 92 | "PHP_CodeSniffer", 93 | "code quality", 94 | "codesniffer", 95 | "composer", 96 | "installer", 97 | "phpcs", 98 | "plugin", 99 | "qa", 100 | "quality", 101 | "standard", 102 | "standards", 103 | "style guide", 104 | "stylecheck", 105 | "tests" 106 | ], 107 | "time": "2018-10-26T13:21:45+00:00" 108 | }, 109 | { 110 | "name": "doctrine/instantiator", 111 | "version": "1.2.0", 112 | "source": { 113 | "type": "git", 114 | "url": "https://github.com/doctrine/instantiator.git", 115 | "reference": "a2c590166b2133a4633738648b6b064edae0814a" 116 | }, 117 | "dist": { 118 | "type": "zip", 119 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a", 120 | "reference": "a2c590166b2133a4633738648b6b064edae0814a", 121 | "shasum": "" 122 | }, 123 | "require": { 124 | "php": "^7.1" 125 | }, 126 | "require-dev": { 127 | "doctrine/coding-standard": "^6.0", 128 | "ext-pdo": "*", 129 | "ext-phar": "*", 130 | "phpbench/phpbench": "^0.13", 131 | "phpstan/phpstan-phpunit": "^0.11", 132 | "phpstan/phpstan-shim": "^0.11", 133 | "phpunit/phpunit": "^7.0" 134 | }, 135 | "type": "library", 136 | "extra": { 137 | "branch-alias": { 138 | "dev-master": "1.2.x-dev" 139 | } 140 | }, 141 | "autoload": { 142 | "psr-4": { 143 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 144 | } 145 | }, 146 | "notification-url": "https://packagist.org/downloads/", 147 | "license": [ 148 | "MIT" 149 | ], 150 | "authors": [ 151 | { 152 | "name": "Marco Pivetta", 153 | "email": "ocramius@gmail.com", 154 | "homepage": "http://ocramius.github.com/" 155 | } 156 | ], 157 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 158 | "homepage": "https://www.doctrine-project.org/projects/instantiator.html", 159 | "keywords": [ 160 | "constructor", 161 | "instantiate" 162 | ], 163 | "time": "2019-03-17T17:37:11+00:00" 164 | }, 165 | { 166 | "name": "myclabs/deep-copy", 167 | "version": "1.9.3", 168 | "source": { 169 | "type": "git", 170 | "url": "https://github.com/myclabs/DeepCopy.git", 171 | "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea" 172 | }, 173 | "dist": { 174 | "type": "zip", 175 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/007c053ae6f31bba39dfa19a7726f56e9763bbea", 176 | "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea", 177 | "shasum": "" 178 | }, 179 | "require": { 180 | "php": "^7.1" 181 | }, 182 | "replace": { 183 | "myclabs/deep-copy": "self.version" 184 | }, 185 | "require-dev": { 186 | "doctrine/collections": "^1.0", 187 | "doctrine/common": "^2.6", 188 | "phpunit/phpunit": "^7.1" 189 | }, 190 | "type": "library", 191 | "autoload": { 192 | "psr-4": { 193 | "DeepCopy\\": "src/DeepCopy/" 194 | }, 195 | "files": [ 196 | "src/DeepCopy/deep_copy.php" 197 | ] 198 | }, 199 | "notification-url": "https://packagist.org/downloads/", 200 | "license": [ 201 | "MIT" 202 | ], 203 | "description": "Create deep copies (clones) of your objects", 204 | "keywords": [ 205 | "clone", 206 | "copy", 207 | "duplicate", 208 | "object", 209 | "object graph" 210 | ], 211 | "time": "2019-08-09T12:45:53+00:00" 212 | }, 213 | { 214 | "name": "phar-io/manifest", 215 | "version": "1.0.3", 216 | "source": { 217 | "type": "git", 218 | "url": "https://github.com/phar-io/manifest.git", 219 | "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" 220 | }, 221 | "dist": { 222 | "type": "zip", 223 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", 224 | "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", 225 | "shasum": "" 226 | }, 227 | "require": { 228 | "ext-dom": "*", 229 | "ext-phar": "*", 230 | "phar-io/version": "^2.0", 231 | "php": "^5.6 || ^7.0" 232 | }, 233 | "type": "library", 234 | "extra": { 235 | "branch-alias": { 236 | "dev-master": "1.0.x-dev" 237 | } 238 | }, 239 | "autoload": { 240 | "classmap": [ 241 | "src/" 242 | ] 243 | }, 244 | "notification-url": "https://packagist.org/downloads/", 245 | "license": [ 246 | "BSD-3-Clause" 247 | ], 248 | "authors": [ 249 | { 250 | "name": "Arne Blankerts", 251 | "role": "Developer", 252 | "email": "arne@blankerts.de" 253 | }, 254 | { 255 | "name": "Sebastian Heuer", 256 | "role": "Developer", 257 | "email": "sebastian@phpeople.de" 258 | }, 259 | { 260 | "name": "Sebastian Bergmann", 261 | "role": "Developer", 262 | "email": "sebastian@phpunit.de" 263 | } 264 | ], 265 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", 266 | "time": "2018-07-08T19:23:20+00:00" 267 | }, 268 | { 269 | "name": "phar-io/version", 270 | "version": "2.0.1", 271 | "source": { 272 | "type": "git", 273 | "url": "https://github.com/phar-io/version.git", 274 | "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" 275 | }, 276 | "dist": { 277 | "type": "zip", 278 | "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", 279 | "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", 280 | "shasum": "" 281 | }, 282 | "require": { 283 | "php": "^5.6 || ^7.0" 284 | }, 285 | "type": "library", 286 | "autoload": { 287 | "classmap": [ 288 | "src/" 289 | ] 290 | }, 291 | "notification-url": "https://packagist.org/downloads/", 292 | "license": [ 293 | "BSD-3-Clause" 294 | ], 295 | "authors": [ 296 | { 297 | "name": "Arne Blankerts", 298 | "role": "Developer", 299 | "email": "arne@blankerts.de" 300 | }, 301 | { 302 | "name": "Sebastian Heuer", 303 | "role": "Developer", 304 | "email": "sebastian@phpeople.de" 305 | }, 306 | { 307 | "name": "Sebastian Bergmann", 308 | "role": "Developer", 309 | "email": "sebastian@phpunit.de" 310 | } 311 | ], 312 | "description": "Library for handling version information and constraints", 313 | "time": "2018-07-08T19:19:57+00:00" 314 | }, 315 | { 316 | "name": "phpcompatibility/php-compatibility", 317 | "version": "9.2.0", 318 | "source": { 319 | "type": "git", 320 | "url": "https://github.com/PHPCompatibility/PHPCompatibility.git", 321 | "reference": "3db1bf1e28123fd574a4ae2e9a84072826d51b5e" 322 | }, 323 | "dist": { 324 | "type": "zip", 325 | "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/3db1bf1e28123fd574a4ae2e9a84072826d51b5e", 326 | "reference": "3db1bf1e28123fd574a4ae2e9a84072826d51b5e", 327 | "shasum": "" 328 | }, 329 | "require": { 330 | "php": ">=5.3", 331 | "squizlabs/php_codesniffer": "^2.3 || ^3.0.2" 332 | }, 333 | "conflict": { 334 | "squizlabs/php_codesniffer": "2.6.2" 335 | }, 336 | "require-dev": { 337 | "phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0" 338 | }, 339 | "suggest": { 340 | "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.", 341 | "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." 342 | }, 343 | "type": "phpcodesniffer-standard", 344 | "notification-url": "https://packagist.org/downloads/", 345 | "license": [ 346 | "LGPL-3.0-or-later" 347 | ], 348 | "authors": [ 349 | { 350 | "name": "Contributors", 351 | "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors" 352 | }, 353 | { 354 | "name": "Wim Godden", 355 | "role": "lead", 356 | "homepage": "https://github.com/wimg" 357 | }, 358 | { 359 | "name": "Juliette Reinders Folmer", 360 | "role": "lead", 361 | "homepage": "https://github.com/jrfnl" 362 | } 363 | ], 364 | "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.", 365 | "homepage": "http://techblog.wimgodden.be/tag/codesniffer/", 366 | "keywords": [ 367 | "compatibility", 368 | "phpcs", 369 | "standards" 370 | ], 371 | "time": "2019-06-27T19:58:56+00:00" 372 | }, 373 | { 374 | "name": "phpcompatibility/phpcompatibility-paragonie", 375 | "version": "1.0.1", 376 | "source": { 377 | "type": "git", 378 | "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git", 379 | "reference": "9160de79fcd683b5c99e9c4133728d91529753ea" 380 | }, 381 | "dist": { 382 | "type": "zip", 383 | "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/9160de79fcd683b5c99e9c4133728d91529753ea", 384 | "reference": "9160de79fcd683b5c99e9c4133728d91529753ea", 385 | "shasum": "" 386 | }, 387 | "require": { 388 | "phpcompatibility/php-compatibility": "^9.0" 389 | }, 390 | "require-dev": { 391 | "dealerdirect/phpcodesniffer-composer-installer": "^0.4.4" 392 | }, 393 | "suggest": { 394 | "dealerdirect/phpcodesniffer-composer-installer": "^0.4.4 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", 395 | "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." 396 | }, 397 | "type": "phpcodesniffer-standard", 398 | "notification-url": "https://packagist.org/downloads/", 399 | "license": [ 400 | "LGPL-3.0-or-later" 401 | ], 402 | "authors": [ 403 | { 404 | "name": "Wim Godden", 405 | "role": "lead" 406 | }, 407 | { 408 | "name": "Juliette Reinders Folmer", 409 | "role": "lead" 410 | } 411 | ], 412 | "description": "A set of rulesets for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by the Paragonie polyfill libraries.", 413 | "homepage": "http://phpcompatibility.com/", 414 | "keywords": [ 415 | "compatibility", 416 | "paragonie", 417 | "phpcs", 418 | "polyfill", 419 | "standards" 420 | ], 421 | "time": "2018-12-16T19:10:44+00:00" 422 | }, 423 | { 424 | "name": "phpcompatibility/phpcompatibility-wp", 425 | "version": "2.0.0", 426 | "source": { 427 | "type": "git", 428 | "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git", 429 | "reference": "cb303f0067cd5b366a41d4fb0e254fb40ff02efd" 430 | }, 431 | "dist": { 432 | "type": "zip", 433 | "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/cb303f0067cd5b366a41d4fb0e254fb40ff02efd", 434 | "reference": "cb303f0067cd5b366a41d4fb0e254fb40ff02efd", 435 | "shasum": "" 436 | }, 437 | "require": { 438 | "phpcompatibility/php-compatibility": "^9.0", 439 | "phpcompatibility/phpcompatibility-paragonie": "^1.0" 440 | }, 441 | "require-dev": { 442 | "dealerdirect/phpcodesniffer-composer-installer": "^0.4.3" 443 | }, 444 | "suggest": { 445 | "dealerdirect/phpcodesniffer-composer-installer": "^0.4.3 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", 446 | "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." 447 | }, 448 | "type": "phpcodesniffer-standard", 449 | "notification-url": "https://packagist.org/downloads/", 450 | "license": [ 451 | "LGPL-3.0-or-later" 452 | ], 453 | "authors": [ 454 | { 455 | "name": "Wim Godden", 456 | "role": "lead" 457 | }, 458 | { 459 | "name": "Juliette Reinders Folmer", 460 | "role": "lead" 461 | } 462 | ], 463 | "description": "A ruleset for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by WordPress.", 464 | "homepage": "http://phpcompatibility.com/", 465 | "keywords": [ 466 | "compatibility", 467 | "phpcs", 468 | "standards", 469 | "wordpress" 470 | ], 471 | "time": "2018-10-07T18:31:37+00:00" 472 | }, 473 | { 474 | "name": "phpdocumentor/reflection-common", 475 | "version": "1.0.1", 476 | "source": { 477 | "type": "git", 478 | "url": "https://github.com/phpDocumentor/ReflectionCommon.git", 479 | "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" 480 | }, 481 | "dist": { 482 | "type": "zip", 483 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", 484 | "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", 485 | "shasum": "" 486 | }, 487 | "require": { 488 | "php": ">=5.5" 489 | }, 490 | "require-dev": { 491 | "phpunit/phpunit": "^4.6" 492 | }, 493 | "type": "library", 494 | "extra": { 495 | "branch-alias": { 496 | "dev-master": "1.0.x-dev" 497 | } 498 | }, 499 | "autoload": { 500 | "psr-4": { 501 | "phpDocumentor\\Reflection\\": [ 502 | "src" 503 | ] 504 | } 505 | }, 506 | "notification-url": "https://packagist.org/downloads/", 507 | "license": [ 508 | "MIT" 509 | ], 510 | "authors": [ 511 | { 512 | "name": "Jaap van Otterdijk", 513 | "email": "opensource@ijaap.nl" 514 | } 515 | ], 516 | "description": "Common reflection classes used by phpdocumentor to reflect the code structure", 517 | "homepage": "http://www.phpdoc.org", 518 | "keywords": [ 519 | "FQSEN", 520 | "phpDocumentor", 521 | "phpdoc", 522 | "reflection", 523 | "static analysis" 524 | ], 525 | "time": "2017-09-11T18:02:19+00:00" 526 | }, 527 | { 528 | "name": "phpdocumentor/reflection-docblock", 529 | "version": "4.3.1", 530 | "source": { 531 | "type": "git", 532 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 533 | "reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c" 534 | }, 535 | "dist": { 536 | "type": "zip", 537 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c", 538 | "reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c", 539 | "shasum": "" 540 | }, 541 | "require": { 542 | "php": "^7.0", 543 | "phpdocumentor/reflection-common": "^1.0.0", 544 | "phpdocumentor/type-resolver": "^0.4.0", 545 | "webmozart/assert": "^1.0" 546 | }, 547 | "require-dev": { 548 | "doctrine/instantiator": "~1.0.5", 549 | "mockery/mockery": "^1.0", 550 | "phpunit/phpunit": "^6.4" 551 | }, 552 | "type": "library", 553 | "extra": { 554 | "branch-alias": { 555 | "dev-master": "4.x-dev" 556 | } 557 | }, 558 | "autoload": { 559 | "psr-4": { 560 | "phpDocumentor\\Reflection\\": [ 561 | "src/" 562 | ] 563 | } 564 | }, 565 | "notification-url": "https://packagist.org/downloads/", 566 | "license": [ 567 | "MIT" 568 | ], 569 | "authors": [ 570 | { 571 | "name": "Mike van Riel", 572 | "email": "me@mikevanriel.com" 573 | } 574 | ], 575 | "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", 576 | "time": "2019-04-30T17:48:53+00:00" 577 | }, 578 | { 579 | "name": "phpdocumentor/type-resolver", 580 | "version": "0.4.0", 581 | "source": { 582 | "type": "git", 583 | "url": "https://github.com/phpDocumentor/TypeResolver.git", 584 | "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" 585 | }, 586 | "dist": { 587 | "type": "zip", 588 | "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", 589 | "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", 590 | "shasum": "" 591 | }, 592 | "require": { 593 | "php": "^5.5 || ^7.0", 594 | "phpdocumentor/reflection-common": "^1.0" 595 | }, 596 | "require-dev": { 597 | "mockery/mockery": "^0.9.4", 598 | "phpunit/phpunit": "^5.2||^4.8.24" 599 | }, 600 | "type": "library", 601 | "extra": { 602 | "branch-alias": { 603 | "dev-master": "1.0.x-dev" 604 | } 605 | }, 606 | "autoload": { 607 | "psr-4": { 608 | "phpDocumentor\\Reflection\\": [ 609 | "src/" 610 | ] 611 | } 612 | }, 613 | "notification-url": "https://packagist.org/downloads/", 614 | "license": [ 615 | "MIT" 616 | ], 617 | "authors": [ 618 | { 619 | "name": "Mike van Riel", 620 | "email": "me@mikevanriel.com" 621 | } 622 | ], 623 | "time": "2017-07-14T14:27:02+00:00" 624 | }, 625 | { 626 | "name": "phpspec/prophecy", 627 | "version": "1.8.1", 628 | "source": { 629 | "type": "git", 630 | "url": "https://github.com/phpspec/prophecy.git", 631 | "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76" 632 | }, 633 | "dist": { 634 | "type": "zip", 635 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/1927e75f4ed19131ec9bcc3b002e07fb1173ee76", 636 | "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76", 637 | "shasum": "" 638 | }, 639 | "require": { 640 | "doctrine/instantiator": "^1.0.2", 641 | "php": "^5.3|^7.0", 642 | "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", 643 | "sebastian/comparator": "^1.1|^2.0|^3.0", 644 | "sebastian/recursion-context": "^1.0|^2.0|^3.0" 645 | }, 646 | "require-dev": { 647 | "phpspec/phpspec": "^2.5|^3.2", 648 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" 649 | }, 650 | "type": "library", 651 | "extra": { 652 | "branch-alias": { 653 | "dev-master": "1.8.x-dev" 654 | } 655 | }, 656 | "autoload": { 657 | "psr-4": { 658 | "Prophecy\\": "src/Prophecy" 659 | } 660 | }, 661 | "notification-url": "https://packagist.org/downloads/", 662 | "license": [ 663 | "MIT" 664 | ], 665 | "authors": [ 666 | { 667 | "name": "Konstantin Kudryashov", 668 | "email": "ever.zet@gmail.com", 669 | "homepage": "http://everzet.com" 670 | }, 671 | { 672 | "name": "Marcello Duarte", 673 | "email": "marcello.duarte@gmail.com" 674 | } 675 | ], 676 | "description": "Highly opinionated mocking framework for PHP 5.3+", 677 | "homepage": "https://github.com/phpspec/prophecy", 678 | "keywords": [ 679 | "Double", 680 | "Dummy", 681 | "fake", 682 | "mock", 683 | "spy", 684 | "stub" 685 | ], 686 | "time": "2019-06-13T12:50:23+00:00" 687 | }, 688 | { 689 | "name": "phpunit/php-code-coverage", 690 | "version": "6.1.4", 691 | "source": { 692 | "type": "git", 693 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 694 | "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d" 695 | }, 696 | "dist": { 697 | "type": "zip", 698 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", 699 | "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", 700 | "shasum": "" 701 | }, 702 | "require": { 703 | "ext-dom": "*", 704 | "ext-xmlwriter": "*", 705 | "php": "^7.1", 706 | "phpunit/php-file-iterator": "^2.0", 707 | "phpunit/php-text-template": "^1.2.1", 708 | "phpunit/php-token-stream": "^3.0", 709 | "sebastian/code-unit-reverse-lookup": "^1.0.1", 710 | "sebastian/environment": "^3.1 || ^4.0", 711 | "sebastian/version": "^2.0.1", 712 | "theseer/tokenizer": "^1.1" 713 | }, 714 | "require-dev": { 715 | "phpunit/phpunit": "^7.0" 716 | }, 717 | "suggest": { 718 | "ext-xdebug": "^2.6.0" 719 | }, 720 | "type": "library", 721 | "extra": { 722 | "branch-alias": { 723 | "dev-master": "6.1-dev" 724 | } 725 | }, 726 | "autoload": { 727 | "classmap": [ 728 | "src/" 729 | ] 730 | }, 731 | "notification-url": "https://packagist.org/downloads/", 732 | "license": [ 733 | "BSD-3-Clause" 734 | ], 735 | "authors": [ 736 | { 737 | "name": "Sebastian Bergmann", 738 | "role": "lead", 739 | "email": "sebastian@phpunit.de" 740 | } 741 | ], 742 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 743 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 744 | "keywords": [ 745 | "coverage", 746 | "testing", 747 | "xunit" 748 | ], 749 | "time": "2018-10-31T16:06:48+00:00" 750 | }, 751 | { 752 | "name": "phpunit/php-file-iterator", 753 | "version": "2.0.2", 754 | "source": { 755 | "type": "git", 756 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 757 | "reference": "050bedf145a257b1ff02746c31894800e5122946" 758 | }, 759 | "dist": { 760 | "type": "zip", 761 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", 762 | "reference": "050bedf145a257b1ff02746c31894800e5122946", 763 | "shasum": "" 764 | }, 765 | "require": { 766 | "php": "^7.1" 767 | }, 768 | "require-dev": { 769 | "phpunit/phpunit": "^7.1" 770 | }, 771 | "type": "library", 772 | "extra": { 773 | "branch-alias": { 774 | "dev-master": "2.0.x-dev" 775 | } 776 | }, 777 | "autoload": { 778 | "classmap": [ 779 | "src/" 780 | ] 781 | }, 782 | "notification-url": "https://packagist.org/downloads/", 783 | "license": [ 784 | "BSD-3-Clause" 785 | ], 786 | "authors": [ 787 | { 788 | "name": "Sebastian Bergmann", 789 | "role": "lead", 790 | "email": "sebastian@phpunit.de" 791 | } 792 | ], 793 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 794 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 795 | "keywords": [ 796 | "filesystem", 797 | "iterator" 798 | ], 799 | "time": "2018-09-13T20:33:42+00:00" 800 | }, 801 | { 802 | "name": "phpunit/php-text-template", 803 | "version": "1.2.1", 804 | "source": { 805 | "type": "git", 806 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 807 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 808 | }, 809 | "dist": { 810 | "type": "zip", 811 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 812 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 813 | "shasum": "" 814 | }, 815 | "require": { 816 | "php": ">=5.3.3" 817 | }, 818 | "type": "library", 819 | "autoload": { 820 | "classmap": [ 821 | "src/" 822 | ] 823 | }, 824 | "notification-url": "https://packagist.org/downloads/", 825 | "license": [ 826 | "BSD-3-Clause" 827 | ], 828 | "authors": [ 829 | { 830 | "name": "Sebastian Bergmann", 831 | "role": "lead", 832 | "email": "sebastian@phpunit.de" 833 | } 834 | ], 835 | "description": "Simple template engine.", 836 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 837 | "keywords": [ 838 | "template" 839 | ], 840 | "time": "2015-06-21T13:50:34+00:00" 841 | }, 842 | { 843 | "name": "phpunit/php-timer", 844 | "version": "2.1.2", 845 | "source": { 846 | "type": "git", 847 | "url": "https://github.com/sebastianbergmann/php-timer.git", 848 | "reference": "1038454804406b0b5f5f520358e78c1c2f71501e" 849 | }, 850 | "dist": { 851 | "type": "zip", 852 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e", 853 | "reference": "1038454804406b0b5f5f520358e78c1c2f71501e", 854 | "shasum": "" 855 | }, 856 | "require": { 857 | "php": "^7.1" 858 | }, 859 | "require-dev": { 860 | "phpunit/phpunit": "^7.0" 861 | }, 862 | "type": "library", 863 | "extra": { 864 | "branch-alias": { 865 | "dev-master": "2.1-dev" 866 | } 867 | }, 868 | "autoload": { 869 | "classmap": [ 870 | "src/" 871 | ] 872 | }, 873 | "notification-url": "https://packagist.org/downloads/", 874 | "license": [ 875 | "BSD-3-Clause" 876 | ], 877 | "authors": [ 878 | { 879 | "name": "Sebastian Bergmann", 880 | "role": "lead", 881 | "email": "sebastian@phpunit.de" 882 | } 883 | ], 884 | "description": "Utility class for timing", 885 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 886 | "keywords": [ 887 | "timer" 888 | ], 889 | "time": "2019-06-07T04:22:29+00:00" 890 | }, 891 | { 892 | "name": "phpunit/php-token-stream", 893 | "version": "3.1.0", 894 | "source": { 895 | "type": "git", 896 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 897 | "reference": "e899757bb3df5ff6e95089132f32cd59aac2220a" 898 | }, 899 | "dist": { 900 | "type": "zip", 901 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e899757bb3df5ff6e95089132f32cd59aac2220a", 902 | "reference": "e899757bb3df5ff6e95089132f32cd59aac2220a", 903 | "shasum": "" 904 | }, 905 | "require": { 906 | "ext-tokenizer": "*", 907 | "php": "^7.1" 908 | }, 909 | "require-dev": { 910 | "phpunit/phpunit": "^7.0" 911 | }, 912 | "type": "library", 913 | "extra": { 914 | "branch-alias": { 915 | "dev-master": "3.1-dev" 916 | } 917 | }, 918 | "autoload": { 919 | "classmap": [ 920 | "src/" 921 | ] 922 | }, 923 | "notification-url": "https://packagist.org/downloads/", 924 | "license": [ 925 | "BSD-3-Clause" 926 | ], 927 | "authors": [ 928 | { 929 | "name": "Sebastian Bergmann", 930 | "email": "sebastian@phpunit.de" 931 | } 932 | ], 933 | "description": "Wrapper around PHP's tokenizer extension.", 934 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 935 | "keywords": [ 936 | "tokenizer" 937 | ], 938 | "time": "2019-07-25T05:29:42+00:00" 939 | }, 940 | { 941 | "name": "phpunit/phpunit", 942 | "version": "7.5.15", 943 | "source": { 944 | "type": "git", 945 | "url": "https://github.com/sebastianbergmann/phpunit.git", 946 | "reference": "d79c053d972856b8b941bb233e39dc521a5093f0" 947 | }, 948 | "dist": { 949 | "type": "zip", 950 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d79c053d972856b8b941bb233e39dc521a5093f0", 951 | "reference": "d79c053d972856b8b941bb233e39dc521a5093f0", 952 | "shasum": "" 953 | }, 954 | "require": { 955 | "doctrine/instantiator": "^1.1", 956 | "ext-dom": "*", 957 | "ext-json": "*", 958 | "ext-libxml": "*", 959 | "ext-mbstring": "*", 960 | "ext-xml": "*", 961 | "myclabs/deep-copy": "^1.7", 962 | "phar-io/manifest": "^1.0.2", 963 | "phar-io/version": "^2.0", 964 | "php": "^7.1", 965 | "phpspec/prophecy": "^1.7", 966 | "phpunit/php-code-coverage": "^6.0.7", 967 | "phpunit/php-file-iterator": "^2.0.1", 968 | "phpunit/php-text-template": "^1.2.1", 969 | "phpunit/php-timer": "^2.1", 970 | "sebastian/comparator": "^3.0", 971 | "sebastian/diff": "^3.0", 972 | "sebastian/environment": "^4.0", 973 | "sebastian/exporter": "^3.1", 974 | "sebastian/global-state": "^2.0", 975 | "sebastian/object-enumerator": "^3.0.3", 976 | "sebastian/resource-operations": "^2.0", 977 | "sebastian/version": "^2.0.1" 978 | }, 979 | "conflict": { 980 | "phpunit/phpunit-mock-objects": "*" 981 | }, 982 | "require-dev": { 983 | "ext-pdo": "*" 984 | }, 985 | "suggest": { 986 | "ext-soap": "*", 987 | "ext-xdebug": "*", 988 | "phpunit/php-invoker": "^2.0" 989 | }, 990 | "bin": [ 991 | "phpunit" 992 | ], 993 | "type": "library", 994 | "extra": { 995 | "branch-alias": { 996 | "dev-master": "7.5-dev" 997 | } 998 | }, 999 | "autoload": { 1000 | "classmap": [ 1001 | "src/" 1002 | ] 1003 | }, 1004 | "notification-url": "https://packagist.org/downloads/", 1005 | "license": [ 1006 | "BSD-3-Clause" 1007 | ], 1008 | "authors": [ 1009 | { 1010 | "name": "Sebastian Bergmann", 1011 | "role": "lead", 1012 | "email": "sebastian@phpunit.de" 1013 | } 1014 | ], 1015 | "description": "The PHP Unit Testing framework.", 1016 | "homepage": "https://phpunit.de/", 1017 | "keywords": [ 1018 | "phpunit", 1019 | "testing", 1020 | "xunit" 1021 | ], 1022 | "time": "2019-08-21T07:05:16+00:00" 1023 | }, 1024 | { 1025 | "name": "sebastian/code-unit-reverse-lookup", 1026 | "version": "1.0.1", 1027 | "source": { 1028 | "type": "git", 1029 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 1030 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" 1031 | }, 1032 | "dist": { 1033 | "type": "zip", 1034 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", 1035 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", 1036 | "shasum": "" 1037 | }, 1038 | "require": { 1039 | "php": "^5.6 || ^7.0" 1040 | }, 1041 | "require-dev": { 1042 | "phpunit/phpunit": "^5.7 || ^6.0" 1043 | }, 1044 | "type": "library", 1045 | "extra": { 1046 | "branch-alias": { 1047 | "dev-master": "1.0.x-dev" 1048 | } 1049 | }, 1050 | "autoload": { 1051 | "classmap": [ 1052 | "src/" 1053 | ] 1054 | }, 1055 | "notification-url": "https://packagist.org/downloads/", 1056 | "license": [ 1057 | "BSD-3-Clause" 1058 | ], 1059 | "authors": [ 1060 | { 1061 | "name": "Sebastian Bergmann", 1062 | "email": "sebastian@phpunit.de" 1063 | } 1064 | ], 1065 | "description": "Looks up which function or method a line of code belongs to", 1066 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 1067 | "time": "2017-03-04T06:30:41+00:00" 1068 | }, 1069 | { 1070 | "name": "sebastian/comparator", 1071 | "version": "3.0.2", 1072 | "source": { 1073 | "type": "git", 1074 | "url": "https://github.com/sebastianbergmann/comparator.git", 1075 | "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" 1076 | }, 1077 | "dist": { 1078 | "type": "zip", 1079 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", 1080 | "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", 1081 | "shasum": "" 1082 | }, 1083 | "require": { 1084 | "php": "^7.1", 1085 | "sebastian/diff": "^3.0", 1086 | "sebastian/exporter": "^3.1" 1087 | }, 1088 | "require-dev": { 1089 | "phpunit/phpunit": "^7.1" 1090 | }, 1091 | "type": "library", 1092 | "extra": { 1093 | "branch-alias": { 1094 | "dev-master": "3.0-dev" 1095 | } 1096 | }, 1097 | "autoload": { 1098 | "classmap": [ 1099 | "src/" 1100 | ] 1101 | }, 1102 | "notification-url": "https://packagist.org/downloads/", 1103 | "license": [ 1104 | "BSD-3-Clause" 1105 | ], 1106 | "authors": [ 1107 | { 1108 | "name": "Jeff Welch", 1109 | "email": "whatthejeff@gmail.com" 1110 | }, 1111 | { 1112 | "name": "Volker Dusch", 1113 | "email": "github@wallbash.com" 1114 | }, 1115 | { 1116 | "name": "Bernhard Schussek", 1117 | "email": "bschussek@2bepublished.at" 1118 | }, 1119 | { 1120 | "name": "Sebastian Bergmann", 1121 | "email": "sebastian@phpunit.de" 1122 | } 1123 | ], 1124 | "description": "Provides the functionality to compare PHP values for equality", 1125 | "homepage": "https://github.com/sebastianbergmann/comparator", 1126 | "keywords": [ 1127 | "comparator", 1128 | "compare", 1129 | "equality" 1130 | ], 1131 | "time": "2018-07-12T15:12:46+00:00" 1132 | }, 1133 | { 1134 | "name": "sebastian/diff", 1135 | "version": "3.0.2", 1136 | "source": { 1137 | "type": "git", 1138 | "url": "https://github.com/sebastianbergmann/diff.git", 1139 | "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" 1140 | }, 1141 | "dist": { 1142 | "type": "zip", 1143 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", 1144 | "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", 1145 | "shasum": "" 1146 | }, 1147 | "require": { 1148 | "php": "^7.1" 1149 | }, 1150 | "require-dev": { 1151 | "phpunit/phpunit": "^7.5 || ^8.0", 1152 | "symfony/process": "^2 || ^3.3 || ^4" 1153 | }, 1154 | "type": "library", 1155 | "extra": { 1156 | "branch-alias": { 1157 | "dev-master": "3.0-dev" 1158 | } 1159 | }, 1160 | "autoload": { 1161 | "classmap": [ 1162 | "src/" 1163 | ] 1164 | }, 1165 | "notification-url": "https://packagist.org/downloads/", 1166 | "license": [ 1167 | "BSD-3-Clause" 1168 | ], 1169 | "authors": [ 1170 | { 1171 | "name": "Kore Nordmann", 1172 | "email": "mail@kore-nordmann.de" 1173 | }, 1174 | { 1175 | "name": "Sebastian Bergmann", 1176 | "email": "sebastian@phpunit.de" 1177 | } 1178 | ], 1179 | "description": "Diff implementation", 1180 | "homepage": "https://github.com/sebastianbergmann/diff", 1181 | "keywords": [ 1182 | "diff", 1183 | "udiff", 1184 | "unidiff", 1185 | "unified diff" 1186 | ], 1187 | "time": "2019-02-04T06:01:07+00:00" 1188 | }, 1189 | { 1190 | "name": "sebastian/environment", 1191 | "version": "4.2.2", 1192 | "source": { 1193 | "type": "git", 1194 | "url": "https://github.com/sebastianbergmann/environment.git", 1195 | "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404" 1196 | }, 1197 | "dist": { 1198 | "type": "zip", 1199 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/f2a2c8e1c97c11ace607a7a667d73d47c19fe404", 1200 | "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404", 1201 | "shasum": "" 1202 | }, 1203 | "require": { 1204 | "php": "^7.1" 1205 | }, 1206 | "require-dev": { 1207 | "phpunit/phpunit": "^7.5" 1208 | }, 1209 | "suggest": { 1210 | "ext-posix": "*" 1211 | }, 1212 | "type": "library", 1213 | "extra": { 1214 | "branch-alias": { 1215 | "dev-master": "4.2-dev" 1216 | } 1217 | }, 1218 | "autoload": { 1219 | "classmap": [ 1220 | "src/" 1221 | ] 1222 | }, 1223 | "notification-url": "https://packagist.org/downloads/", 1224 | "license": [ 1225 | "BSD-3-Clause" 1226 | ], 1227 | "authors": [ 1228 | { 1229 | "name": "Sebastian Bergmann", 1230 | "email": "sebastian@phpunit.de" 1231 | } 1232 | ], 1233 | "description": "Provides functionality to handle HHVM/PHP environments", 1234 | "homepage": "http://www.github.com/sebastianbergmann/environment", 1235 | "keywords": [ 1236 | "Xdebug", 1237 | "environment", 1238 | "hhvm" 1239 | ], 1240 | "time": "2019-05-05T09:05:15+00:00" 1241 | }, 1242 | { 1243 | "name": "sebastian/exporter", 1244 | "version": "3.1.1", 1245 | "source": { 1246 | "type": "git", 1247 | "url": "https://github.com/sebastianbergmann/exporter.git", 1248 | "reference": "06a9a5947f47b3029d76118eb5c22802e5869687" 1249 | }, 1250 | "dist": { 1251 | "type": "zip", 1252 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/06a9a5947f47b3029d76118eb5c22802e5869687", 1253 | "reference": "06a9a5947f47b3029d76118eb5c22802e5869687", 1254 | "shasum": "" 1255 | }, 1256 | "require": { 1257 | "php": "^7.0", 1258 | "sebastian/recursion-context": "^3.0" 1259 | }, 1260 | "require-dev": { 1261 | "ext-mbstring": "*", 1262 | "phpunit/phpunit": "^6.0" 1263 | }, 1264 | "type": "library", 1265 | "extra": { 1266 | "branch-alias": { 1267 | "dev-master": "3.1.x-dev" 1268 | } 1269 | }, 1270 | "autoload": { 1271 | "classmap": [ 1272 | "src/" 1273 | ] 1274 | }, 1275 | "notification-url": "https://packagist.org/downloads/", 1276 | "license": [ 1277 | "BSD-3-Clause" 1278 | ], 1279 | "authors": [ 1280 | { 1281 | "name": "Sebastian Bergmann", 1282 | "email": "sebastian@phpunit.de" 1283 | }, 1284 | { 1285 | "name": "Jeff Welch", 1286 | "email": "whatthejeff@gmail.com" 1287 | }, 1288 | { 1289 | "name": "Volker Dusch", 1290 | "email": "github@wallbash.com" 1291 | }, 1292 | { 1293 | "name": "Adam Harvey", 1294 | "email": "aharvey@php.net" 1295 | }, 1296 | { 1297 | "name": "Bernhard Schussek", 1298 | "email": "bschussek@gmail.com" 1299 | } 1300 | ], 1301 | "description": "Provides the functionality to export PHP variables for visualization", 1302 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 1303 | "keywords": [ 1304 | "export", 1305 | "exporter" 1306 | ], 1307 | "time": "2019-08-11T12:43:14+00:00" 1308 | }, 1309 | { 1310 | "name": "sebastian/global-state", 1311 | "version": "2.0.0", 1312 | "source": { 1313 | "type": "git", 1314 | "url": "https://github.com/sebastianbergmann/global-state.git", 1315 | "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" 1316 | }, 1317 | "dist": { 1318 | "type": "zip", 1319 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", 1320 | "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", 1321 | "shasum": "" 1322 | }, 1323 | "require": { 1324 | "php": "^7.0" 1325 | }, 1326 | "require-dev": { 1327 | "phpunit/phpunit": "^6.0" 1328 | }, 1329 | "suggest": { 1330 | "ext-uopz": "*" 1331 | }, 1332 | "type": "library", 1333 | "extra": { 1334 | "branch-alias": { 1335 | "dev-master": "2.0-dev" 1336 | } 1337 | }, 1338 | "autoload": { 1339 | "classmap": [ 1340 | "src/" 1341 | ] 1342 | }, 1343 | "notification-url": "https://packagist.org/downloads/", 1344 | "license": [ 1345 | "BSD-3-Clause" 1346 | ], 1347 | "authors": [ 1348 | { 1349 | "name": "Sebastian Bergmann", 1350 | "email": "sebastian@phpunit.de" 1351 | } 1352 | ], 1353 | "description": "Snapshotting of global state", 1354 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1355 | "keywords": [ 1356 | "global state" 1357 | ], 1358 | "time": "2017-04-27T15:39:26+00:00" 1359 | }, 1360 | { 1361 | "name": "sebastian/object-enumerator", 1362 | "version": "3.0.3", 1363 | "source": { 1364 | "type": "git", 1365 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 1366 | "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" 1367 | }, 1368 | "dist": { 1369 | "type": "zip", 1370 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", 1371 | "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", 1372 | "shasum": "" 1373 | }, 1374 | "require": { 1375 | "php": "^7.0", 1376 | "sebastian/object-reflector": "^1.1.1", 1377 | "sebastian/recursion-context": "^3.0" 1378 | }, 1379 | "require-dev": { 1380 | "phpunit/phpunit": "^6.0" 1381 | }, 1382 | "type": "library", 1383 | "extra": { 1384 | "branch-alias": { 1385 | "dev-master": "3.0.x-dev" 1386 | } 1387 | }, 1388 | "autoload": { 1389 | "classmap": [ 1390 | "src/" 1391 | ] 1392 | }, 1393 | "notification-url": "https://packagist.org/downloads/", 1394 | "license": [ 1395 | "BSD-3-Clause" 1396 | ], 1397 | "authors": [ 1398 | { 1399 | "name": "Sebastian Bergmann", 1400 | "email": "sebastian@phpunit.de" 1401 | } 1402 | ], 1403 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 1404 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 1405 | "time": "2017-08-03T12:35:26+00:00" 1406 | }, 1407 | { 1408 | "name": "sebastian/object-reflector", 1409 | "version": "1.1.1", 1410 | "source": { 1411 | "type": "git", 1412 | "url": "https://github.com/sebastianbergmann/object-reflector.git", 1413 | "reference": "773f97c67f28de00d397be301821b06708fca0be" 1414 | }, 1415 | "dist": { 1416 | "type": "zip", 1417 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", 1418 | "reference": "773f97c67f28de00d397be301821b06708fca0be", 1419 | "shasum": "" 1420 | }, 1421 | "require": { 1422 | "php": "^7.0" 1423 | }, 1424 | "require-dev": { 1425 | "phpunit/phpunit": "^6.0" 1426 | }, 1427 | "type": "library", 1428 | "extra": { 1429 | "branch-alias": { 1430 | "dev-master": "1.1-dev" 1431 | } 1432 | }, 1433 | "autoload": { 1434 | "classmap": [ 1435 | "src/" 1436 | ] 1437 | }, 1438 | "notification-url": "https://packagist.org/downloads/", 1439 | "license": [ 1440 | "BSD-3-Clause" 1441 | ], 1442 | "authors": [ 1443 | { 1444 | "name": "Sebastian Bergmann", 1445 | "email": "sebastian@phpunit.de" 1446 | } 1447 | ], 1448 | "description": "Allows reflection of object attributes, including inherited and non-public ones", 1449 | "homepage": "https://github.com/sebastianbergmann/object-reflector/", 1450 | "time": "2017-03-29T09:07:27+00:00" 1451 | }, 1452 | { 1453 | "name": "sebastian/recursion-context", 1454 | "version": "3.0.0", 1455 | "source": { 1456 | "type": "git", 1457 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1458 | "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" 1459 | }, 1460 | "dist": { 1461 | "type": "zip", 1462 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", 1463 | "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", 1464 | "shasum": "" 1465 | }, 1466 | "require": { 1467 | "php": "^7.0" 1468 | }, 1469 | "require-dev": { 1470 | "phpunit/phpunit": "^6.0" 1471 | }, 1472 | "type": "library", 1473 | "extra": { 1474 | "branch-alias": { 1475 | "dev-master": "3.0.x-dev" 1476 | } 1477 | }, 1478 | "autoload": { 1479 | "classmap": [ 1480 | "src/" 1481 | ] 1482 | }, 1483 | "notification-url": "https://packagist.org/downloads/", 1484 | "license": [ 1485 | "BSD-3-Clause" 1486 | ], 1487 | "authors": [ 1488 | { 1489 | "name": "Jeff Welch", 1490 | "email": "whatthejeff@gmail.com" 1491 | }, 1492 | { 1493 | "name": "Sebastian Bergmann", 1494 | "email": "sebastian@phpunit.de" 1495 | }, 1496 | { 1497 | "name": "Adam Harvey", 1498 | "email": "aharvey@php.net" 1499 | } 1500 | ], 1501 | "description": "Provides functionality to recursively process PHP variables", 1502 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 1503 | "time": "2017-03-03T06:23:57+00:00" 1504 | }, 1505 | { 1506 | "name": "sebastian/resource-operations", 1507 | "version": "2.0.1", 1508 | "source": { 1509 | "type": "git", 1510 | "url": "https://github.com/sebastianbergmann/resource-operations.git", 1511 | "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" 1512 | }, 1513 | "dist": { 1514 | "type": "zip", 1515 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", 1516 | "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", 1517 | "shasum": "" 1518 | }, 1519 | "require": { 1520 | "php": "^7.1" 1521 | }, 1522 | "type": "library", 1523 | "extra": { 1524 | "branch-alias": { 1525 | "dev-master": "2.0-dev" 1526 | } 1527 | }, 1528 | "autoload": { 1529 | "classmap": [ 1530 | "src/" 1531 | ] 1532 | }, 1533 | "notification-url": "https://packagist.org/downloads/", 1534 | "license": [ 1535 | "BSD-3-Clause" 1536 | ], 1537 | "authors": [ 1538 | { 1539 | "name": "Sebastian Bergmann", 1540 | "email": "sebastian@phpunit.de" 1541 | } 1542 | ], 1543 | "description": "Provides a list of PHP built-in functions that operate on resources", 1544 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations", 1545 | "time": "2018-10-04T04:07:39+00:00" 1546 | }, 1547 | { 1548 | "name": "sebastian/version", 1549 | "version": "2.0.1", 1550 | "source": { 1551 | "type": "git", 1552 | "url": "https://github.com/sebastianbergmann/version.git", 1553 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" 1554 | }, 1555 | "dist": { 1556 | "type": "zip", 1557 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", 1558 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", 1559 | "shasum": "" 1560 | }, 1561 | "require": { 1562 | "php": ">=5.6" 1563 | }, 1564 | "type": "library", 1565 | "extra": { 1566 | "branch-alias": { 1567 | "dev-master": "2.0.x-dev" 1568 | } 1569 | }, 1570 | "autoload": { 1571 | "classmap": [ 1572 | "src/" 1573 | ] 1574 | }, 1575 | "notification-url": "https://packagist.org/downloads/", 1576 | "license": [ 1577 | "BSD-3-Clause" 1578 | ], 1579 | "authors": [ 1580 | { 1581 | "name": "Sebastian Bergmann", 1582 | "role": "lead", 1583 | "email": "sebastian@phpunit.de" 1584 | } 1585 | ], 1586 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1587 | "homepage": "https://github.com/sebastianbergmann/version", 1588 | "time": "2016-10-03T07:35:21+00:00" 1589 | }, 1590 | { 1591 | "name": "squizlabs/php_codesniffer", 1592 | "version": "3.4.2", 1593 | "source": { 1594 | "type": "git", 1595 | "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", 1596 | "reference": "b8a7362af1cc1aadb5bd36c3defc4dda2cf5f0a8" 1597 | }, 1598 | "dist": { 1599 | "type": "zip", 1600 | "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/b8a7362af1cc1aadb5bd36c3defc4dda2cf5f0a8", 1601 | "reference": "b8a7362af1cc1aadb5bd36c3defc4dda2cf5f0a8", 1602 | "shasum": "" 1603 | }, 1604 | "require": { 1605 | "ext-simplexml": "*", 1606 | "ext-tokenizer": "*", 1607 | "ext-xmlwriter": "*", 1608 | "php": ">=5.4.0" 1609 | }, 1610 | "require-dev": { 1611 | "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" 1612 | }, 1613 | "bin": [ 1614 | "bin/phpcs", 1615 | "bin/phpcbf" 1616 | ], 1617 | "type": "library", 1618 | "extra": { 1619 | "branch-alias": { 1620 | "dev-master": "3.x-dev" 1621 | } 1622 | }, 1623 | "notification-url": "https://packagist.org/downloads/", 1624 | "license": [ 1625 | "BSD-3-Clause" 1626 | ], 1627 | "authors": [ 1628 | { 1629 | "name": "Greg Sherwood", 1630 | "role": "lead" 1631 | } 1632 | ], 1633 | "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", 1634 | "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", 1635 | "keywords": [ 1636 | "phpcs", 1637 | "standards" 1638 | ], 1639 | "time": "2019-04-10T23:49:02+00:00" 1640 | }, 1641 | { 1642 | "name": "symfony/polyfill-ctype", 1643 | "version": "v1.12.0", 1644 | "source": { 1645 | "type": "git", 1646 | "url": "https://github.com/symfony/polyfill-ctype.git", 1647 | "reference": "550ebaac289296ce228a706d0867afc34687e3f4" 1648 | }, 1649 | "dist": { 1650 | "type": "zip", 1651 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4", 1652 | "reference": "550ebaac289296ce228a706d0867afc34687e3f4", 1653 | "shasum": "" 1654 | }, 1655 | "require": { 1656 | "php": ">=5.3.3" 1657 | }, 1658 | "suggest": { 1659 | "ext-ctype": "For best performance" 1660 | }, 1661 | "type": "library", 1662 | "extra": { 1663 | "branch-alias": { 1664 | "dev-master": "1.12-dev" 1665 | } 1666 | }, 1667 | "autoload": { 1668 | "psr-4": { 1669 | "Symfony\\Polyfill\\Ctype\\": "" 1670 | }, 1671 | "files": [ 1672 | "bootstrap.php" 1673 | ] 1674 | }, 1675 | "notification-url": "https://packagist.org/downloads/", 1676 | "license": [ 1677 | "MIT" 1678 | ], 1679 | "authors": [ 1680 | { 1681 | "name": "Gert de Pagter", 1682 | "email": "BackEndTea@gmail.com" 1683 | }, 1684 | { 1685 | "name": "Symfony Community", 1686 | "homepage": "https://symfony.com/contributors" 1687 | } 1688 | ], 1689 | "description": "Symfony polyfill for ctype functions", 1690 | "homepage": "https://symfony.com", 1691 | "keywords": [ 1692 | "compatibility", 1693 | "ctype", 1694 | "polyfill", 1695 | "portable" 1696 | ], 1697 | "time": "2019-08-06T08:03:45+00:00" 1698 | }, 1699 | { 1700 | "name": "theseer/tokenizer", 1701 | "version": "1.1.3", 1702 | "source": { 1703 | "type": "git", 1704 | "url": "https://github.com/theseer/tokenizer.git", 1705 | "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9" 1706 | }, 1707 | "dist": { 1708 | "type": "zip", 1709 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9", 1710 | "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9", 1711 | "shasum": "" 1712 | }, 1713 | "require": { 1714 | "ext-dom": "*", 1715 | "ext-tokenizer": "*", 1716 | "ext-xmlwriter": "*", 1717 | "php": "^7.0" 1718 | }, 1719 | "type": "library", 1720 | "autoload": { 1721 | "classmap": [ 1722 | "src/" 1723 | ] 1724 | }, 1725 | "notification-url": "https://packagist.org/downloads/", 1726 | "license": [ 1727 | "BSD-3-Clause" 1728 | ], 1729 | "authors": [ 1730 | { 1731 | "name": "Arne Blankerts", 1732 | "role": "Developer", 1733 | "email": "arne@blankerts.de" 1734 | } 1735 | ], 1736 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", 1737 | "time": "2019-06-13T22:48:21+00:00" 1738 | }, 1739 | { 1740 | "name": "webmozart/assert", 1741 | "version": "1.4.0", 1742 | "source": { 1743 | "type": "git", 1744 | "url": "https://github.com/webmozart/assert.git", 1745 | "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9" 1746 | }, 1747 | "dist": { 1748 | "type": "zip", 1749 | "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9", 1750 | "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9", 1751 | "shasum": "" 1752 | }, 1753 | "require": { 1754 | "php": "^5.3.3 || ^7.0", 1755 | "symfony/polyfill-ctype": "^1.8" 1756 | }, 1757 | "require-dev": { 1758 | "phpunit/phpunit": "^4.6", 1759 | "sebastian/version": "^1.0.1" 1760 | }, 1761 | "type": "library", 1762 | "extra": { 1763 | "branch-alias": { 1764 | "dev-master": "1.3-dev" 1765 | } 1766 | }, 1767 | "autoload": { 1768 | "psr-4": { 1769 | "Webmozart\\Assert\\": "src/" 1770 | } 1771 | }, 1772 | "notification-url": "https://packagist.org/downloads/", 1773 | "license": [ 1774 | "MIT" 1775 | ], 1776 | "authors": [ 1777 | { 1778 | "name": "Bernhard Schussek", 1779 | "email": "bschussek@gmail.com" 1780 | } 1781 | ], 1782 | "description": "Assertions to validate method input/output with nice error messages.", 1783 | "keywords": [ 1784 | "assert", 1785 | "check", 1786 | "validate" 1787 | ], 1788 | "time": "2018-12-25T11:19:39+00:00" 1789 | }, 1790 | { 1791 | "name": "wp-coding-standards/wpcs", 1792 | "version": "2.1.1", 1793 | "source": { 1794 | "type": "git", 1795 | "url": "https://github.com/WordPress/WordPress-Coding-Standards.git", 1796 | "reference": "bd9c33152115e6741e3510ff7189605b35167908" 1797 | }, 1798 | "dist": { 1799 | "type": "zip", 1800 | "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/bd9c33152115e6741e3510ff7189605b35167908", 1801 | "reference": "bd9c33152115e6741e3510ff7189605b35167908", 1802 | "shasum": "" 1803 | }, 1804 | "require": { 1805 | "php": ">=5.4", 1806 | "squizlabs/php_codesniffer": "^3.3.1" 1807 | }, 1808 | "require-dev": { 1809 | "dealerdirect/phpcodesniffer-composer-installer": "^0.5.0", 1810 | "phpcompatibility/php-compatibility": "^9.0", 1811 | "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" 1812 | }, 1813 | "suggest": { 1814 | "dealerdirect/phpcodesniffer-composer-installer": "^0.5.0 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically." 1815 | }, 1816 | "type": "phpcodesniffer-standard", 1817 | "notification-url": "https://packagist.org/downloads/", 1818 | "license": [ 1819 | "MIT" 1820 | ], 1821 | "authors": [ 1822 | { 1823 | "name": "Contributors", 1824 | "homepage": "https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards/graphs/contributors" 1825 | } 1826 | ], 1827 | "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions", 1828 | "keywords": [ 1829 | "phpcs", 1830 | "standards", 1831 | "wordpress" 1832 | ], 1833 | "time": "2019-05-21T02:50:00+00:00" 1834 | } 1835 | ], 1836 | "aliases": [], 1837 | "minimum-stability": "stable", 1838 | "stability-flags": { 1839 | "10up/phpcs-composer": 20 1840 | }, 1841 | "prefer-stable": false, 1842 | "prefer-lowest": false, 1843 | "platform": { 1844 | "php": ">=7.0" 1845 | }, 1846 | "platform-dev": [] 1847 | } 1848 | -------------------------------------------------------------------------------- /includes/classes/Command.php: -------------------------------------------------------------------------------- 1 | [ '\WP_CLI', 'success' ], 45 | 'debug' => [ '\WP_CLI', 'debug' ], 46 | 'warning' => [ '\WP_CLI', 'warning' ], 47 | 'notice' => [ '\WP_CLI', 'line' ], 48 | ]; 49 | 50 | $urls_per_page = apply_filters( 'tenup_sitemaps_urls_per_page', 200 ); 51 | 52 | if ( ! array_key_exists( 'range', $assoc_args ) ) { 53 | $assoc_args['range'] = 'all'; 54 | } 55 | 56 | $sitemap = new Sitemap( $assoc_args['range'], $urls_per_page, [], $logger ); 57 | 58 | $sitemap->build(); 59 | $sitemap->write(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /includes/classes/Sitemap.php: -------------------------------------------------------------------------------- 1 | ..., 40 | * 'debug' => ..., 41 | * 'notice' => ..., 42 | * 'warning' => ... 43 | * ] 44 | * 45 | * @var array 46 | * @since 1.2 47 | */ 48 | private $logger; 49 | 50 | /** 51 | * URLs to include in each sitemap page 52 | * 53 | * @var int 54 | * @since 1.2 55 | */ 56 | private $urls_per_page; 57 | 58 | /** 59 | * URLs to process in each DB cycle 60 | * 61 | * @var int 62 | * @since 1.2 63 | */ 64 | private $process_page_size = 500; 65 | 66 | /** 67 | * Create a new empty sitemap 68 | * 69 | * @param string $range Range of content 70 | * @param integer $urls_per_page URLs per sitemap page 71 | * @param array $urls Preexisting URLs to use 72 | * @param array $logger Logged to use 73 | * @since 1.2 74 | */ 75 | public function __construct( $range = 'all', $urls_per_page = 200, $urls = [], $logger = null ) { 76 | if ( ! empty( $range ) && 'all' !== $range ) { 77 | $this->range = date( 'Y-m-d H:i:s', strtotime( '-' . (int) $range . ' month' ) ); 78 | } else { 79 | $this->range = '0000-00-00 00:00:00'; 80 | } 81 | 82 | $this->urls = $urls; 83 | $this->logger = $logger; 84 | $this->urls_per_page = $urls_per_page; 85 | } 86 | 87 | /** 88 | * Log a message 89 | * 90 | * @param string $message Message to log 91 | * @param string $type Type of message 92 | * @since 1.2 93 | */ 94 | public function log( $message, $type = 'notice' ) { 95 | if ( ! empty( $this->logger ) && ! empty( $this->logger[ $type ] ) ) { 96 | call_user_func( $this->logger[ $type ], $message ); 97 | } 98 | } 99 | 100 | /** 101 | * Build entire sitemap 102 | * 103 | * @since 1.2 104 | */ 105 | public function build() { 106 | $this->build_homepage(); 107 | 108 | if ( apply_filters( 'tenup_sitemaps_index_post_types', true ) ) { 109 | $this->build_post_types(); 110 | } 111 | 112 | if ( apply_filters( 'tenup_sitemaps_index_terms', true ) ) { 113 | $this->build_terms(); 114 | } 115 | 116 | if ( apply_filters( 'tenup_sitemaps_index_authors', false ) ) { 117 | $this->build_authors(); 118 | } 119 | } 120 | 121 | /** 122 | * Add post types to URLs 123 | * 124 | * @since 1.2 125 | */ 126 | public function build_post_types() { 127 | global $wpdb; 128 | 129 | $args = [ 130 | 'public' => true, 131 | ]; 132 | 133 | $post_types = get_post_types( $args ); 134 | 135 | if ( ! empty( $post_types['attachment'] ) ) { 136 | unset( $post_types['attachment'] ); 137 | } 138 | 139 | $post_types = apply_filters( 'tenup_sitemaps_post_types', $post_types ); 140 | 141 | foreach ( $post_types as $post_type ) { 142 | $offset = 0; 143 | 144 | $post_type_archive_url = [ 145 | 'url' => get_post_type_archive_link( $post_type ), 146 | 'modified' => time(), 147 | ]; 148 | 149 | $post_type_archive_url = apply_filters( 'tenup_sitemaps_index_post_type_archive', $post_type_archive_url, $post_type ); 150 | 151 | if ( ! empty( $post_type_archive_url ) ) { 152 | $urls[] = $post_type_archive_url; 153 | } 154 | 155 | while ( true ) { 156 | $this->log( 'Processing post type `' . $post_type . '` from offset ' . $offset, 'debug' ); 157 | 158 | // phpcs:disable 159 | $results = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_title, post_date_gmt, post_content FROM {$wpdb->prefix}posts WHERE post_status = 'publish' AND post_type = '%s' AND post_date_gmt >= '%s' ORDER BY post_date_gmt DESC LIMIT %d, %d", $post_type, $this->range, (int) $offset, (int) $this->process_page_size ), ARRAY_A ); 160 | // phpcs:enable 161 | 162 | if ( empty( $results ) ) { 163 | break; 164 | } 165 | 166 | foreach ( $results as $result ) { 167 | $permalink = get_permalink( $result['ID'] ); 168 | 169 | $url = [ 170 | 'ID' => (int) $result['ID'], 171 | 'url' => $permalink, 172 | 'modified' => strtotime( $result['post_date_gmt'] ), 173 | ]; 174 | 175 | if ( apply_filters( 'tenup_sitemaps_index_images', true, $result['ID'] ) ) { 176 | $images = []; 177 | 178 | $thumbnail_id = get_post_thumbnail_id( $result['ID'] ); 179 | 180 | if ( $thumbnail_id ) { 181 | $images[] = Utils\prepare_sitemap_image( $thumbnail_id ); 182 | } 183 | 184 | $images = array_merge( $images, Utils\parse_html_images( $result['post_content'] ) ); 185 | 186 | $gallery_images = Utils\parse_galleries( $result['post_content'], $result['ID'] ); 187 | 188 | foreach ( $gallery_images as $image_id ) { 189 | $images[] = Utils\prepare_sitemap_image( $image_id ); 190 | } 191 | 192 | if ( ! empty( $images ) ) { 193 | // Make sure unique 194 | $image_urls = []; 195 | 196 | foreach ( $images as $key => $image ) { 197 | if ( empty( $image['url'] ) || ! empty( $image_urls[ $image['url'] ] ) ) { 198 | unset( $images[ $key ] ); 199 | } 200 | 201 | $image_urls[ $image['url'] ] = true; 202 | } 203 | 204 | $url['images'] = array_values( $images ); 205 | } 206 | } 207 | 208 | /** 209 | * Should return an array like so: 210 | * 211 | * [ 212 | * 'sp' => 'spanish link', 213 | * 'fr' => 'french link,' 214 | * ] 215 | */ 216 | $translations = apply_filters( 'tenup_sitemaps_post_translations', [], $result['ID'], $post_type ); 217 | 218 | if ( ! empty( $translations ) ) { 219 | $url['translations'] = $translations; 220 | } 221 | 222 | $url = apply_filters( 'tenup_sitemaps_index_post', $url, $result['ID'], $post_type ); 223 | 224 | if ( ! empty( $url ) && ! empty( $url['url'] ) ) { 225 | $this->urls[] = $url; 226 | 227 | $this->log( 228 | sprintf( 229 | '%s (%d) added to page %d.', 230 | $permalink, 231 | count( $this->urls ), 232 | ceil( count( $this->urls ) / $this->urls_per_page ) 233 | ), 234 | 'notice' 235 | ); 236 | } else { 237 | $this->log( 238 | sprintf( 239 | 'Could not add %s.', 240 | $permalink 241 | ), 242 | 'warning' 243 | ); 244 | } 245 | 246 | $this->stop_the_insanity(); 247 | } 248 | 249 | $offset += $this->process_page_size; 250 | } 251 | } 252 | } 253 | 254 | /** 255 | * Add terms to URLs 256 | * 257 | * @since 1.2 258 | */ 259 | public function build_terms() { 260 | global $wpdb; 261 | 262 | $args = [ 263 | 'public' => true, 264 | ]; 265 | 266 | $taxonomies = get_taxonomies( $args ); 267 | 268 | $taxonomies = apply_filters( 'tenup_sitemaps_taxonomies', $taxonomies ); 269 | 270 | foreach ( $taxonomies as $taxonomy ) { 271 | $offset = 0; 272 | 273 | while ( true ) { 274 | $this->log( 'Processing taxonomy `' . $taxonomy . '` from offset ' . $offset, 'debug' ); 275 | 276 | // phpcs:disable 277 | $results = $wpdb->get_results( $wpdb->prepare( "SELECT term_taxonomy.term_id as term_id, name, slug FROM {$wpdb->prefix}term_taxonomy as term_taxonomy, {$wpdb->prefix}terms as terms WHERE term_taxonomy.term_id = terms.term_id AND term_taxonomy.taxonomy = '%s' AND term_taxonomy.count > 0 ORDER BY term_taxonomy.term_id DESC LIMIT %d, %d", $taxonomy, (int) $offset, (int) $this->process_page_size ), ARRAY_A ); 278 | // phpcs:enable 279 | 280 | if ( empty( $results ) ) { 281 | break; 282 | } 283 | 284 | foreach ( $results as $result ) { 285 | $permalink = get_term_link( (int) $result['term_id'] ); 286 | 287 | $url = [ 288 | 'ID' => (int) $result['term_id'], 289 | 'url' => $permalink, 290 | 'modified' => time(), 291 | ]; 292 | 293 | /** 294 | * Should return an array like so: 295 | * 296 | * [ 297 | * 'sp' => 'spanish link', 298 | * 'fr' => 'french link,' 299 | * ] 300 | */ 301 | $translations = apply_filters( 'tenup_sitemaps_term_translations', [], $result['term_id'], $taxonomy ); 302 | 303 | if ( ! empty( $translations ) ) { 304 | $url['translations'] = $translations; 305 | } 306 | 307 | $url = apply_filters( 'tenup_sitemaps_index_term', $url, $result['term_id'], $taxonomy ); 308 | 309 | if ( ! empty( $url ) && ! empty( $url['url'] ) ) { 310 | $this->urls[] = $url; 311 | 312 | $this->log( 313 | sprintf( 314 | '%s (%d) added to page %d.', 315 | $permalink, 316 | count( $this->urls ), 317 | ceil( count( $this->urls ) / $this->urls_per_page ) 318 | ), 319 | 'notice' 320 | ); 321 | } else { 322 | $this->log( 323 | sprintf( 324 | 'Could not add %s.', 325 | $permalink 326 | ), 327 | 'warning' 328 | ); 329 | } 330 | 331 | $this->stop_the_insanity(); 332 | } 333 | 334 | $offset += $this->process_page_size; 335 | } 336 | } 337 | } 338 | 339 | /** 340 | * Add authors to urls 341 | * 342 | * @since 1.2 343 | */ 344 | public function build_authors() { 345 | global $wpdb; 346 | 347 | $offset = 0; 348 | 349 | $roles = [ 350 | 'administrator', 351 | 'editor', 352 | 'author', 353 | ]; 354 | 355 | $roles = apply_filters( 'tenup_sitemaps_user_roles', $roles ); 356 | 357 | if ( empty( $roles ) ) { 358 | return; 359 | } 360 | 361 | $roles_like = ''; 362 | 363 | foreach ( $roles as $role ) { 364 | if ( ! empty( $roles_like ) ) { 365 | $roles_like .= ' OR '; 366 | } 367 | 368 | $roles_like .= "usermeta.meta_value LIKE '%%\"" . esc_sql( $role ) . "\"%%'"; 369 | } 370 | 371 | $roles_like = '(' . $roles_like . ')'; 372 | 373 | $capability_col = $wpdb->prefix . 'capabilities'; 374 | 375 | if ( is_multisite() && 1 < (int) get_current_blog_id() ) { 376 | $capability_col = $wpdb->prefix . get_current_blog_id() . '_capabilities'; 377 | } 378 | 379 | while ( true ) { 380 | // phpcs:disable 381 | $results = $wpdb->get_results( sprintf( "SELECT * FROM {$wpdb->prefix}users as users, {$wpdb->prefix}usermeta as usermeta WHERE users.ID = usermeta.user_id AND usermeta.meta_key = '{$capability_col}' AND {$roles_like} ORDER BY user_login ASC LIMIT %d, %d", (int) $offset, (int) $this->process_page_size ), ARRAY_A ); 382 | // phpcs:enable 383 | 384 | if ( empty( $results ) ) { 385 | break; 386 | } 387 | 388 | foreach ( $results as $result ) { 389 | $permalink = get_author_posts_url( (int) $result['term_id'] ); 390 | 391 | $url = [ 392 | 'ID' => (int) $result['ID'], 393 | 'url' => $permalink, 394 | 'modified' => time(), 395 | ]; 396 | 397 | $url = apply_filters( 'tenup_sitemaps_index_author', $url, $result['ID'] ); 398 | 399 | if ( ! empty( $url ) && ! empty( $url['url'] ) ) { 400 | $this->urls[] = $url; 401 | 402 | $this->log( 403 | sprintf( 404 | '%s (%d) added to page %d.', 405 | $permalink, 406 | count( $this->urls ), 407 | ceil( count( $this->urls ) / $this->urls_per_page ) 408 | ), 409 | 'notice' 410 | ); 411 | } else { 412 | $this->log( 413 | sprintf( 414 | 'Could not add %s.', 415 | $permalink 416 | ), 417 | 'warning' 418 | ); 419 | } 420 | 421 | $this->stop_the_insanity(); 422 | } 423 | 424 | $offset += $this->process_page_size; 425 | } 426 | } 427 | 428 | /** 429 | * Add homepage url 430 | * 431 | * @since 1.2 432 | */ 433 | public function build_homepage() { 434 | $homepage_url = [ 435 | 'url' => home_url(), 436 | 'modified' => time(), 437 | ]; 438 | 439 | $homepage_url = apply_filters( 'tenup_sitemaps_index_homepage', $homepage_url ); 440 | 441 | if ( ! empty( $homepage_url ) ) { 442 | $this->urls[] = $homepage_url; 443 | } 444 | } 445 | 446 | /** 447 | * Write sitemap to options 448 | * 449 | * @since 1.2 450 | */ 451 | public function write() { 452 | $total_pages = ceil( count( $this->urls ) / $this->urls_per_page ); 453 | 454 | update_option( 'tenup_sitemaps_total_pages', (int) $total_pages, false ); 455 | 456 | for ( $i = 1; $i <= $total_pages; $i++ ) { 457 | $data = array_slice( $this->urls, ( ( $i - 1 ) * $this->urls_per_page ), $this->urls_per_page ); 458 | 459 | // phpcs:disable 460 | $this->log( 'Saving sitemap page ' . $i . '. Total option size is ~' . round( strlen( serialize( $data ) ) / 1024 ) . ' kilobytes.', 'debug' ); 461 | // phpcs:enable 462 | 463 | update_option( 'tenup_sitemaps_page_' . $i, $data, false ); 464 | } 465 | 466 | $this->log( 'Sitemap generated. ' . count( $this->urls ) . ' urls included. ' . $total_pages . ' pages created.', 'success' ); 467 | } 468 | 469 | /** 470 | * Get urls 471 | * 472 | * @since 1.2 473 | * @return array 474 | */ 475 | public function get_urls() { 476 | return $this->urls; 477 | } 478 | 479 | /** 480 | * Get urls per page 481 | * 482 | * @since 1.2 483 | * @return int 484 | */ 485 | public function get_urls_per_page() { 486 | return $this->urls_per_page; 487 | } 488 | 489 | /** 490 | * Get range 491 | * 492 | * @since 1.2 493 | * @return string 494 | */ 495 | public function get_range() { 496 | return $this->range; 497 | } 498 | 499 | /** 500 | * Get logger 501 | * 502 | * @since 1.2 503 | * @return array 504 | */ 505 | public function get_logger() { 506 | return $this->logger; 507 | } 508 | 509 | 510 | /** 511 | * Clear all of the caches for memory management 512 | * 513 | * @since 1.2 514 | */ 515 | private function stop_the_insanity() { 516 | global $wpdb, $wp_object_cache; 517 | 518 | $one_hundred_mb = 104857600; 519 | if ( memory_get_usage() <= $one_hundred_mb ) { 520 | return; 521 | } 522 | 523 | $wpdb->queries = array(); 524 | 525 | if ( is_object( $wp_object_cache ) ) { 526 | $wp_object_cache->group_ops = array(); 527 | $wp_object_cache->stats = array(); 528 | $wp_object_cache->memcache_debug = array(); 529 | $wp_object_cache->cache = array(); 530 | 531 | if ( method_exists( $wp_object_cache, '__remoteset' ) ) { 532 | $wp_object_cache->__remoteset(); // important 533 | } 534 | } 535 | 536 | gc_collect_cycles(); 537 | } 538 | } 539 | -------------------------------------------------------------------------------- /includes/core.php: -------------------------------------------------------------------------------- 1 | set_404(); 35 | status_header( 404 ); 36 | } 37 | } 38 | 39 | /** 40 | * Render sitemap 41 | * 42 | * @param string $template Template file to use 43 | * @return string 44 | */ 45 | function load_sitemap_template( $template ) { 46 | 47 | if ( 'true' === get_query_var( 'sitemap' ) ) { 48 | if ( ! Utils\sitemap_setup() ) { 49 | return $template; 50 | } 51 | 52 | if ( ! empty( get_query_var( 'sitemap-page' ) ) ) { 53 | $template = __DIR__ . '/templates/sitemap-page.php'; 54 | } else { 55 | $template = __DIR__ . '/templates/sitemap-root.php'; 56 | } 57 | } 58 | 59 | return $template; 60 | } 61 | 62 | /** 63 | * Add rewrite rules/tags 64 | */ 65 | function create_rewrites() { 66 | add_rewrite_tag( '%sitemap%', 'true' ); 67 | 68 | add_rewrite_tag( '%sitemap-page%', '[0-9]+' ); 69 | add_rewrite_rule( '^sitemap.xml$', 'index.php?sitemap=true', 'top' ); 70 | add_rewrite_rule( '^sitemap-page-([0-9]+).xml$', 'index.php?sitemap=true&sitemap-page=$matches[1]', 'top' ); 71 | add_action( 'redirect_canonical', __NAMESPACE__ . '\disable_canonical_redirects_for_sitemap_xml', 10, 2 ); 72 | } 73 | 74 | /** 75 | * Disable Main Query when rendering sitemaps 76 | * 77 | * @param array|null $posts array of post data or null 78 | * @param \WP_Query $query The WP_Query instance. 79 | * @return array 80 | */ 81 | function disable_main_query_for_sitemap_xml( $posts, $query ) { 82 | 83 | if ( $query->is_main_query() && ! empty( $query->query_vars['sitemap'] ) ) { 84 | if ( Utils\sitemap_setup() ) { 85 | $posts = []; 86 | } 87 | } 88 | 89 | return $posts; 90 | } 91 | 92 | /** 93 | * Disable canonical redirects for the sitemap files 94 | * 95 | * @param string $redirect_url URL to redirect to 96 | * @param string $requested_url Originally requested url 97 | * @return string URL to redirect 98 | */ 99 | function disable_canonical_redirects_for_sitemap_xml( $redirect_url, $requested_url ) { 100 | if ( preg_match( '/sitemap(\-page\-[0-9]+)?\.xml/i', $requested_url ) ) { 101 | return $requested_url; 102 | } 103 | 104 | return $redirect_url; 105 | } 106 | 107 | /** 108 | * Add the sitemap URL to robots.txt 109 | * 110 | * @param string $output Robots.txt output. 111 | * @return string 112 | */ 113 | function add_sitemap_robots_txt( $output ) { 114 | $url = site_url( '/sitemap.xml' ); 115 | $output .= "\nSitemap: {$url}\n"; 116 | return $output; 117 | } 118 | -------------------------------------------------------------------------------- /includes/templates/sitemap-page.php: -------------------------------------------------------------------------------- 1 | '; 11 | 12 | $sitemap_page = get_query_var( 'sitemap-page' ); 13 | 14 | $links = apply_filters( 'tenup_sitemaps_page_links', get_option( 'tenup_sitemaps_page_' . $sitemap_page ), $sitemap_page ); 15 | 16 | if ( empty( $links ) ) { 17 | $links = []; 18 | } 19 | ?> 20 | 21 | 22 | 23 | 28 | 29 | 30 | 31 | 32 | 33 | $translated_url ) : ?> 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /includes/templates/sitemap-root.php: -------------------------------------------------------------------------------- 1 | '; 11 | 12 | $total_pages = get_option( 'tenup_sitemaps_total_pages', 0 ); 13 | ?> 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /includes/utils.php: -------------------------------------------------------------------------------- 1 | $id, 56 | 'url' => $src, 57 | 'alt' => prepare_valid_xml( $alt ), 58 | 'title' => prepare_valid_xml( $title ), 59 | ]; 60 | } 61 | 62 | /** 63 | * Get attached image URL with filters applied. Adapted from core for speed. 64 | * 65 | * @param int $post_id ID of the post. 66 | * @return string 67 | */ 68 | function get_image_url( $post_id ) { 69 | static $uploads; 70 | 71 | if ( empty( $uploads ) ) { 72 | $uploads = wp_upload_dir(); 73 | } 74 | 75 | if ( false !== $uploads['error'] ) { 76 | return ''; 77 | } 78 | 79 | $file = get_post_meta( $post_id, '_wp_attached_file', true ); 80 | 81 | if ( empty( $file ) ) { 82 | return ''; 83 | } 84 | 85 | // Check that the upload base exists in the file location. 86 | if ( 0 === strpos( $file, $uploads['basedir'] ) ) { 87 | $src = str_replace( $uploads['basedir'], $uploads['baseurl'], $file ); 88 | } elseif ( false !== strpos( $file, 'wp-content/uploads' ) ) { 89 | $src = $uploads['baseurl'] . substr( $file, ( strpos( $file, 'wp-content/uploads' ) + 18 ) ); 90 | } else { 91 | // It's a newly uploaded file, therefore $file is relative to the baseurl. 92 | $src = $uploads['baseurl'] . '/' . $file; 93 | } 94 | 95 | return apply_filters( 'wp_get_attachment_url', $src, $post_id ); 96 | } 97 | 98 | /** 99 | * Parse `` tags in content. 100 | * 101 | * @param string $content Content string to parse. 102 | * 103 | * @return array 104 | */ 105 | function parse_html_images( $content ) { 106 | $images = []; 107 | 108 | if ( empty( $content ) ) { 109 | return $images; 110 | } 111 | 112 | // Prevent DOMDocument from bubbling warnings about invalid HTML. 113 | libxml_use_internal_errors( true ); 114 | 115 | $post_dom = new \DOMDocument(); 116 | $post_dom->loadHTML( '' . $content ); 117 | 118 | // Clear the errors, so they don't get kept in memory. 119 | libxml_clear_errors(); 120 | 121 | foreach ( $post_dom->getElementsByTagName( 'img' ) as $img ) { 122 | $src = $img->getAttribute( 'src' ); 123 | 124 | if ( empty( $src ) ) { 125 | continue; 126 | } 127 | 128 | $class = $img->getAttribute( 'class' ); 129 | 130 | if ( 131 | ! empty( $class ) 132 | && false === strpos( $class, 'size-full' ) 133 | && preg_match( '|wp-image-(?P\d+)|', $class, $matches ) 134 | && get_post_status( $matches['id'] ) 135 | ) { 136 | $images[] = prepare_sitemap_image( $matches['id'] ); 137 | continue; 138 | } 139 | 140 | $src = get_absolute_url( $src ); 141 | 142 | if ( false === strpos( $src, str_replace( 'www.', '', wp_parse_url( home_url(), PHP_URL_HOST ) ) ) ) { 143 | continue; 144 | } 145 | 146 | $images[] = array( 147 | 'url' => $src, 148 | 'title' => $img->getAttribute( 'title' ), 149 | 'alt' => $img->getAttribute( 'alt' ), 150 | ); 151 | } 152 | 153 | return $images; 154 | } 155 | 156 | /** 157 | * Retrieves galleries from the passed content. 158 | * 159 | * Forked from core to skip executing shortcodes for performance. 160 | * 161 | * @param string $content Content to parse for shortcodes. 162 | * @return array A list of arrays, each containing gallery data. 163 | */ 164 | function get_content_galleries( $content ) { 165 | if ( ! has_shortcode( $content, 'gallery' ) ) { 166 | return array(); 167 | } 168 | 169 | $galleries = array(); 170 | 171 | if ( ! preg_match_all( '/' . get_shortcode_regex() . '/s', $content, $matches, PREG_SET_ORDER ) ) { 172 | return $galleries; 173 | } 174 | 175 | foreach ( $matches as $shortcode ) { 176 | if ( 'gallery' === $shortcode[2] ) { 177 | $attributes = shortcode_parse_atts( $shortcode[3] ); 178 | 179 | if ( '' === $attributes ) { 180 | $attributes = array(); 181 | } 182 | 183 | $galleries[] = $attributes; 184 | } 185 | } 186 | 187 | return $galleries; 188 | } 189 | 190 | /** 191 | * Parse gallery shortcodes in a given content. 192 | * 193 | * @param string $content Content string. 194 | * @param int $post_id ID of post being parsed. 195 | * 196 | * @return array Set of attachment objects. 197 | */ 198 | function parse_galleries( $content, $post_id ) { 199 | $attachments = array(); 200 | $galleries = get_content_galleries( $content ); 201 | 202 | foreach ( $galleries as $gallery ) { 203 | $id = $post_id; 204 | 205 | if ( ! empty( $gallery['id'] ) ) { 206 | $id = intval( $gallery['id'] ); 207 | } 208 | 209 | // Forked from core gallery_shortcode() to have exact same logic. 210 | if ( ! empty( $gallery['ids'] ) ) { 211 | $gallery['include'] = $gallery['ids']; 212 | } 213 | 214 | $gallery_attachments = get_gallery_attachments( $id, $gallery ); 215 | 216 | $attachments = array_merge( $attachments, $gallery_attachments ); 217 | } 218 | 219 | return array_unique( $attachments ); 220 | } 221 | 222 | /** 223 | 224 | 225 | /** 226 | * Returns the attachments for a gallery. 227 | * 228 | * @param int $id The post ID. 229 | * @param array $gallery The gallery config. 230 | * 231 | * @return array The selected attachments. 232 | */ 233 | function get_gallery_attachments( $id, $gallery ) { 234 | // When there are attachments to include. 235 | if ( ! empty( $gallery['include'] ) ) { 236 | return wp_parse_id_list( $gallery['include'] ); 237 | } 238 | 239 | return get_gallery_attachments_for_parent( $id, $gallery ); 240 | } 241 | 242 | /** 243 | * Returns the attachments for the given ID. 244 | * 245 | * @param int $id The post ID. 246 | * @param array $gallery The gallery config. 247 | * 248 | * @return array The selected attachments. 249 | */ 250 | function get_gallery_attachments_for_parent( $id, $gallery ) { 251 | $query = array( 252 | 'post_parent' => $id, 253 | ); 254 | 255 | // When there are posts that should be excluded from result set. 256 | if ( ! empty( $gallery['exclude'] ) ) { 257 | $query['post__not_in'] = wp_parse_id_list( $gallery['exclude'] ); 258 | } 259 | 260 | return get_attachments( $query ); 261 | } 262 | 263 | /** 264 | * Returns the attachments. 265 | * 266 | * @param array $args Array with query args. 267 | * 268 | * @return array The found attachments. 269 | */ 270 | function get_attachments( $args ) { 271 | $default_args = array( 272 | 'post_status' => 'inherit', 273 | 'post_type' => 'attachment', 274 | 'post_mime_type' => 'image', 275 | 'orderby' => 'date', 276 | 'order' => 'DESC', 277 | 'ignore_sticky_posts' => true, 278 | 'no_found_rows' => true, 279 | 'fields' => 'ids', 280 | 'cache_results' => false, 281 | 'update_post_meta_cache' => false, 282 | 'update_post_term_cache' => false, 283 | ); 284 | 285 | $args = wp_parse_args( $args, $default_args ); 286 | 287 | $get_attachments = new \WP_Query( $args ); 288 | 289 | return $get_attachments->posts; 290 | } 291 | 292 | /** 293 | * Check if a url is relative 294 | * 295 | * @param string $url Url to check 296 | * @return boolean 297 | */ 298 | function is_url_relative( $url ) { 299 | return ( strpos( $url, 'http' ) !== 0 && strpos( $url, '//' ) !== 0 ); 300 | } 301 | 302 | /** 303 | * Make absolute URL for domain or protocol-relative one. 304 | * 305 | * @param string $src URL to process. 306 | * 307 | * @return string 308 | */ 309 | function get_absolute_url( $src ) { 310 | if ( empty( $src ) || ! is_string( $src ) ) { 311 | return $src; 312 | } 313 | 314 | if ( is_url_relative( $src ) === true ) { 315 | if ( '/' !== $src[0] ) { 316 | return $src; 317 | } 318 | // The URL is relative, we'll have to make it absolute. 319 | return home_url() . $src; 320 | } 321 | 322 | if ( 0 !== strpos( $src, 'http' ) ) { 323 | // Protocol relative URL, we add the scheme as the standard requires a protocol. 324 | return wp_parse_url( home_url(), PHP_URL_SCHEME ) . ':' . $src; 325 | } 326 | 327 | return $src; 328 | } 329 | -------------------------------------------------------------------------------- /phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10up PHPCS extended. 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | ./tests/ 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === 10up Sitemaps === 2 | Contributors: 10up, tlovett1, jeffpaul 3 | Tags: sitemap, wp-cli, cli 4 | Tested up to: 6.7 5 | Stable tag: 1.0.4 6 | License: GPL-2.0-or-later 7 | License URI: https://spdx.org/licenses/GPL-2.0-or-later.html 8 | 9 | Simple sitemaps plugin that performs at scale. 10 | 11 | == Description == 12 | 13 | This is a simple sitemap plugin meant to run at scale. Sitemaps are only updated via WP-CLI. Output is saved in an option for fast reading/displaying on the front end. 14 | 15 | == Setup/Usage == 16 | 17 | 1. Install the plugin. 18 | 2. Run the WP-CLI command: `wp tenup-sitemaps generate` 19 | 3. Add WP-CLI command to cron job. For multisite, add a command for each site in the network. 20 | 21 | You can pass `--range` to the `generate` command to only index content within a certain age range. `wp tenup-sitemaps generate --range=12` would only index content created/updated within the last 12 months. 22 | 23 | The plugin indexes all public posts, post type archives, and public taxonomy term archives. For posts, images are parsed and included. Translated content needs to be manually filtered in via `tenup_sitemaps_term_translations`. 24 | 25 | == Changelog == 26 | 27 | = 1.0.4 - 2023-09-07 = 28 | * **Fixed:** Prefix sitemap index in `robots.txt` with line feed. 29 | 30 | = 1.0.3 - 2019-08-12 = 31 | * **Fixed:** No empty urls in sitemap. 32 | 33 | = 1.0.2 - 2019-08-05 = 34 | * **Added:** WordPress Plugin type. 35 | 36 | = 1.0.1 - 2019-08-05 = 37 | * **Changed:** Package name. 38 | * **Fixed:** Log url properly. 39 | 40 | = 1.0.0 - 2019-08-01 = 41 | * **Added:** Homepage and post type archive. 42 | * **Added:** Progress estimator. 43 | * **Added:** `stop_the_insanity()` calls. 44 | * **Added:** `robots_txt` filter to include the `sitemap.xml` file. 45 | * **Added:** Page link filter. 46 | * **Removed:** `changefrew` and `priority` from template. 47 | 48 | = 0.1.0 - 2019-06-26 = 49 | * **Added:** Initial plugin release! 🎉 50 | * **Added:** Sitemaps are only updated via WP-CLI. 51 | * **Added:** Output is saved in an option for fast reading/displaying on the front end. 52 | -------------------------------------------------------------------------------- /tests/TestSitemap.php: -------------------------------------------------------------------------------- 1 | set_permalink_structure('/%postname%/'); 22 | 23 | update_option( "rewrite_rules", true ); 24 | 25 | $wp_rewrite->flush_rules( true ); 26 | } 27 | 28 | /** 29 | * Test setting up the sitemap class 30 | * 31 | * @since 1.2 32 | */ 33 | public function testConstruct() { 34 | $logger = [ 35 | 'success' => 'success', 36 | 'debug' => 'debug', 37 | 'warning' => 'warning', 38 | 'notice' => 'line', 39 | ]; 40 | 41 | $sitemap = new Sitemap( 6, 50, [ 'test' ], $logger ); 42 | 43 | $this->assertEquals( date( 'Y-m-d', strtotime( '-6 month' ) ), date( 'Y-m-d', strtotime( $sitemap->get_range() ) ) ); 44 | $this->assertEquals( 50, $sitemap->get_urls_per_page() ); 45 | $this->assertEquals( [ 'test' ], $sitemap->get_urls() ); 46 | $this->assertEquals( $logger, $sitemap->get_logger() ); 47 | } 48 | 49 | /** 50 | * Test logger 51 | * 52 | * @since 1.2 53 | */ 54 | public function testLog() { 55 | $func = function() { 56 | $this->assertTrue( true ); 57 | }; 58 | 59 | $logger = [ 60 | 'success' => $func, 61 | ]; 62 | 63 | $sitemap = new Sitemap( 6, 50, [ 'test' ], $logger ); 64 | 65 | $sitemap->log( 'test', 'success' ); 66 | } 67 | 68 | /** 69 | * Test building post types 70 | * 71 | * @since 1.2 72 | */ 73 | public function testBuildPostTypes() { 74 | $args = array( 75 | 'post_status' => 'publish', 76 | 'post_type' => 'post', 77 | 'post_title' => 'Test Post One', 78 | ); 79 | 80 | wp_insert_post( $args ); 81 | 82 | $args = array( 83 | 'post_status' => 'publish', 84 | 'post_type' => 'post', 85 | 'post_title' => 'Test Post Two', 86 | ); 87 | 88 | $post_id = wp_insert_post( $args ); 89 | 90 | $args = array( 91 | 'post_status' => 'publish', 92 | 'post_type' => 'post', 93 | 'post_title' => 'Test Post Three', 94 | ); 95 | 96 | wp_insert_post( $args ); 97 | 98 | $args = array( 99 | 'post_status' => 'publish', 100 | 'post_type' => 'tsm_test', 101 | 'post_title' => 'Test Custom One', 102 | ); 103 | 104 | wp_insert_post( $args ); 105 | 106 | $args = array( 107 | 'post_status' => 'publish', 108 | 'post_type' => 'tsm_test_private', 109 | 'post_title' => 'Test Custom Two', 110 | ); 111 | 112 | wp_insert_post( $args ); 113 | 114 | $sitemap = new Sitemap(); 115 | $sitemap->build_post_types(); 116 | 117 | $urls = $sitemap->get_urls(); 118 | $links = wp_list_pluck( $urls, 'url' ); 119 | $ids = wp_list_pluck( $urls, 'ID' ); 120 | 121 | $this->assertEquals( 4, count( $urls ) ); 122 | $this->assertTrue( in_array( (int) $post_id, $ids, true ) ); 123 | $this->assertTrue( in_array( home_url() . '/test-post-two/', $links, true ) ); 124 | } 125 | 126 | /** 127 | * Test building terms 128 | * 129 | * @since 1.2 130 | */ 131 | public function testBuildTerms() { 132 | $term1 = wp_insert_term( 'Cat 1', 'category' ); 133 | $term2 = wp_insert_term( 'Tag 1', 'post_tag' ); 134 | 135 | $args = array( 136 | 'post_status' => 'publish', 137 | 'post_type' => 'post', 138 | 'post_title' => 'Test Post One', 139 | 'post_category' => [ $term1['term_id'] ], 140 | ); 141 | 142 | wp_insert_post( $args ); 143 | 144 | $args = array( 145 | 'post_status' => 'publish', 146 | 'post_type' => 'post', 147 | 'post_title' => 'Test Post Two', 148 | 'post_category' => [ $term1['term_id'] ], 149 | ); 150 | 151 | $post_id = wp_insert_post( $args ); 152 | 153 | wp_set_post_terms( $post_id, [ $term2['term_id'] ], 'post_tag' ); 154 | 155 | $sitemap = new Sitemap(); 156 | $sitemap->build_terms(); 157 | 158 | $urls = $sitemap->get_urls(); 159 | $links = wp_list_pluck( $urls, 'url' ); 160 | 161 | $this->assertTrue( in_array( get_term_link( $term1['term_id'] ), $links, true ) ); 162 | $this->assertTrue( in_array( get_term_link( $term2['term_id'] ), $links, true ) ); 163 | } 164 | 165 | /** 166 | * Test sitemaps writing 167 | * 168 | * @since 1.2 169 | */ 170 | public function testSitemapWrite() { 171 | $urls = []; 172 | 173 | for ( $i = 1; $i <= 52; $i++ ) { 174 | $urls[] = [ 175 | 'ID' => $i, 176 | 'url' => 'url' . $i, 177 | ]; 178 | } 179 | 180 | $sitemap = new Sitemap( 'all', 10, $urls ); 181 | $sitemap->write(); 182 | 183 | $num_pages = get_option( 'tenup_sitemaps_total_pages' ); 184 | 185 | $this->assertEquals( 6, $num_pages ); 186 | 187 | for ( $i = 1; $i <= $num_pages; $i++ ) { 188 | $page = get_option( 'tenup_sitemaps_page_' . $i ); 189 | 190 | $this->assertTrue( ! empty( $page ) ); 191 | } 192 | 193 | $page2 = get_option( 'tenup_sitemaps_page_2' ); 194 | 195 | $this->assertEquals( 11, $page2[0]['ID'] ); 196 | } 197 | 198 | /** 199 | * Test building homepage 200 | * 201 | * @since 1.2 202 | */ 203 | public function testBuildHomepage() { 204 | $sitemap = new Sitemap(); 205 | $sitemap->build_homepage(); 206 | 207 | $urls = $sitemap->get_urls(); 208 | 209 | $this->assertEquals( 1, count( $urls ) ); 210 | $this->assertEquals( home_url(), $urls[0]['url'] ); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | [], 47 | ]; 48 | } 49 | 50 | tests_add_filter( 'translations_api', __NAMESPACE__ . '\skip_translations_api' ); 51 | 52 | /** 53 | * Register post types for testing 54 | * 55 | * @since 1.2 56 | */ 57 | function register_post_types() { 58 | $args = array( 59 | 'public' => true, 60 | 'taxonomies' => array( 'post_tag', 'category' ), 61 | ); 62 | 63 | register_post_type( 'tsm_test', $args ); 64 | 65 | $args = array( 66 | 'taxonomies' => array( 'post_tag', 'category' ), 67 | 'public' => false, 68 | ); 69 | 70 | register_post_type( 'tsm_test_private', $args ); 71 | } 72 | 73 | require_once $_tests_dir . '/includes/bootstrap.php'; 74 | --------------------------------------------------------------------------------