├── .editorconfig ├── .github ├── demos.md └── workflows │ ├── deploy-wpe-dev.yml │ ├── draft-release.yml │ ├── phpcs.yml │ └── phpstan.yml ├── .gitignore ├── LICENSE ├── acf ├── qmx-acf-collector.php ├── qmx-acf-data.php └── qmx-acf-output.php ├── composer.json ├── composer.lock ├── constants ├── qmx-constants-collector.php ├── qmx-constants-data.php └── qmx-constants-output.php ├── dev ├── create-release-files.sh ├── patches │ └── acf-pro-stubs.patch ├── phpstan-bootstrap.php └── wordpress-overrides.stub ├── files ├── qmx-files-collector.php ├── qmx-files-data.php └── qmx-files-output.php ├── globals ├── qmx-globals-collector.php ├── qmx-globals-data.php └── qmx-globals-output.php ├── heartbeat ├── qmx-heartbeat-collector.php └── qmx-heartbeat-output.php ├── image-sizes ├── qmx-image-sizes-collector.php ├── qmx-image-sizes-data.php └── qmx-image-sizes-output.php ├── paths ├── qmx-paths-collector.php ├── qmx-paths-data.php └── qmx-paths-output.php ├── phpcs.xml ├── phpstan-baseline.neon ├── phpstan.neon ├── qmx-conditionals.php ├── qmx-time-hooks.php ├── query-monitor-extend.php ├── readme.md └── time ├── qmx-time-collector.php ├── qmx-time-data.php └── qmx-time-output.php /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = tab 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.{yml,json,svg,xml}] 11 | indent_style = space 12 | indent_size = 2 -------------------------------------------------------------------------------- /.github/demos.md: -------------------------------------------------------------------------------- 1 | # Demos 2 | 3 | Demos of QMX are available via the [WordPress Playground](https://developer.wordpress.org/playground/): 4 | 5 | - [Install as plugin](https://playground.wordpress.net/#%7B%22landingPage%22:%22/wp-admin/plugins.php%22,%22steps%22:%5B%7B%22step%22:%22login%22,%22username%22:%22admin%22,%22password%22:%22password%22%7D,%7B%22step%22:%22installPlugin%22,%22pluginZipFile%22:%7B%22resource%22:%22wordpress.org/plugins%22,%22slug%22:%22query-monitor%22%7D%7D,%7B%22step%22:%22installPlugin%22,%22pluginZipFile%22:%7B%22resource%22:%22url%22,%22url%22:%22https://calebstauffer.wpengine.com/plugin-proxy.php?repo=crstauf/query-monitor-extend&name=plugin.zip%22,%22caption%22:%22Installing%20Query%20Monitor%20Extend%22%7D%7D%5D%7D) 6 | - [Install as mu-plugin](https://playground.wordpress.net/#%7B%22landingPage%22:%22/wp-admin/plugins.php?plugin_status=mustuse%22,%22steps%22:%5B%7B%22step%22:%22login%22,%22username%22:%22admin%22,%22password%22:%22password%22%7D,%7B%22step%22:%22installPlugin%22,%22pluginZipFile%22:%7B%22resource%22:%22wordpress.org/plugins%22,%22slug%22:%22query-monitor%22%7D%7D,%7B%22step%22:%22mkdir%22,%22path%22:%22/wordpress/qmx%22%7D,%7B%22step%22:%22writeFile%22,%22path%22:%22/wordpress/qmx/mu-plugin.zip%22,%22data%22:%7B%22resource%22:%22url%22,%22url%22:%22https://calebstauffer.wpengine.com/plugin-proxy.php?repo=crstauf/query-monitor-extend&name=mu-plugin.zip%22,%22caption%22:%22Downloading%20Query%20Monitor%20Extend%22%7D,%22progress%22:%7B%22weight%22:2,%22caption%22:%22Installing%20Query%20Monitor%20Extend%22%7D%7D,%7B%22step%22:%22unzip%22,%22zipPath%22:%22/wordpress/qmx/mu-plugin.zip%22,%22extractToPath%22:%22/wordpress/qmx%22%7D,%7B%22step%22:%22mv%22,%22fromPath%22:%22/wordpress/qmx/mu-plugins/query-monitor-extend%22,%22toPath%22:%22/wordpress/wp-content/mu-plugins/query-monitor-extend%22%7D,%7B%22step%22:%22mv%22,%22fromPath%22:%22/wordpress/qmx/mu-plugins/load-qmx.php%22,%22toPath%22:%22/wordpress/wp-content/mu-plugins/load-qmx.php%22%7D%5D%7D) 7 | 8 | ## URL Generation 9 | 10 | WordPress Playground URLs are generated using the below JavaScript, available at https://jsfiddle.net/crstauf/m54cdobk/. 11 | 12 | ```js 13 | const muplugin = false; 14 | 15 | let zipArtifactUrl = 'https://calebstauffer.wpengine.com/plugin-proxy.php?repo=crstauf/query-monitor-extend&name=plugin.zip'; 16 | 17 | // Install as plugin. 18 | let blueprint = { 19 | landingPage: '/wp-admin/plugins.php', 20 | steps: [ 21 | { 22 | step: 'login', 23 | username: 'admin', 24 | password: 'password', 25 | }, 26 | { 27 | step: 'installPlugin', 28 | pluginZipFile: { 29 | resource: 'wordpress.org/plugins', 30 | slug: 'query-monitor', 31 | }, 32 | }, 33 | { 34 | step: 'installPlugin', 35 | pluginZipFile: { 36 | resource: 'url', 37 | url: zipArtifactUrl, 38 | caption: "Installing Query Monitor Extend", 39 | }, 40 | }, 41 | ], 42 | }; 43 | 44 | // Install as mu-plugin. 45 | if ( muplugin ) { 46 | zipArtifactUrl = 'https://calebstauffer.wpengine.com/plugin-proxy.php?repo=crstauf/query-monitor-extend&name=mu-plugin.zip'; 47 | 48 | blueprint.landingPage = '/wp-admin/plugins.php?plugin_status=mustuse'; 49 | 50 | blueprint.steps = [ 51 | { 52 | step: 'login', 53 | username: 'admin', 54 | password: 'password', 55 | }, 56 | { 57 | step: 'installPlugin', 58 | pluginZipFile: { 59 | resource: 'wordpress.org/plugins', 60 | slug: 'query-monitor', 61 | }, 62 | }, 63 | { 64 | step: 'mkdir', 65 | path: '/wordpress/qmx', 66 | }, 67 | { 68 | step: 'writeFile', 69 | path: '/wordpress/qmx/mu-plugin.zip', 70 | data: { 71 | resource: 'url', 72 | url: zipArtifactUrl, 73 | caption: 'Downloading Query Monitor Extend', 74 | }, 75 | progress: { 76 | weight: 2, 77 | caption: 'Installing Query Monitor Extend', 78 | }, 79 | }, 80 | { 81 | step: 'unzip', 82 | zipPath: '/wordpress/qmx/mu-plugin.zip', 83 | extractToPath: '/wordpress/qmx', 84 | }, 85 | { 86 | step: 'mv', 87 | fromPath: '/wordpress/qmx/mu-plugins/query-monitor-extend', 88 | toPath: '/wordpress/wp-content/mu-plugins/query-monitor-extend', 89 | }, 90 | { 91 | step: 'mv', 92 | fromPath: '/wordpress/qmx/mu-plugins/load-qmx.php', 93 | toPath: '/wordpress/wp-content/mu-plugins/load-qmx.php', 94 | } 95 | ]; 96 | } 97 | 98 | const encoded = JSON.stringify( blueprint ); 99 | document.write( 'https://playground.wordpress.net/#' + encodeURI( encoded ) ); 100 | ``` -------------------------------------------------------------------------------- /.github/workflows/deploy-wpe-dev.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to Dev 2 | 3 | on: workflow_dispatch 4 | 5 | env: 6 | INSTALL: cssllcqmx 7 | SSH_HOST: cssllcqmx.ssh.wpengine.net 8 | 9 | jobs: 10 | 11 | backup-plugins: 12 | runs-on: ubuntu-latest 13 | steps: 14 | 15 | - name: Install SSH key 16 | uses: shimataro/ssh-key-action@v2 17 | with: 18 | key: ${{ secrets.WPENGINE_SSH_KEY_PRIVATE }} 19 | name: deploy-wpengine 20 | known_hosts: ${{ secrets.KNOWN_HOSTS }} 21 | 22 | - name: Backup plugins 23 | run: ssh -i ~/.ssh/deploy-wpengine $INSTALL@$SSH_HOST 'zip -r --filesync --test --display-bytes --display-counts sites/${{ env.INSTALL }}/_wpeprivate/backup-plugins.zip sites/${{ env.INSTALL }}/wp-content/plugins' 24 | 25 | deploy-plugins: 26 | runs-on: ubuntu-latest 27 | needs: [ backup-plugins ] 28 | 29 | steps: 30 | - uses: actions/checkout@v4 31 | with: 32 | fetch-depth: '0' 33 | 34 | - name: Install SSH key 35 | uses: shimataro/ssh-key-action@v2 36 | with: 37 | key: ${{ secrets.WPENGINE_SSH_KEY_PRIVATE }} 38 | name: deploy-wpengine 39 | known_hosts: ${{ secrets.KNOWN_HOSTS }} 40 | 41 | - name: Deploy plugins 42 | run: | 43 | rsync -chavP --delete --stats \ 44 | -e 'ssh -i ~/.ssh/deploy-wpengine' \ 45 | --exclude /.git/ \ 46 | --exclude /.github/ \ 47 | --exclude /.gitignore \ 48 | --exclude /.ssh/ \ 49 | ./ "$INSTALL@$SSH_HOST:/sites/$INSTALL/wp-content/plugins/query-monitor-extend" -------------------------------------------------------------------------------- /.github/workflows/draft-release.yml: -------------------------------------------------------------------------------- 1 | name: "Draft new release" 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version: 7 | description: "Version number" 8 | required: true 9 | 10 | jobs: 11 | 12 | draft-new-release: 13 | name: "Draft new release" 14 | runs-on: ubuntu-latest 15 | steps: 16 | 17 | - uses: actions/checkout@v4 18 | 19 | - name: "Create release artifacts" 20 | run: | 21 | sh dev/create-release-files.sh 22 | 23 | - name: "Draft new release" 24 | uses: softprops/action-gh-release@v2 25 | with: 26 | token: "${{ secrets.GITHUB_TOKEN }}" 27 | draft: true 28 | name: "${{ github.event.inputs.version }}" 29 | files: | 30 | releases/plugin.zip 31 | releases/mu-plugin.zip -------------------------------------------------------------------------------- /.github/workflows/phpcs.yml: -------------------------------------------------------------------------------- 1 | name: PHPCS 2 | run-name: Run PHPCS on `${{ github.head_ref || github.ref_name }}` 3 | 4 | on: 5 | push: 6 | branches: 7 | - master 8 | paths: 9 | - acf/*.php 10 | - constants/*.php 11 | - files/*.php 12 | - globals/*.php 13 | - heartbeat/*.php 14 | - image-sizes/*.php 15 | - paths/*.php 16 | - time/*.php 17 | - qmx-conditionals.php 18 | - qmx-time-hooks.php 19 | - query-monitor-extend.php 20 | - phpcs.xml 21 | - .github/workflows/phpcs.yml 22 | pull_request: 23 | paths: 24 | - acf/*.php 25 | - constants/*.php 26 | - files/*.php 27 | - globals/*.php 28 | - heartbeat/*.php 29 | - image-sizes/*.php 30 | - paths/*.php 31 | - time/*.php 32 | - qmx-conditionals.php 33 | - qmx-time-hooks.php 34 | - query-monitor-extend.php 35 | - phpcs.xml 36 | - .github/workflows/phpcs.yml 37 | workflow_dispatch: 38 | 39 | concurrency: 40 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 41 | cancel-in-progress: true 42 | 43 | jobs: 44 | phpcs: 45 | runs-on: ubuntu-latest 46 | steps: 47 | 48 | - uses: actions/checkout@v4 49 | 50 | - name: Detect File Changes 51 | uses: dorny/paths-filter@v3 52 | id: filter 53 | with: 54 | list-files: shell 55 | filters: | 56 | wpcontent: 57 | - added|modified: 'acf/*.php' 58 | - added|modified: 'constants/*.php' 59 | - added|modified: 'files/*.php' 60 | - added|modified: 'globals/*.php' 61 | - added|modified: 'heartbeat/*.php' 62 | - added|modified: 'image-sizes/*.php' 63 | - added|modified: 'paths/*.php' 64 | - added|modified: 'time/*.php' 65 | - added|modified: 'qmx-conditionals.php' 66 | - added|modified: 'qmx-time-hooks.php' 67 | - added|modified: 'query-monitor-extend.php' 68 | 69 | - name: Setup PHP 70 | if: ${{ steps.filter.outputs.wpcontent == 'true' }} 71 | uses: "shivammathur/setup-php@v2" 72 | with: 73 | php-version: "8.2" 74 | ini-values: "memory_limit=1G" 75 | coverage: none 76 | 77 | - name: Validate composer.json and composer.lock 78 | if: ${{ steps.filter.outputs.wpcontent == 'true' }} 79 | run: composer validate --no-check-publish 80 | 81 | - name: Install Composer dependencies 82 | if: ${{ steps.filter.outputs.wpcontent == 'true' }} 83 | run: | 84 | composer install --prefer-dist --no-progress --no-suggest --optimize-autoloader 85 | 86 | - name: Run PHPCS checks 87 | if: ${{ steps.filter.outputs.wpcontent == 'true' }} 88 | run: ./vendor/bin/phpcs ${{ steps.filter.outputs.wpcontent_files }} -------------------------------------------------------------------------------- /.github/workflows/phpstan.yml: -------------------------------------------------------------------------------- 1 | name: PHPStan 2 | run-name: Run PHPStan on `${{ github.head_ref || github.ref_name }}` 3 | 4 | on: 5 | push: 6 | branches: 7 | - master 8 | paths: 9 | - acf/*.php 10 | - constants/*.php 11 | - files/*.php 12 | - globals/*.php 13 | - heartbeat/*.php 14 | - image-sizes/*.php 15 | - paths/*.php 16 | - time/*.php 17 | - qmx-conditionals.php 18 | - qmx-time-hooks.php 19 | - query-monitor-extend.php 20 | - .github/workflows/phpstan.yml 21 | - phpstan-baseline.neon 22 | - phpstan.neon 23 | pull_request: 24 | paths: 25 | - acf/*.php 26 | - constants/*.php 27 | - files/*.php 28 | - globals/*.php 29 | - heartbeat/*.php 30 | - image-sizes/*.php 31 | - paths/*.php 32 | - time/*.php 33 | - qmx-conditionals.php 34 | - qmx-time-hooks.php 35 | - query-monitor-extend.php 36 | - phpcs.xml 37 | - .github/workflows/phpcs.yml 38 | workflow_dispatch: 39 | 40 | concurrency: 41 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 42 | cancel-in-progress: true 43 | 44 | jobs: 45 | phpstan: 46 | runs-on: ubuntu-latest 47 | steps: 48 | 49 | - uses: actions/checkout@v4 50 | 51 | - name: Detect File Changes 52 | uses: dorny/paths-filter@v3 53 | id: filter 54 | with: 55 | list-files: shell 56 | filters: | 57 | wpcontent: 58 | - added|modified: 'acf/*.php' 59 | - added|modified: 'constants/*.php' 60 | - added|modified: 'files/*.php' 61 | - added|modified: 'globals/*.php' 62 | - added|modified: 'heartbeat/*.php' 63 | - added|modified: 'image-sizes/*.php' 64 | - added|modified: 'paths/*.php' 65 | - added|modified: 'time/*.php' 66 | - added|modified: 'qmx-conditionals.php' 67 | - added|modified: 'qmx-time-hooks.php' 68 | - added|modified: 'query-monitor-extend.php' 69 | 70 | - name: Setup PHP 71 | if: ${{ steps.filter.outputs.wpcontent == 'true' }} 72 | uses: "shivammathur/setup-php@v2" 73 | with: 74 | php-version: "8.2" 75 | ini-values: "memory_limit=1G" 76 | coverage: none 77 | 78 | - name: Validate composer.json and composer.lock 79 | if: ${{ steps.filter.outputs.wpcontent == 'true' }} 80 | run: composer validate --no-check-publish 81 | 82 | - name: Install Composer dependencies 83 | if: ${{ steps.filter.outputs.wpcontent == 'true' }} 84 | run: | 85 | composer install --prefer-dist --no-progress --no-suggest --optimize-autoloader 86 | 87 | - name: Run PHPStan checks 88 | if: ${{ steps.filter.outputs.wpcontent == 'true' }} 89 | run: ./vendor/bin/phpstan analyse ${{ steps.filter.outputs.wpcontent_files }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /*.savedSearch 2 | /.phpstan-cache 3 | /vendor 4 | /releases -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /acf/qmx-acf-collector.php: -------------------------------------------------------------------------------- 1 | 7 | * @property-read QMX_Data_ACF $data 8 | */ 9 | class QMX_Collector_ACF extends QM_DataCollector { 10 | 11 | public $id = 'acf'; 12 | 13 | public function __construct() { 14 | parent::__construct(); 15 | 16 | add_action( 'acf/init', array( $this, 'action__acf_init' ) ); 17 | 18 | add_filter( 'acf/settings/load_json', array( $this, 'filter__acf_settings_load_json' ), 99999 ); 19 | add_filter( 'acf/pre_load_value', array( $this, 'filter__acf_pre_load_value' ), 10, 3 ); 20 | add_filter( 'acf/load_field_groups', array( $this, 'filter__acf_load_field_groups' ) ); 21 | 22 | // Set default value. Filter applied in output. 23 | $this->data->local_json['save'] = get_stylesheet_directory() . '/acf-json'; 24 | } 25 | 26 | public function get_storage(): QM_Data { 27 | require_once 'qmx-acf-data.php'; 28 | return new QMX_Data_ACF(); 29 | } 30 | 31 | public function process() { 32 | } 33 | 34 | /** 35 | * @param mixed $parent 36 | * @return mixed 37 | */ 38 | public static function get_fields_group( $parent ) { 39 | if ( is_null( $parent ) ) { 40 | return null; 41 | } 42 | 43 | $group = acf_get_field_group( $parent ); 44 | 45 | if ( false === $group ) { 46 | $field = acf_get_field( $parent ); 47 | 48 | if ( false === $field ) { 49 | return $parent; 50 | } 51 | 52 | return static::get_fields_group( $field['parent'] ); 53 | } 54 | 55 | return $group; 56 | } 57 | 58 | public function action__acf_init() : void { 59 | $files = acf_get_local_json_files(); 60 | $groups = array(); 61 | 62 | foreach ( $files as $group_key => $filepath ) { 63 | $groups[ $group_key ] = acf_get_field_group( $group_key ); 64 | } 65 | 66 | $this->data->local_json['groups'] = $groups; 67 | } 68 | 69 | /** 70 | * @param string[] $paths 71 | * @return string[] 72 | */ 73 | public function filter__acf_settings_load_json( array $paths ) : array { 74 | if ( did_action( 'qm/cease' ) ) { 75 | return $paths; 76 | } 77 | 78 | $this->data->local_json['load'] = $paths; 79 | 80 | return $paths; 81 | } 82 | 83 | /** 84 | * @return string[] 85 | */ 86 | protected static function get_start_trace_functions() { 87 | $functions = null; 88 | 89 | if ( ! is_null( $functions ) ) { 90 | return $functions; 91 | } 92 | 93 | $functions = (array) apply_filters( 'qmx/collector/acf/start_trace_functions', array( 94 | 'get_field', 95 | 'get_field_object', 96 | 'have_rows', 97 | ) ); 98 | 99 | $functions = array_filter( $functions, static function ( $item ) { 100 | return is_string( $item ); 101 | } ); 102 | 103 | return $functions; 104 | } 105 | 106 | /** 107 | * @param mixed $short_circuit 108 | * @param int|string $post_id 109 | * @param array $field 110 | * @return mixed 111 | */ 112 | public function filter__acf_pre_load_value( $short_circuit, $post_id, $field ) { 113 | if ( did_action( 'qm/cease' ) || ! function_exists( 'acf_get_valid_post_id' ) ) { 114 | return $short_circuit; 115 | } 116 | 117 | $args = array(); 118 | $full_stack_trace = apply_filters( 'qmx/collector/acf/full_stack_trace', is_admin(), $post_id, $field ); 119 | 120 | if ( ! $full_stack_trace ) { 121 | $args['ignore_hook'] = array( current_filter() ); 122 | }; 123 | 124 | $trace = new QM_Backtrace( $args ); 125 | 126 | if ( false === $full_stack_trace ) { 127 | foreach ( $trace->get_trace() as $frame ) { 128 | if ( 129 | in_array( $frame['function'], static::get_start_trace_functions() ) 130 | && false === stripos( $frame['file'], constant( 'ACF_PATH' ) ) 131 | ) { 132 | break; 133 | } 134 | 135 | if ( ! empty( $trace->get_trace()[1] ) ) { 136 | $trace->ignore( 1 ); 137 | } 138 | } 139 | } 140 | 141 | $row = array( 142 | 'field' => $field, 143 | 'post_id' => acf_get_valid_post_id( $post_id ), 144 | 'trace' => $trace, 145 | 'exists' => ! empty( $field['key'] ), 146 | 'caller' => $trace->get_trace()[0], 147 | 'group' => null, 148 | 'hash' => null, 149 | 'duplicate' => false, 150 | ); 151 | 152 | if ( ! empty( $field['key'] ) ) { 153 | $row['group'] = static::get_fields_group( $field['parent'] ); 154 | } 155 | 156 | $hash = md5( (string) json_encode( $row ) ); 157 | $row['hash'] = $hash; 158 | 159 | if ( array_key_exists( $hash, $this->data->counts ) ) { 160 | $this->data->counts[ $hash ]++; 161 | 162 | if ( apply_filters( 'qmx/collector/acf/hide_duplicates', false ) ) { 163 | return $short_circuit; 164 | } 165 | 166 | $row['duplicate'] = true; 167 | } 168 | 169 | if ( ! empty( $field['key'] ) ) { 170 | $this->data->field_keys[ $field['key'] ] = $field['name']; 171 | } else { 172 | $this->data->field_keys[ $field['name'] ] = $field['name']; 173 | } 174 | 175 | if ( ! empty( $row['group'] ) ) { 176 | $key = $row['group']; 177 | $title = $key; 178 | 179 | if ( is_array( $row['group'] ) ) { 180 | $key = $row['group']['key']; 181 | $title = $row['group']['title']; 182 | } 183 | 184 | $this->data->field_groups[ $key ] = $title; 185 | } 186 | 187 | $this->data->post_ids[ ( string ) $post_id ] = $post_id; 188 | $this->data->callers[ $row['caller']['function'] . '()' ] = 1; 189 | $this->data->counts[ $hash ] = 1; 190 | $this->data->fields[] = $row; 191 | 192 | return $short_circuit; 193 | } 194 | 195 | /** 196 | * @param array $field_groups 197 | * @return array 198 | */ 199 | public function filter__acf_load_field_groups( array $field_groups ) : array { 200 | static $processed = array(); 201 | 202 | if ( empty( $field_groups ) ) { 203 | return $field_groups; 204 | } 205 | 206 | $hash = wp_hash( (string) json_encode( $field_groups ) ); 207 | 208 | if ( in_array( $hash, $processed ) ) { 209 | return $field_groups; 210 | } 211 | 212 | $processed[] = $hash; 213 | 214 | foreach ( $field_groups as $field_group ) { 215 | $key = wp_hash( (string) json_encode( $field_group ) ); 216 | 217 | if ( array_key_exists( $key, $this->data->loaded_field_groups ) ) { 218 | continue; 219 | } 220 | 221 | $this->data->loaded_field_groups[ $key ] = array( 222 | 'id' => $field_group['ID'], 223 | 'group' => $field_group['key'], 224 | 'title' => $field_group['title'], 225 | 'rules' => $field_group['location'], 226 | ); 227 | } 228 | 229 | return $field_groups; 230 | } 231 | 232 | public function get_concerned_actions() { 233 | $actions = array( 234 | 'acf/init', 235 | ); 236 | 237 | return $actions; 238 | } 239 | 240 | public function get_concerned_filters() { 241 | $filters = array( 242 | 'acf/is_field_group_key', 243 | 'acf/is_field_key', 244 | 'acf/load_field_group', 245 | 'acf/pre_load_post_id', 246 | 'acf/validate_post_id', 247 | 'acf/pre_load_value', 248 | 'acf/load_value', 249 | 'acf/settings/load_json', 250 | ); 251 | 252 | if ( is_admin() ) { 253 | $filters = array_merge( $filters, array( 254 | 'acf/settings/save_json', 255 | 'acf/load_field_groups', 256 | ) ); 257 | } 258 | 259 | sort( $filters, SORT_STRING ); 260 | 261 | return $filters; 262 | } 263 | 264 | public function get_concerned_constants() { 265 | return array( 266 | 'ACF_LITE', 267 | ); 268 | } 269 | 270 | } 271 | 272 | QM_Collectors::add( new QMX_Collector_ACF ); 273 | -------------------------------------------------------------------------------- /acf/qmx-acf-data.php: -------------------------------------------------------------------------------- 1 | $fields 9 | */ 10 | public $fields = array(); 11 | 12 | /** 13 | * @var array $field_keys 14 | */ 15 | public $field_keys = array(); 16 | 17 | /** 18 | * @var array $post_ids 19 | */ 20 | public $post_ids = array(); 21 | 22 | /** 23 | * @var array $callers 24 | */ 25 | public $callers = array(); 26 | 27 | /** 28 | * @var array $counts 29 | */ 30 | public $counts = array(); 31 | 32 | /** 33 | * @var array $field_groups 34 | */ 35 | public $field_groups = array(); 36 | 37 | /** 38 | * @var array $local_json 39 | */ 40 | public $local_json = array(); 41 | 42 | /** 43 | * @var array $loaded_field_groups 44 | */ 45 | public $loaded_field_groups = array(); 46 | 47 | } 48 | -------------------------------------------------------------------------------- /acf/qmx-acf-output.php: -------------------------------------------------------------------------------- 1 | collector->get_data(); 35 | 36 | $this->output_fields_table(); 37 | $this->output_local_json(); 38 | $this->output_concerns(); 39 | 40 | if ( is_admin() ) { 41 | $this->output_field_groups_table(); 42 | } 43 | } 44 | 45 | protected function output_fields_table() : void { 46 | /** @var QMX_Data_ACF */ 47 | $data = $this->collector->get_data(); 48 | 49 | if ( empty( $data->fields ) ) { 50 | $this->before_non_tabular_output(); 51 | echo '

No Advanced Custom Fields found.

'; 52 | $this->after_non_tabular_output(); 53 | return; 54 | } 55 | 56 | echo ''; 57 | 58 | natsort( $data->field_keys ); 59 | natsort( $data->post_ids ); 60 | natsort( $data->field_groups ); 61 | natsort( $data->callers ); 62 | 63 | $this->before_tabular_output(); 64 | 65 | echo ''; 66 | 67 | echo ''; 68 | 69 | echo ''; 70 | echo $this->build_sorter( '#' ); 71 | echo ''; 72 | 73 | echo ''; 74 | echo $this->build_filter( 75 | 'acf-field', 76 | $data->field_keys, 77 | __( 'Field', 'query-monitor' ), 78 | array( 79 | 'prepend' => array( 80 | 'qmx-acf-no-field' => 'Missing', 81 | ), 82 | ) 83 | ); 84 | echo ''; 85 | 86 | echo ''; 87 | echo $this->build_filter( 'acf-post', $data->post_ids, __( 'Post ID', 'query-monitor' ) ); 88 | echo ''; 89 | 90 | echo ''; 91 | echo $this->build_filter( 'acf-group', $data->field_groups, __( 'Group', 'query-monitor' ) ); 92 | echo ''; 93 | 94 | echo ''; 95 | echo $this->build_filter( 'acf-caller', array_keys( $data->callers ), __( 'Caller', 'query-monitor' ) ); 96 | echo ''; 97 | 98 | echo ''; 99 | 100 | echo ''; 101 | 102 | echo ''; 103 | 104 | foreach ( $data->fields as $row_num => $row ) { 105 | $row_attr = array( 106 | 'class' => '', 107 | ); 108 | 109 | if ( ! $row['exists'] ) { 110 | $row_attr['class'] .= ' qm-warn'; 111 | } 112 | 113 | $row_attr['data-qm-acf-field'] = $row['field']['name'] . ' ' . $row['field']['key']; 114 | $row_attr['data-qm-acf-post'] = $row['post_id']; 115 | $row_attr['data-qm-acf-caller'] = $row['caller']['function'] . '()'; 116 | $row_attr['data-qm-acf-group'] = 'qmx-acf-no-group'; 117 | 118 | if ( empty( $row['field']['key'] ) ) { 119 | $row_attr['data-qm-acf-field'] .= ' qmx-acf-no-field'; 120 | } 121 | 122 | if ( ! empty( $row['group'] ) ) { 123 | $row_attr['data-qm-acf-group'] = $row['group']['key']; 124 | } 125 | 126 | if ( 127 | ! empty( $row['duplicate'] ) 128 | && static::identify_duplicates() 129 | ) { 130 | $row_attr['class'] .= ' qm-highlight'; 131 | } 132 | 133 | $attr = ''; 134 | 135 | foreach ( $row_attr as $a => $v ) { 136 | $attr .= ' ' . $a . '="' . esc_attr( trim( ( string ) $v ) ) . '"'; 137 | } 138 | 139 | echo ''; 140 | 141 | # Number 142 | echo '' . esc_html( (string) ( $row_num + 1 ) ) . ''; 143 | 144 | # Field name 145 | echo ''; 146 | $this->output_column_field_name( $row ); 147 | echo ''; 148 | 149 | # Post ID 150 | echo '' . esc_html( $row['post_id'] ) . ''; 151 | 152 | # Field group 153 | echo ''; 154 | $this->output_column_field_group( $row ); 155 | echo ''; 156 | 157 | # Caller 158 | echo ''; 159 | $this->output_column_caller( $row ); 160 | echo ''; 161 | 162 | echo ''; 163 | } 164 | 165 | echo ''; 166 | 167 | echo ''; 168 | echo ''; 169 | printf( 'Total: %d', count( $data->fields ) ); 170 | echo ''; 171 | echo ''; 172 | 173 | echo ''; 174 | echo ''; 175 | } 176 | 177 | protected function output_field_groups_table() : void { 178 | /** @var QMX_Data_ACF */ 179 | $data = $this->collector->get_data(); 180 | $id = 'qm-acf-loaded_field_groups'; 181 | $name = 'Advanced Custom Fields: Loaded Field Groups'; 182 | 183 | printf( 184 | '
', 185 | esc_attr( $id ) 186 | ); 187 | 188 | echo ''; 189 | 190 | printf( 191 | '', 192 | esc_attr( $id ), 193 | esc_html( $name ) 194 | ); 195 | 196 | echo ''; 197 | 198 | echo ''; 199 | 200 | echo ''; 203 | 204 | echo ''; 207 | 208 | echo ''; 211 | 212 | echo ''; 213 | 214 | echo ''; 215 | 216 | echo ''; 217 | 218 | $row_num = 1; 219 | 220 | foreach ( $data->loaded_field_groups as $row ) { 221 | $class = ''; 222 | 223 | if ( 1 === ( $row_num % 2 ) ) { 224 | $class = ' class="qm-odd"'; 225 | } 226 | 227 | echo ''; 228 | 229 | # Field group name 230 | echo ''; 233 | 234 | # Field group key 235 | echo ''; 238 | 239 | # Field group rules 240 | echo ''; 243 | 244 | echo ''; 245 | 246 | $row_num++; 247 | } 248 | 249 | echo ''; 250 | 251 | echo ''; 252 | echo ''; 253 | printf( '', count( $data->loaded_field_groups ) ); 254 | echo ''; 255 | echo ''; 256 | 257 | echo '

%2$s

'; 201 | echo esc_html__( 'Field Group', 'query-monitor-extend' ); 202 | echo ''; 205 | echo esc_html__( 'Key', 'query-monitor-extend' ); 206 | echo ''; 209 | echo esc_html__( 'Rules', 'query-monitor-extend' ); 210 | echo '
'; 231 | $this->output_column_field_group_title( $row ); 232 | echo ''; 236 | $this->output_column_field_group_key( $row ); 237 | echo ''; 241 | $this->output_column_field_group_rules( $row ); 242 | echo '
Total: %d
'; 258 | echo '
'; 259 | } 260 | 261 | /** 262 | * @param array $row 263 | */ 264 | protected function output_column_field_name( array $row ) : void { 265 | echo esc_html( $row['field']['name'] ); 266 | 267 | if ( ! $row['exists'] ) { 268 | echo ' (Missing)'; 269 | return; 270 | } 271 | 272 | if ( 273 | ! empty( $row['duplicate'] ) 274 | && static::identify_duplicates() 275 | ) { 276 | echo ' (Duplicate)'; 277 | } 278 | 279 | $parent = $row['field']['parent']; 280 | 281 | if ( ! empty( $row['group'] ) ) { 282 | $parent = $row['group']['key']; 283 | } 284 | 285 | echo self::build_toggler(); 286 | 287 | echo '
'; 288 | echo esc_html( 'Key: ' . $row['field']['key'] ); 289 | echo '
' . esc_html( 'Parent: ' . $parent ); 290 | echo '
'; 291 | } 292 | 293 | /** 294 | * @param array $row 295 | */ 296 | protected function output_column_field_group_title( array $row ) : void { 297 | $title = $row['title']; 298 | 299 | if ( empty( $title ) ) { 300 | return; 301 | } 302 | 303 | if ( empty( $row['id'] ) || ! current_user_can( 'edit_post', $row['id'] ) ) { 304 | echo esc_html( $title ); 305 | return; 306 | } 307 | 308 | $url = add_query_arg( array( 309 | 'post' => $row['id'], 310 | 'action' => 'edit', 311 | ), admin_url( 'post.php' ) ); 312 | 313 | printf( 314 | '%2$s%3$s', 315 | esc_url( $url ), 316 | esc_html( $title ), 317 | QueryMonitor::icon( 'edit' ) 318 | ); 319 | } 320 | 321 | /** 322 | * @param array $row 323 | */ 324 | protected function output_column_field_group_key( array $row ) : void { 325 | /** @var QMX_Data_ACF */ 326 | $data = $this->collector->get_data(); 327 | $group = $row['group']; 328 | 329 | if ( empty( $group ) ) { 330 | return; 331 | } 332 | 333 | if ( 334 | ! function_exists( 'acf_is_local_field_group' ) 335 | || ! acf_is_local_field_group( $group ) 336 | || ! array_key_exists( $group, $data->local_json['groups'] ) 337 | ) { 338 | echo esc_html( $group ); 339 | return; 340 | } 341 | 342 | $filepath = $data->local_json['groups'][ $group ]['local_file']; 343 | 344 | if ( ! file_exists( $filepath ) ) { 345 | echo esc_html( $group ); 346 | return; 347 | } 348 | 349 | echo QM_Output_Html::output_filename( $group, $filepath ); 350 | } 351 | 352 | /** 353 | * @param array $row 354 | */ 355 | protected function output_column_field_group( array $row ) : void { 356 | $group = $row['group']; 357 | 358 | if ( empty( $group ) ) { 359 | return; 360 | } 361 | 362 | if ( empty( $group['ID'] ) || ! current_user_can( 'edit_post', $group['ID'] ) ) { 363 | echo esc_html( $group['title'] ); 364 | return; 365 | } 366 | 367 | $url = add_query_arg( array( 368 | 'post' => $group['ID'], 369 | 'action' => 'edit', 370 | ), admin_url( 'post.php' ) ); 371 | 372 | printf( 373 | '%2$s%3$s', 374 | esc_url( $url ), 375 | esc_html( $group['title'] ), 376 | QueryMonitor::icon( 'edit' ) 377 | ); 378 | } 379 | 380 | /** 381 | * @param array $row 382 | */ 383 | protected function output_column_field_group_rules( array $row ) : void { 384 | $rules = $row['rules']; 385 | 386 | if ( empty( $rules ) ) { 387 | return; 388 | } 389 | 390 | echo '
';
391 | 		print_r( $rules );
392 | 		echo '
'; 393 | } 394 | 395 | /** 396 | * @param array $row 397 | */ 398 | protected function output_column_caller( array $row ) : void { 399 | $trace = $row['trace']; 400 | $filtered_trace = $trace->get_display_trace(); 401 | $caller_name = self::output_filename( $row['caller']['function'] . '()', $row['caller']['file'], $row['caller']['line'] ); 402 | $stack = array(); 403 | 404 | array_shift( $filtered_trace ); 405 | 406 | foreach ( $filtered_trace as $item ) { 407 | $item = wp_parse_args( $item, array( 408 | 'file' => '', 409 | 'line' => '', 410 | ) ); 411 | 412 | if ( empty( $item['display'] ) ) { 413 | continue; 414 | } 415 | 416 | $stack[] = self::output_filename( $item['display'], $item['file'], $item['line'] ); 417 | } 418 | 419 | if ( 1 < count( $stack ) ) { 420 | echo self::build_toggler(); 421 | } 422 | 423 | echo '
    '; 424 | printf( '
  1. %s
  2. ', $caller_name ); 425 | 426 | if ( 1 < count( $stack ) ) { 427 | echo '
  3. ' . implode( '
  4. ', $stack ) . '
  5. '; 428 | } 429 | 430 | echo '
'; 431 | } 432 | 433 | protected function output_local_json() : void { 434 | $id = 'qm-acf-local_json'; 435 | $name = 'Advanced Custom Fields: Local JSON'; 436 | $data = $this->collector->get_data(); 437 | 438 | printf( 439 | '
', 440 | esc_attr( $id ) 441 | ); 442 | 443 | echo ''; 444 | 445 | printf( 446 | '', 447 | esc_attr( $id ), 448 | esc_html( $name ), 449 | 'Documentation' 450 | ); 451 | 452 | echo ''; 453 | echo ''; 454 | echo ''; 455 | echo ''; 456 | echo ''; 457 | echo ''; 458 | echo ''; 459 | 460 | echo ''; 461 | 462 | if ( ! empty( $data->local_json['save'] ) ) { 463 | $directory = apply_filters( 'acf/settings/save_json', $data->local_json['save'] ); 464 | echo ''; 465 | echo ''; 466 | echo ''; 467 | echo ''; 468 | echo ''; 469 | } 470 | 471 | if ( ! empty( $data->local_json['load'] ) ) { 472 | $i = 0; 473 | 474 | foreach ( $data->local_json['load'] as $path ) { 475 | echo ''; 476 | 477 | if ( 0 === $i ) { 478 | echo ''; 479 | echo ''; 480 | } 481 | 482 | echo ''; 483 | echo ''; 484 | 485 | echo ''; 486 | 487 | $i++; 488 | } 489 | } 490 | 491 | echo ''; 492 | echo '

%2$s
%3$s

ActionHookPaths
Saveacf/settings/save_json' . QM_Output_Html::output_filename( $this->remove_abspath( $directory ), $directory ) . '
Loadacf/settings/load_json' . esc_html( (string) $i ) . '' . QM_Output_Html::output_filename( $this->remove_abspath( $path ), $path ) . '
'; 493 | 494 | $this->output_local_json_field_groups(); 495 | 496 | echo '
'; 497 | } 498 | 499 | protected function output_local_json_field_groups() : void { 500 | $data = $this->collector->get_data(); 501 | 502 | if ( empty( $data->local_json['groups'] ) ) { 503 | echo '

No local JSON field groups found.

'; 504 | return; 505 | } 506 | 507 | $groups = array(); 508 | 509 | foreach ( $data->local_json['groups'] as $group ) { 510 | $groups[ $group['title'] ] = $group; 511 | } 512 | 513 | ksort( $groups, SORT_NATURAL ); 514 | 515 | echo ''; 516 | echo ''; 517 | echo ''; 518 | echo ''; 519 | echo ''; 520 | echo ''; 521 | echo ''; 522 | echo ''; 523 | echo ''; 524 | echo ''; 525 | 526 | foreach ( $groups as $group ) { 527 | printf( 528 | '', 529 | esc_html( $group['title'] ), 530 | esc_html( $group['key'] ), 531 | QM_Output_Html::output_filename( $this->remove_abspath( $group['local_file'] ), $group['local_file'] ) 532 | ); 533 | } 534 | 535 | echo ''; 536 | printf( '', count( $data->local_json['groups'] ) ); 537 | echo '

Field Groups

TitleKeyFile
%s%s%s
Total: %d
'; 538 | } 539 | 540 | public function remove_abspath( string $path ) : string { 541 | return str_replace( ABSPATH, '', $path ); 542 | } 543 | 544 | /** 545 | * @param array> $menu 546 | * @return array> 547 | */ 548 | public function panel_menu( array $menu ) { 549 | /** @var QMX_Data_ACF */ 550 | $data = $this->collector->get_data(); 551 | 552 | if ( empty( $data->local_json['groups'] ) ) { 553 | $data->local_json['groups'] = array(); 554 | } 555 | 556 | $menu['qm-acf'] = $this->menu( array( 557 | 'title' => esc_html__( 'Advanced Custom Fields', 'query-monitor-extend' ), 558 | 'id' => 'query-monitor-extend-acf', 559 | ) ); 560 | 561 | if ( is_admin() ) { 562 | $menu['qm-acf']['children']['loaded_field_groups'] = array( 563 | 'title' => esc_html__( 'Field Groups', 'query-monitor-extend' ) . sprintf( ' (%d)', count( $data->loaded_field_groups ) ), 564 | 'href' => '#qm-acf-loaded_field_groups', 565 | 'id' => 'query-monitor-extend-acf-loaded_field_groups', 566 | ); 567 | } 568 | 569 | $menu['qm-acf']['children']['local_json'] = array( 570 | 'title' => esc_html__( 'Local JSON', 'query-monitor-extend' ) . sprintf( ' (%d)', count( $data->local_json['groups'] ) ), 571 | 'href' => '#qm-acf-local_json', 572 | 'id' => 'query-monitor-extend-acf-local_json', 573 | ); 574 | 575 | return $menu; 576 | } 577 | } 578 | 579 | add_filter( 'qm/outputter/html', static function ( array $output ) : array { 580 | if ( $collector = QM_Collectors::get( 'acf' ) ) { 581 | $output['acf'] = new QMX_Output_Html_ACF( $collector ); 582 | } 583 | 584 | return $output; 585 | }, 70 ); 586 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crstauf/query-monitor-extend", 3 | "description": "Query Monitor Extend", 4 | "license": "GPL-3.0", 5 | "type": "wordpress-plugin", 6 | "authors": [ 7 | { 8 | "name": "Caleb Stauffer", 9 | "homepage": "https://develop.calebstauffer.com" 10 | } 11 | ], 12 | "require-dev": { 13 | "automattic/vipwpcs": "^3.0", 14 | "szepeviktor/phpstan-wordpress": "^1.1", 15 | "phpstan/extension-installer": "^1.1", 16 | "php-stubs/acf-pro-stubs": "^6.0", 17 | "php-stubs/wordpress-stubs": "^6.0", 18 | "squizlabs/php_codesniffer": "^3.7", 19 | "mediawiki/mediawiki-codesniffer": "^41.0", 20 | "cweagans/composer-patches": "~1.0", 21 | "johnbillion/query-monitor": "^3.13" 22 | }, 23 | "config": { 24 | "allow-plugins": { 25 | "dealerdirect/phpcodesniffer-composer-installer": true, 26 | "phpstan/extension-installer": true, 27 | "cweagans/composer-patches": true, 28 | "composer/installers": true 29 | } 30 | }, 31 | "extra": { 32 | "installer-paths": { 33 | "vendor/wordpress-plugins/{$name}/": [ 34 | "type:wordpress-plugin" 35 | ] 36 | }, 37 | "patches": { 38 | "php-stubs/acf-pro-stubs": { 39 | "PHPStan return types": "dev/patches/acf-pro-stubs.patch" 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /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": "c15e1991174264259e3bed8dad689df6", 8 | "packages": [], 9 | "packages-dev": [ 10 | { 11 | "name": "automattic/vipwpcs", 12 | "version": "3.0.0", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/Automattic/VIP-Coding-Standards.git", 16 | "reference": "1b8960ebff9ea3eb482258a906ece4d1ee1e25fd" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/Automattic/VIP-Coding-Standards/zipball/1b8960ebff9ea3eb482258a906ece4d1ee1e25fd", 21 | "reference": "1b8960ebff9ea3eb482258a906ece4d1ee1e25fd", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "php": ">=5.4", 26 | "phpcsstandards/phpcsextra": "^1.1.0", 27 | "phpcsstandards/phpcsutils": "^1.0.8", 28 | "sirbrillig/phpcs-variable-analysis": "^2.11.17", 29 | "squizlabs/php_codesniffer": "^3.7.2", 30 | "wp-coding-standards/wpcs": "^3.0" 31 | }, 32 | "require-dev": { 33 | "php-parallel-lint/php-console-highlighter": "^1.0.0", 34 | "php-parallel-lint/php-parallel-lint": "^1.3.2", 35 | "phpcompatibility/php-compatibility": "^9", 36 | "phpcsstandards/phpcsdevtools": "^1.0", 37 | "phpunit/phpunit": "^4 || ^5 || ^6 || ^7" 38 | }, 39 | "type": "phpcodesniffer-standard", 40 | "notification-url": "https://packagist.org/downloads/", 41 | "license": [ 42 | "MIT" 43 | ], 44 | "authors": [ 45 | { 46 | "name": "Contributors", 47 | "homepage": "https://github.com/Automattic/VIP-Coding-Standards/graphs/contributors" 48 | } 49 | ], 50 | "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress VIP minimum coding conventions", 51 | "keywords": [ 52 | "phpcs", 53 | "standards", 54 | "static analysis", 55 | "wordpress" 56 | ], 57 | "support": { 58 | "issues": "https://github.com/Automattic/VIP-Coding-Standards/issues", 59 | "source": "https://github.com/Automattic/VIP-Coding-Standards", 60 | "wiki": "https://github.com/Automattic/VIP-Coding-Standards/wiki" 61 | }, 62 | "time": "2023-09-05T11:01:05+00:00" 63 | }, 64 | { 65 | "name": "composer/installers", 66 | "version": "v2.2.0", 67 | "source": { 68 | "type": "git", 69 | "url": "https://github.com/composer/installers.git", 70 | "reference": "c29dc4b93137acb82734f672c37e029dfbd95b35" 71 | }, 72 | "dist": { 73 | "type": "zip", 74 | "url": "https://api.github.com/repos/composer/installers/zipball/c29dc4b93137acb82734f672c37e029dfbd95b35", 75 | "reference": "c29dc4b93137acb82734f672c37e029dfbd95b35", 76 | "shasum": "" 77 | }, 78 | "require": { 79 | "composer-plugin-api": "^1.0 || ^2.0", 80 | "php": "^7.2 || ^8.0" 81 | }, 82 | "require-dev": { 83 | "composer/composer": "1.6.* || ^2.0", 84 | "composer/semver": "^1 || ^3", 85 | "phpstan/phpstan": "^0.12.55", 86 | "phpstan/phpstan-phpunit": "^0.12.16", 87 | "symfony/phpunit-bridge": "^5.3", 88 | "symfony/process": "^5" 89 | }, 90 | "type": "composer-plugin", 91 | "extra": { 92 | "class": "Composer\\Installers\\Plugin", 93 | "branch-alias": { 94 | "dev-main": "2.x-dev" 95 | }, 96 | "plugin-modifies-install-path": true 97 | }, 98 | "autoload": { 99 | "psr-4": { 100 | "Composer\\Installers\\": "src/Composer/Installers" 101 | } 102 | }, 103 | "notification-url": "https://packagist.org/downloads/", 104 | "license": [ 105 | "MIT" 106 | ], 107 | "authors": [ 108 | { 109 | "name": "Kyle Robinson Young", 110 | "email": "kyle@dontkry.com", 111 | "homepage": "https://github.com/shama" 112 | } 113 | ], 114 | "description": "A multi-framework Composer library installer", 115 | "homepage": "https://composer.github.io/installers/", 116 | "keywords": [ 117 | "Dolibarr", 118 | "Eliasis", 119 | "Hurad", 120 | "ImageCMS", 121 | "Kanboard", 122 | "Lan Management System", 123 | "MODX Evo", 124 | "MantisBT", 125 | "Mautic", 126 | "Maya", 127 | "OXID", 128 | "Plentymarkets", 129 | "Porto", 130 | "RadPHP", 131 | "SMF", 132 | "Starbug", 133 | "Thelia", 134 | "Whmcs", 135 | "WolfCMS", 136 | "agl", 137 | "annotatecms", 138 | "attogram", 139 | "bitrix", 140 | "cakephp", 141 | "chef", 142 | "cockpit", 143 | "codeigniter", 144 | "concrete5", 145 | "croogo", 146 | "dokuwiki", 147 | "drupal", 148 | "eZ Platform", 149 | "elgg", 150 | "expressionengine", 151 | "fuelphp", 152 | "grav", 153 | "installer", 154 | "itop", 155 | "known", 156 | "kohana", 157 | "laravel", 158 | "lavalite", 159 | "lithium", 160 | "magento", 161 | "majima", 162 | "mako", 163 | "matomo", 164 | "mediawiki", 165 | "miaoxing", 166 | "modulework", 167 | "modx", 168 | "moodle", 169 | "osclass", 170 | "pantheon", 171 | "phpbb", 172 | "piwik", 173 | "ppi", 174 | "processwire", 175 | "puppet", 176 | "pxcms", 177 | "reindex", 178 | "roundcube", 179 | "shopware", 180 | "silverstripe", 181 | "sydes", 182 | "sylius", 183 | "tastyigniter", 184 | "wordpress", 185 | "yawik", 186 | "zend", 187 | "zikula" 188 | ], 189 | "support": { 190 | "issues": "https://github.com/composer/installers/issues", 191 | "source": "https://github.com/composer/installers/tree/v2.2.0" 192 | }, 193 | "funding": [ 194 | { 195 | "url": "https://packagist.com", 196 | "type": "custom" 197 | }, 198 | { 199 | "url": "https://github.com/composer", 200 | "type": "github" 201 | }, 202 | { 203 | "url": "https://tidelift.com/funding/github/packagist/composer/composer", 204 | "type": "tidelift" 205 | } 206 | ], 207 | "time": "2022-08-20T06:45:11+00:00" 208 | }, 209 | { 210 | "name": "composer/semver", 211 | "version": "3.3.2", 212 | "source": { 213 | "type": "git", 214 | "url": "https://github.com/composer/semver.git", 215 | "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" 216 | }, 217 | "dist": { 218 | "type": "zip", 219 | "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", 220 | "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", 221 | "shasum": "" 222 | }, 223 | "require": { 224 | "php": "^5.3.2 || ^7.0 || ^8.0" 225 | }, 226 | "require-dev": { 227 | "phpstan/phpstan": "^1.4", 228 | "symfony/phpunit-bridge": "^4.2 || ^5" 229 | }, 230 | "type": "library", 231 | "extra": { 232 | "branch-alias": { 233 | "dev-main": "3.x-dev" 234 | } 235 | }, 236 | "autoload": { 237 | "psr-4": { 238 | "Composer\\Semver\\": "src" 239 | } 240 | }, 241 | "notification-url": "https://packagist.org/downloads/", 242 | "license": [ 243 | "MIT" 244 | ], 245 | "authors": [ 246 | { 247 | "name": "Nils Adermann", 248 | "email": "naderman@naderman.de", 249 | "homepage": "http://www.naderman.de" 250 | }, 251 | { 252 | "name": "Jordi Boggiano", 253 | "email": "j.boggiano@seld.be", 254 | "homepage": "http://seld.be" 255 | }, 256 | { 257 | "name": "Rob Bast", 258 | "email": "rob.bast@gmail.com", 259 | "homepage": "http://robbast.nl" 260 | } 261 | ], 262 | "description": "Semver library that offers utilities, version constraint parsing and validation.", 263 | "keywords": [ 264 | "semantic", 265 | "semver", 266 | "validation", 267 | "versioning" 268 | ], 269 | "support": { 270 | "irc": "irc://irc.freenode.org/composer", 271 | "issues": "https://github.com/composer/semver/issues", 272 | "source": "https://github.com/composer/semver/tree/3.3.2" 273 | }, 274 | "funding": [ 275 | { 276 | "url": "https://packagist.com", 277 | "type": "custom" 278 | }, 279 | { 280 | "url": "https://github.com/composer", 281 | "type": "github" 282 | }, 283 | { 284 | "url": "https://tidelift.com/funding/github/packagist/composer/composer", 285 | "type": "tidelift" 286 | } 287 | ], 288 | "time": "2022-04-01T19:23:25+00:00" 289 | }, 290 | { 291 | "name": "composer/spdx-licenses", 292 | "version": "1.5.7", 293 | "source": { 294 | "type": "git", 295 | "url": "https://github.com/composer/spdx-licenses.git", 296 | "reference": "c848241796da2abf65837d51dce1fae55a960149" 297 | }, 298 | "dist": { 299 | "type": "zip", 300 | "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/c848241796da2abf65837d51dce1fae55a960149", 301 | "reference": "c848241796da2abf65837d51dce1fae55a960149", 302 | "shasum": "" 303 | }, 304 | "require": { 305 | "php": "^5.3.2 || ^7.0 || ^8.0" 306 | }, 307 | "require-dev": { 308 | "phpstan/phpstan": "^0.12.55", 309 | "symfony/phpunit-bridge": "^4.2 || ^5" 310 | }, 311 | "type": "library", 312 | "extra": { 313 | "branch-alias": { 314 | "dev-main": "1.x-dev" 315 | } 316 | }, 317 | "autoload": { 318 | "psr-4": { 319 | "Composer\\Spdx\\": "src" 320 | } 321 | }, 322 | "notification-url": "https://packagist.org/downloads/", 323 | "license": [ 324 | "MIT" 325 | ], 326 | "authors": [ 327 | { 328 | "name": "Nils Adermann", 329 | "email": "naderman@naderman.de", 330 | "homepage": "http://www.naderman.de" 331 | }, 332 | { 333 | "name": "Jordi Boggiano", 334 | "email": "j.boggiano@seld.be", 335 | "homepage": "http://seld.be" 336 | }, 337 | { 338 | "name": "Rob Bast", 339 | "email": "rob.bast@gmail.com", 340 | "homepage": "http://robbast.nl" 341 | } 342 | ], 343 | "description": "SPDX licenses list and validation library.", 344 | "keywords": [ 345 | "license", 346 | "spdx", 347 | "validator" 348 | ], 349 | "support": { 350 | "irc": "irc://irc.freenode.org/composer", 351 | "issues": "https://github.com/composer/spdx-licenses/issues", 352 | "source": "https://github.com/composer/spdx-licenses/tree/1.5.7" 353 | }, 354 | "funding": [ 355 | { 356 | "url": "https://packagist.com", 357 | "type": "custom" 358 | }, 359 | { 360 | "url": "https://github.com/composer", 361 | "type": "github" 362 | }, 363 | { 364 | "url": "https://tidelift.com/funding/github/packagist/composer/composer", 365 | "type": "tidelift" 366 | } 367 | ], 368 | "time": "2022-05-23T07:37:50+00:00" 369 | }, 370 | { 371 | "name": "cweagans/composer-patches", 372 | "version": "1.7.3", 373 | "source": { 374 | "type": "git", 375 | "url": "https://github.com/cweagans/composer-patches.git", 376 | "reference": "e190d4466fe2b103a55467dfa83fc2fecfcaf2db" 377 | }, 378 | "dist": { 379 | "type": "zip", 380 | "url": "https://api.github.com/repos/cweagans/composer-patches/zipball/e190d4466fe2b103a55467dfa83fc2fecfcaf2db", 381 | "reference": "e190d4466fe2b103a55467dfa83fc2fecfcaf2db", 382 | "shasum": "" 383 | }, 384 | "require": { 385 | "composer-plugin-api": "^1.0 || ^2.0", 386 | "php": ">=5.3.0" 387 | }, 388 | "require-dev": { 389 | "composer/composer": "~1.0 || ~2.0", 390 | "phpunit/phpunit": "~4.6" 391 | }, 392 | "type": "composer-plugin", 393 | "extra": { 394 | "class": "cweagans\\Composer\\Patches" 395 | }, 396 | "autoload": { 397 | "psr-4": { 398 | "cweagans\\Composer\\": "src" 399 | } 400 | }, 401 | "notification-url": "https://packagist.org/downloads/", 402 | "license": [ 403 | "BSD-3-Clause" 404 | ], 405 | "authors": [ 406 | { 407 | "name": "Cameron Eagans", 408 | "email": "me@cweagans.net" 409 | } 410 | ], 411 | "description": "Provides a way to patch Composer packages.", 412 | "support": { 413 | "issues": "https://github.com/cweagans/composer-patches/issues", 414 | "source": "https://github.com/cweagans/composer-patches/tree/1.7.3" 415 | }, 416 | "time": "2022-12-20T22:53:13+00:00" 417 | }, 418 | { 419 | "name": "dealerdirect/phpcodesniffer-composer-installer", 420 | "version": "v1.0.0", 421 | "source": { 422 | "type": "git", 423 | "url": "https://github.com/PHPCSStandards/composer-installer.git", 424 | "reference": "4be43904336affa5c2f70744a348312336afd0da" 425 | }, 426 | "dist": { 427 | "type": "zip", 428 | "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da", 429 | "reference": "4be43904336affa5c2f70744a348312336afd0da", 430 | "shasum": "" 431 | }, 432 | "require": { 433 | "composer-plugin-api": "^1.0 || ^2.0", 434 | "php": ">=5.4", 435 | "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" 436 | }, 437 | "require-dev": { 438 | "composer/composer": "*", 439 | "ext-json": "*", 440 | "ext-zip": "*", 441 | "php-parallel-lint/php-parallel-lint": "^1.3.1", 442 | "phpcompatibility/php-compatibility": "^9.0", 443 | "yoast/phpunit-polyfills": "^1.0" 444 | }, 445 | "type": "composer-plugin", 446 | "extra": { 447 | "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" 448 | }, 449 | "autoload": { 450 | "psr-4": { 451 | "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" 452 | } 453 | }, 454 | "notification-url": "https://packagist.org/downloads/", 455 | "license": [ 456 | "MIT" 457 | ], 458 | "authors": [ 459 | { 460 | "name": "Franck Nijhof", 461 | "email": "franck.nijhof@dealerdirect.com", 462 | "homepage": "http://www.frenck.nl", 463 | "role": "Developer / IT Manager" 464 | }, 465 | { 466 | "name": "Contributors", 467 | "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" 468 | } 469 | ], 470 | "description": "PHP_CodeSniffer Standards Composer Installer Plugin", 471 | "homepage": "http://www.dealerdirect.com", 472 | "keywords": [ 473 | "PHPCodeSniffer", 474 | "PHP_CodeSniffer", 475 | "code quality", 476 | "codesniffer", 477 | "composer", 478 | "installer", 479 | "phpcbf", 480 | "phpcs", 481 | "plugin", 482 | "qa", 483 | "quality", 484 | "standard", 485 | "standards", 486 | "style guide", 487 | "stylecheck", 488 | "tests" 489 | ], 490 | "support": { 491 | "issues": "https://github.com/PHPCSStandards/composer-installer/issues", 492 | "source": "https://github.com/PHPCSStandards/composer-installer" 493 | }, 494 | "time": "2023-01-05T11:28:13+00:00" 495 | }, 496 | { 497 | "name": "johnbillion/query-monitor", 498 | "version": "3.13.1", 499 | "source": { 500 | "type": "git", 501 | "url": "https://github.com/johnbillion/query-monitor.git", 502 | "reference": "e82322cb1f54348f0bc9d61a99f495c48862e43c" 503 | }, 504 | "dist": { 505 | "type": "zip", 506 | "url": "https://api.github.com/repos/johnbillion/query-monitor/zipball/e82322cb1f54348f0bc9d61a99f495c48862e43c", 507 | "reference": "e82322cb1f54348f0bc9d61a99f495c48862e43c", 508 | "shasum": "" 509 | }, 510 | "require": { 511 | "composer/installers": "^1.0 || ^2.0", 512 | "php": ">=7.4.0" 513 | }, 514 | "require-dev": { 515 | "codeception/module-asserts": "^1.0", 516 | "codeception/module-db": "^1.0", 517 | "codeception/module-webdriver": "^1.0", 518 | "codeception/util-universalframework": "^1.0", 519 | "dealerdirect/phpcodesniffer-composer-installer": "0.7.2", 520 | "ergebnis/composer-normalize": "^2", 521 | "johnbillion/plugin-infrastructure": "dev-trunk", 522 | "lucatume/wp-browser": "^3.0.21", 523 | "phpcompatibility/phpcompatibility-wp": "2.1.4", 524 | "phpstan/phpstan": "^1.0", 525 | "phpstan/phpstan-phpunit": "^1.0", 526 | "roots/wordpress": "*", 527 | "squizlabs/php_codesniffer": "3.7.1", 528 | "szepeviktor/phpstan-wordpress": "1.3.0", 529 | "wp-coding-standards/wpcs": "2.3.0" 530 | }, 531 | "type": "wordpress-plugin", 532 | "extra": { 533 | "wordpress-install-dir": "vendor/roots/wordpress" 534 | }, 535 | "autoload": { 536 | "classmap": [ 537 | "classes", 538 | "data", 539 | "output" 540 | ] 541 | }, 542 | "notification-url": "https://packagist.org/downloads/", 543 | "license": [ 544 | "GPL-2.0-or-later" 545 | ], 546 | "authors": [ 547 | { 548 | "name": "John Blackbourn", 549 | "homepage": "https://johnblackbourn.com/" 550 | } 551 | ], 552 | "description": "The Developer Tools panel for WordPress.", 553 | "homepage": "https://github.com/johnbillion/query-monitor/", 554 | "support": { 555 | "forum": "https://wordpress.org/support/plugin/query-monitor", 556 | "issues": "https://github.com/johnbillion/query-monitor/issues", 557 | "source": "https://github.com/johnbillion/query-monitor" 558 | }, 559 | "funding": [ 560 | { 561 | "url": "https://github.com/sponsors/johnbillion", 562 | "type": "github" 563 | } 564 | ], 565 | "time": "2023-07-15T14:57:34+00:00" 566 | }, 567 | { 568 | "name": "mediawiki/mediawiki-codesniffer", 569 | "version": "v41.0.0", 570 | "source": { 571 | "type": "git", 572 | "url": "https://github.com/wikimedia/mediawiki-tools-codesniffer.git", 573 | "reference": "0ccdbc10ad819e86d7872175e1649037a3352000" 574 | }, 575 | "dist": { 576 | "type": "zip", 577 | "url": "https://api.github.com/repos/wikimedia/mediawiki-tools-codesniffer/zipball/0ccdbc10ad819e86d7872175e1649037a3352000", 578 | "reference": "0ccdbc10ad819e86d7872175e1649037a3352000", 579 | "shasum": "" 580 | }, 581 | "require": { 582 | "composer/semver": "3.3.2", 583 | "composer/spdx-licenses": "~1.5.2", 584 | "ext-json": "*", 585 | "ext-mbstring": "*", 586 | "php": ">=7.4.0", 587 | "squizlabs/php_codesniffer": "3.7.2", 588 | "symfony/polyfill-php80": "^1.26.0" 589 | }, 590 | "require-dev": { 591 | "mediawiki/mediawiki-phan-config": "0.12.0", 592 | "mediawiki/minus-x": "1.1.1", 593 | "php-parallel-lint/php-console-highlighter": "1.0.0", 594 | "php-parallel-lint/php-parallel-lint": "1.3.2", 595 | "phpunit/phpunit": "9.5.28" 596 | }, 597 | "type": "phpcodesniffer-standard", 598 | "autoload": { 599 | "psr-4": { 600 | "MediaWiki\\Sniffs\\": "MediaWiki/Sniffs/", 601 | "MediaWiki\\Sniffs\\Tests\\": "MediaWiki/Tests/" 602 | } 603 | }, 604 | "notification-url": "https://packagist.org/downloads/", 605 | "license": [ 606 | "GPL-2.0-or-later" 607 | ], 608 | "description": "MediaWiki CodeSniffer Standards", 609 | "homepage": "https://www.mediawiki.org/wiki/Manual:Coding_conventions/PHP", 610 | "keywords": [ 611 | "codesniffer", 612 | "mediawiki" 613 | ], 614 | "support": { 615 | "source": "https://github.com/wikimedia/mediawiki-tools-codesniffer/tree/v41.0.0" 616 | }, 617 | "time": "2023-02-25T22:10:21+00:00" 618 | }, 619 | { 620 | "name": "php-stubs/acf-pro-stubs", 621 | "version": "v6.1.7", 622 | "source": { 623 | "type": "git", 624 | "url": "https://github.com/php-stubs/acf-pro-stubs.git", 625 | "reference": "0e6c6da1c581b7a6fa555da1ccd90260b5bbc495" 626 | }, 627 | "dist": { 628 | "type": "zip", 629 | "url": "https://api.github.com/repos/php-stubs/acf-pro-stubs/zipball/0e6c6da1c581b7a6fa555da1ccd90260b5bbc495", 630 | "reference": "0e6c6da1c581b7a6fa555da1ccd90260b5bbc495", 631 | "shasum": "" 632 | }, 633 | "require": { 634 | "php-stubs/wordpress-stubs": "^4.7 || ^5.0 || ^6.0" 635 | }, 636 | "require-dev": { 637 | "php": "~7.1", 638 | "php-stubs/generator": "^0.8" 639 | }, 640 | "suggest": { 641 | "symfony/polyfill-php73": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", 642 | "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan" 643 | }, 644 | "type": "library", 645 | "notification-url": "https://packagist.org/downloads/", 646 | "license": [ 647 | "GPL-2.0-or-later" 648 | ], 649 | "description": "Advanced Custom Fields PRO stubs for static analysis.", 650 | "homepage": "https://github.com/php-stubs/acf-pro-stubs", 651 | "keywords": [ 652 | "PHPStan", 653 | "acf", 654 | "static analysis", 655 | "wordpress" 656 | ], 657 | "support": { 658 | "issues": "https://github.com/php-stubs/acf-pro-stubs/issues", 659 | "source": "https://github.com/php-stubs/acf-pro-stubs/tree/v6.1.7" 660 | }, 661 | "time": "2023-07-21T09:18:17+00:00" 662 | }, 663 | { 664 | "name": "php-stubs/wordpress-stubs", 665 | "version": "v6.3.0", 666 | "source": { 667 | "type": "git", 668 | "url": "https://github.com/php-stubs/wordpress-stubs.git", 669 | "reference": "adda7609e71d5f4dc7b87c74f8ec9e3437d2e92c" 670 | }, 671 | "dist": { 672 | "type": "zip", 673 | "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/adda7609e71d5f4dc7b87c74f8ec9e3437d2e92c", 674 | "reference": "adda7609e71d5f4dc7b87c74f8ec9e3437d2e92c", 675 | "shasum": "" 676 | }, 677 | "require-dev": { 678 | "nikic/php-parser": "^4.13", 679 | "php": "^7.4 || ~8.0.0", 680 | "php-stubs/generator": "^0.8.3", 681 | "phpdocumentor/reflection-docblock": "^5.3", 682 | "phpstan/phpstan": "^1.10.12", 683 | "phpunit/phpunit": "^9.5" 684 | }, 685 | "suggest": { 686 | "paragonie/sodium_compat": "Pure PHP implementation of libsodium", 687 | "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan" 688 | }, 689 | "type": "library", 690 | "notification-url": "https://packagist.org/downloads/", 691 | "license": [ 692 | "MIT" 693 | ], 694 | "description": "WordPress function and class declaration stubs for static analysis.", 695 | "homepage": "https://github.com/php-stubs/wordpress-stubs", 696 | "keywords": [ 697 | "PHPStan", 698 | "static analysis", 699 | "wordpress" 700 | ], 701 | "support": { 702 | "issues": "https://github.com/php-stubs/wordpress-stubs/issues", 703 | "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.3.0" 704 | }, 705 | "time": "2023-08-10T16:34:11+00:00" 706 | }, 707 | { 708 | "name": "phpcsstandards/phpcsextra", 709 | "version": "1.1.2", 710 | "source": { 711 | "type": "git", 712 | "url": "https://github.com/PHPCSStandards/PHPCSExtra.git", 713 | "reference": "746c3190ba8eb2f212087c947ba75f4f5b9a58d5" 714 | }, 715 | "dist": { 716 | "type": "zip", 717 | "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/746c3190ba8eb2f212087c947ba75f4f5b9a58d5", 718 | "reference": "746c3190ba8eb2f212087c947ba75f4f5b9a58d5", 719 | "shasum": "" 720 | }, 721 | "require": { 722 | "php": ">=5.4", 723 | "phpcsstandards/phpcsutils": "^1.0.8", 724 | "squizlabs/php_codesniffer": "^3.7.1" 725 | }, 726 | "require-dev": { 727 | "php-parallel-lint/php-console-highlighter": "^1.0", 728 | "php-parallel-lint/php-parallel-lint": "^1.3.2", 729 | "phpcsstandards/phpcsdevcs": "^1.1.6", 730 | "phpcsstandards/phpcsdevtools": "^1.2.1", 731 | "phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0" 732 | }, 733 | "type": "phpcodesniffer-standard", 734 | "extra": { 735 | "branch-alias": { 736 | "dev-stable": "1.x-dev", 737 | "dev-develop": "1.x-dev" 738 | } 739 | }, 740 | "notification-url": "https://packagist.org/downloads/", 741 | "license": [ 742 | "LGPL-3.0-or-later" 743 | ], 744 | "authors": [ 745 | { 746 | "name": "Juliette Reinders Folmer", 747 | "homepage": "https://github.com/jrfnl", 748 | "role": "lead" 749 | }, 750 | { 751 | "name": "Contributors", 752 | "homepage": "https://github.com/PHPCSStandards/PHPCSExtra/graphs/contributors" 753 | } 754 | ], 755 | "description": "A collection of sniffs and standards for use with PHP_CodeSniffer.", 756 | "keywords": [ 757 | "PHP_CodeSniffer", 758 | "phpcbf", 759 | "phpcodesniffer-standard", 760 | "phpcs", 761 | "standards", 762 | "static analysis" 763 | ], 764 | "support": { 765 | "issues": "https://github.com/PHPCSStandards/PHPCSExtra/issues", 766 | "source": "https://github.com/PHPCSStandards/PHPCSExtra" 767 | }, 768 | "time": "2023-09-20T22:06:18+00:00" 769 | }, 770 | { 771 | "name": "phpcsstandards/phpcsutils", 772 | "version": "1.0.8", 773 | "source": { 774 | "type": "git", 775 | "url": "https://github.com/PHPCSStandards/PHPCSUtils.git", 776 | "reference": "69465cab9d12454e5e7767b9041af0cd8cd13be7" 777 | }, 778 | "dist": { 779 | "type": "zip", 780 | "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/69465cab9d12454e5e7767b9041af0cd8cd13be7", 781 | "reference": "69465cab9d12454e5e7767b9041af0cd8cd13be7", 782 | "shasum": "" 783 | }, 784 | "require": { 785 | "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0", 786 | "php": ">=5.4", 787 | "squizlabs/php_codesniffer": "^3.7.1 || 4.0.x-dev@dev" 788 | }, 789 | "require-dev": { 790 | "ext-filter": "*", 791 | "php-parallel-lint/php-console-highlighter": "^1.0", 792 | "php-parallel-lint/php-parallel-lint": "^1.3.2", 793 | "phpcsstandards/phpcsdevcs": "^1.1.6", 794 | "yoast/phpunit-polyfills": "^1.0.5 || ^2.0.0" 795 | }, 796 | "type": "phpcodesniffer-standard", 797 | "extra": { 798 | "branch-alias": { 799 | "dev-stable": "1.x-dev", 800 | "dev-develop": "1.x-dev" 801 | } 802 | }, 803 | "autoload": { 804 | "classmap": [ 805 | "PHPCSUtils/" 806 | ] 807 | }, 808 | "notification-url": "https://packagist.org/downloads/", 809 | "license": [ 810 | "LGPL-3.0-or-later" 811 | ], 812 | "authors": [ 813 | { 814 | "name": "Juliette Reinders Folmer", 815 | "homepage": "https://github.com/jrfnl", 816 | "role": "lead" 817 | }, 818 | { 819 | "name": "Contributors", 820 | "homepage": "https://github.com/PHPCSStandards/PHPCSUtils/graphs/contributors" 821 | } 822 | ], 823 | "description": "A suite of utility functions for use with PHP_CodeSniffer", 824 | "homepage": "https://phpcsutils.com/", 825 | "keywords": [ 826 | "PHP_CodeSniffer", 827 | "phpcbf", 828 | "phpcodesniffer-standard", 829 | "phpcs", 830 | "phpcs3", 831 | "standards", 832 | "static analysis", 833 | "tokens", 834 | "utility" 835 | ], 836 | "support": { 837 | "docs": "https://phpcsutils.com/", 838 | "issues": "https://github.com/PHPCSStandards/PHPCSUtils/issues", 839 | "source": "https://github.com/PHPCSStandards/PHPCSUtils" 840 | }, 841 | "time": "2023-07-16T21:39:41+00:00" 842 | }, 843 | { 844 | "name": "phpstan/extension-installer", 845 | "version": "1.3.1", 846 | "source": { 847 | "type": "git", 848 | "url": "https://github.com/phpstan/extension-installer.git", 849 | "reference": "f45734bfb9984c6c56c4486b71230355f066a58a" 850 | }, 851 | "dist": { 852 | "type": "zip", 853 | "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/f45734bfb9984c6c56c4486b71230355f066a58a", 854 | "reference": "f45734bfb9984c6c56c4486b71230355f066a58a", 855 | "shasum": "" 856 | }, 857 | "require": { 858 | "composer-plugin-api": "^2.0", 859 | "php": "^7.2 || ^8.0", 860 | "phpstan/phpstan": "^1.9.0" 861 | }, 862 | "require-dev": { 863 | "composer/composer": "^2.0", 864 | "php-parallel-lint/php-parallel-lint": "^1.2.0", 865 | "phpstan/phpstan-strict-rules": "^0.11 || ^0.12 || ^1.0" 866 | }, 867 | "type": "composer-plugin", 868 | "extra": { 869 | "class": "PHPStan\\ExtensionInstaller\\Plugin" 870 | }, 871 | "autoload": { 872 | "psr-4": { 873 | "PHPStan\\ExtensionInstaller\\": "src/" 874 | } 875 | }, 876 | "notification-url": "https://packagist.org/downloads/", 877 | "license": [ 878 | "MIT" 879 | ], 880 | "description": "Composer plugin for automatic installation of PHPStan extensions", 881 | "support": { 882 | "issues": "https://github.com/phpstan/extension-installer/issues", 883 | "source": "https://github.com/phpstan/extension-installer/tree/1.3.1" 884 | }, 885 | "time": "2023-05-24T08:59:17+00:00" 886 | }, 887 | { 888 | "name": "phpstan/phpstan", 889 | "version": "1.10.37", 890 | "source": { 891 | "type": "git", 892 | "url": "https://github.com/phpstan/phpstan.git", 893 | "reference": "058ba07e92f744d4dcf6061ae75283d0c6456f2e" 894 | }, 895 | "dist": { 896 | "type": "zip", 897 | "url": "https://api.github.com/repos/phpstan/phpstan/zipball/058ba07e92f744d4dcf6061ae75283d0c6456f2e", 898 | "reference": "058ba07e92f744d4dcf6061ae75283d0c6456f2e", 899 | "shasum": "" 900 | }, 901 | "require": { 902 | "php": "^7.2|^8.0" 903 | }, 904 | "conflict": { 905 | "phpstan/phpstan-shim": "*" 906 | }, 907 | "bin": [ 908 | "phpstan", 909 | "phpstan.phar" 910 | ], 911 | "type": "library", 912 | "autoload": { 913 | "files": [ 914 | "bootstrap.php" 915 | ] 916 | }, 917 | "notification-url": "https://packagist.org/downloads/", 918 | "license": [ 919 | "MIT" 920 | ], 921 | "description": "PHPStan - PHP Static Analysis Tool", 922 | "keywords": [ 923 | "dev", 924 | "static analysis" 925 | ], 926 | "support": { 927 | "docs": "https://phpstan.org/user-guide/getting-started", 928 | "forum": "https://github.com/phpstan/phpstan/discussions", 929 | "issues": "https://github.com/phpstan/phpstan/issues", 930 | "security": "https://github.com/phpstan/phpstan/security/policy", 931 | "source": "https://github.com/phpstan/phpstan-src" 932 | }, 933 | "funding": [ 934 | { 935 | "url": "https://github.com/ondrejmirtes", 936 | "type": "github" 937 | }, 938 | { 939 | "url": "https://github.com/phpstan", 940 | "type": "github" 941 | }, 942 | { 943 | "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", 944 | "type": "tidelift" 945 | } 946 | ], 947 | "time": "2023-10-02T16:18:37+00:00" 948 | }, 949 | { 950 | "name": "sirbrillig/phpcs-variable-analysis", 951 | "version": "v2.11.17", 952 | "source": { 953 | "type": "git", 954 | "url": "https://github.com/sirbrillig/phpcs-variable-analysis.git", 955 | "reference": "3b71162a6bf0cde2bff1752e40a1788d8273d049" 956 | }, 957 | "dist": { 958 | "type": "zip", 959 | "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/3b71162a6bf0cde2bff1752e40a1788d8273d049", 960 | "reference": "3b71162a6bf0cde2bff1752e40a1788d8273d049", 961 | "shasum": "" 962 | }, 963 | "require": { 964 | "php": ">=5.4.0", 965 | "squizlabs/php_codesniffer": "^3.5.6" 966 | }, 967 | "require-dev": { 968 | "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || ^1.0", 969 | "phpcsstandards/phpcsdevcs": "^1.1", 970 | "phpstan/phpstan": "^1.7", 971 | "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.5 || ^7.0 || ^8.0 || ^9.0", 972 | "sirbrillig/phpcs-import-detection": "^1.1", 973 | "vimeo/psalm": "^0.2 || ^0.3 || ^1.1 || ^4.24 || ^5.0@beta" 974 | }, 975 | "type": "phpcodesniffer-standard", 976 | "autoload": { 977 | "psr-4": { 978 | "VariableAnalysis\\": "VariableAnalysis/" 979 | } 980 | }, 981 | "notification-url": "https://packagist.org/downloads/", 982 | "license": [ 983 | "BSD-2-Clause" 984 | ], 985 | "authors": [ 986 | { 987 | "name": "Sam Graham", 988 | "email": "php-codesniffer-variableanalysis@illusori.co.uk" 989 | }, 990 | { 991 | "name": "Payton Swick", 992 | "email": "payton@foolord.com" 993 | } 994 | ], 995 | "description": "A PHPCS sniff to detect problems with variables.", 996 | "keywords": [ 997 | "phpcs", 998 | "static analysis" 999 | ], 1000 | "support": { 1001 | "issues": "https://github.com/sirbrillig/phpcs-variable-analysis/issues", 1002 | "source": "https://github.com/sirbrillig/phpcs-variable-analysis", 1003 | "wiki": "https://github.com/sirbrillig/phpcs-variable-analysis/wiki" 1004 | }, 1005 | "time": "2023-08-05T23:46:11+00:00" 1006 | }, 1007 | { 1008 | "name": "squizlabs/php_codesniffer", 1009 | "version": "3.7.2", 1010 | "source": { 1011 | "type": "git", 1012 | "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", 1013 | "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" 1014 | }, 1015 | "dist": { 1016 | "type": "zip", 1017 | "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", 1018 | "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", 1019 | "shasum": "" 1020 | }, 1021 | "require": { 1022 | "ext-simplexml": "*", 1023 | "ext-tokenizer": "*", 1024 | "ext-xmlwriter": "*", 1025 | "php": ">=5.4.0" 1026 | }, 1027 | "require-dev": { 1028 | "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" 1029 | }, 1030 | "bin": [ 1031 | "bin/phpcs", 1032 | "bin/phpcbf" 1033 | ], 1034 | "type": "library", 1035 | "extra": { 1036 | "branch-alias": { 1037 | "dev-master": "3.x-dev" 1038 | } 1039 | }, 1040 | "notification-url": "https://packagist.org/downloads/", 1041 | "license": [ 1042 | "BSD-3-Clause" 1043 | ], 1044 | "authors": [ 1045 | { 1046 | "name": "Greg Sherwood", 1047 | "role": "lead" 1048 | } 1049 | ], 1050 | "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", 1051 | "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", 1052 | "keywords": [ 1053 | "phpcs", 1054 | "standards", 1055 | "static analysis" 1056 | ], 1057 | "support": { 1058 | "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", 1059 | "source": "https://github.com/squizlabs/PHP_CodeSniffer", 1060 | "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" 1061 | }, 1062 | "time": "2023-02-22T23:07:41+00:00" 1063 | }, 1064 | { 1065 | "name": "symfony/polyfill-php73", 1066 | "version": "v1.28.0", 1067 | "source": { 1068 | "type": "git", 1069 | "url": "https://github.com/symfony/polyfill-php73.git", 1070 | "reference": "fe2f306d1d9d346a7fee353d0d5012e401e984b5" 1071 | }, 1072 | "dist": { 1073 | "type": "zip", 1074 | "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fe2f306d1d9d346a7fee353d0d5012e401e984b5", 1075 | "reference": "fe2f306d1d9d346a7fee353d0d5012e401e984b5", 1076 | "shasum": "" 1077 | }, 1078 | "require": { 1079 | "php": ">=7.1" 1080 | }, 1081 | "type": "library", 1082 | "extra": { 1083 | "branch-alias": { 1084 | "dev-main": "1.28-dev" 1085 | }, 1086 | "thanks": { 1087 | "name": "symfony/polyfill", 1088 | "url": "https://github.com/symfony/polyfill" 1089 | } 1090 | }, 1091 | "autoload": { 1092 | "files": [ 1093 | "bootstrap.php" 1094 | ], 1095 | "psr-4": { 1096 | "Symfony\\Polyfill\\Php73\\": "" 1097 | }, 1098 | "classmap": [ 1099 | "Resources/stubs" 1100 | ] 1101 | }, 1102 | "notification-url": "https://packagist.org/downloads/", 1103 | "license": [ 1104 | "MIT" 1105 | ], 1106 | "authors": [ 1107 | { 1108 | "name": "Nicolas Grekas", 1109 | "email": "p@tchwork.com" 1110 | }, 1111 | { 1112 | "name": "Symfony Community", 1113 | "homepage": "https://symfony.com/contributors" 1114 | } 1115 | ], 1116 | "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", 1117 | "homepage": "https://symfony.com", 1118 | "keywords": [ 1119 | "compatibility", 1120 | "polyfill", 1121 | "portable", 1122 | "shim" 1123 | ], 1124 | "support": { 1125 | "source": "https://github.com/symfony/polyfill-php73/tree/v1.28.0" 1126 | }, 1127 | "funding": [ 1128 | { 1129 | "url": "https://symfony.com/sponsor", 1130 | "type": "custom" 1131 | }, 1132 | { 1133 | "url": "https://github.com/fabpot", 1134 | "type": "github" 1135 | }, 1136 | { 1137 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1138 | "type": "tidelift" 1139 | } 1140 | ], 1141 | "time": "2023-01-26T09:26:14+00:00" 1142 | }, 1143 | { 1144 | "name": "symfony/polyfill-php80", 1145 | "version": "v1.28.0", 1146 | "source": { 1147 | "type": "git", 1148 | "url": "https://github.com/symfony/polyfill-php80.git", 1149 | "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" 1150 | }, 1151 | "dist": { 1152 | "type": "zip", 1153 | "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", 1154 | "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", 1155 | "shasum": "" 1156 | }, 1157 | "require": { 1158 | "php": ">=7.1" 1159 | }, 1160 | "type": "library", 1161 | "extra": { 1162 | "branch-alias": { 1163 | "dev-main": "1.28-dev" 1164 | }, 1165 | "thanks": { 1166 | "name": "symfony/polyfill", 1167 | "url": "https://github.com/symfony/polyfill" 1168 | } 1169 | }, 1170 | "autoload": { 1171 | "files": [ 1172 | "bootstrap.php" 1173 | ], 1174 | "psr-4": { 1175 | "Symfony\\Polyfill\\Php80\\": "" 1176 | }, 1177 | "classmap": [ 1178 | "Resources/stubs" 1179 | ] 1180 | }, 1181 | "notification-url": "https://packagist.org/downloads/", 1182 | "license": [ 1183 | "MIT" 1184 | ], 1185 | "authors": [ 1186 | { 1187 | "name": "Ion Bazan", 1188 | "email": "ion.bazan@gmail.com" 1189 | }, 1190 | { 1191 | "name": "Nicolas Grekas", 1192 | "email": "p@tchwork.com" 1193 | }, 1194 | { 1195 | "name": "Symfony Community", 1196 | "homepage": "https://symfony.com/contributors" 1197 | } 1198 | ], 1199 | "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", 1200 | "homepage": "https://symfony.com", 1201 | "keywords": [ 1202 | "compatibility", 1203 | "polyfill", 1204 | "portable", 1205 | "shim" 1206 | ], 1207 | "support": { 1208 | "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0" 1209 | }, 1210 | "funding": [ 1211 | { 1212 | "url": "https://symfony.com/sponsor", 1213 | "type": "custom" 1214 | }, 1215 | { 1216 | "url": "https://github.com/fabpot", 1217 | "type": "github" 1218 | }, 1219 | { 1220 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1221 | "type": "tidelift" 1222 | } 1223 | ], 1224 | "time": "2023-01-26T09:26:14+00:00" 1225 | }, 1226 | { 1227 | "name": "szepeviktor/phpstan-wordpress", 1228 | "version": "v1.3.0", 1229 | "source": { 1230 | "type": "git", 1231 | "url": "https://github.com/szepeviktor/phpstan-wordpress.git", 1232 | "reference": "5b5cc77ed51fdaf64efe3f00b5aae4b709d2cfa9" 1233 | }, 1234 | "dist": { 1235 | "type": "zip", 1236 | "url": "https://api.github.com/repos/szepeviktor/phpstan-wordpress/zipball/5b5cc77ed51fdaf64efe3f00b5aae4b709d2cfa9", 1237 | "reference": "5b5cc77ed51fdaf64efe3f00b5aae4b709d2cfa9", 1238 | "shasum": "" 1239 | }, 1240 | "require": { 1241 | "php": "^7.2 || ^8.0", 1242 | "php-stubs/wordpress-stubs": "^4.7 || ^5.0 || ^6.0", 1243 | "phpstan/phpstan": "^1.10.0", 1244 | "symfony/polyfill-php73": "^1.12.0" 1245 | }, 1246 | "require-dev": { 1247 | "composer/composer": "^2.1.14", 1248 | "dealerdirect/phpcodesniffer-composer-installer": "^1.0", 1249 | "php-parallel-lint/php-parallel-lint": "^1.1", 1250 | "phpstan/phpstan-strict-rules": "^1.2", 1251 | "phpunit/phpunit": "^8.0 || ^9.0", 1252 | "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^0.8" 1253 | }, 1254 | "type": "phpstan-extension", 1255 | "extra": { 1256 | "phpstan": { 1257 | "includes": [ 1258 | "extension.neon" 1259 | ] 1260 | } 1261 | }, 1262 | "autoload": { 1263 | "psr-4": { 1264 | "SzepeViktor\\PHPStan\\WordPress\\": "src/" 1265 | } 1266 | }, 1267 | "notification-url": "https://packagist.org/downloads/", 1268 | "license": [ 1269 | "MIT" 1270 | ], 1271 | "description": "WordPress extensions for PHPStan", 1272 | "keywords": [ 1273 | "PHPStan", 1274 | "code analyse", 1275 | "code analysis", 1276 | "static analysis", 1277 | "wordpress" 1278 | ], 1279 | "support": { 1280 | "issues": "https://github.com/szepeviktor/phpstan-wordpress/issues", 1281 | "source": "https://github.com/szepeviktor/phpstan-wordpress/tree/v1.3.0" 1282 | }, 1283 | "time": "2023-04-23T06:15:06+00:00" 1284 | }, 1285 | { 1286 | "name": "wp-coding-standards/wpcs", 1287 | "version": "3.0.1", 1288 | "source": { 1289 | "type": "git", 1290 | "url": "https://github.com/WordPress/WordPress-Coding-Standards.git", 1291 | "reference": "b4caf9689f1a0e4a4c632679a44e638c1c67aff1" 1292 | }, 1293 | "dist": { 1294 | "type": "zip", 1295 | "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/b4caf9689f1a0e4a4c632679a44e638c1c67aff1", 1296 | "reference": "b4caf9689f1a0e4a4c632679a44e638c1c67aff1", 1297 | "shasum": "" 1298 | }, 1299 | "require": { 1300 | "ext-filter": "*", 1301 | "ext-libxml": "*", 1302 | "ext-tokenizer": "*", 1303 | "ext-xmlreader": "*", 1304 | "php": ">=5.4", 1305 | "phpcsstandards/phpcsextra": "^1.1.0", 1306 | "phpcsstandards/phpcsutils": "^1.0.8", 1307 | "squizlabs/php_codesniffer": "^3.7.2" 1308 | }, 1309 | "require-dev": { 1310 | "php-parallel-lint/php-console-highlighter": "^1.0.0", 1311 | "php-parallel-lint/php-parallel-lint": "^1.3.2", 1312 | "phpcompatibility/php-compatibility": "^9.0", 1313 | "phpcsstandards/phpcsdevtools": "^1.2.0", 1314 | "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" 1315 | }, 1316 | "suggest": { 1317 | "ext-iconv": "For improved results", 1318 | "ext-mbstring": "For improved results" 1319 | }, 1320 | "type": "phpcodesniffer-standard", 1321 | "notification-url": "https://packagist.org/downloads/", 1322 | "license": [ 1323 | "MIT" 1324 | ], 1325 | "authors": [ 1326 | { 1327 | "name": "Contributors", 1328 | "homepage": "https://github.com/WordPress/WordPress-Coding-Standards/graphs/contributors" 1329 | } 1330 | ], 1331 | "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions", 1332 | "keywords": [ 1333 | "phpcs", 1334 | "standards", 1335 | "static analysis", 1336 | "wordpress" 1337 | ], 1338 | "support": { 1339 | "issues": "https://github.com/WordPress/WordPress-Coding-Standards/issues", 1340 | "source": "https://github.com/WordPress/WordPress-Coding-Standards", 1341 | "wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki" 1342 | }, 1343 | "funding": [ 1344 | { 1345 | "url": "https://opencollective.com/thewpcc/contribute/wp-php-63406", 1346 | "type": "custom" 1347 | } 1348 | ], 1349 | "time": "2023-09-14T07:06:09+00:00" 1350 | } 1351 | ], 1352 | "aliases": [], 1353 | "minimum-stability": "stable", 1354 | "stability-flags": [], 1355 | "prefer-stable": false, 1356 | "prefer-lowest": false, 1357 | "platform": [], 1358 | "platform-dev": [], 1359 | "plugin-api-version": "2.3.0" 1360 | } 1361 | -------------------------------------------------------------------------------- /constants/qmx-constants-collector.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | class QMX_Collector_Constants extends QM_DataCollector { 9 | 10 | public $id = 'constants'; 11 | 12 | public function name() : string { 13 | return __( 'Constants', 'query-monitor-extend' ); 14 | } 15 | 16 | public function get_storage(): QM_Data { 17 | require_once 'qmx-constants-data.php'; 18 | return new QMX_Data_Constants(); 19 | } 20 | 21 | public function process() : void { 22 | if ( did_action( 'qm/cease' ) ) { 23 | return; 24 | } 25 | 26 | $constants = get_defined_constants( true ); 27 | 28 | $this->data['constants'] = $constants['user']; 29 | } 30 | 31 | } 32 | 33 | add_filter( 'qm/collectors', static function ( array $collectors ) : array { 34 | $collectors['constants'] = new QMX_Collector_Constants; 35 | return $collectors; 36 | } ); 37 | -------------------------------------------------------------------------------- /constants/qmx-constants-data.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | public $constants; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /constants/qmx-constants-output.php: -------------------------------------------------------------------------------- 1 | collector->get_data(); 17 | 18 | echo '
'; 19 | 20 | if ( ! empty( $data->constants ) ) { 21 | echo ''; 22 | echo ''; 23 | echo ''; 24 | echo ''; 25 | 26 | echo ''; 29 | 30 | echo ''; 33 | 34 | echo ''; 37 | 38 | echo ''; 41 | 42 | echo ''; 43 | echo ''; 44 | 45 | echo ''; 46 | 47 | $bools = array( true => 'true', false => 'false' ); 48 | $i = 1; 49 | 50 | foreach ( $data->constants as $constant => $value ) { 51 | echo ''; 52 | echo ''; 53 | echo ''; 54 | echo ''; 55 | echo ''; 56 | echo ''; 57 | } 58 | 59 | echo ''; 60 | echo ''; 61 | 62 | echo ''; 63 | echo '
' . esc_html( $this->collector->name() ) . '
'; 27 | echo $this->build_sorter( __( '', 'query-monitor-extend' ) ); 28 | echo ''; 31 | echo $this->build_sorter( __( 'Constant', 'query-monitor-extend' ) ); 32 | echo ''; 35 | echo __( 'Value', 'query-monitor-extend' ); 36 | echo ''; 39 | echo $this->build_sorter( __( 'Type', 'query-monitor-extend' ) ); 40 | echo '
' . $i++ . '' . esc_html( $constant ) . '' . esc_html( (string) QM_Util::display_variable( $value ) ) . '' . esc_html( gettype( $value ) ) . '
'; 64 | 65 | } else { 66 | 67 | echo '
'; 68 | echo '

' . esc_html__( 'None', 'query-monitor' ) . '

'; 69 | echo '
'; 70 | 71 | } 72 | 73 | echo '
'; 74 | } 75 | 76 | /** 77 | * @param array> $menu 78 | * @return array> 79 | */ 80 | public function panel_menu( array $menu ) { 81 | $menu['constants'] = $this->menu( array( 82 | 'title' => esc_html__( 'Constants', 'query-monitor-extend' ), 83 | 'id' => 'query-monitor-extend-constants', 84 | ) ); 85 | 86 | return $menu; 87 | } 88 | 89 | } 90 | 91 | add_filter( 'qm/outputter/html', static function ( array $output ) : array { 92 | if ( $collector = QM_Collectors::get( 'constants' ) ) { 93 | $output['constants'] = new QMX_Output_Html_Constants( $collector ); 94 | } 95 | 96 | return $output; 97 | }, 70 ); 98 | -------------------------------------------------------------------------------- /dev/create-release-files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Creates release zips with the following panels: 4 | # - ACF 5 | # - Constants 6 | # - Files 7 | # - Heartbeat 8 | # - Image Sizes 9 | # - Paths 10 | # - Time 11 | 12 | rm -rf releases; 13 | 14 | echo "Making directories..."; 15 | 16 | mkdir releases; 17 | mkdir releases/plugins; 18 | mkdir releases/mu-plugins; 19 | mkdir releases/plugins/query-monitor-extend; 20 | mkdir releases/mu-plugins/query-monitor-extend; 21 | 22 | echo "Copying directories..."; 23 | 24 | # ACF 25 | cp -r acf/ releases/plugins/query-monitor-extend/acf; 26 | cp -r acf/ releases/mu-plugins/query-monitor-extend/acf; 27 | echo "- acf"; 28 | 29 | # Constants 30 | cp -r constants/ releases/plugins/query-monitor-extend/constants; 31 | cp -r constants/ releases/mu-plugins/query-monitor-extend/constants; 32 | echo "- constants"; 33 | 34 | # Files 35 | cp -r files/ releases/plugins/query-monitor-extend/files; 36 | cp -r files/ releases/mu-plugins/query-monitor-extend/files; 37 | echo "- files"; 38 | 39 | # Globals 40 | cp -r globals/ releases/plugins/query-monitor-extend/globals; 41 | cp -r globals/ releases/mu-plugins/query-monitor-extend/globals; 42 | echo "- globals"; 43 | 44 | # Heartbeat 45 | cp -r heartbeat/ releases/plugins/query-monitor-extend/heartbeat; 46 | cp -r heartbeat/ releases/mu-plugins/query-monitor-extend/heartbeat; 47 | echo "- heartbeat"; 48 | 49 | # Image Sizes 50 | cp -r image-sizes/ releases/plugins/query-monitor-extend/image-sizes; 51 | cp -r image-sizes/ releases/mu-plugins/query-monitor-extend/image-sizes; 52 | echo "- image-sizes"; 53 | 54 | # Paths 55 | cp -r paths/ releases/plugins/query-monitor-extend/paths; 56 | cp -r paths/ releases/mu-plugins/query-monitor-extend/paths; 57 | echo "- paths"; 58 | 59 | # Time 60 | cp -r time/ releases/plugins/query-monitor-extend/time; 61 | cp -r time/ releases/mu-plugins/query-monitor-extend/time; 62 | echo "- time"; 63 | 64 | echo "Copying files..."; 65 | 66 | # Conditionals 67 | cp qmx-conditionals.php releases/plugins/query-monitor-extend/qmx-conditionals.php; 68 | cp qmx-conditionals.php releases/mu-plugins/query-monitor-extend/qmx-conditionals.php; 69 | echo "- qmx-conditionals.php"; 70 | 71 | # Plugin file 72 | cp query-monitor-extend.php releases/plugins/query-monitor-extend/query-monitor-extend.php; 73 | cp query-monitor-extend.php releases/mu-plugins/query-monitor-extend/query-monitor-extend.php; 74 | echo "- query-monitor-extend.php"; 75 | 76 | # Time hooks 77 | cp qmx-time-hooks.php releases/plugins/query-monitor-extend/qmx-time-hooks.php; 78 | cp qmx-time-hooks.php releases/mu-plugins/query-monitor-extend/qmx-time-hooks.php; 79 | echo "- qmx-time-hooks.php"; 80 | 81 | echo "Creating mu-plugin loader..."; 82 | 83 | # Create file to load mu-plugin 84 | head -13 query-monitor-extend.php > releases/mu-plugins/load-qmx.php; 85 | echo "add_action( 'plugin_loaded', static function ( \$plugin ) : void {" >> releases/mu-plugins/load-qmx.php; 86 | echo "\tif ( trailingslashit( constant( 'WP_PLUGIN_DIR' ) ) . 'query-monitor/query-monitor.php' !== \$plugin ) {\n\t\treturn;\n\t}\n" >> releases/mu-plugins/load-qmx.php; 87 | echo "\trequire_once 'query-monitor-extend/query-monitor-extend.php';" >> releases/mu-plugins/load-qmx.php; 88 | echo "} );" >> releases/mu-plugins/load-qmx.php; 89 | 90 | echo ""; 91 | echo "Done copying files."; 92 | 93 | echo ""; 94 | echo "===========================================" 95 | echo ""; 96 | 97 | ls -lR releases; 98 | 99 | echo ""; 100 | echo "===========================================" 101 | echo ""; 102 | 103 | echo "Creating zips..." 104 | echo "" 105 | 106 | cd releases/plugins/; 107 | zip --test --display-bytes --display-counts -r "../plugin.zip" query-monitor-extend/; 108 | cd ../../; 109 | 110 | echo "" 111 | 112 | cd releases/; 113 | zip --test --display-bytes --display-counts -r "mu-plugin.zip" mu-plugins/; 114 | cd ../../; 115 | 116 | echo "" 117 | echo "Done creating zip files." 118 | echo "" 119 | 120 | echo "Complete" 121 | -------------------------------------------------------------------------------- /dev/patches/acf-pro-stubs.patch: -------------------------------------------------------------------------------- 1 | diff --git a/acf-pro-stubs.php b/acf-pro-stubs.php 2 | --- dev/null 3 | +++ b/acf-pro-stubs.php 4 | @@ -21074,8 +21074,8 @@ function acf_register_field_type_info($info) 5 | * @date 31/5/17 6 | * @since 5.6.0 7 | * 8 | -* @param void 9 | -* @return void 10 | +* @param string $name 11 | +* @return mixed 12 | */ 13 | function acf_get_field_type($name) 14 | { 15 | -------------------------------------------------------------------------------- /dev/phpstan-bootstrap.php: -------------------------------------------------------------------------------- 1 | 7 | * @property-write QMX_Data_Files $data 8 | */ 9 | class QMX_Collector_Files extends QM_DataCollector { 10 | 11 | public $id = 'files'; 12 | 13 | public function name() : string { 14 | return __( 'Files', 'query-monitor-extend' ); 15 | } 16 | 17 | public function get_storage(): QM_Data { 18 | require_once 'qmx-files-data.php'; 19 | return new QMX_Data_Files(); 20 | } 21 | 22 | public function process() { 23 | if ( did_action( 'qm/cease' ) ) { 24 | return; 25 | } 26 | 27 | $files_with_errors = array(); 28 | $collector = QM_Collectors::get( 'php_errors' ); 29 | 30 | if ( ! is_null( $collector ) ) { 31 | $php_errors = $collector->get_data(); 32 | 33 | if ( ! empty( $php_errors['errors'] ) ) { 34 | foreach ( $php_errors['errors'] as $type => $errors ) { 35 | foreach ( $errors as $error ) { 36 | $files_with_errors[ $error['file'] ] = 1; 37 | } 38 | } 39 | } 40 | } 41 | 42 | foreach ( get_included_files() as $i => $filepath ) { 43 | $this->data->files[] = array( 44 | 'path' => $filepath, 45 | 'component' => QM_Util::get_file_component( $filepath ), 46 | 'has_error' => array_key_exists( $filepath, $files_with_errors ), 47 | ); 48 | } 49 | } 50 | 51 | } 52 | 53 | add_filter( 'qm/collectors', static function ( array $collectors ) : array { 54 | $collectors['files'] = new QMX_Collector_Files; 55 | return $collectors; 56 | } ); 57 | -------------------------------------------------------------------------------- /files/qmx-files-data.php: -------------------------------------------------------------------------------- 1 | > $files 9 | */ 10 | public $files = array(); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /files/qmx-files-output.php: -------------------------------------------------------------------------------- 1 | collector->get_data(); 19 | 20 | echo '
'; 21 | 22 | if ( ! empty( $data->files ) ) { 23 | $files_with_errors = 0; 24 | $path_components = array(); 25 | $components = array(); 26 | 27 | $largest_file = array( 28 | 'path' => null, 29 | 'size' => 0 30 | ); 31 | 32 | foreach ( $data->files as &$file ) { 33 | $file['_path_components'] = array(); 34 | 35 | foreach ( array_filter( explode( '/', str_replace( ABSPATH, '', dirname( $file['path'] ) ) ) ) as $path_component ) { 36 | $file['_path_components'][ $path_component ] = 1; 37 | $path_components[ $path_component ] = 1; 38 | 39 | foreach ( explode( '-', $path_component ) as $smaller_path_component ) { 40 | $file['_path_components'][ $smaller_path_component ] = 1; 41 | $path_components[ $smaller_path_component ] = 1; 42 | } 43 | } 44 | 45 | $filesize = @filesize( $file['path'] ); 46 | 47 | if ( empty( $filesize ) ) { 48 | $filesize = 0; 49 | } 50 | 51 | if ( $filesize > $largest_file['size'] ) { 52 | $largest_file = array( 53 | 'path' => $file['path'], 54 | 'size' => filesize( $file['path'] ), 55 | ); 56 | } 57 | 58 | $components[ ( string ) $file['component']->name ] = 1; 59 | } 60 | 61 | echo ''; 62 | echo ''; 63 | echo ''; 64 | echo ''; 65 | 66 | echo ''; 69 | 70 | echo ''; 73 | 74 | echo ''; 77 | 78 | echo ''; 81 | 82 | echo ''; 83 | echo ''; 84 | 85 | echo ''; 86 | 87 | $total_file_size = 0; 88 | 89 | foreach ( $data->files as $i => $file ) { 90 | 91 | $filesize = @filesize( $file['path'] ); 92 | 93 | if ( empty( $filesize ) ) { 94 | $filesize = 0; 95 | } 96 | 97 | $total_file_size += $filesize; 98 | 99 | if ( ! empty( $file['has_error'] ) ) { 100 | $files_with_errors++; 101 | } 102 | 103 | echo ''; 108 | 109 | echo ''; 110 | echo ''; 111 | echo ''; 114 | 115 | echo ''; 116 | echo ''; 117 | } 118 | 119 | echo ''; 120 | echo ''; 121 | 122 | echo ''; 123 | echo ''; 129 | echo ''; 130 | 131 | echo ''; 132 | echo ''; 140 | echo ''; 141 | echo ''; 142 | echo ''; 143 | 144 | echo ''; 145 | echo '
' . esc_html( $this->collector->name() ) . '
'; 67 | echo $this->build_sorter( __( '', 'query-monitor-extend' ) ); 68 | echo ''; 71 | echo $this->build_filter( 'path', array_map( 'esc_attr', array_keys( $path_components ) ), __( 'Path', 'query-monitor-extend' ) ); 72 | echo ''; 75 | echo $this->build_sorter( __( 'Filesize', 'query-monitor-extend' ) ); 76 | echo ''; 79 | echo $this->build_filter( 'component', array_map( 'esc_attr', array_keys( $components ) ), __( 'Component', 'query-monitor-extend' ) ); 80 | echo '
' . ( $i + 1 ) . '' . QM_Output_Html::output_filename( str_replace( ABSPATH, '', $file['path'] ), $file['path'] ) . ''; 112 | echo ! empty( $filesize ) ? $this->human_file_size( $filesize ) : '—'; 113 | echo '' . esc_html( $file['component']->name ) . '
'; 124 | printf( 125 | esc_html__( 'Files in filter: %s', 'query-monitor-extend' ), 126 | '' . esc_html( number_format_i18n( count( $data->files ) ) ) . '' 127 | ); 128 | echo '
' . 133 | 'Total: ' . esc_html( number_format_i18n( count( $data->files ) ) ) . '' . 134 | ( 135 | ! empty( $files_with_errors ) 136 | ? ', With error(s): ' . esc_html( number_format_i18n( $files_with_errors ) ) . '' 137 | : '' 138 | ) . 139 | '=' . $this->human_file_size( $total_file_size ) . 'Components: ' . count( $components ) . '
'; 146 | 147 | echo ''; 148 | 149 | } else { 150 | 151 | echo '
'; 152 | echo '

' . esc_html__( 'None', 'query-monitor' ) . '

'; 153 | echo '
'; 154 | 155 | } 156 | 157 | echo '
'; 158 | } 159 | 160 | /** 161 | * @param string[] $title 162 | * @return string[] 163 | */ 164 | public function admin_title( array $title ) { 165 | $data = $this->collector->get_data(); 166 | 167 | if ( ! empty( $data->files ) ) { 168 | $_title = sprintf( esc_html_x( '%s F', 'Files count', 'query-monitor-extend' ), number_format_i18n( count( $data->files ) ) ); 169 | $_title = preg_replace( '#\s?([^0-9,\.]+)#', '$1', $_title ); 170 | 171 | if ( is_null( $_title ) ) { 172 | return $title; 173 | } 174 | 175 | $title[] = $_title; 176 | } 177 | 178 | return $title; 179 | } 180 | 181 | /** 182 | * @param array> $menu 183 | * @return array> 184 | */ 185 | public function panel_menu( array $menu ) { 186 | $menu['files'] = $this->menu( array( 187 | 'title' => esc_html__( 'Files', 'query-monitor-extend' ), 188 | 'id' => 'query-monitor-extend-files', 189 | ) ); 190 | 191 | return $menu; 192 | } 193 | 194 | private function human_file_size( int $bytes ) : string { 195 | $places = (int) strlen( (string) $bytes ); 196 | $filesize_units = 'BKMGTP'; 197 | $factor = ( int ) floor( ( $places - 1 ) / 3 ); 198 | 199 | return sprintf( "%.2f", $bytes / pow( 1024, $factor ) ) . @$filesize_units[ $factor ]; 200 | } 201 | 202 | } 203 | 204 | add_filter( 'qm/outputter/html', static function ( array $output ) : array { 205 | if ( $collector = QM_Collectors::get( 'files' ) ) { 206 | $output['files'] = new QMX_Output_Html_Files( $collector ); 207 | } 208 | 209 | return $output; 210 | }, 70 ); 211 | -------------------------------------------------------------------------------- /globals/qmx-globals-collector.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | class QMX_Collector_Globals extends QM_DataCollector { 9 | 10 | public $id = 'globals'; 11 | 12 | public function name() : string { 13 | return __( 'SERVER, GET, POST', 'query-monitor-extend' ); 14 | } 15 | 16 | public function get_storage(): QM_Data { 17 | require_once 'qmx-globals-data.php'; 18 | return new QMX_Data_Globals(); 19 | } 20 | 21 | public function process() : void { 22 | if ( did_action( 'qm/cease' ) ) { 23 | return; 24 | } 25 | 26 | $this->data['server'] = $_SERVER; 27 | 28 | if ( ! empty( $_GET ) ) { 29 | $this->data['get'] = $_GET; 30 | } 31 | 32 | if ( ! empty( $_POST ) ) { 33 | $this->data['post'] = $_POST; 34 | } 35 | } 36 | 37 | } 38 | 39 | add_filter( 'qm/collectors', static function ( array $collectors ) : array { 40 | $collectors['globals'] = new QMX_Collector_Globals; 41 | return $collectors; 42 | } ); 43 | -------------------------------------------------------------------------------- /globals/qmx-globals-data.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | public $server; 11 | 12 | /** 13 | * @var array 14 | */ 15 | public $get; 16 | 17 | /** 18 | * @var array 19 | */ 20 | public $post; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /globals/qmx-globals-output.php: -------------------------------------------------------------------------------- 1 | collector->get_data(); 18 | 19 | if ( ! empty( $data->server ) ) { 20 | $rows = $data->server; 21 | 22 | echo '
'; 23 | 24 | echo ''; 25 | echo ''; 26 | echo ''; 27 | echo ''; 28 | 29 | echo ''; 32 | 33 | echo ''; 36 | 37 | echo ''; 40 | 41 | echo ''; 42 | echo ''; 43 | 44 | echo ''; 45 | 46 | $bools = array( true => 'true', false => 'false' ); 47 | $i = 1; 48 | 49 | foreach ( $rows as $key => $value ) { 50 | if ( is_string( $value ) ) { 51 | $value = sanitize_textarea_field( stripslashes( $value ) ); 52 | } 53 | 54 | echo ''; 55 | echo ''; 56 | echo ''; 57 | echo ''; 58 | echo ''; 59 | } 60 | 61 | echo ''; 62 | 63 | echo '
$_SERVER
'; 30 | echo $this->build_sorter( __( '', 'query-monitor-extend' ) ); 31 | echo ''; 34 | echo $this->build_sorter( __( 'Key', 'query-monitor-extend' ) ); 35 | echo ''; 38 | echo __( 'Value', 'query-monitor-extend' ); 39 | echo '
' . $i++ . '' . esc_html( $key ) . '' . esc_html( (string) QM_Util::display_variable( $value ) ) . '
'; 64 | 65 | echo '
'; 66 | 67 | } 68 | 69 | if ( ! empty( $data->get ) ) { 70 | $rows = $data->get; 71 | 72 | echo '
'; 73 | 74 | echo ''; 75 | echo ''; 76 | echo ''; 77 | echo ''; 78 | 79 | echo ''; 82 | 83 | echo ''; 86 | 87 | echo ''; 90 | 91 | echo ''; 92 | echo ''; 93 | 94 | echo ''; 95 | 96 | $bools = array( true => 'true', false => 'false' ); 97 | $i = 1; 98 | 99 | foreach ( $rows as $key => $value ) { 100 | if ( is_string( $value ) ) { 101 | $value = sanitize_textarea_field( stripslashes( $value ) ); 102 | } 103 | 104 | echo ''; 105 | echo ''; 106 | echo ''; 107 | echo ''; 108 | echo ''; 109 | } 110 | 111 | echo ''; 112 | 113 | echo '
$_GET
'; 80 | echo $this->build_sorter( __( '', 'query-monitor-extend' ) ); 81 | echo ''; 84 | echo $this->build_sorter( __( 'Key', 'query-monitor-extend' ) ); 85 | echo ''; 88 | echo __( 'Value', 'query-monitor-extend' ); 89 | echo '
' . $i++ . '' . esc_html( $key ) . '' . esc_html( (string) QM_Util::display_variable( $value ) ) . '
'; 114 | 115 | echo '
'; 116 | 117 | } 118 | 119 | if ( ! empty( $data->post ) ) { 120 | $rows = $data->post; 121 | 122 | echo '
'; 123 | 124 | echo ''; 125 | echo ''; 126 | echo ''; 127 | echo ''; 128 | 129 | echo ''; 132 | 133 | echo ''; 136 | 137 | echo ''; 140 | 141 | echo ''; 142 | echo ''; 143 | 144 | echo ''; 145 | 146 | $bools = array( true => 'true', false => 'false' ); 147 | $i = 1; 148 | 149 | foreach ( $rows as $key => $value ) { 150 | if ( is_string( $value ) ) { 151 | $value = sanitize_textarea_field( stripslashes( $value ) ); 152 | } 153 | 154 | echo ''; 155 | echo ''; 156 | echo ''; 157 | echo ''; 158 | echo ''; 159 | } 160 | 161 | echo ''; 162 | 163 | echo '
$_POST
'; 130 | echo $this->build_sorter( __( '', 'query-monitor-extend' ) ); 131 | echo ''; 134 | echo $this->build_sorter( __( 'Key', 'query-monitor-extend' ) ); 135 | echo ''; 138 | echo __( 'Value', 'query-monitor-extend' ); 139 | echo '
' . $i++ . '' . esc_html( $key ) . '' . esc_html( (string) QM_Util::display_variable( $value ) ) . '
'; 164 | 165 | echo '
'; 166 | 167 | } 168 | } 169 | 170 | /** 171 | * @param array> $menu 172 | * @return array> 173 | */ 174 | public function panel_menu( array $menu ) { 175 | /** @var QMX_Data_Globals */ 176 | $data = $this->collector->get_data(); 177 | 178 | if ( ! empty( $data->server ) ) { 179 | $menu['global-server'] = $this->menu( array( 180 | 'title' => '$_SERVER', 181 | 'id' => 'query-monitor-extend-global-server', 182 | 'href' => '#qm-global-server', 183 | ) ); 184 | } 185 | 186 | if ( ! empty( $data->get ) ) { 187 | $menu['global-get'] = $this->menu( array( 188 | 'title' => '$_GET', 189 | 'id' => 'query-monitor-extend-global-get', 190 | 'href' => '#qm-global-get', 191 | ) ); 192 | } 193 | 194 | if ( ! empty( $data->post ) ) { 195 | $menu['global-post'] = $this->menu( array( 196 | 'title' => '$_POST', 197 | 'id' => 'query-monitor-extend-global-post', 198 | 'href' => '#qm-global-post', 199 | ) ); 200 | } 201 | 202 | return $menu; 203 | } 204 | 205 | } 206 | 207 | add_filter( 'qm/outputter/html', static function ( array $output ) : array { 208 | if ( $collector = QM_Collectors::get( 'globals' ) ) { 209 | $output['globals'] = new QMX_Output_Html_Globals( $collector ); 210 | } 211 | 212 | return $output; 213 | }, 70 ); 214 | -------------------------------------------------------------------------------- /heartbeat/qmx-heartbeat-collector.php: -------------------------------------------------------------------------------- 1 | qm_no_jquery() ) { 13 | return; 14 | } 15 | 16 | add_action( 'wp_enqueue_scripts', array( &$this, 'add_inline_script' ) ); 17 | add_action( 'admin_enqueue_scripts', array( &$this, 'add_inline_script' ) ); 18 | } 19 | 20 | public function add_inline_script() : void { 21 | if ( did_action( 'qm/cease' ) ) { 22 | return; 23 | } 24 | 25 | wp_add_inline_script( 'heartbeat', $this->inlineScript_heartbeat() ); 26 | } 27 | 28 | public function inlineScript_heartbeat() : string { 29 | ob_start(); 30 | ?> 31 | 32 | var qmx_heartbeat = { 33 | 34 | _beat: null, 35 | _beats: [], 36 | _start: 0, 37 | _table: null, 38 | _tab: null, 39 | _table_ready: false, 40 | 41 | _prev_dub: 0, 42 | 43 | init: function() { 44 | 45 | var that = this; 46 | var d = new Date(); 47 | this._start = d.getTime(); 48 | 49 | jQuery( document ).on( 'heartbeat-send', function() { 50 | var d = new Date(); 51 | var lub = d.getTime(); 52 | 53 | if ( !that._table_ready ) { 54 | that._beat = { lub: lub }; 55 | return; 56 | } 57 | 58 | var count = ( that._beats.find( 'tr' ).length + 1 ); 59 | 60 | if ( 61 | 2 == count 62 | && that._beats.find( 'tr' ).hasClass( 'listening' ) 63 | ) { 64 | that._beats.html( '' ); 65 | count = 1; 66 | } 67 | 68 | that.add_table_row( that.get_table_row( 69 | count, 70 | lub, 71 | '', 72 | ( 73 | 0 == that._prev_dub 74 | ? '-' 75 | : ( lub - that._prev_dub ) 76 | ), 77 | '-' 78 | ) ); 79 | that.update_tab( count ); 80 | that._beat = that._beats.find( 'tr:first-child' ); 81 | } ); 82 | 83 | jQuery( document ).on( 'heartbeat-tick', function() { 84 | var d = new Date(); 85 | var dub = d.getTime(); 86 | 87 | if ( !that._table_ready ) { 88 | that._beat.dub = dub; 89 | that['_beats'].push( that._beat ); 90 | that._beat = null; 91 | return; 92 | } 93 | 94 | var dub_secs = ( dub - that._start ) / 1000; 95 | var duration = ( ( ( dub - that._start ) / 1000 ) - parseFloat( that._beat.find( 'td.lub' ).text() ) ); 96 | 97 | that._beat.find( 'td.dub' ).html( dub_secs.toFixed( 3 ) ); 98 | that._beat.find( 'td.dur' ).html( duration.toFixed( 3 ) ); 99 | that._prev_dub = dub; 100 | } ); 101 | 102 | }, 103 | 104 | populate_table: function() { 105 | this._table_ready = true; 106 | this._table = jQuery( id() ) . ' > table' ) ?> ); 107 | this._beats = this._table.find( 'tbody' ); 108 | }, 109 | 110 | add_table_row: function( tr ) { 111 | this._table.find( 'tbody' ).prepend( tr ); 112 | }, 113 | 114 | get_table_row: function( index, lub, dub, since_last, duration ) { 115 | var lub_diff = ( lub - this._start ) / 1000; 116 | var dub_diff = '-'; 117 | var duration_secs = '-'; 118 | 119 | if ( '' !== dub ) { 120 | dub_diff = ( dub - this._start ) / 1000; 121 | dub_diff = dub_diff.toFixed( 3 ); 122 | } 123 | 124 | if ( !isNaN( duration ) ) { 125 | duration_secs = duration / 1000; 126 | duration_secs = duration_secs.toFixed( 3 ); 127 | } 128 | 129 | var since_last_secs = '-' != since_last ? since_last / 1000 : '-'; 130 | since_last_secs = '-' != since_last_secs ? since_last_secs.toFixed( 3 ) : '-'; 131 | 132 | return '' + 139 | '' + index + '' + 140 | '' + lub_diff.toFixed( 3 ) + '' + 141 | '' + dub_diff + '' + 142 | '' + since_last_secs + '' + 143 | '' + duration_secs + '' + 144 | ''; 145 | }, 146 | 147 | update_tab: function( count ) { 148 | this._tab = jQuery( '#qm-panel-menu button[data-qm-href=id() ) ) ?>]' ); 149 | this._tab.html( 'Heartbeats (' + count + ')' ); 150 | } 151 | 152 | }; 153 | 154 | qmx_heartbeat.init(); 155 | 156 | collector->get_data(); 21 | 22 | echo '
'; 23 | 24 | if ( $this->collector->qm_no_jquery() ) { 25 | 26 | echo '
'; 27 | echo '

Heartbeat logging requires jQuery, which has been prevented by QM_NO_JQUERY.

'; 28 | echo '
'; 29 | 30 | } else if ( wp_script_is( 'heartbeat', 'done' ) ) { 31 | echo ''; 32 | echo ''; 33 | 34 | echo ''; 35 | echo ''; 36 | 37 | echo ''; 38 | echo ''; 39 | echo ''; 40 | echo ''; 41 | echo ''; 42 | 43 | echo ''; 44 | echo ''; 45 | 46 | echo ''; 47 | echo ''; 48 | echo ''; 51 | echo ''; 52 | echo ''; 53 | 54 | echo '
' . esc_html( $this->collector->name() ) . '
LubDubTime since previousDuration
'; 49 | echo '

Listening for first heartbeat...

'; 50 | echo '
'; 55 | echo ''; 56 | } else { 57 | 58 | echo '
'; 59 | echo '

' . esc_html__( 'No heartbeat detected.', 'query-monitor' ) . '

'; 60 | echo '
'; 61 | 62 | } 63 | 64 | echo '
'; 65 | 66 | $this->current_id = 'qm-heartbeat'; 67 | $this->current_name = 'Heartbeat'; 68 | 69 | $this->output_concerns(); 70 | } 71 | 72 | /** 73 | * @param array> $menu 74 | * @return array> 75 | */ 76 | public function panel_menu( array $menu ) { 77 | $menu['qm-heartbeat'] = $this->menu( array( 78 | 'title' => esc_html__( 'Heartbeats (0)' ), 79 | 'id' => 'query-monitor-extend-heartbeat', 80 | ) ); 81 | 82 | return $menu; 83 | } 84 | } 85 | 86 | add_filter( 'qm/outputter/html', static function ( array $output ) : array { 87 | if ( $collector = QM_Collectors::get( 'heartbeat' ) ) { 88 | $output['heartbeat'] = new QMX_Output_Html_Heartbeat( $collector ); 89 | } 90 | 91 | return $output; 92 | }, 70 ); 93 | -------------------------------------------------------------------------------- /image-sizes/qmx-image-sizes-collector.php: -------------------------------------------------------------------------------- 1 | 7 | * @property-write QMX_Data_Image_Sizes $data 8 | */ 9 | class QMX_Collector_Image_Sizes extends QM_DataCollector { 10 | 11 | public $id = 'image_sizes'; 12 | 13 | public function __construct() { 14 | parent::__construct(); 15 | 16 | $this->data->sizes = array( 17 | 'thumbnail' => array( 18 | 'width' => intval( get_option( 'thumbnail_size_w' ) ), 19 | 'height' => intval( get_option( 'thumbnail_size_h' ) ), 20 | 'used' => 0, 21 | 'source' => 'native', 22 | 'crop' => true, 23 | 'num' => 1, 24 | ), 25 | 'medium' => array( 26 | 'width' => intval( get_option( 'medium_size_w' ) ), 27 | 'height' => intval( get_option( 'medium_size_h' ) ), 28 | 'used' => 0, 29 | 'source' => 'native', 30 | 'crop' => false, 31 | 'num' => 2, 32 | ), 33 | 'medium_large' => array( 34 | 'width' => intval( get_option( 'medium_large_size_w' ) ), 35 | 'height' => intval( get_option( 'medium_large_size_h' ) ), 36 | 'used' => 0, 37 | 'source' => 'native', 38 | 'crop' => false, 39 | 'num' => 3, 40 | ), 41 | 'large' => array( 42 | 'width' => intval( get_option( 'large_size_w' ) ), 43 | 'height' => intval( get_option( 'large_size_h' ) ), 44 | 'used' => 0, 45 | 'source' => 'native', 46 | 'crop' => false, 47 | 'num' => 4, 48 | ), 49 | 50 | /** 51 | * @see _wp_add_additional_image_sizes() 52 | * @since WP 5.3.0 53 | */ 54 | '1536x1536' => array( 55 | 'width' => 1536, 56 | 'height' => 1536, 57 | 'used' => 0, 58 | 'source' => 'native', 59 | 'crop' => false, 60 | 'num' => 5, 61 | ), 62 | '2048x2048' => array( 63 | 'width' => 2048, 64 | 'height' => 2048, 65 | 'used' => 0, 66 | 'source' => 'native', 67 | 'crop' => false, 68 | 'num' => 6, 69 | ), 70 | ); 71 | 72 | add_action( 'plugins_loaded', array( &$this, 'action__plugins_loaded' ) ); 73 | add_action( 'after_setup_theme', array( &$this, 'action__after_setup_theme' ) ); 74 | add_action( 'wp_enqueue_scripts', array( &$this, 'add_inline_script' ), -998 ); 75 | add_action( 'admin_enqueue_scripts', array( &$this, 'add_inline_script' ), -998 ); 76 | add_action( 'login_enqueue_scripts', array( &$this, 'add_inline_script' ), -998 ); 77 | add_action( 'enqueue_embed_scripts', array( &$this, 'add_inline_script' ), -998 ); 78 | 79 | add_action( 'wp', array( $this, 'action__wp' ) ); 80 | add_filter( 'wp_get_attachment_image_src', array( $this, 'filter__wp_get_attachment_image_src' ), 10, 3 ); 81 | } 82 | 83 | public function get_storage(): QM_Data { 84 | require_once 'qmx-image-sizes-data.php'; 85 | return new QMX_Data_Image_Sizes(); 86 | } 87 | 88 | public function get_concerned_filters() { 89 | return array( 90 | 'wp_get_attachment_image_src', 91 | ); 92 | } 93 | 94 | public function action__plugins_loaded() : void { 95 | if ( 'plugins_loaded' !== current_action() || did_action( 'qm/cease' ) ) { 96 | return; 97 | } 98 | 99 | $this->process_added_image_sizes( 'plugin' ); 100 | } 101 | 102 | public function action__after_setup_theme() : void { 103 | if ( 'after_setup_theme' !== current_action() || did_action( 'qm/cease' ) ) { 104 | return; 105 | } 106 | 107 | $this->process_added_image_sizes( 'theme' ); 108 | } 109 | 110 | public function action__wp() : void { 111 | if ( did_action( 'qm/cease' ) ) { 112 | return; 113 | } 114 | 115 | $post = get_queried_object(); 116 | 117 | if ( empty( $post ) || ! is_object( $post ) || ! is_a( $post, WP_Post::class ) ) { 118 | return; 119 | } 120 | 121 | $blocks = parse_blocks( $post->post_content ); 122 | 123 | if ( empty( $blocks ) ) { 124 | return; 125 | } 126 | 127 | foreach ( $blocks as $block ) { 128 | if ( 'core/image' !== $block['blockName'] || empty( $block['attrs'] ) || empty( $block['attrs']['sizeSlug'] ) ) { 129 | continue; 130 | } 131 | 132 | $size = $block['attrs']['sizeSlug']; 133 | 134 | if ( ! array_key_exists( $size, $this->data->sizes ) ) { 135 | continue; 136 | } 137 | 138 | $this->data->sizes[ $size ]['used']++; 139 | } 140 | } 141 | 142 | /** 143 | * @param false|array $image 144 | * @param int $attachment_id 145 | * @param string|int[] $size 146 | * @return false|array 147 | * 148 | * Removed type constraint on $attachment_id parameter: 149 | * https://github.com/crstauf/query-monitor-extend/issues/77. 150 | */ 151 | public function filter__wp_get_attachment_image_src( $image, $attachment_id, $size ) { 152 | if ( did_action( 'qm/cease' ) ) { 153 | return $image; 154 | } 155 | 156 | # If specifying custom dimensions, bail. 157 | if ( is_array( $size ) ) { 158 | return $image; 159 | } 160 | 161 | # If size is not registered, bail. 162 | if ( ! array_key_exists( $size, $this->data->sizes ) ) { 163 | return $image; 164 | } 165 | 166 | $this->data->sizes[ $size ]['used']++; 167 | 168 | return $image; 169 | } 170 | 171 | /** 172 | * @param string $source 173 | * @return void 174 | */ 175 | protected function process_added_image_sizes( string $source = 'unknown' ) : void { 176 | global $_wp_additional_image_sizes; 177 | 178 | $num = count( $this->data->sizes ); 179 | 180 | if ( 181 | is_array( $_wp_additional_image_sizes ) 182 | && ! empty( $_wp_additional_image_sizes ) 183 | ) { 184 | foreach ( $_wp_additional_image_sizes as $id => $size ) { 185 | if ( ! array_key_exists( $id, $this->data->sizes ) ) { 186 | $this->data->sizes[ $id ] = array_merge( 187 | array( 188 | 'num' => ++$num, 189 | 'source' => apply_filters( 'qmx/image-size/source', $source, $id, $size ), 190 | 'used' => 0, 191 | ), 192 | $size 193 | ); 194 | } 195 | } 196 | } 197 | } 198 | 199 | public function process() { 200 | if ( did_action( 'qm/cease' ) ) { 201 | return; 202 | } 203 | 204 | $this->process_added_image_sizes(); 205 | 206 | $this->data->sizes = array_map( array( &$this, 'add_ratio' ), $this->data->sizes ); 207 | 208 | $counts = array( 'dimensions' => array(), 'ratios' => array() ); 209 | 210 | foreach ( $this->data->sizes as $size ) { 211 | $key = $size['width'] . ':' . $size['height'] . ' - ' . ( bool ) $size['crop']; 212 | 213 | if ( ! array_key_exists( $key, $counts['dimensions'] ) ) { 214 | $counts['dimensions'][ $key ] = 0; 215 | } 216 | 217 | $counts['dimensions'][ $key ]++; 218 | 219 | $key = $size['ratio'] . ' - ' . ( bool ) $size['crop']; 220 | if ( 0 !== $size['ratio'] ) { 221 | if ( ! array_key_exists( $key, $counts['ratios'] ) ) { 222 | $counts['ratios'][ $key ] = 0; 223 | } 224 | 225 | $counts['ratios'][ $key ]++; 226 | } 227 | } 228 | 229 | foreach ( array( 'dimensions', 'ratios' ) as $type ) { 230 | $counts[ $type ] = array_filter( $counts[ $type ], function ( $v ) { 231 | return $v > 1; 232 | } ); 233 | } 234 | 235 | $this->data->duplicates = $counts; 236 | } 237 | 238 | /** 239 | * @param array $size 240 | * @return array 241 | */ 242 | private function add_ratio( array $size ) : array { 243 | if ( 244 | ! array_key_exists( 'width', $size ) 245 | || ! array_key_exists( 'height', $size ) 246 | ) { 247 | return $size; 248 | } 249 | 250 | $num1 = $size['width']; 251 | $num2 = $size['height']; 252 | 253 | while ( 0 !== $num2 ) { 254 | $t = $num1 % $num2; 255 | $num1 = $num2; 256 | $num2 = $t; 257 | } 258 | 259 | $size['_gcd'] = $num1; // greatest common denominator 260 | unset( $num1, $num2 ); 261 | 262 | $size['ratio'] = ( 263 | 0 === $size['height'] 264 | ? 0 265 | : $size['width'] / $size['height'] 266 | ); 267 | 268 | return $size; 269 | } 270 | 271 | public function add_inline_script() : void { 272 | if ( did_action( 'qm/cease' ) ) { 273 | return; 274 | } 275 | 276 | wp_add_inline_script( 'query-monitor', $this->inlineScript_queryMonitor() ); 277 | } 278 | 279 | protected function inlineScript_queryMonitor() : string { 280 | ob_start(); 281 | ?> 282 | 283 | if ( window.jQuery ) { 284 | 285 | jQuery( function( $ ) { 286 | 287 | $( 'td[data-qmx-image-size-width]' ) 288 | .on( 'mouseenter', function() { qmx_image_size_highlighter__mouseenter( 'width', this ); } ) 289 | .on( 'mouseleave', function() { qmx_image_size_highlighter__mouseleave( 'width', this ); } ); 290 | 291 | $( 'td[data-qmx-image-size-height]' ) 292 | .on( 'mouseenter', function() { qmx_image_size_highlighter__mouseenter( 'height', this ); } ) 293 | .on( 'mouseleave', function() { qmx_image_size_highlighter__mouseleave( 'height', this ); } ); 294 | 295 | $( 'td[data-qmx-image-size-ratio]' ) 296 | .on( 'mouseenter', function() { qmx_image_size_highlighter__mouseenter( 'ratio', this ); } ) 297 | .on( 'mouseleave', function() { qmx_image_size_highlighter__mouseleave( 'ratio', this ); } ); 298 | 299 | } ); 300 | 301 | function qmx_image_size_highlighter__mouseenter( prop, el ) { 302 | jQuery( el ).addClass( 'qm-highlight' ); 303 | var tr = jQuery( el ).closest( 'tr' ); 304 | var value = jQuery( el ).attr( 'data-qmx-image-size-' + prop ); 305 | var table = jQuery( el ).closest( 'table' ).find( 'tr[data-qmx-image-size-' + prop + '="' + value + '"]' ).not( tr ).addClass( 'qm-highlight' ); 306 | } 307 | 308 | function qmx_image_size_highlighter__mouseleave( prop, el ) { 309 | jQuery( el ).removeClass( 'qm-highlight' ); 310 | jQuery( el ).closest( 'table' ).find( 'tr.qm-highlight' ).removeClass( 'qm-highlight' ); 311 | } 312 | 313 | } 314 | 315 | > $sizes 9 | */ 10 | public $sizes = array(); 11 | 12 | /** 13 | * @var array> 14 | */ 15 | public $duplicates = array(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /image-sizes/qmx-image-sizes-output.php: -------------------------------------------------------------------------------- 1 | collector; 22 | /** @var QMX_Data_Image_Sizes */ 23 | $data = $collector->get_data(); 24 | 25 | echo '
'; 26 | 27 | if ( ! empty( $data->sizes ) ) { 28 | echo ''; 29 | echo ''; 30 | echo ''; 31 | echo ''; 32 | 33 | echo ''; 36 | 37 | echo ''; 40 | 41 | echo ''; 44 | 45 | echo ''; 48 | 49 | echo ''; 52 | 53 | echo ''; 56 | 57 | echo ''; 60 | 61 | echo ''; 64 | 65 | echo ''; 66 | echo ''; 67 | 68 | echo ''; 69 | 70 | $sources = array(); 71 | $uses = 0; 72 | 73 | foreach ( $data->sizes as $id => $row ) { 74 | $ratio = array( 75 | $row['width'], 76 | $row['height'] 77 | ); 78 | 79 | if ( 80 | ! empty( $row['width'] ) 81 | && ! empty( $row['height'] ) 82 | ) { 83 | $ratio = array( 84 | $row['width'] / $row['_gcd'], 85 | $row['height'] / $row['_gcd'] 86 | ); 87 | } 88 | 89 | if ( $ratio === array( $row['width'], $row['height'] ) ) { 90 | $ratio = array( '—' ); 91 | } 92 | 93 | $uses += $row['used']; 94 | 95 | echo ''; 96 | echo ''; 97 | echo ''; 98 | echo ''; 99 | echo ''; 100 | echo ''; 101 | echo ''; 102 | echo ''; 103 | echo ''; 104 | echo ''; 105 | 106 | if ( ! array_key_exists( $row['source'], $sources ) ) { 107 | $sources[ $row['source'] ] = 0; 108 | } 109 | 110 | $sources[ $row['source'] ]++; 111 | } 112 | 113 | echo ''; 114 | echo ''; 115 | 116 | $sources = array_map( 117 | function ( $k, $v ) { 118 | return ucwords( ( string ) $k ) . ': ' . $v; 119 | }, 120 | array_keys( $sources ), 121 | $sources 122 | ); 123 | 124 | echo ''; 125 | echo ''; 126 | echo ''; 127 | echo ''; 128 | echo ''; 129 | echo ''; 130 | echo ''; 131 | 132 | echo ''; 133 | echo '
' . esc_html( $this->name() ) . '
'; 34 | echo $this->build_sorter( __( '', 'query-monitor-extend' ) ); 35 | echo ''; 38 | echo $this->build_sorter( __( 'ID', 'query-monitor-extend' ) ); 39 | echo ''; 42 | echo $this->build_sorter( __( 'Uses', 'query-monitor-extend' ) ); 43 | echo ''; 46 | echo $this->build_sorter( __( 'Width', 'query-monitor-extend' ) ); 47 | echo ''; 50 | echo $this->build_sorter( __( 'Height', 'query-monitor-extend' ) ); 51 | echo ''; 54 | echo $this->build_sorter( __( 'Ratio', 'query-monitor-extend' ) ); 55 | echo ''; 58 | echo __( 'Cropped', 'query-monitor-extend' ); 59 | echo ''; 62 | echo $this->build_sorter( __( 'Source', 'query-monitor-extend' ) ); 63 | echo '
' . esc_html( $row['num'] ) . '' . esc_html( $id ) . '' . esc_html( $row['used'] ) . '' . esc_html( $row['width'] ) . '' . esc_html( $row['height'] ) . '' . esc_html( implode( ':', $ratio ) ) . '' . ( $row['crop'] ? '' : '' ) . '' . esc_html( $row['source'] ) . '
Total: ' . esc_html( number_format_i18n( count( $data->sizes ) ) ) . 'Uses: ' . esc_html( number_format_i18n( $uses ) ) . 'Duplicates: ' . esc_html( number_format_i18n( array_sum( $data->duplicates['dimensions'] ) ) ) . 'Duplicates: ' . esc_html( number_format_i18n( array_sum( $data->duplicates['ratios'] ) ) ) . '' . implode( ', ', $sources ) . '
'; 134 | 135 | } else { 136 | 137 | echo '
'; 138 | echo '

' . esc_html__( 'None', 'query-monitor' ) . '

'; 139 | echo '
'; 140 | 141 | } 142 | 143 | echo '
'; 144 | 145 | $this->current_id = 'qm-image_sizes'; 146 | $this->current_name = 'Image Sizes'; 147 | 148 | $this->output_concerns(); 149 | } 150 | 151 | /** 152 | * @param array> $menu 153 | * @return array> 154 | */ 155 | public function panel_menu( array $menu ) { 156 | $menu['qm-image_sizes'] = $this->menu( array( 157 | 'title' => esc_html__( 'Image Sizes', 'query-monitor-extend' ), 158 | 'id' => 'query-monitor-extend-image_sizes', 159 | ) ); 160 | 161 | return $menu; 162 | } 163 | } 164 | 165 | add_filter( 'qm/outputter/html', static function ( array $output ) : array { 166 | if ( $collector = QM_Collectors::get( 'image_sizes' ) ) { 167 | $output['image_sizes'] = new QMX_Output_Html_Image_Sizes( $collector ); 168 | } 169 | 170 | return $output; 171 | }, 70 ); 172 | -------------------------------------------------------------------------------- /paths/qmx-paths-collector.php: -------------------------------------------------------------------------------- 1 | 7 | * @property-write QMX_Data_Paths $data 8 | */ 9 | class QMX_Collector_Paths extends QM_DataCollector { 10 | 11 | public $id = 'paths'; 12 | 13 | public function name() : string { 14 | return __( 'Paths', 'query-monitor-extend' ); 15 | } 16 | 17 | public function get_storage(): QM_Data { 18 | require_once 'qmx-paths-data.php'; 19 | return new QMX_Data_Paths(); 20 | } 21 | 22 | public function process() { 23 | if ( did_action( 'qm/cease' ) ) { 24 | return; 25 | } 26 | 27 | $this->data->paths = apply_filters( 'qmx/collector/paths', array( 28 | 'ABSPATH' => defined( 'ABSPATH' ) ? constant( 'ABSPATH' ) : 'undefined', 29 | 'COOKIEPATH' => defined( 'COOKIEPATH' ) ? constant( 'COOKIEPATH' ) : 'undefined', 30 | 'SITECOOKIEPATH' => defined( 'SITECOOKIEPATH' ) ? constant( 'SITECOOKIEPATH' ) : 'undefined', 31 | 'DOMAIN_CURRENT_SITE' => defined( 'DOMAIN_CURRENT_SITE' ) ? constant( 'DOMAIN_CURRENT_SITE' ) : 'undefined', 32 | 'PATH_CURRENT_SITE' => defined( 'PATH_CURRENT_SITE' ) ? constant( 'PATH_CURRENT_SITE' ) : 'undefined', 33 | 'WP_SITEURL' => defined( 'WP_SITEURL' ) ? constant( 'WP_SITEURL' ) : 'undefined', 34 | 'site_url()' => site_url(), 35 | 'get_site_url()' => get_site_url(), 36 | 'network_site_url()' => network_site_url(), 37 | 'WP_HOME' => defined( 'WP_HOME' ) ? constant( 'WP_HOME' ) : 'undefined', 38 | 'home_url()' => home_url(), 39 | 'get_home_url()' => get_home_url(), 40 | 'network_home_url()' => network_home_url(), 41 | 'get_home_path()' => function_exists( 'get_home_path' ) ? get_home_path() : 'undefined', 42 | 'WP_CONTENT_URL' => defined( 'WP_CONTENT_URL' ) ? constant( 'WP_CONTENT_URL' ) : 'undefined', 43 | 'WP_CONTENT_DIR' => defined( 'WP_CONTENT_DIR' ) ? constant( 'WP_CONTENT_DIR' ) : 'undefined', 44 | 'content_url()' => content_url(), 45 | 'WP_PLUGIN_URL' => defined( 'WP_PLUGIN_URL' ) ? constant( 'WP_PLUGIN_URL' ) : 'undefined', 46 | 'WP_PLUGIN_DIR' => defined( 'WP_PLUGIN_DIR' ) ? constant( 'WP_PLUGIN_DIR' ) : 'undefined', 47 | 'PLUGINS_COOKIE_PATH' => defined( 'PLUGINS_COOKIE_PATH' ) ? constant( 'PLUGINS_COOKIE_PATH' ) : 'undefined', 48 | 'plugins_url()' => plugins_url(), 49 | 'plugin_dir_url( __FILE__ )' => plugin_dir_url( __FILE__ ), 50 | 'plugin_dir_path( __FILE__ )' => plugin_dir_path( __FILE__ ), 51 | 'plugin_basename( __FILE__ )' => plugin_basename( __FILE__ ), 52 | 'WPMU_PLUGIN_DIR' => defined( 'WPMU_PLUGIN_DIR' ) ? constant( 'WPMU_PLUGIN_DIR' ) : 'undefined', 53 | 'WPMU_PLUGIN_URL' => defined( 'WPMU_PLUGIN_URL' ) ? constant( 'WPMU_PLUGIN_URL' ) : 'undefined', 54 | 'get_theme_root()' => get_theme_root(), 55 | 'get_theme_roots()' => get_theme_roots(), 56 | 'get_theme_root_uri()' => get_theme_root_uri(), 57 | 'get_template_directory()' => get_template_directory(), 58 | 'TEMPLATEPATH' => defined( 'TEMPLATEPATH' ) ? constant( 'TEMPLATEPATH' ) : 'undefined', 59 | 'get_template_directory_uri()' => get_template_directory_uri(), 60 | 'get_stylesheet_uri()' => get_stylesheet_uri(), 61 | 'get_stylesheet_directory()' => get_stylesheet_directory(), 62 | 'STYLESHEETPATH' => defined( 'STYLESHEETPATH' ) ? constant( 'STYLESHEETPATH' ) : 'undefined', 63 | 'get_stylesheet_directory_uri()' => get_stylesheet_directory_uri(), 64 | 'admin_url()' => admin_url(), 65 | 'get_admin_url()' => get_admin_url(), 66 | 'network_admin_url()' => network_admin_url(), 67 | 'ADMIN_COOKIE_PATH' => defined( 'ADMIN_COOKIE_PATH' ) ? constant( 'ADMIN_COOKIE_PATH' ) : 'undefined', 68 | 'WPINC' => defined( 'WPINC' ) ? constant( 'WPINC' ) : 'undefined', 69 | 'includes_url()' => includes_url(), 70 | 'WP_LANG_DIR' => defined( 'WP_LANG_DIR' ) ? constant( 'WP_LANG_DIR' ) : 'undefined', 71 | 'BLOGUPLOADDIR' => defined( 'BLOGUPLOADDIR' ) ? constant( 'BLOGUPLOADDIR' ) : 'undefined', 72 | 'UPLOADBLOGSDIR' => defined( 'UPLOADBLOGSDIR' ) ? constant( 'UPLOADBLOGSDIR' ) : 'undefined', 73 | 'UPLOADS' => defined( 'UPLOADS' ) ? constant( 'UPLOADS' ) : 'undefined', 74 | 'wp_upload_dir()' => wp_upload_dir(), 75 | 'get_theme_file_path()' => get_theme_file_path(), 76 | 'get_theme_file_uri()' => get_theme_file_uri(), 77 | ) ); 78 | 79 | if ( defined( 'WP_DEBUG_LOG' ) && is_string( constant( 'WP_DEBUG_LOG' ) ) ) { 80 | $this->data->paths['WP_DEBUG_LOG'] = constant( 'WP_DEBUG_LOG' ); 81 | } 82 | 83 | ksort( $this->data->paths, SORT_FLAG_CASE | SORT_STRING ); 84 | } 85 | 86 | public function get_concerned_filters() { 87 | return array( 88 | 'admin_url', 89 | 'content_url', 90 | 'home_url', 91 | 'includes_url', 92 | 'plugins_url', 93 | 'network_admin_url', 94 | 'network_home_url', 95 | 'network_site_url', 96 | 'site_url', 97 | 'stylesheet_directory', 98 | 'stylesheet_directory_uri', 99 | 'stylesheet_uri', 100 | 'template_directory', 101 | 'template_directory_uri', 102 | 'theme_file_path', 103 | 'theme_file_uri', 104 | 'theme_root', 105 | 'theme_root_uri', 106 | 'upload_dir', 107 | ); 108 | } 109 | 110 | public function get_concerned_constants() { 111 | return array( 112 | 'ABSPATH', 113 | 'COOKIEPATH', 114 | 'SITECOOKIEPATH', 115 | 'DOMAIN_CURRENT_SITE', 116 | 'PATH_CURRENT_SITE', 117 | 'WP_SITEURL', 118 | 'WP_HOME', 119 | 'WP_CONTENT_URL', 120 | 'WP_CONTENT_DIR', 121 | 'WP_PLUGIN_URL', 122 | 'WP_PLUGIN_DIR', 123 | 'PLUGINS_COOKIE_PATH', 124 | 'WPMU_PLUGIN_DIR', 125 | 'WPMU_PLUGIN_URL', 126 | 'TEMPLATEPATH', 127 | 'STYLESHEETPATH', 128 | 'ADMIN_COOKIE_PATH', 129 | 'WPINC', 130 | 'WP_LANG_DIR', 131 | 'BLOGUPLOADDIR', 132 | 'UPLOADBLOGSDIR', 133 | 'UPLOADS', 134 | ); 135 | } 136 | 137 | public function get_concerned_options() { 138 | return array( 139 | 'siteurl', 140 | 'home', 141 | ); 142 | } 143 | 144 | } 145 | 146 | add_filter( 'qm/collectors', static function ( array $collectors ) : array { 147 | $collectors['paths'] = new QMX_Collector_Paths; 148 | return $collectors; 149 | } ); 150 | -------------------------------------------------------------------------------- /paths/qmx-paths-data.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | public $paths = array(); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /paths/qmx-paths-output.php: -------------------------------------------------------------------------------- 1 | collector->get_data(); 22 | 23 | echo '
'; 24 | 25 | if ( ! empty( $data->paths ) ) { 26 | echo ''; 27 | echo ''; 28 | echo ''; 29 | echo ''; 30 | 31 | echo ''; 34 | 35 | echo ''; 38 | 39 | echo ''; 40 | echo ''; 41 | 42 | echo ''; 43 | 44 | foreach ( $data->paths as $var => $value ) { 45 | echo ''; 46 | echo ''; 47 | 48 | if ( is_string( $value ) ) { 49 | 50 | # Remove ABSPATH and add back to support paths without ABSPATH. 51 | $possible_path = str_replace( ABSPATH, '', $value ); 52 | $possible_path = ABSPATH . $possible_path; 53 | 54 | $value = esc_html( $value ); 55 | 56 | if ( file_exists( $possible_path ) ) { 57 | $value = QM_Output_Html::output_filename( $value, $possible_path ); 58 | } 59 | 60 | echo ''; 61 | 62 | } else { 63 | 64 | echo ''; 67 | 68 | } 69 | echo ''; 70 | } 71 | 72 | echo ''; 73 | echo ''; 74 | 75 | echo ''; 76 | echo '
' . esc_html( $this->collector->name() ) . '
'; 32 | echo $this->build_sorter( __( 'Constant/Function', 'query-monitor-extend' ) ); 33 | echo ''; 36 | echo __( 'Path', 'query-monitor-extend' ); 37 | echo '
' . esc_html( $var ) . '' . $value . ''; 65 | self::output_inner( $value ); 66 | echo '
'; 77 | 78 | } else { 79 | 80 | echo '
'; 81 | echo '

' . esc_html__( 'None', 'query-monitor' ) . '

'; 82 | echo '
'; 83 | 84 | } 85 | 86 | echo '
'; 87 | 88 | $this->current_id = 'qm-paths'; 89 | $this->current_name = 'Paths'; 90 | 91 | $this->output_concerns(); 92 | } 93 | 94 | /** 95 | * @param array> $menu 96 | * @return array> 97 | */ 98 | public function panel_menu( array $menu ) { 99 | $menu['qm-paths'] = $this->menu( array( 100 | 'title' => esc_html__( 'Paths', 'query-monitor-extend' ), 101 | 'id' => 'query-monitor-extend-paths', 102 | ) ); 103 | 104 | return $menu; 105 | } 106 | 107 | } 108 | 109 | add_filter( 'qm/outputter/html', static function ( array $output ) : array { 110 | if ( $collector = QM_Collectors::get( 'paths' ) ) { 111 | $output['paths'] = new QMX_Output_Html_Paths( $collector ); 112 | } 113 | 114 | return $output; 115 | }, 70 ); 116 | -------------------------------------------------------------------------------- /phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Query Monitor Extend Coding Standards. 4 | 5 | acf 6 | constants 7 | files 8 | heartbeat 9 | globals 10 | image-sizes 11 | paths 12 | time 13 | qmx-conditionals.php 14 | qmx-time-hooks.php 15 | query-monitor-extend.php 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | *-config.php 30 | *assets/ 31 | *vendor/ 32 | *tests/* 33 | extensions\/(?!twig).* 34 | *webpack/ 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 0 76 | 77 | 78 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 3 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /phpstan-baseline.neon: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crstauf/query-monitor-extend/a41cb14a1899bea5582e3d50efb61f1095913635/phpstan-baseline.neon -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | includes: 2 | - phpstan-baseline.neon 3 | 4 | parameters: 5 | level: 8 6 | paths: 7 | - acf/ 8 | - constants/ 9 | - files/ 10 | - globals/ 11 | - heartbeat/ 12 | - image-sizes/ 13 | - paths/ 14 | - time/ 15 | - qmx-conditionals.php 16 | - query-monitor-extend.php 17 | excludePaths: 18 | - vendor/ 19 | bootstrapFiles: 20 | - dev/phpstan-bootstrap.php 21 | - vendor/php-stubs/wordpress-stubs/wordpress-stubs.php 22 | - vendor/php-stubs/acf-pro-stubs/acf-pro-stubs.php 23 | stubFiles: 24 | - dev/wordpress-overrides.stub 25 | dynamicConstantNames: 26 | - CONCATENATE_SCRIPTS 27 | - COMPRESS_SCRIPTS 28 | - COMPRESS_CSS 29 | tmpDir: .phpstan-cache/ 30 | reportUnmatchedIgnoredErrors: false 31 | ignoreErrors: 32 | - '#^Function yoast_get_primary_term_id not found.$#' 33 | # Uses func_get_args() 34 | - '#^Function apply_filters invoked with [34567] parameters, 2 required\.$#' 35 | - '#^Function do_action invoked with [3456] parameters, 1-2 required\.$#' 36 | - '#^Function current_user_can invoked with 2 parameters, 1 required\.$#' 37 | - '#^Function add_query_arg invoked with [123] parameters?, 0 required\.$#' 38 | - '#^Function add_theme_support invoked with [2345] parameters, 1 required\.$#' 39 | - '#^Function wp_sprintf invoked with [23456] parameters, 1 required\.$#' 40 | # https://core.trac.wordpress.org/ticket/43304 41 | - '/^Parameter #2 \$deprecated of function load_plugin_textdomain expects string, false given\.$/' 42 | # WP-CLI accepts a class as callable 43 | - '/^Parameter #2 \$callable of static method WP_CLI::add_command\(\) expects callable\(\): mixed, \S+ given\.$/' 44 | - '#PHPDoc tag @throws with type .*? is not subtype of Throwable#' 45 | - '#Function gravity_form not found.#' 46 | - '/^Parameter #1 \$args of function wp_nav_menu expects.* given\.$/' 47 | 48 | checkAlwaysTrueStrictComparison: true 49 | 50 | # Unfortunately, DocBlocks can't be relied upon in WordPress. 51 | treatPhpDocTypesAsCertain: false -------------------------------------------------------------------------------- /qmx-conditionals.php: -------------------------------------------------------------------------------- 1 | current_priority(); 22 | } 23 | 24 | do_action( $action, $label, $priority ); 25 | } 26 | 27 | /** 28 | * @return void 29 | */ 30 | function qmx_start() : void { 31 | qmx_time_hooks( 'start' ); 32 | } 33 | 34 | /** 35 | * @return void 36 | */ 37 | function qmx_lap() : void { 38 | qmx_time_hooks( 'lap' ); 39 | } 40 | 41 | /** 42 | * @return void 43 | */ 44 | function qmx_stop() : void { 45 | qmx_time_hooks( 'stop' ); 46 | } 47 | 48 | /** 49 | * @return void 50 | */ 51 | function qmx_auto() : void { 52 | if ( ! doing_action() ) { 53 | return; 54 | } 55 | 56 | qmx_time_hooks( 'start' ); 57 | add_action( current_action(), 'qmx_stop', PHP_INT_MAX ); 58 | } 59 | -------------------------------------------------------------------------------- /query-monitor-extend.php: -------------------------------------------------------------------------------- 1 | Query Monitor %3$s', 62 | 'https://wordpress.org/plugins/query-monitor/', 63 | 'https://github.com/johnbillion/query-monitor/releases/tag/', 64 | constant( 'QMX_TESTED_WITH_QM' ) 65 | ) 66 | ); 67 | 68 | if ( class_exists( 'QueryMonitor' ) ) { 69 | return $meta; 70 | } 71 | 72 | $first = array_shift( $meta ); 73 | 74 | array_unshift( 75 | $meta, 76 | $first, 77 | sprintf( 78 | 'Requires Query Monitor', 79 | 'https://wordpress.org/plugins/query-monitor/' 80 | ) 81 | ); 82 | 83 | return $meta; 84 | }, 10, 2 ); 85 | 86 | if ( ! class_exists( 'QueryMonitor' ) || did_action( 'qm/cease' ) ) { 87 | return; 88 | } 89 | 90 | $collector_names = apply_filters( 'qmx/collectors', array( 91 | 'acf', 92 | 'constants', 93 | 'files', 94 | 'globals', 95 | 'heartbeat', 96 | 'image-sizes', 97 | 'paths', 98 | 'time', 99 | ) ); 100 | 101 | $dir = trailingslashit( __DIR__ ); 102 | 103 | /** 104 | * If loading as an mu-plugin, add "query-monitor-extend" 105 | * to directory path. 106 | */ 107 | if ( trailingslashit( constant( 'WPMU_PLUGIN_DIR' ) ) === $dir ) { 108 | $dir .= 'query-monitor-extend/'; 109 | } 110 | 111 | # Include all collector and outputters. 112 | foreach ( $collector_names as $collector_name ) { 113 | $files = array( 114 | sprintf( '%1$s%2$s/qmx-%2$s-collector.php', $dir, $collector_name ), 115 | sprintf( '%1$s%2$s/qmx-%2$s-output.php', $dir, $collector_name ), 116 | ); 117 | 118 | $files = array_filter( $files, 'file_exists' ); 119 | 120 | if ( count( $files ) !== 2 ) { 121 | continue; 122 | } 123 | 124 | foreach ( $files as $file ) { 125 | require_once $file; 126 | } 127 | } 128 | 129 | # Include hook timing feature. 130 | include_once $dir . 'qmx-time-hooks.php'; 131 | 132 | # Include additional conditionals. 133 | include_once $dir . 'qmx-conditionals.php'; 134 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ![WP tested 6.6.1](https://img.shields.io/badge/WP-Tested_v6.6.1-blue) 2 | ![QM tested up to 3.16.4](https://img.shields.io/badge/QM-Tested_v3.16.4-blue) 3 | ![License: GPL v3](https://img.shields.io/badge/License-GPL_v3-blue) 4 | [![PHPCS](https://github.com/crstauf/query-monitor-extend/actions/workflows/phpcs.yml/badge.svg)](https://github.com/crstauf/query-monitor-extend/actions/workflows/phpcs.yml) 5 | [![PHPStan](https://github.com/crstauf/query-monitor-extend/actions/workflows/phpstan.yml/badge.svg)](https://github.com/crstauf/query-monitor-extend/actions/workflows/phpstan.yml) 6 | 7 | # Query Monitor Extend 8 | 9 | > WordPress plugin with customizations to enhance and extend the already awesome [Query Monitor](https://github.com/johnbillion/query-monitor) plugin by [John Blackbourn](https://github.com/johnbillion/). 10 | 11 | ## Panels 12 | 13 | | Panel | Description | 14 | | :---------- | :---------- | 15 | | ACF | Calls to [`the_field()`](https://www.advancedcustomfields.com/resources/the_field/) and [`get_field()`](https://www.advancedcustomfields.com/resources/get_field/), and [Local JSON](https://www.advancedcustomfields.com/resources/local-json/) configuration | 16 | | Constants | User defined constants: [`get_defined_constants( true )['user']`](https://www.php.net/manual/en/function.get-defined-constants.php) | 17 | | Files | Included files: [`get_included_files()`](https://www.php.net/manual/en/function.get-included-files.php) | 18 | | $_SERVER | Dump of [`$_SERVER`](https://www.php.net/manual/en/reserved.variables.server.php) | 19 | | $_GET | Dump of [`$_GET`](https://www.php.net/manual/en/reserved.variables.get.php) (if set) | 20 | | $_POST | Dump of [`$_POST`](https://www.php.net/manual/en/reserved.variables.post.php) (if set) | 21 | | Heartbeats | Monitors [WordPress' Heartbeat](https://developer.wordpress.org/plugins/javascript/heartbeat-api/) | 22 | | Image Sizes | Names, count, width, height, ratio, cropped, and source of WordPress registered [image sizes](https://developer.wordpress.org/themes/functionality/featured-images-post-thumbnails/) | 23 | | Paths | Constants and functions for WordPress URLs and paths | 24 | | Time | Current time in timezones: UTC, server, WordPress, and browser | 25 | 26 | ## Demo 27 | 28 | Demos of QMX are available via the [WordPress Playground](https://developer.wordpress.org/playground/): 29 | 30 | - [Install as plugin](https://playground.wordpress.net/#%7B%22landingPage%22:%22/wp-admin/plugins.php%22,%22steps%22:%5B%7B%22step%22:%22login%22,%22username%22:%22admin%22,%22password%22:%22password%22%7D,%7B%22step%22:%22installPlugin%22,%22pluginZipFile%22:%7B%22resource%22:%22wordpress.org/plugins%22,%22slug%22:%22query-monitor%22%7D%7D,%7B%22step%22:%22installPlugin%22,%22pluginZipFile%22:%7B%22resource%22:%22url%22,%22url%22:%22https://calebstauffer.wpengine.com/plugin-proxy.php?repo=crstauf/query-monitor-extend&name=plugin.zip%22,%22caption%22:%22Installing%20Query%20Monitor%20Extend%22%7D%7D%5D%7D) 31 | - [Install as mu-plugin](https://playground.wordpress.net/#%7B%22landingPage%22:%22/wp-admin/plugins.php?plugin_status=mustuse%22,%22steps%22:%5B%7B%22step%22:%22login%22,%22username%22:%22admin%22,%22password%22:%22password%22%7D,%7B%22step%22:%22installPlugin%22,%22pluginZipFile%22:%7B%22resource%22:%22wordpress.org/plugins%22,%22slug%22:%22query-monitor%22%7D%7D,%7B%22step%22:%22mkdir%22,%22path%22:%22/wordpress/qmx%22%7D,%7B%22step%22:%22writeFile%22,%22path%22:%22/wordpress/qmx/mu-plugin.zip%22,%22data%22:%7B%22resource%22:%22url%22,%22url%22:%22https://calebstauffer.wpengine.com/plugin-proxy.php?repo=crstauf/query-monitor-extend&name=mu-plugin.zip%22,%22caption%22:%22Downloading%20Query%20Monitor%20Extend%22%7D,%22progress%22:%7B%22weight%22:2,%22caption%22:%22Installing%20Query%20Monitor%20Extend%22%7D%7D,%7B%22step%22:%22unzip%22,%22zipPath%22:%22/wordpress/qmx/mu-plugin.zip%22,%22extractToPath%22:%22/wordpress/qmx%22%7D,%7B%22step%22:%22mv%22,%22fromPath%22:%22/wordpress/qmx/mu-plugins/query-monitor-extend%22,%22toPath%22:%22/wordpress/wp-content/mu-plugins/query-monitor-extend%22%7D,%7B%22step%22:%22mv%22,%22fromPath%22:%22/wordpress/qmx/mu-plugins/load-qmx.php%22,%22toPath%22:%22/wordpress/wp-content/mu-plugins/load-qmx.php%22%7D%5D%7D) 32 | 33 | See [Demos document](.github/demos.md) for more info. 34 | 35 | ## Installing 36 | 37 | Recent [releases](https://github.com/crstauf/query-monitor-extend/releases) contain zip files for installation as a WordPress [plugin](https://github.com/crstauf/query-monitor-extend/releases/latest/download/plugin.zip) and [mu-plugin](https://github.com/crstauf/query-monitor-extend/releases/latest/download/mu-plugin.zip). 38 | 39 | ### Composer 40 | 41 | If you prefer to use [Composer](https://getcomposer.org/), you can install this package with the following command. 42 | 43 | ```shell 44 | composer require --dev crstauf/query-monitor-extend 45 | ``` 46 | -------------------------------------------------------------------------------- /time/qmx-time-collector.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | class QMX_Collector_Time extends QM_DataCollector { 9 | 10 | public $id = 'time'; 11 | 12 | public function name() : string { 13 | return __( 'Time', 'query-monitor-extend' ); 14 | } 15 | 16 | public function get_storage() : QM_Data { 17 | require_once 'qmx-time-data.php'; 18 | return new QMX_Data_Time(); 19 | } 20 | 21 | public function process() : void { 22 | if ( did_action( 'qm/cease' ) ) { 23 | return; 24 | } 25 | 26 | $this->data['functions'] = array( 27 | 'UTC' => 'get_utc', 28 | 'Server' => 'get_server', 29 | 'WordPress' => 'get_wp', 30 | 'Browser' => 'get_browser', 31 | ); 32 | } 33 | 34 | public function get_utc() : string { 35 | $datetime = date_create( 'now', new DateTimeZone( 'UTC' ) ); 36 | $datetime->setTimezone( new DateTimeZone( 'UTC' ) ); 37 | 38 | return $datetime->format( 'D, M j, Y H:i:s' ); 39 | } 40 | 41 | public function get_server() : string { 42 | $datetime = date_create( 'now', new DateTimeZone( 'UTC' ) ); 43 | 44 | if ( ! empty( ini_get( 'date.timezone' ) ) ) { 45 | $datetime->setTimezone( new DateTimeZone( ini_get( 'date.timezone' ) ) ); 46 | } 47 | 48 | return $datetime->format( 'D, M j, Y H:i:s T' ); 49 | } 50 | 51 | public function get_server_offset() : string { 52 | $datetime = date_create( "now", new DateTimeZone( 'UTC' ) ); 53 | 54 | if ( ! empty( ini_get( 'date.timezone' ) ) ) { 55 | $datetime->setTimezone( new DateTimeZone( ini_get( 'date.timezone' ) ) ); 56 | } 57 | 58 | return $datetime->format( 'Z' ); 59 | } 60 | 61 | public function get_server_timezone() : string { 62 | $datetime = date_create( "now", new DateTimeZone( 'UTC' ) ); 63 | 64 | if ( ! empty( ini_get( 'date.timezone' ) ) ) { 65 | $datetime->setTimezone( new DateTimeZone( ini_get( 'date.timezone' ) ) ); 66 | } 67 | 68 | return $datetime->format( 'T' ); 69 | } 70 | 71 | public function get_wp() : string { 72 | return current_time( 'D, M j, Y H:i:s T' ); 73 | } 74 | 75 | public function get_wp_offset() : float { 76 | return ( float ) get_option( 'gmt_offset' ); 77 | } 78 | 79 | public function get_wp_timezone() : string { 80 | return current_time( 'T' ); 81 | } 82 | 83 | public function get_browser() : string { 84 | return '-'; 85 | } 86 | 87 | } 88 | 89 | add_filter( 'qm/collectors', static function ( array $collectors ) : array { 90 | $collectors['time'] = new QMX_Collector_Time; 91 | return $collectors; 92 | } ); 93 | -------------------------------------------------------------------------------- /time/qmx-time-data.php: -------------------------------------------------------------------------------- 1 | $functions 9 | */ 10 | public $functions = array(); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /time/qmx-time-output.php: -------------------------------------------------------------------------------- 1 | collector->get_data(); 19 | 20 | echo '
' . 21 | '
'; 22 | 23 | foreach ( $data->functions as $label => $function ) { 24 | if ( is_callable( array( $this->collector, $function ) ) ) { 25 | echo '
' . 26 | '

' . esc_html( $label ) . '

' . 27 | '

' . $this->collector->$function() . '

' . 28 | '
'; 29 | } 30 | } 31 | 32 | echo '
'; 33 | ?> 34 | 35 | 112 | 113 | '; 115 | } 116 | 117 | /** 118 | * @param array> $menu 119 | * @return array> 120 | */ 121 | public function panel_menu( array $menu ) { 122 | $menu['time'] = $this->menu( array( 123 | 'title' => esc_html__( 'Time', 'query-monitor-extend' ), 124 | 'id' => 'query-monitor-extend-time', 125 | ) ); 126 | 127 | return $menu; 128 | } 129 | 130 | } 131 | 132 | add_filter( 'qm/outputter/html', static function ( array $output ) : array { 133 | if ( $collector = QM_Collectors::get( 'time' ) ) { 134 | $output['time'] = new QMX_Output_Html_Time( $collector ); 135 | } 136 | 137 | return $output; 138 | }, 70 ); 139 | --------------------------------------------------------------------------------