├── favicon.ico ├── .github ├── phpwpinfo.png ├── banner-github.png ├── PULL_REQUEST_TEMPLATE.md ├── ISSUE_TEMPLATE.md └── CONTRIBUTING.md ├── .gitignore ├── CHANGELOG.md ├── .htaccess ├── README.md ├── .ddev └── config.yaml ├── LICENSE.md └── phpwpinfo.php /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeAPI/phpwpinfo/HEAD/favicon.ico -------------------------------------------------------------------------------- /.github/phpwpinfo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeAPI/phpwpinfo/HEAD/.github/phpwpinfo.png -------------------------------------------------------------------------------- /.github/banner-github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BeAPI/phpwpinfo/HEAD/.github/banner-github.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .lando.yml 2 | .idea 3 | wordpress 4 | adminer.php 5 | error_log.txt 6 | config 7 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | This pull request fixes issue #{id}. 10 | 11 | It will apply the following changes : 12 | 13 | * 14 | * 15 | * 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 10 | 11 | #### Version information 12 | 13 | * Tool version : 14 | 15 | #### Steps to reproduce 16 | 17 | 1. 18 | 2. 19 | 3. 20 | 21 | #### What was expected 22 | 23 | 24 | #### What happened instead 25 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog ## 2 | 3 | ## 1.6.1 4 | 5 | - Better handle error management for Redis test connexion 6 | 7 | ## 1.6.0 8 | 9 | - Better handle error management for MySQL test connexion 10 | - Improve check with -1 value on PHP conf 11 | - Improve form for mail checker (allow to customize mailfrom and fix a return-path) 12 | 13 | ## 1.5.0 14 | 15 | - ADD: Favicon 16 | - ADD: WordPress Handbook link 17 | - UPDATE: Required version and modules 18 | - UPDATE: PHP requirement change from 5.6 to 7.4 19 | 20 | ## 1.4.0 21 | - Refactor readme 22 | - Branding 23 | - Add readme's installation section and usage 24 | 25 | ## 1.1 26 | * Implement recommended/required for each config/module 27 | * Improve test config value 28 | 29 | ## 1.0 30 | * Initial release -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | # BEGIN WordPress 2 | 3 | RewriteEngine On 4 | RewriteBase / 5 | RewriteRule ^phpwpinfo\.php$ - [L] 6 | RewriteCond %{REQUEST_FILENAME} !-f 7 | RewriteCond %{REQUEST_FILENAME} !-d 8 | RewriteRule . /phpwpinfo.php [L] 9 | 10 | # END WordPress 11 | 12 | # BEGIN PHP WordPress Info 13 | 14 | SetEnv http_mod_env on 15 | 16 | 17 | SetEnv http_mod_rewrite on 18 | 19 | 20 | 21 | SetEnv http_mod_deflate on 22 | 23 | 24 | 25 | SetEnv http_mod_expires on 26 | 27 | 28 | 29 | SetEnv http_mod_filter on 30 | 31 | 32 | 33 | SetEnv http_mod_headers on 34 | 35 | 36 | 37 | SetEnv http_mod_mime on 38 | 39 | 40 | 41 | SetEnv http_mod_setenvif on 42 | 43 | 44 | # END PHP WordPress Info -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Thanks for contributing ! 2 | 3 | # Getting Started 4 | 5 | * Make sure you have a [GitHub account](https://github.com/signup/free). 6 | * Check if your issue has already been discussed (or even fixed) earlier. You can [search for existing issues](../../../issues?q=is%3Aissue). 7 | * Assuming it does not already exist, [create a new issue](../../../issues/new). 8 | * Clearly describe the issue. In case you want to report a bug, include steps to reproduce it. 9 | * Make sure you fill in the requested details to provide as much information as possible. 10 | * Fork the repository on GitHub. 11 | 12 | # Making Changes 13 | 14 | * Create a topic branch from where you want to base your work. 15 | * This is usually the `master` branch. 16 | * Only target release branches if you are certain your fix must be on that branch. 17 | * To quickly create a topic branch based on the `master` branch: 18 | * `git checkout -b issue/{issue-number} master` 19 | * a good example is `issue/7` 20 | * Make commits of logical units and that messages are helpful. 21 | 22 | # Submitting Changes 23 | 24 | * Push your changes to the according topic branch in your fork of the repository. 25 | * [Create a pull request](../../../compare) to our repository. 26 | * Wait for feedback. The team looks at pull requests on a regular basis. 27 | 28 | # License 29 | 30 | By contributing code, you grant its use under the [GPLv3 or Later license](../LICENSE.md). 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # phpwpinfo 4 | 5 | phpwpinfo provides an equivalent to the `phpinfo()` function but with more WordPress requirements [details](#what-) about the PHP/MySQL/Apache environment and also offers suggestions for improvement. 6 | 7 | This tool allows you to quickly test environment server where you want to install WordPress. 8 | It is written in full PHP and use Bootstrap for HTML/CSS/JS purpose, provided by [CDN](http://www.bootstrapcdn.com). 9 | 10 | # How ? 11 | 12 | ## Installation 13 | 14 | > **Note** 15 | > 16 | > You'll need the .htaccess file included for the PHP-FPM/FastCGI implementations 17 | 18 | ### Total 19 | 20 | Just run : 21 | 22 | ```git clone https://github.com/BeAPI/phpwpinfo.git``` 23 | 24 | ### Partial 25 | 26 | Talking about the main file ([phpwpinfo.php](https://github.com/BeAPI/phpwpinfo/blob/master/phpwpinfo.php)), copy it's content, download the [raw](https://raw.githubusercontent.com/BeAPI/phpwpinfo/master/phpwpinfo.php) file or even better "wget" it directly on your server : 27 | 28 | ```wget https://raw.githubusercontent.com/BeAPI/phpwpinfo/master/phpwpinfo.php``` 29 | 30 | ## Usage 31 | 32 | Then simply reach the my-site.com/phpwpinfo.php url on your site to get the results, which are protected with the following credentials : 33 | * Login : wordpress 34 | * Password : wordpress 35 | 36 | # What ? 37 | 38 | ## 1. Self deletion 39 | 40 | This tool can, if your server allow it, delete itself. 41 | 42 | ## 2. What it checks for 43 | 44 | It tests various elements such as : 45 | * PHP & MySQL Version 46 | * Apache modules 47 | * PHP Extensions 48 | * PHP Configuration 49 | * MySQL Configuration 50 | * Mail server feature 51 | 52 | ## 3. Display phpinfo and more 53 | 54 | It also allows you to quickly view `phpinfo()` and MySQL variables. 55 | 56 | ## 4. Quick install 57 | 58 | Finally, it allows (if you server allows it) to quickly install : 59 | * [Adminer](http://www.adminer.org/en/) 60 | * [Latest version of WordPress (US)](http://wordpress.org/) 61 | 62 | ## Contributing 63 | 64 | Please refer to the [contributing guidelines](.github/CONTRIBUTING.md) to increase the chance of your pull request to be merged and/or receive the best support for your issue. 65 | 66 | ### Issues & features request / proposal 67 | 68 | If you identify any errors or have an idea for improving this tool, feel free to open an [issue](../../issues/new). Please provide as much info as needed in order to help us resolving / approve your request. 69 | 70 | # Who ? 71 | 72 | ![Be API Github Banner](.github/banner-github.png) 73 | 74 | Created by [Be API](https://beapi.fr), the French WordPress leader agency since 2009. Based in Paris, we are more than 30 people and always [hiring](https://beapi.workable.com) some fun and talented guys. So we will be pleased to work with you. 75 | 76 | This tool is only maintained, which means we do not guarantee some free support. Consider reporting an [issue](#issues--features-request--proposal) and be patient. 77 | 78 | If you really like what we do or want to thank us for our quick work, feel free to [donate](https://www.paypal.me/BeAPI) as much as you want / can, even 1€ is a great gift for buying cofee :) 79 | 80 | ## License 81 | 82 | phpwpinfo is licensed under the [GPLv3 or later](LICENSE.md). 83 | -------------------------------------------------------------------------------- /.ddev/config.yaml: -------------------------------------------------------------------------------- 1 | name: phpwpinfo 2 | type: php 3 | docroot: "" 4 | php_version: "8.2" 5 | webserver_type: nginx-fpm 6 | xdebug_enabled: false 7 | additional_hostnames: [] 8 | additional_fqdns: [] 9 | database: 10 | type: mariadb 11 | version: "10.11" 12 | use_dns_when_possible: true 13 | composer_version: "2" 14 | web_environment: [] 15 | corepack_enable: false 16 | 17 | # Key features of DDEV's config.yaml: 18 | 19 | # name: # Name of the project, automatically provides 20 | # http://projectname.ddev.site and https://projectname.ddev.site 21 | 22 | # type: # backdrop, craftcms, django4, drupal, drupal6, drupal7, laravel, magento, magento2, php, python, shopware6, silverstripe, typo3, wordpress 23 | # See https://ddev.readthedocs.io/en/stable/users/quickstart/ for more 24 | # information on the different project types 25 | # "drupal" covers recent Drupal 8+ 26 | 27 | # docroot: # Relative path to the directory containing index.php. 28 | 29 | # php_version: "8.2" # PHP version to use, "5.6", "7.0", "7.1", "7.2", "7.3", "7.4", "8.0", "8.1", "8.2", "8.3" 30 | 31 | # You can explicitly specify the webimage but this 32 | # is not recommended, as the images are often closely tied to DDEV's' behavior, 33 | # so this can break upgrades. 34 | 35 | # webimage: # nginx/php docker image. 36 | 37 | # database: 38 | # type: # mysql, mariadb, postgres 39 | # version: # database version, like "10.11" or "8.0" 40 | # MariaDB versions can be 5.5-10.8, 10.11, and 11.4. 41 | # MySQL versions can be 5.5-8.0. 42 | # PostgreSQL versions can be 9-16. 43 | 44 | # router_http_port: # Port to be used for http (defaults to global configuration, usually 80) 45 | # router_https_port: # Port for https (defaults to global configuration, usually 443) 46 | 47 | # xdebug_enabled: false # Set to true to enable Xdebug and "ddev start" or "ddev restart" 48 | # Note that for most people the commands 49 | # "ddev xdebug" to enable Xdebug and "ddev xdebug off" to disable it work better, 50 | # as leaving Xdebug enabled all the time is a big performance hit. 51 | 52 | # xhprof_enabled: false # Set to true to enable Xhprof and "ddev start" or "ddev restart" 53 | # Note that for most people the commands 54 | # "ddev xhprof" to enable Xhprof and "ddev xhprof off" to disable it work better, 55 | # as leaving Xhprof enabled all the time is a big performance hit. 56 | 57 | # webserver_type: nginx-fpm, apache-fpm, or nginx-gunicorn 58 | 59 | # timezone: Europe/Berlin 60 | # This is the timezone used in the containers and by PHP; 61 | # it can be set to any valid timezone, 62 | # see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones 63 | # For example Europe/Dublin or MST7MDT 64 | 65 | # composer_root: 66 | # Relative path to the Composer root directory from the project root. This is 67 | # the directory which contains the composer.json and where all Composer related 68 | # commands are executed. 69 | 70 | # composer_version: "2" 71 | # You can set it to "" or "2" (default) for Composer v2 or "1" for Composer v1 72 | # to use the latest major version available at the time your container is built. 73 | # It is also possible to use each other Composer version channel. This includes: 74 | # - 2.2 (latest Composer LTS version) 75 | # - stable 76 | # - preview 77 | # - snapshot 78 | # Alternatively, an explicit Composer version may be specified, for example "2.2.18". 79 | # To reinstall Composer after the image was built, run "ddev debug refresh". 80 | 81 | # nodejs_version: "20" 82 | # change from the default system Node.js version to any other version. 83 | # Numeric version numbers can be complete (i.e. 18.15.0) or 84 | # incomplete (18, 17.2, 16). 'lts' and 'latest' can be used as well along with 85 | # other named releases. 86 | # see https://www.npmjs.com/package/n#specifying-nodejs-versions 87 | # Note that you can continue using 'ddev nvm' or nvm inside the web container 88 | # to change the project's installed node version if you need to. 89 | 90 | # corepack_enable: false 91 | # Change to 'true' to 'corepack enable' and gain access to latest versions of yarn/pnpm 92 | 93 | # additional_hostnames: 94 | # - somename 95 | # - someothername 96 | # would provide http and https URLs for "somename.ddev.site" 97 | # and "someothername.ddev.site". 98 | 99 | # additional_fqdns: 100 | # - example.com 101 | # - sub1.example.com 102 | # would provide http and https URLs for "example.com" and "sub1.example.com" 103 | # Please take care with this because it can cause great confusion. 104 | 105 | # upload_dirs: "custom/upload/dir" 106 | # 107 | # upload_dirs: 108 | # - custom/upload/dir 109 | # - ../private 110 | # 111 | # would set the destination paths for ddev import-files to /custom/upload/dir 112 | # When Mutagen is enabled this path is bind-mounted so that all the files 113 | # in the upload_dirs don't have to be synced into Mutagen. 114 | 115 | # disable_upload_dirs_warning: false 116 | # If true, turns off the normal warning that says 117 | # "You have Mutagen enabled and your 'php' project type doesn't have upload_dirs set" 118 | 119 | # ddev_version_constraint: "" 120 | # Example: 121 | # ddev_version_constraint: ">= 1.22.4" 122 | # This will enforce that the running ddev version is within this constraint. 123 | # See https://github.com/Masterminds/semver#checking-version-constraints for 124 | # supported constraint formats 125 | 126 | # working_dir: 127 | # web: /var/www/html 128 | # db: /home 129 | # would set the default working directory for the web and db services. 130 | # These values specify the destination directory for ddev ssh and the 131 | # directory in which commands passed into ddev exec are run. 132 | 133 | # omit_containers: [db, ddev-ssh-agent] 134 | # Currently only these containers are supported. Some containers can also be 135 | # omitted globally in the ~/.ddev/global_config.yaml. Note that if you omit 136 | # the "db" container, several standard features of DDEV that access the 137 | # database container will be unusable. In the global configuration it is also 138 | # possible to omit ddev-router, but not here. 139 | 140 | # performance_mode: "global" 141 | # DDEV offers performance optimization strategies to improve the filesystem 142 | # performance depending on your host system. Should be configured globally. 143 | # 144 | # If set, will override the global config. Possible values are: 145 | # - "global": uses the value from the global config. 146 | # - "none": disables performance optimization for this project. 147 | # - "mutagen": enables Mutagen for this project. 148 | # - "nfs": enables NFS for this project. 149 | # 150 | # See https://ddev.readthedocs.io/en/stable/users/install/performance/#nfs 151 | # See https://ddev.readthedocs.io/en/stable/users/install/performance/#mutagen 152 | 153 | # fail_on_hook_fail: False 154 | # Decide whether 'ddev start' should be interrupted by a failing hook 155 | 156 | # host_https_port: "59002" 157 | # The host port binding for https can be explicitly specified. It is 158 | # dynamic unless otherwise specified. 159 | # This is not used by most people, most people use the *router* instead 160 | # of the localhost port. 161 | 162 | # host_webserver_port: "59001" 163 | # The host port binding for the ddev-webserver can be explicitly specified. It is 164 | # dynamic unless otherwise specified. 165 | # This is not used by most people, most people use the *router* instead 166 | # of the localhost port. 167 | 168 | # host_db_port: "59002" 169 | # The host port binding for the ddev-dbserver can be explicitly specified. It is dynamic 170 | # unless explicitly specified. 171 | 172 | # mailpit_http_port: "8025" 173 | # mailpit_https_port: "8026" 174 | # The Mailpit ports can be changed from the default 8025 and 8026 175 | 176 | # host_mailpit_port: "8025" 177 | # The mailpit port is not normally bound on the host at all, instead being routed 178 | # through ddev-router, but it can be bound directly to localhost if specified here. 179 | 180 | # webimage_extra_packages: [php7.4-tidy, php-bcmath] 181 | # Extra Debian packages that are needed in the webimage can be added here 182 | 183 | # dbimage_extra_packages: [telnet,netcat] 184 | # Extra Debian packages that are needed in the dbimage can be added here 185 | 186 | # use_dns_when_possible: true 187 | # If the host has internet access and the domain configured can 188 | # successfully be looked up, DNS will be used for hostname resolution 189 | # instead of editing /etc/hosts 190 | # Defaults to true 191 | 192 | # project_tld: ddev.site 193 | # The top-level domain used for project URLs 194 | # The default "ddev.site" allows DNS lookup via a wildcard 195 | # If you prefer you can change this to "ddev.local" to preserve 196 | # pre-v1.9 behavior. 197 | 198 | # ngrok_args: --basic-auth username:pass1234 199 | # Provide extra flags to the "ngrok http" command, see 200 | # https://ngrok.com/docs/ngrok-agent/config or run "ngrok http -h" 201 | 202 | # disable_settings_management: false 203 | # If true, DDEV will not create CMS-specific settings files like 204 | # Drupal's settings.php/settings.ddev.php or TYPO3's additional.php 205 | # In this case the user must provide all such settings. 206 | 207 | # You can inject environment variables into the web container with: 208 | # web_environment: 209 | # - SOMEENV=somevalue 210 | # - SOMEOTHERENV=someothervalue 211 | 212 | # no_project_mount: false 213 | # (Experimental) If true, DDEV will not mount the project into the web container; 214 | # the user is responsible for mounting it manually or via a script. 215 | # This is to enable experimentation with alternate file mounting strategies. 216 | # For advanced users only! 217 | 218 | # bind_all_interfaces: false 219 | # If true, host ports will be bound on all network interfaces, 220 | # not the localhost interface only. This means that ports 221 | # will be available on the local network if the host firewall 222 | # allows it. 223 | 224 | # default_container_timeout: 120 225 | # The default time that DDEV waits for all containers to become ready can be increased from 226 | # the default 120. This helps in importing huge databases, for example. 227 | 228 | #web_extra_exposed_ports: 229 | #- name: nodejs 230 | # container_port: 3000 231 | # http_port: 2999 232 | # https_port: 3000 233 | #- name: something 234 | # container_port: 4000 235 | # https_port: 4000 236 | # http_port: 3999 237 | # Allows a set of extra ports to be exposed via ddev-router 238 | # Fill in all three fields even if you don’t intend to use the https_port! 239 | # If you don’t add https_port, then it defaults to 0 and ddev-router will fail to start. 240 | # 241 | # The port behavior on the ddev-webserver must be arranged separately, for example 242 | # using web_extra_daemons. 243 | # For example, with a web app on port 3000 inside the container, this config would 244 | # expose that web app on https://.ddev.site:9999 and http://.ddev.site:9998 245 | # web_extra_exposed_ports: 246 | # - name: myapp 247 | # container_port: 3000 248 | # http_port: 9998 249 | # https_port: 9999 250 | 251 | #web_extra_daemons: 252 | #- name: "http-1" 253 | # command: "/var/www/html/node_modules/.bin/http-server -p 3000" 254 | # directory: /var/www/html 255 | #- name: "http-2" 256 | # command: "/var/www/html/node_modules/.bin/http-server /var/www/html/sub -p 3000" 257 | # directory: /var/www/html 258 | 259 | # override_config: false 260 | # By default, config.*.yaml files are *merged* into the configuration 261 | # But this means that some things can't be overridden 262 | # For example, if you have 'use_dns_when_possible: true'' you can't override it with a merge 263 | # and you can't erase existing hooks or all environment variables. 264 | # However, with "override_config: true" in a particular config.*.yaml file, 265 | # 'use_dns_when_possible: false' can override the existing values, and 266 | # hooks: 267 | # post-start: [] 268 | # or 269 | # web_environment: [] 270 | # or 271 | # additional_hostnames: [] 272 | # can have their intended affect. 'override_config' affects only behavior of the 273 | # config.*.yaml file it exists in. 274 | 275 | # Many DDEV commands can be extended to run tasks before or after the 276 | # DDEV command is executed, for example "post-start", "post-import-db", 277 | # "pre-composer", "post-composer" 278 | # See https://ddev.readthedocs.io/en/stable/users/extend/custom-commands/ for more 279 | # information on the commands that can be extended and the tasks you can define 280 | # for them. Example: 281 | #hooks: 282 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | phpwpinfo / Provides an equivalent to the `phpinfo()` but with more WordPress requirements details. 2 | 3 | Copyright 2017-2018 Be API and contributors 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | 19 | This program incorporates work covered by the following copyright and 20 | permission notices: 21 | 22 | phpwpinfo / Provides an equivalent to the `phpinfo()` but with more WordPress requirements details. 23 | 24 | Copyright 2017-2018 Be API and contributors 25 | 26 | phpwpinfo is released under the GPL 27 | 28 | 29 | =============================================================================== 30 | 31 | GNU GENERAL PUBLIC LICENSE 32 | Version 3, 29 June 2007 33 | 34 | Copyright (C) 2007 Free Software Foundation, Inc. 35 | Everyone is permitted to copy and distribute verbatim copies 36 | of this license document, but changing it is not allowed. 37 | 38 | Preamble 39 | 40 | The GNU General Public License is a free, copyleft license for 41 | software and other kinds of works. 42 | 43 | The licenses for most software and other practical works are designed 44 | to take away your freedom to share and change the works. By contrast, 45 | the GNU General Public License is intended to guarantee your freedom to 46 | share and change all versions of a program--to make sure it remains free 47 | software for all its users. We, the Free Software Foundation, use the 48 | GNU General Public License for most of our software; it applies also to 49 | any other work released this way by its authors. You can apply it to 50 | your programs, too. 51 | 52 | When we speak of free software, we are referring to freedom, not 53 | price. Our General Public Licenses are designed to make sure that you 54 | have the freedom to distribute copies of free software (and charge for 55 | them if you wish), that you receive source code or can get it if you 56 | want it, that you can change the software or use pieces of it in new 57 | free programs, and that you know you can do these things. 58 | 59 | To protect your rights, we need to prevent others from denying you 60 | these rights or asking you to surrender the rights. Therefore, you have 61 | certain responsibilities if you distribute copies of the software, or if 62 | you modify it: responsibilities to respect the freedom of others. 63 | 64 | For example, if you distribute copies of such a program, whether 65 | gratis or for a fee, you must pass on to the recipients the same 66 | freedoms that you received. You must make sure that they, too, receive 67 | or can get the source code. And you must show them these terms so they 68 | know their rights. 69 | 70 | Developers that use the GNU GPL protect your rights with two steps: 71 | (1) assert copyright on the software, and (2) offer you this License 72 | giving you legal permission to copy, distribute and/or modify it. 73 | 74 | For the developers' and authors' protection, the GPL clearly explains 75 | that there is no warranty for this free software. For both users' and 76 | authors' sake, the GPL requires that modified versions be marked as 77 | changed, so that their problems will not be attributed erroneously to 78 | authors of previous versions. 79 | 80 | Some devices are designed to deny users access to install or run 81 | modified versions of the software inside them, although the manufacturer 82 | can do so. This is fundamentally incompatible with the aim of 83 | protecting users' freedom to change the software. The systematic 84 | pattern of such abuse occurs in the area of products for individuals to 85 | use, which is precisely where it is most unacceptable. Therefore, we 86 | have designed this version of the GPL to prohibit the practice for those 87 | products. If such problems arise substantially in other domains, we 88 | stand ready to extend this provision to those domains in future versions 89 | of the GPL, as needed to protect the freedom of users. 90 | 91 | Finally, every program is threatened constantly by software patents. 92 | States should not allow patents to restrict development and use of 93 | software on general-purpose computers, but in those that do, we wish to 94 | avoid the special danger that patents applied to a free program could 95 | make it effectively proprietary. To prevent this, the GPL assures that 96 | patents cannot be used to render the program non-free. 97 | 98 | The precise terms and conditions for copying, distribution and 99 | modification follow. 100 | 101 | TERMS AND CONDITIONS 102 | 103 | 0. Definitions. 104 | 105 | "This License" refers to version 3 of the GNU General Public License. 106 | 107 | "Copyright" also means copyright-like laws that apply to other kinds of 108 | works, such as semiconductor masks. 109 | 110 | "The Program" refers to any copyrightable work licensed under this 111 | License. Each licensee is addressed as "you". "Licensees" and 112 | "recipients" may be individuals or organizations. 113 | 114 | To "modify" a work means to copy from or adapt all or part of the work 115 | in a fashion requiring copyright permission, other than the making of an 116 | exact copy. The resulting work is called a "modified version" of the 117 | earlier work or a work "based on" the earlier work. 118 | 119 | A "covered work" means either the unmodified Program or a work based 120 | on the Program. 121 | 122 | To "propagate" a work means to do anything with it that, without 123 | permission, would make you directly or secondarily liable for 124 | infringement under applicable copyright law, except executing it on a 125 | computer or modifying a private copy. Propagation includes copying, 126 | distribution (with or without modification), making available to the 127 | public, and in some countries other activities as well. 128 | 129 | To "convey" a work means any kind of propagation that enables other 130 | parties to make or receive copies. Mere interaction with a user through 131 | a computer network, with no transfer of a copy, is not conveying. 132 | 133 | An interactive user interface displays "Appropriate Legal Notices" 134 | to the extent that it includes a convenient and prominently visible 135 | feature that (1) displays an appropriate copyright notice, and (2) 136 | tells the user that there is no warranty for the work (except to the 137 | extent that warranties are provided), that licensees may convey the 138 | work under this License, and how to view a copy of this License. If 139 | the interface presents a list of user commands or options, such as a 140 | menu, a prominent item in the list meets this criterion. 141 | 142 | 1. Source Code. 143 | 144 | The "source code" for a work means the preferred form of the work 145 | for making modifications to it. "Object code" means any non-source 146 | form of a work. 147 | 148 | A "Standard Interface" means an interface that either is an official 149 | standard defined by a recognized standards body, or, in the case of 150 | interfaces specified for a particular programming language, one that 151 | is widely used among developers working in that language. 152 | 153 | The "System Libraries" of an executable work include anything, other 154 | than the work as a whole, that (a) is included in the normal form of 155 | packaging a Major Component, but which is not part of that Major 156 | Component, and (b) serves only to enable use of the work with that 157 | Major Component, or to implement a Standard Interface for which an 158 | implementation is available to the public in source code form. A 159 | "Major Component", in this context, means a major essential component 160 | (kernel, window system, and so on) of the specific operating system 161 | (if any) on which the executable work runs, or a compiler used to 162 | produce the work, or an object code interpreter used to run it. 163 | 164 | The "Corresponding Source" for a work in object code form means all 165 | the source code needed to generate, install, and (for an executable 166 | work) run the object code and to modify the work, including scripts to 167 | control those activities. However, it does not include the work's 168 | System Libraries, or general-purpose tools or generally available free 169 | programs which are used unmodified in performing those activities but 170 | which are not part of the work. For example, Corresponding Source 171 | includes interface definition files associated with source files for 172 | the work, and the source code for shared libraries and dynamically 173 | linked subprograms that the work is specifically designed to require, 174 | such as by intimate data communication or control flow between those 175 | subprograms and other parts of the work. 176 | 177 | The Corresponding Source need not include anything that users 178 | can regenerate automatically from other parts of the Corresponding 179 | Source. 180 | 181 | The Corresponding Source for a work in source code form is that 182 | same work. 183 | 184 | 2. Basic Permissions. 185 | 186 | All rights granted under this License are granted for the term of 187 | copyright on the Program, and are irrevocable provided the stated 188 | conditions are met. This License explicitly affirms your unlimited 189 | permission to run the unmodified Program. The output from running a 190 | covered work is covered by this License only if the output, given its 191 | content, constitutes a covered work. This License acknowledges your 192 | rights of fair use or other equivalent, as provided by copyright law. 193 | 194 | You may make, run and propagate covered works that you do not 195 | convey, without conditions so long as your license otherwise remains 196 | in force. You may convey covered works to others for the sole purpose 197 | of having them make modifications exclusively for you, or provide you 198 | with facilities for running those works, provided that you comply with 199 | the terms of this License in conveying all material for which you do 200 | not control copyright. Those thus making or running the covered works 201 | for you must do so exclusively on your behalf, under your direction 202 | and control, on terms that prohibit them from making any copies of 203 | your copyrighted material outside their relationship with you. 204 | 205 | Conveying under any other circumstances is permitted solely under 206 | the conditions stated below. Sublicensing is not allowed; section 10 207 | makes it unnecessary. 208 | 209 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 210 | 211 | No covered work shall be deemed part of an effective technological 212 | measure under any applicable law fulfilling obligations under article 213 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 214 | similar laws prohibiting or restricting circumvention of such 215 | measures. 216 | 217 | When you convey a covered work, you waive any legal power to forbid 218 | circumvention of technological measures to the extent such circumvention 219 | is effected by exercising rights under this License with respect to 220 | the covered work, and you disclaim any intention to limit operation or 221 | modification of the work as a means of enforcing, against the work's 222 | users, your or third parties' legal rights to forbid circumvention of 223 | technological measures. 224 | 225 | 4. Conveying Verbatim Copies. 226 | 227 | You may convey verbatim copies of the Program's source code as you 228 | receive it, in any medium, provided that you conspicuously and 229 | appropriately publish on each copy an appropriate copyright notice; 230 | keep intact all notices stating that this License and any 231 | non-permissive terms added in accord with section 7 apply to the code; 232 | keep intact all notices of the absence of any warranty; and give all 233 | recipients a copy of this License along with the Program. 234 | 235 | You may charge any price or no price for each copy that you convey, 236 | and you may offer support or warranty protection for a fee. 237 | 238 | 5. Conveying Modified Source Versions. 239 | 240 | You may convey a work based on the Program, or the modifications to 241 | produce it from the Program, in the form of source code under the 242 | terms of section 4, provided that you also meet all of these conditions: 243 | 244 | a) The work must carry prominent notices stating that you modified 245 | it, and giving a relevant date. 246 | 247 | b) The work must carry prominent notices stating that it is 248 | released under this License and any conditions added under section 249 | 7. This requirement modifies the requirement in section 4 to 250 | "keep intact all notices". 251 | 252 | c) You must license the entire work, as a whole, under this 253 | License to anyone who comes into possession of a copy. This 254 | License will therefore apply, along with any applicable section 7 255 | additional terms, to the whole of the work, and all its parts, 256 | regardless of how they are packaged. This License gives no 257 | permission to license the work in any other way, but it does not 258 | invalidate such permission if you have separately received it. 259 | 260 | d) If the work has interactive user interfaces, each must display 261 | Appropriate Legal Notices; however, if the Program has interactive 262 | interfaces that do not display Appropriate Legal Notices, your 263 | work need not make them do so. 264 | 265 | A compilation of a covered work with other separate and independent 266 | works, which are not by their nature extensions of the covered work, 267 | and which are not combined with it such as to form a larger program, 268 | in or on a volume of a storage or distribution medium, is called an 269 | "aggregate" if the compilation and its resulting copyright are not 270 | used to limit the access or legal rights of the compilation's users 271 | beyond what the individual works permit. Inclusion of a covered work 272 | in an aggregate does not cause this License to apply to the other 273 | parts of the aggregate. 274 | 275 | 6. Conveying Non-Source Forms. 276 | 277 | You may convey a covered work in object code form under the terms 278 | of sections 4 and 5, provided that you also convey the 279 | machine-readable Corresponding Source under the terms of this License, 280 | in one of these ways: 281 | 282 | a) Convey the object code in, or embodied in, a physical product 283 | (including a physical distribution medium), accompanied by the 284 | Corresponding Source fixed on a durable physical medium 285 | customarily used for software interchange. 286 | 287 | b) Convey the object code in, or embodied in, a physical product 288 | (including a physical distribution medium), accompanied by a 289 | written offer, valid for at least three years and valid for as 290 | long as you offer spare parts or customer support for that product 291 | model, to give anyone who possesses the object code either (1) a 292 | copy of the Corresponding Source for all the software in the 293 | product that is covered by this License, on a durable physical 294 | medium customarily used for software interchange, for a price no 295 | more than your reasonable cost of physically performing this 296 | conveying of source, or (2) access to copy the 297 | Corresponding Source from a network server at no charge. 298 | 299 | c) Convey individual copies of the object code with a copy of the 300 | written offer to provide the Corresponding Source. This 301 | alternative is allowed only occasionally and noncommercially, and 302 | only if you received the object code with such an offer, in accord 303 | with subsection 6b. 304 | 305 | d) Convey the object code by offering access from a designated 306 | place (gratis or for a charge), and offer equivalent access to the 307 | Corresponding Source in the same way through the same place at no 308 | further charge. You need not require recipients to copy the 309 | Corresponding Source along with the object code. If the place to 310 | copy the object code is a network server, the Corresponding Source 311 | may be on a different server (operated by you or a third party) 312 | that supports equivalent copying facilities, provided you maintain 313 | clear directions next to the object code saying where to find the 314 | Corresponding Source. Regardless of what server hosts the 315 | Corresponding Source, you remain obligated to ensure that it is 316 | available for as long as needed to satisfy these requirements. 317 | 318 | e) Convey the object code using peer-to-peer transmission, provided 319 | you inform other peers where the object code and Corresponding 320 | Source of the work are being offered to the general public at no 321 | charge under subsection 6d. 322 | 323 | A separable portion of the object code, whose source code is excluded 324 | from the Corresponding Source as a System Library, need not be 325 | included in conveying the object code work. 326 | 327 | A "User Product" is either (1) a "consumer product", which means any 328 | tangible personal property which is normally used for personal, family, 329 | or household purposes, or (2) anything designed or sold for incorporation 330 | into a dwelling. In determining whether a product is a consumer product, 331 | doubtful cases shall be resolved in favor of coverage. For a particular 332 | product received by a particular user, "normally used" refers to a 333 | typical or common use of that class of product, regardless of the status 334 | of the particular user or of the way in which the particular user 335 | actually uses, or expects or is expected to use, the product. A product 336 | is a consumer product regardless of whether the product has substantial 337 | commercial, industrial or non-consumer uses, unless such uses represent 338 | the only significant mode of use of the product. 339 | 340 | "Installation Information" for a User Product means any methods, 341 | procedures, authorization keys, or other information required to install 342 | and execute modified versions of a covered work in that User Product from 343 | a modified version of its Corresponding Source. The information must 344 | suffice to ensure that the continued functioning of the modified object 345 | code is in no case prevented or interfered with solely because 346 | modification has been made. 347 | 348 | If you convey an object code work under this section in, or with, or 349 | specifically for use in, a User Product, and the conveying occurs as 350 | part of a transaction in which the right of possession and use of the 351 | User Product is transferred to the recipient in perpetuity or for a 352 | fixed term (regardless of how the transaction is characterized), the 353 | Corresponding Source conveyed under this section must be accompanied 354 | by the Installation Information. But this requirement does not apply 355 | if neither you nor any third party retains the ability to install 356 | modified object code on the User Product (for example, the work has 357 | been installed in ROM). 358 | 359 | The requirement to provide Installation Information does not include a 360 | requirement to continue to provide support service, warranty, or updates 361 | for a work that has been modified or installed by the recipient, or for 362 | the User Product in which it has been modified or installed. Access to a 363 | network may be denied when the modification itself materially and 364 | adversely affects the operation of the network or violates the rules and 365 | protocols for communication across the network. 366 | 367 | Corresponding Source conveyed, and Installation Information provided, 368 | in accord with this section must be in a format that is publicly 369 | documented (and with an implementation available to the public in 370 | source code form), and must require no special password or key for 371 | unpacking, reading or copying. 372 | 373 | 7. Additional Terms. 374 | 375 | "Additional permissions" are terms that supplement the terms of this 376 | License by making exceptions from one or more of its conditions. 377 | Additional permissions that are applicable to the entire Program shall 378 | be treated as though they were included in this License, to the extent 379 | that they are valid under applicable law. If additional permissions 380 | apply only to part of the Program, that part may be used separately 381 | under those permissions, but the entire Program remains governed by 382 | this License without regard to the additional permissions. 383 | 384 | When you convey a copy of a covered work, you may at your option 385 | remove any additional permissions from that copy, or from any part of 386 | it. (Additional permissions may be written to require their own 387 | removal in certain cases when you modify the work.) You may place 388 | additional permissions on material, added by you to a covered work, 389 | for which you have or can give appropriate copyright permission. 390 | 391 | Notwithstanding any other provision of this License, for material you 392 | add to a covered work, you may (if authorized by the copyright holders of 393 | that material) supplement the terms of this License with terms: 394 | 395 | a) Disclaiming warranty or limiting liability differently from the 396 | terms of sections 15 and 16 of this License; or 397 | 398 | b) Requiring preservation of specified reasonable legal notices or 399 | author attributions in that material or in the Appropriate Legal 400 | Notices displayed by works containing it; or 401 | 402 | c) Prohibiting misrepresentation of the origin of that material, or 403 | requiring that modified versions of such material be marked in 404 | reasonable ways as different from the original version; or 405 | 406 | d) Limiting the use for publicity purposes of names of licensors or 407 | authors of the material; or 408 | 409 | e) Declining to grant rights under trademark law for use of some 410 | trade names, trademarks, or service marks; or 411 | 412 | f) Requiring indemnification of licensors and authors of that 413 | material by anyone who conveys the material (or modified versions of 414 | it) with contractual assumptions of liability to the recipient, for 415 | any liability that these contractual assumptions directly impose on 416 | those licensors and authors. 417 | 418 | All other non-permissive additional terms are considered "further 419 | restrictions" within the meaning of section 10. If the Program as you 420 | received it, or any part of it, contains a notice stating that it is 421 | governed by this License along with a term that is a further 422 | restriction, you may remove that term. If a license document contains 423 | a further restriction but permits relicensing or conveying under this 424 | License, you may add to a covered work material governed by the terms 425 | of that license document, provided that the further restriction does 426 | not survive such relicensing or conveying. 427 | 428 | If you add terms to a covered work in accord with this section, you 429 | must place, in the relevant source files, a statement of the 430 | additional terms that apply to those files, or a notice indicating 431 | where to find the applicable terms. 432 | 433 | Additional terms, permissive or non-permissive, may be stated in the 434 | form of a separately written license, or stated as exceptions; 435 | the above requirements apply either way. 436 | 437 | 8. Termination. 438 | 439 | You may not propagate or modify a covered work except as expressly 440 | provided under this License. Any attempt otherwise to propagate or 441 | modify it is void, and will automatically terminate your rights under 442 | this License (including any patent licenses granted under the third 443 | paragraph of section 11). 444 | 445 | However, if you cease all violation of this License, then your 446 | license from a particular copyright holder is reinstated (a) 447 | provisionally, unless and until the copyright holder explicitly and 448 | finally terminates your license, and (b) permanently, if the copyright 449 | holder fails to notify you of the violation by some reasonable means 450 | prior to 60 days after the cessation. 451 | 452 | Moreover, your license from a particular copyright holder is 453 | reinstated permanently if the copyright holder notifies you of the 454 | violation by some reasonable means, this is the first time you have 455 | received notice of violation of this License (for any work) from that 456 | copyright holder, and you cure the violation prior to 30 days after 457 | your receipt of the notice. 458 | 459 | Termination of your rights under this section does not terminate the 460 | licenses of parties who have received copies or rights from you under 461 | this License. If your rights have been terminated and not permanently 462 | reinstated, you do not qualify to receive new licenses for the same 463 | material under section 10. 464 | 465 | 9. Acceptance Not Required for Having Copies. 466 | 467 | You are not required to accept this License in order to receive or 468 | run a copy of the Program. Ancillary propagation of a covered work 469 | occurring solely as a consequence of using peer-to-peer transmission 470 | to receive a copy likewise does not require acceptance. However, 471 | nothing other than this License grants you permission to propagate or 472 | modify any covered work. These actions infringe copyright if you do 473 | not accept this License. Therefore, by modifying or propagating a 474 | covered work, you indicate your acceptance of this License to do so. 475 | 476 | 10. Automatic Licensing of Downstream Recipients. 477 | 478 | Each time you convey a covered work, the recipient automatically 479 | receives a license from the original licensors, to run, modify and 480 | propagate that work, subject to this License. You are not responsible 481 | for enforcing compliance by third parties with this License. 482 | 483 | An "entity transaction" is a transaction transferring control of an 484 | organization, or substantially all assets of one, or subdividing an 485 | organization, or merging organizations. If propagation of a covered 486 | work results from an entity transaction, each party to that 487 | transaction who receives a copy of the work also receives whatever 488 | licenses to the work the party's predecessor in interest had or could 489 | give under the previous paragraph, plus a right to possession of the 490 | Corresponding Source of the work from the predecessor in interest, if 491 | the predecessor has it or can get it with reasonable efforts. 492 | 493 | You may not impose any further restrictions on the exercise of the 494 | rights granted or affirmed under this License. For example, you may 495 | not impose a license fee, royalty, or other charge for exercise of 496 | rights granted under this License, and you may not initiate litigation 497 | (including a cross-claim or counterclaim in a lawsuit) alleging that 498 | any patent claim is infringed by making, using, selling, offering for 499 | sale, or importing the Program or any portion of it. 500 | 501 | 11. Patents. 502 | 503 | A "contributor" is a copyright holder who authorizes use under this 504 | License of the Program or a work on which the Program is based. The 505 | work thus licensed is called the contributor's "contributor version". 506 | 507 | A contributor's "essential patent claims" are all patent claims 508 | owned or controlled by the contributor, whether already acquired or 509 | hereafter acquired, that would be infringed by some manner, permitted 510 | by this License, of making, using, or selling its contributor version, 511 | but do not include claims that would be infringed only as a 512 | consequence of further modification of the contributor version. For 513 | purposes of this definition, "control" includes the right to grant 514 | patent sublicenses in a manner consistent with the requirements of 515 | this License. 516 | 517 | Each contributor grants you a non-exclusive, worldwide, royalty-free 518 | patent license under the contributor's essential patent claims, to 519 | make, use, sell, offer for sale, import and otherwise run, modify and 520 | propagate the contents of its contributor version. 521 | 522 | In the following three paragraphs, a "patent license" is any express 523 | agreement or commitment, however denominated, not to enforce a patent 524 | (such as an express permission to practice a patent or covenant not to 525 | sue for patent infringement). To "grant" such a patent license to a 526 | party means to make such an agreement or commitment not to enforce a 527 | patent against the party. 528 | 529 | If you convey a covered work, knowingly relying on a patent license, 530 | and the Corresponding Source of the work is not available for anyone 531 | to copy, free of charge and under the terms of this License, through a 532 | publicly available network server or other readily accessible means, 533 | then you must either (1) cause the Corresponding Source to be so 534 | available, or (2) arrange to deprive yourself of the benefit of the 535 | patent license for this particular work, or (3) arrange, in a manner 536 | consistent with the requirements of this License, to extend the patent 537 | license to downstream recipients. "Knowingly relying" means you have 538 | actual knowledge that, but for the patent license, your conveying the 539 | covered work in a country, or your recipient's use of the covered work 540 | in a country, would infringe one or more identifiable patents in that 541 | country that you have reason to believe are valid. 542 | 543 | If, pursuant to or in connection with a single transaction or 544 | arrangement, you convey, or propagate by procuring conveyance of, a 545 | covered work, and grant a patent license to some of the parties 546 | receiving the covered work authorizing them to use, propagate, modify 547 | or convey a specific copy of the covered work, then the patent license 548 | you grant is automatically extended to all recipients of the covered 549 | work and works based on it. 550 | 551 | A patent license is "discriminatory" if it does not include within 552 | the scope of its coverage, prohibits the exercise of, or is 553 | conditioned on the non-exercise of one or more of the rights that are 554 | specifically granted under this License. You may not convey a covered 555 | work if you are a party to an arrangement with a third party that is 556 | in the business of distributing software, under which you make payment 557 | to the third party based on the extent of your activity of conveying 558 | the work, and under which the third party grants, to any of the 559 | parties who would receive the covered work from you, a discriminatory 560 | patent license (a) in connection with copies of the covered work 561 | conveyed by you (or copies made from those copies), or (b) primarily 562 | for and in connection with specific products or compilations that 563 | contain the covered work, unless you entered into that arrangement, 564 | or that patent license was granted, prior to 28 March 2007. 565 | 566 | Nothing in this License shall be construed as excluding or limiting 567 | any implied license or other defenses to infringement that may 568 | otherwise be available to you under applicable patent law. 569 | 570 | 12. No Surrender of Others' Freedom. 571 | 572 | If conditions are imposed on you (whether by court order, agreement or 573 | otherwise) that contradict the conditions of this License, they do not 574 | excuse you from the conditions of this License. If you cannot convey a 575 | covered work so as to satisfy simultaneously your obligations under this 576 | License and any other pertinent obligations, then as a consequence you may 577 | not convey it at all. For example, if you agree to terms that obligate you 578 | to collect a royalty for further conveying from those to whom you convey 579 | the Program, the only way you could satisfy both those terms and this 580 | License would be to refrain entirely from conveying the Program. 581 | 582 | 13. Use with the GNU Affero General Public License. 583 | 584 | Notwithstanding any other provision of this License, you have 585 | permission to link or combine any covered work with a work licensed 586 | under version 3 of the GNU Affero General Public License into a single 587 | combined work, and to convey the resulting work. The terms of this 588 | License will continue to apply to the part which is the covered work, 589 | but the special requirements of the GNU Affero General Public License, 590 | section 13, concerning interaction through a network will apply to the 591 | combination as such. 592 | 593 | 14. Revised Versions of this License. 594 | 595 | The Free Software Foundation may publish revised and/or new versions of 596 | the GNU General Public License from time to time. Such new versions will 597 | be similar in spirit to the present version, but may differ in detail to 598 | address new problems or concerns. 599 | 600 | Each version is given a distinguishing version number. If the 601 | Program specifies that a certain numbered version of the GNU General 602 | Public License "or any later version" applies to it, you have the 603 | option of following the terms and conditions either of that numbered 604 | version or of any later version published by the Free Software 605 | Foundation. If the Program does not specify a version number of the 606 | GNU General Public License, you may choose any version ever published 607 | by the Free Software Foundation. 608 | 609 | If the Program specifies that a proxy can decide which future 610 | versions of the GNU General Public License can be used, that proxy's 611 | public statement of acceptance of a version permanently authorizes you 612 | to choose that version for the Program. 613 | 614 | Later license versions may give you additional or different 615 | permissions. However, no additional obligations are imposed on any 616 | author or copyright holder as a result of your choosing to follow a 617 | later version. 618 | 619 | 15. Disclaimer of Warranty. 620 | 621 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 622 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 623 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 624 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 625 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 626 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 627 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 628 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 629 | 630 | 16. Limitation of Liability. 631 | 632 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 633 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 634 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 635 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 636 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 637 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 638 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 639 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 640 | SUCH DAMAGES. 641 | 642 | 17. Interpretation of Sections 15 and 16. 643 | 644 | If the disclaimer of warranty and limitation of liability provided 645 | above cannot be given local legal effect according to their terms, 646 | reviewing courts shall apply local law that most closely approximates 647 | an absolute waiver of all civil liability in connection with the 648 | Program, unless a warranty or assumption of liability accompanies a 649 | copy of the Program in return for a fee. 650 | 651 | END OF TERMS AND CONDITIONS 652 | 653 | How to Apply These Terms to Your New Programs 654 | 655 | If you develop a new program, and you want it to be of the greatest 656 | possible use to the public, the best way to achieve this is to make it 657 | free software which everyone can redistribute and change under these terms. 658 | 659 | To do so, attach the following notices to the program. It is safest 660 | to attach them to the start of each source file to most effectively 661 | state the exclusion of warranty; and each file should have at least 662 | the "copyright" line and a pointer to where the full notice is found. 663 | 664 | 665 | Copyright (C) 666 | 667 | This program is free software: you can redistribute it and/or modify 668 | it under the terms of the GNU General Public License as published by 669 | the Free Software Foundation, either version 3 of the License, or 670 | (at your option) any later version. 671 | 672 | This program is distributed in the hope that it will be useful, 673 | but WITHOUT ANY WARRANTY; without even the implied warranty of 674 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 675 | GNU General Public License for more details. 676 | 677 | You should have received a copy of the GNU General Public License 678 | along with this program. If not, see . 679 | 680 | Also add information on how to contact you by electronic and paper mail. 681 | 682 | If the program does terminal interaction, make it output a short 683 | notice like this when it starts in an interactive mode: 684 | 685 | Copyright (C) 686 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 687 | This is free software, and you are welcome to redistribute it 688 | under certain conditions; type `show c' for details. 689 | 690 | The hypothetical commands `show w' and `show c' should show the appropriate 691 | parts of the General Public License. Of course, your program's commands 692 | might be different; for a GUI interface, you would use an "about box". 693 | 694 | You should also get your employer (if you work as a programmer) or school, 695 | if any, to sign a "copyright disclaimer" for the program, if necessary. 696 | For more information on this, and how to apply and follow the GNU GPL, see 697 | . 698 | 699 | The GNU General Public License does not permit incorporating your program 700 | into proprietary programs. If your program is a subroutine library, you 701 | may consider it more useful to permit linking proprietary applications with 702 | the library. If this is what you want to do, use the GNU Lesser General 703 | Public License instead of this License. But first, please read 704 | . 705 | 706 | WRITTEN OFFER 707 | 708 | The source code for any program binaries or compressed scripts that are 709 | included with phpwpinfo can be freely obtained at the following URL: 710 | 711 | https://github.com/BeAPI/phpwpinfo/ 712 | -------------------------------------------------------------------------------- /phpwpinfo.php: -------------------------------------------------------------------------------- 1 | init_all_tests(); 41 | } 42 | 43 | class PHP_WP_Info { 44 | 45 | private bool $debug_mode = true; 46 | private $php_version = '≥7.4'; 47 | private $mysql_version = '8.0'; // TODO: Min MariaDB version ? 48 | private $curl_version = '7.38'; 49 | private $redis_version = '3.0'; // TODO: Check vs plugin ? 50 | 51 | private $db_infos = array(); 52 | private $db_link = false; 53 | 54 | private $redis_infos = array(); 55 | private $redis_link = false; 56 | 57 | public function __construct() { 58 | @session_start(); 59 | 60 | if ( $this->debug_mode === true ) { 61 | ini_set( 'display_errors', 1 ); 62 | ini_set( 'log_errors', 1 ); 63 | ini_set( 'error_log', __DIR__ . '/error_log.txt' ); 64 | error_reporting( E_ALL ); 65 | } 66 | 67 | // Check GET for phpinfo 68 | if ( isset( $_GET['phpinfo'] ) && $_GET['phpinfo'] === 'true' ) { 69 | phpinfo(); 70 | exit(); 71 | } 72 | 73 | // Check GET for self-destruction 74 | if ( isset( $_GET['self-destruction'] ) && $_GET['self-destruction'] === 'true' ) { 75 | @unlink( __FILE__ ); 76 | clearstatcache(); 77 | if ( is_file( __FILE__ ) ) { 78 | die( 'Self-destruction KO ! Sorry, but you must remove me manually !' ); 79 | } 80 | die( 'Self-destruction OK !' ); 81 | } 82 | 83 | $this->_check_request_database(); 84 | $this->_check_request_redis(); 85 | $this->_check_request_adminer(); 86 | $this->_check_request_wordpress(); 87 | } 88 | 89 | public function init_all_tests() { 90 | $this->get_header(); 91 | 92 | $this->test_versions(); 93 | $this->test_php_config(); 94 | $this->test_php_extensions(); 95 | $this->test_database_config(); 96 | $this->test_apache_modules(); 97 | $this->test_form_mail(); 98 | $this->test_form_redis(); 99 | $this->test_form_connectivity(); 100 | 101 | $this->get_footer(); 102 | } 103 | 104 | /** 105 | * Main test, check if php/databse/git are installed and right version for WP 106 | */ 107 | public function test_versions() { 108 | $this->html_table_open( 'General informations & tests PHP/Database Version', 109 | '', 110 | 'Required', 111 | 'Recommended', 112 | 'Current' ); 113 | 114 | // Webserver used 115 | $this->html_table_row( 'Web server', $this->_get_current_webserver(), '', '', 'info', 3 ); 116 | 117 | // Test PHP Version 118 | if ( strpos( PHP_SAPI, 'cgi' ) !== false ) { 119 | $this->html_table_row( 'PHP Type', 'CGI with Apache Worker or another webserver', '', '', 'success', 3 ); 120 | } else { 121 | $this->html_table_row( 'PHP Type', 'Apache Module (low performance)', '', '', 'warning', 3 ); 122 | } 123 | 124 | // Test PHP Version 125 | $php_version = PHP_VERSION; 126 | if ( version_compare( $php_version, $this->php_version, '>=' ) ) { 127 | $this->html_table_row( 'PHP Version', $this->php_version, '> 7.3', $php_version, 'success' ); 128 | } else { 129 | $this->html_table_row( 'PHP Version', $this->php_version, '> 7.3', $php_version, 'error' ); 130 | } 131 | 132 | // Test Database Client extensions/version. 133 | if ( ! extension_loaded( 'mysqli' ) || ! is_callable( 'mysqli_connect' ) ) { 134 | $this->html_table_row( 'PHP MySQLi Extension', 'Yes', 'Yes', 'Not installed', 'error' ); 135 | } else { 136 | $this->html_table_row( 'PHP MySQLi Extension', 'Yes', 'Yes', 'Installed', 'success' ); 137 | $this->html_table_row( 'PHP MySQLi Client Version', 138 | $this->mysql_version, 139 | '> 5.5', 140 | mysqli_get_client_info(), 141 | 'info' ); 142 | } 143 | 144 | // Test Databse Server Version 145 | if ( $this->db_link !== false && is_callable( 'mysqli_get_server_info' ) ) { 146 | $mysql_version = preg_replace( '/[^0-9.].*/', '', mysqli_get_server_info( $this->db_link ) ); 147 | if ( version_compare( $mysql_version, $this->mysql_version, '>=' ) ) { 148 | $this->html_table_row( 'Database Version', $this->mysql_version, '> 5.5', $mysql_version, 'success' ); 149 | } else { 150 | $this->html_table_row( 'Database Version', $this->mysql_version, '> 5.5', $mysql_version, 'error' ); 151 | } 152 | } else { 153 | // Show Database Form 154 | $this->html_form_database( $this->db_infos === false ); 155 | 156 | $this->html_table_row( 'Database Version', 157 | $this->mysql_version, 158 | '-', 159 | 'Not available, needs database credentials.', 160 | 'warning' ); 161 | } 162 | 163 | // Test if the server is connected to the server by attempt to find the IP(v4) of www.google.fr 164 | if ( gethostbyname( 'www.google.fr' ) !== 'www.google.fr' ) { 165 | $this->html_table_row( 'Internet connectivity (Google)', 'No', 'Yes', 'Yes', 'success' ); 166 | } else { 167 | $this->html_table_row( 'Internet connectivity (Google)', 'No', 'Yes', 'No', 'error' ); 168 | } 169 | 170 | // Test if the command 'git' exists, so it tests if Git is installed 171 | if ( $this->_command_exists( 'git' ) === true ) { 172 | $this->html_table_row( 'GIT is installed?', 'No', 'Yes', 'Yes' ); 173 | } else { 174 | $this->html_table_row( 'GIT is installed?', 'No', 'Yes', 'No', 'error' ); 175 | } 176 | 177 | $this->html_table_row( 'Remote IP via $_SERVER["REMOTE_ADDR"]', '', '', $_SERVER['REMOTE_ADDR'], 'info' ); 178 | 179 | if ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) { 180 | $this->html_table_row( 'Remote IP via $_SERVER["HTTP_X_FORWARDED_FOR"]', 181 | '', 182 | '', 183 | $_SERVER['HTTP_X_FORWARDED_FOR'], 184 | 'info' ); 185 | } 186 | 187 | if ( isset( $_SERVER['HTTP_X_FORWARDED'] ) ) { 188 | $this->html_table_row( 'Remote IP via $_SERVER["HTTP_X_FORWARDED"]', 189 | '', 190 | '', 191 | $_SERVER['HTTP_X_FORWARDED'], 192 | 'info' ); 193 | } 194 | 195 | if ( isset( $_SERVER['HTTP_CLIENT_IP'] ) ) { 196 | $this->html_table_row( 'Remote IP via $_SERVER["HTTP_CLIENT_IP"]', 197 | '', 198 | '', 199 | $_SERVER['HTTP_CLIENT_IP'], 200 | 'info' ); 201 | } 202 | 203 | $this->html_table_row( 'Real remote IP via AJAX call', '', '', '... js loading ...', 'warning realip' ); 204 | } 205 | 206 | public function test_php_extensions() { 207 | $this->html_table_open( 'PHP Extensions', '', 'Required', 'Recommended', 'Current' ); 208 | 209 | $extensions = array( 210 | // Higly recommanded 211 | 'json' => 'error', 212 | 'curl' => 'error', 213 | 'dom' => 'error', 214 | 'exif' => 'info', 215 | 'fileinfo' => 'info', 216 | 'igbinary' => 'info', 217 | 'intl' => 'error', 218 | 'mbstring' => 'error', 219 | 'openssl' => 'error', 220 | 'pcre' => 'error', 221 | 'zlib' => 'error', 222 | 'iconv' => 'error', 223 | 'xml' => 'error', 224 | 'zip' => 'info', 225 | // Optional 226 | 'xmlreader' => 'error', // TO DELETE? (Not in handbook) 227 | // Cache 228 | 'apcu' => 'info', 229 | 'memcache' => 'info', // TO DELETE? (Not in handbook) 230 | 'memcached' => 'info', 231 | 'redis' => 'info', 232 | // Others 233 | 'ftp' => 'info', 234 | 'ssh2' => 'info', 235 | 'sockets' => 'info', 236 | // Debug 237 | 'blackfire' => 'info', 238 | 'newrelic' => 'info', 239 | 'xdebug' => 'info', 240 | // Deprecated ? 241 | 'suhosin' => 'info', // TO DELETE? (Not in handbook + Deprecated) 242 | 'tidy' => 'info', // TO DELETE? (Not in handbook) 243 | ); 244 | 245 | foreach ( $extensions as $extension => $status ) { 246 | if ( ! extension_loaded( $extension ) ) { 247 | $is_wp_requirements = ( 'error' === $status ) ? 'Yes' : 'No'; 248 | $this->html_table_row( $extension, $is_wp_requirements, 'Yes', 'Not installed', $status ); 249 | } else { 250 | $this->html_table_row( $extension, 'Yes', 'Yes', 'Installed', 'success' ); 251 | } 252 | } 253 | 254 | /** 255 | * Check GD and Imagick like WordPress does. 256 | */ 257 | $gd = extension_loaded( 'gd' ) && function_exists( 'gd_info' ); 258 | $imagick = extension_loaded( 'imagick' ) && class_exists( 'Imagick', false ) && class_exists( 'ImagickPixel', 259 | false ) && version_compare( phpversion( 'imagick' ), 260 | '2.2.0', 261 | '>=' ); 262 | 263 | // GD/Imagick lib. 264 | if ( $gd ) { 265 | $this->html_table_row( 'Image manipulation (GD)', 'Yes', 'Yes', 'Installed', 'success' ); 266 | } 267 | 268 | if ( $imagick ) { 269 | $this->html_table_row( 'Image manipulation (Imagick)', 'Yes', 'Yes', 'Installed', 'success' ); 270 | } 271 | 272 | if ( ! $gd && ! $imagick ) { 273 | $this->html_table_row( 'Image manipulation (GD, Imagick)', 'Yes', 'Yes', 'Not installed', 'error' ); 274 | } 275 | 276 | if ( is_callable( 'opcache_reset' ) ) { 277 | $this->html_table_row( 'Opcode (Zend OPcache, APC, Xcache, eAccelerator or Zend Optimizer)', 278 | 'No', 279 | 'Yes', 280 | 'Zend OPcache Installed', 281 | 'success' ); 282 | } elseif ( is_callable( 'eaccelerator_put' ) ) { 283 | $this->html_table_row( 'Opcode (Zend OPcache, APC, Xcache, eAccelerator or Zend Optimizer)', 284 | 'No', 285 | 'Yes', 286 | 'eAccelerator Installed', 287 | 'success' ); 288 | } elseif ( is_callable( 'xcache_set' ) ) { 289 | $this->html_table_row( 'Opcode (Zend OPcache, APC, Xcache, eAccelerator or Zend Optimizer)', 290 | 'No', 291 | 'Yes', 292 | 'XCache Installed', 293 | 'success' ); 294 | } elseif ( is_callable( 'apc_store' ) ) { 295 | $this->html_table_row( 'Opcode (Zend OPcache, APC, Xcache, eAccelerator or Zend Optimizer)', 296 | 'No', 297 | 'Yes', 298 | 'APC Installed', 299 | 'success' ); 300 | } elseif ( is_callable( 'zend_optimizer_version' ) ) { 301 | $this->html_table_row( 'Opcode (Zend OPcache, APC, Xcache, eAccelerator or Zend Optimizerr)', 302 | 'No', 303 | 'Yes', 304 | 'Zend Optimizer Installed', 305 | 'success' ); 306 | } else { 307 | $this->html_table_row( 'Opcode (Zend OPcache, APC, Xcache, eAccelerator or Zend Optimizer)', 308 | 'No', 309 | 'Yes', 310 | 'Not installed', 311 | 'warning' ); 312 | } 313 | 314 | if ( ! is_callable( 'finfo_open' ) && ! is_callable( 'mime_content_type' ) ) { 315 | $this->html_table_row( 'Mime type', 'Yes*', 'Yes', 'Not installed', 'warning' ); 316 | } else { 317 | $this->html_table_row( 'Mime type', 'Yes*', 'Yes', 'Installed', 'success' ); 318 | } 319 | 320 | if ( ! is_callable( 'hash' ) && ! is_callable( 'mhash' ) ) { 321 | $this->html_table_row( 'Hash', 'No', 'Yes', 'Not installed', 'info' ); 322 | } else { 323 | $this->html_table_row( 'Hash', 'No', 'Yes', 'Installed', 'success' ); 324 | } 325 | 326 | if ( ! is_callable( 'set_time_limit' ) ) { 327 | $this->html_table_row( 'set_time_limit', 'No', 'Yes', 'Not Available', 'info' ); 328 | } else { 329 | $this->html_table_row( 'set_time_limit', 'No', 'Yes', 'Available', 'success' ); 330 | } 331 | 332 | if ( extension_loaded( 'curl' ) ) { 333 | $curl = curl_version(); 334 | $this->html_table_row( 'Curl version', 335 | '-', 336 | $this->curl_version, 337 | sprintf( '%s %s', $curl['version'], $curl['ssl_version'] ), 338 | 'info' ); 339 | } 340 | 341 | $this->html_table_close( '(*) Items with an asterisk are not required by WordPress, but it is highly recommended by me!' ); 342 | } 343 | 344 | public function test_apache_modules() { 345 | if ( $this->_get_current_webserver() !== 'Apache' ) { 346 | return false; 347 | } 348 | 349 | $current_modules = (array) $this->_get_apache_modules(); 350 | $modules = array( 351 | 'mod_deflate' => false, 352 | 'mod_env' => false, 353 | 'mod_expires' => false, 354 | 'mod_headers' => false, 355 | 'mod_filter' => false, 356 | 'mod_mime' => false, 357 | 'mod_rewrite' => true, 358 | 'mod_setenvif' => false, 359 | ); 360 | 361 | $this->html_table_open( 'Apache Modules', '', 'Required', 'Recommended', 'Current' ); 362 | 363 | foreach ( $modules as $module => $is_required ) { 364 | $is_required = ( $is_required === true ) ? 'Yes' : 'No'; // Boolean to Yes/NO 365 | 366 | $name = ucfirst( str_replace( 'mod_', '', $module ) ); 367 | if ( ! in_array( $module, $current_modules, true ) ) { 368 | $this->html_table_row( $name, $is_required, 'Recommended', 'Not installed', 'error' ); 369 | } else { 370 | $this->html_table_row( $name, $is_required, 'Recommended', 'Installed', 'success' ); 371 | } 372 | } 373 | 374 | $this->html_table_close(); 375 | 376 | return true; 377 | } 378 | 379 | public function test_php_config() { 380 | $this->html_table_open( 'PHP Configuration', '', 'Required', 'Recommended', 'Current' ); 381 | 382 | $value = date_default_timezone_get(); 383 | if ( empty( $value ) ) { 384 | $this->html_table_row( 'date_default_timezone ', '-', '-', 'Not empty value', 'warning' ); 385 | } else { 386 | $this->html_table_row( 'date_default_timezone ', '-', '-', $value, 'info' ); 387 | } 388 | 389 | $value = ini_get( 'register_argc_argv ' ); 390 | if ( strtolower( $value ) === 'on' ) { 391 | $this->html_table_row( 'register_argc_argv ', '-', 'Off', 'On', 'warning' ); 392 | } else { 393 | $this->html_table_row( 'register_argc_argv ', '-', 'Off', 'Off', 'success' ); 394 | } 395 | 396 | $value = $this->return_bytes( ini_get( 'memory_limit' ) ); 397 | if ( $value !== '-1' && (int) $value < $this->return_bytes( '64M' ) ) { 398 | $this->html_table_row( 'memory_limit', '64 MB', '256 MB', $this->_format_bytes( $value ), 'error' ); 399 | } else { 400 | $status = ( (int) $value >= $this->return_bytes( '256M' ) || $value === '-1' ) ? 'success' : 'warning'; 401 | $this->html_table_row( 'memory_limit', '64 MB', '256 MB', $this->_format_bytes( $value ), $status ); 402 | } 403 | 404 | $value = ini_get( 'max_input_vars' ); 405 | if ( (int) $value < 5000 ) { 406 | $this->html_table_row( 'max_input_vars', '5000', '10000', $value, 'error' ); 407 | } else { 408 | $status = ( (int) $value >= 10000 ) ? 'success' : 'warning'; 409 | $this->html_table_row( 'max_input_vars', '5000', '10000', $value, $status ); 410 | } 411 | 412 | $value = ini_get( 'max_execution_time' ); 413 | if ( $value !== '-1' && (int) $value < 60 ) { 414 | $this->html_table_row( 'max_execution_time', '-', '300', $value, 'error' ); 415 | } else { 416 | $status = ( (int) $value >= 300 || $value === '-1' ) ? 'success' : 'warning'; 417 | $this->html_table_row( 'max_execution_time', '-', '300', $value, $status ); 418 | } 419 | 420 | $value = ini_get( 'max_input_time' ); 421 | if ( $value !== '-1' && (int) $value < 60 ) { 422 | $this->html_table_row( 'max_input_time', '-', '300', $value, 'error' ); 423 | } else { 424 | $status = ( (int) $value >= 300 || $value === '-1' ) ? 'success' : 'warning'; 425 | $this->html_table_row( 'max_input_time', '-', '300', $value, $status ); 426 | } 427 | 428 | $value = ini_get( 'file_uploads' ); 429 | if ( strtolower( $value ) === 'on' || $value === '1' ) { 430 | $this->html_table_row( 'file_uploads', 'On', 'On', 'On', 'success' ); 431 | } else { 432 | $this->html_table_row( 'file_uploads', 'On', 'On', 'Off', 'error' ); 433 | } 434 | 435 | $value = $this->return_bytes( ini_get( 'upload_max_filesize' ) ); 436 | if ( (int) $value < $this->return_bytes( '32M' ) ) { 437 | $this->html_table_row( 'upload_max_filesize', '32 MB', '128 MB', $this->_format_bytes( $value ), 'error' ); 438 | } else { 439 | $status = ( (int) $value >= $this->return_bytes( '128M' ) ) ? 'success' : 'warning'; 440 | $this->html_table_row( 'upload_max_filesize', '32 MB', '128 MB', $this->_format_bytes( $value ), $status ); 441 | } 442 | 443 | $value = $this->return_bytes( ini_get( 'post_max_size' ) ); 444 | if ( (int) $value < $this->return_bytes( '32M' ) ) { 445 | $this->html_table_row( 'post_max_size', '32 MB', '128 MB', $this->_format_bytes( $value ), 'warning' ); 446 | } else { 447 | $status = ( (int) $value >= $this->return_bytes( '128M' ) ) ? 'success' : 'warning'; 448 | $this->html_table_row( 'post_max_size', '32 MB', '128 MB', $this->_format_bytes( $value ), $status ); 449 | } 450 | 451 | $value = ini_get( 'short_open_tag' ); 452 | if ( strtolower( $value ) === 'on' ) { 453 | $this->html_table_row( 'short_open_tag', '-', 'Off', 'On', 'warning' ); 454 | } else { 455 | $this->html_table_row( 'short_open_tag', '-', 'Off', 'Off', 'success' ); 456 | } 457 | 458 | $value = ini_get( 'open_basedir' ); 459 | $this->html_table_row( 'open_basedir', $value, '', '', 'info', 3 ); 460 | 461 | $value = ini_get( 'zlib.output_compression' ); 462 | $this->html_table_row( 'zlib.output_compression', $value, '', '', 'info', 3 ); 463 | 464 | $value = ini_get( 'output_handler' ); 465 | $this->html_table_row( 'output_handler', $value, '', '', 'info', 3 ); 466 | 467 | $value = ini_get( 'disable_functions' ); 468 | $this->html_table_row( 'disable_functions', $value, '', '', 'info', 3 ); 469 | 470 | $value = ini_get( 'expose_php' ); 471 | if ( $value === '0' || strtolower( $value ) === 'off' || empty( $value ) ) { 472 | $this->html_table_row( 'expose_php', '-', '0 or Off', $value, 'success' ); 473 | } else { 474 | $this->html_table_row( 'expose_php', '-', '0 or Off', $value, 'error' ); 475 | } 476 | 477 | $value = ini_get( 'upload_tmp_dir' ); 478 | $this->html_table_row( 'upload_tmp_dir', $value, '', '', 'info', 3 ); 479 | if ( is_dir( $value ) && @is_writable( $value ) ) { 480 | $this->html_table_row( 'upload_tmp_dir writable ?', '-', 'Yes', 'Yes', 'success' ); 481 | } else { 482 | $this->html_table_row( 'upload_tmp_dir writable ?', '-', 'Yes', 'No', 'error' ); 483 | } 484 | 485 | $value = '/tmp/'; 486 | $this->html_table_row( 'System temp dir', $value, '', '', 'info', 3 ); 487 | if ( is_dir( $value ) && @is_writable( $value ) ) { 488 | $this->html_table_row( 'System temp dir writable ?', '-', 'Yes', 'Yes', 'success' ); 489 | } else { 490 | $this->html_table_row( 'System temp dir writable ?', '-', 'Yes', 'No', 'error' ); 491 | } 492 | 493 | $value = __DIR__; 494 | $this->html_table_row( 'Current dir', $value, '', '', 'info', 3 ); 495 | if ( is_dir( $value ) && @is_writable( $value ) ) { 496 | $this->html_table_row( 'Current dir writable ?', 'Yes', 'Yes', 'Yes', 'success' ); 497 | } else { 498 | $this->html_table_row( 'Current dir writable ?', 'Yes', 'Yes', 'No', 'error' ); 499 | } 500 | 501 | if ( is_callable( 'apc_store' ) ) { 502 | $value = $this->return_bytes( ini_get( 'apc.shm_size' ) ); 503 | if ( (int) $value < $this->return_bytes( '32M' ) ) { 504 | $this->html_table_row( 'apc.shm_size', '32 MB', '128 MB', $this->_format_bytes( $value ), 'error' ); 505 | } else { 506 | $status = ( (int) $value >= $this->return_bytes( '128M' ) ) ? 'success' : 'warning'; 507 | $this->html_table_row( 'apc.shm_size', '32 MB', '128 MB', $this->_format_bytes( $value ), $status ); 508 | } 509 | } 510 | 511 | $this->html_table_close(); 512 | } 513 | 514 | /** 515 | * Convert PHP variable (G/M/K) to bytes 516 | * Source: https://php.net/manual/fr/function.ini-get.php 517 | * @return int|string 518 | * 519 | * @param $val 520 | */ 521 | public function return_bytes( $val ) { 522 | $val = trim( $val ); 523 | $last = strtolower( $val[ strlen( $val ) - 1 ] ); 524 | $val = (int) $val; 525 | switch ( $last ) { 526 | // Le modifieur 'G' est disponible depuis PHP 5.1.0 527 | case 'g': 528 | $val *= 1024; 529 | case 'm': 530 | $val *= 1024; 531 | case 'k': 532 | $val *= 1024; 533 | } 534 | 535 | return $val; 536 | } 537 | 538 | /** 539 | * @return void 540 | */ 541 | public function test_database_config() { 542 | if ( $this->db_link === false ) { 543 | return; 544 | } 545 | 546 | $this->html_table_open( 'Database Configuration', '', 'Required', 'Recommended', 'Current' ); 547 | 548 | $result = mysqli_query( $this->db_link, "SHOW VARIABLES LIKE 'have_query_cache'" ); 549 | if ( $result !== false ) { 550 | while ( $row = mysqli_fetch_assoc( $result ) ) { 551 | if ( strtolower( $row['Value'] ) === 'yes' ) { 552 | $this->html_table_row( 'Query cache', '-', 'False', $row['Value'], 'error' ); 553 | } else { 554 | $this->html_table_row( 'Query cache', '-', 'False', $row['Value'], 'success' ); 555 | } 556 | } 557 | } 558 | 559 | $result = mysqli_query( $this->db_link, "SHOW VARIABLES LIKE 'query_cache_size'" ); 560 | if ( $result !== false ) { 561 | while ( $row = mysqli_fetch_assoc( $result ) ) { 562 | if ( (int) $row['Value'] >= $this->return_bytes( '8M' ) ) { 563 | $status = ( (int) $row['Value'] >= $this->return_bytes( '64M' ) ) ? 'success' : 'warning'; 564 | $this->html_table_row( 'Query cache size', 565 | '8M', 566 | '64MB', 567 | $this->_format_bytes( (int) $row['Value'] ), 568 | $status ); 569 | } else { 570 | $this->html_table_row( 'Query cache size', 571 | '8M', 572 | '64MB', 573 | $this->_format_bytes( (int) $row['Value'] ), 574 | 'error' ); 575 | } 576 | } 577 | } 578 | 579 | $result = mysqli_query( $this->db_link, "SHOW VARIABLES LIKE 'query_cache_type'" ); 580 | if ( $result !== false ) { 581 | while ( $row = mysqli_fetch_assoc( $result ) ) { 582 | if ( strtolower( $row['Value'] ) === 'on' || strtolower( $row['Value'] ) === '1' ) { 583 | $this->html_table_row( 'Query cache type', 584 | '0 or off', 585 | '1 or on', 586 | strtolower( $row['Value'] ), 587 | 'error' ); 588 | } else { 589 | $this->html_table_row( 'Query cache type', 590 | '0', 591 | $row['Value'], 592 | strtolower( $row['Value'] ), 593 | 'success' ); 594 | } 595 | } 596 | } 597 | 598 | $is_log_slow_queries = false; 599 | $result = mysqli_query( $this->db_link, "SHOW VARIABLES LIKE 'log_slow_queries'" ); 600 | if ( $result !== false ) { 601 | while ( $row = mysqli_fetch_assoc( $result ) ) { 602 | if ( strtolower( $row['Value'] ) === 'yes' || strtolower( $row['Value'] ) === 'on' ) { 603 | $is_log_slow_queries = true; 604 | $this->html_table_row( 'Log slow queries', 'No', 'Yes', 'Yes', 'success' ); 605 | } else { 606 | $is_log_slow_queries = false; 607 | $this->html_table_row( 'Log slow queries', 'No', 'Yes', 'False', 'error' ); 608 | } 609 | } 610 | } 611 | 612 | $result = mysqli_query( $this->db_link, "SHOW VARIABLES LIKE 'long_query_time'" ); 613 | if ( $is_log_slow_queries === true && $result !== false ) { 614 | while ( $row = mysqli_fetch_assoc( $result ) ) { 615 | if ( (int) $row['Value'] <= 2 ) { 616 | $this->html_table_row( 'Long query time (sec)', '2', '1', ( (int) $row['Value'] ), 'success' ); 617 | } else { 618 | $this->html_table_row( 'Long query time (sec)', '2', '1', ( (int) $row['Value'] ), 'error' ); 619 | } 620 | } 621 | } 622 | // $this->html_table_close( '(*) Items with an asterisk are not required by WordPress, but it is highly recommended by me!' ); 623 | } 624 | 625 | public function test_form_mail() { 626 | $this->html_table_open( 'Email Configuration', '', '', '' ); 627 | $this->html_form_email(); 628 | $this->html_table_close(); 629 | } 630 | 631 | /** 632 | * Start HTML, call CSS/JS from CDN 633 | * Link to Github 634 | * TODO: Add links to Codex/WP.org 635 | * TODO: Add colors legend 636 | */ 637 | public function get_header() { 638 | $output = ''; 639 | $output .= '' . "\n"; 640 | $output .= '' . "\n"; 641 | $output .= '' . "\n"; 642 | $output .= '' . "\n"; 643 | $output .= '' . "\n"; 644 | $output .= 'PHPWPInfo' . "\n"; 645 | $output .= '' . "\n"; 646 | $output .= '' . "\n"; 647 | $output .= '' . "\n"; 648 | $output .= '' . "\n"; 649 | $output .= '' . "\n"; 650 | $output .= '
' . "\n"; 651 | $output .= '' . "\n"; 706 | 707 | echo $output; 708 | } 709 | 710 | /** 711 | * Close HTML, call JS 712 | */ 713 | public function get_footer() { 714 | $output = ''; 715 | 716 | $output .= '' . "\n"; 717 | $output .= '
' . "\n"; 718 | 719 | $output .= '' . "\n"; 720 | $output .= '' . "\n"; 721 | 722 | $output .= '' . "\n"; 727 | 728 | $output .= '' . "\n"; 729 | 730 | $output .= '' . "\n"; 731 | $output .= '' . "\n"; 732 | 733 | echo $output; 734 | } 735 | 736 | /** 737 | * Open a HTML table 738 | * 739 | * @param string $title 740 | * @param string $col1 741 | * @param string $col2 742 | * @param string $col3 743 | * @param string $col4 744 | */ 745 | public function html_table_open( $title = '', $col1 = '', $col2 = '', $col3 = '', $col4 = '' ) { 746 | $output = ''; 747 | $output .= '' . "\n"; 748 | $output .= '' . "\n"; 749 | $output .= '' . "\n"; 750 | 751 | if ( ! empty( $col1 ) || ! empty( $col2 ) || ! empty( $col3 ) || ! empty( $col4 ) ) { 752 | $output .= '' . "\n"; 753 | $output .= '' . "\n"; 754 | if ( ! empty( $col4 ) ) { 755 | $output .= '' . "\n"; 756 | $output .= '' . "\n"; 757 | $output .= '' . "\n"; 758 | } else { 759 | $output .= '' . "\n"; 760 | $output .= '' . "\n"; 761 | } 762 | $output .= '' . "\n"; 763 | } 764 | 765 | $output .= '' . "\n"; 766 | $output .= '' . "\n"; 767 | 768 | echo $output; 769 | } 770 | 771 | /** 772 | * Close HTML table 773 | * 774 | * @param string $description 775 | */ 776 | public function html_table_close( $description = '' ) { 777 | $output = ''; 778 | $output .= '' . "\n"; 779 | $output .= '
' . $title . '
' . $col1 . '' . $col2 . '' . $col3 . '' . $col4 . '' . $col2 . '' . $col3 . '
' . "\n"; 780 | 781 | if ( ! empty( $description ) ) { 782 | $output .= '

' . $description . '

' . "\n"; 783 | } 784 | 785 | echo $output; 786 | } 787 | 788 | /** 789 | * Add table row 790 | * Status available : success, error, warning, info 791 | * 792 | * @param string $col1 793 | * @param string $col2 794 | * @param string $col3 795 | * @param string $col4 796 | * @param string $status 797 | * @param bool $colspan 798 | */ 799 | public function html_table_row( $col1, $col2, $col3, $col4, $status = 'success', $colspan = false ) { 800 | $output = ''; 801 | $output .= '' . "\n"; 802 | 803 | if ( $colspan !== false ) { 804 | $output .= '' . $col1 . '' . "\n"; 805 | $output .= '' . $col2 . '' . "\n"; 806 | } else { 807 | $output .= '' . $col1 . '' . "\n"; 808 | $output .= '' . $col2 . '' . "\n"; 809 | $output .= '' . $col3 . '' . "\n"; 810 | $output .= '' . $col4 . '' . "\n"; 811 | } 812 | 813 | $output .= '' . "\n"; 814 | 815 | echo $output; 816 | } 817 | 818 | /** 819 | * Form HTML for Database Login 820 | * 821 | * @return void [description] 822 | * 823 | * @param boolean $show_error [description] 824 | * 825 | */ 826 | public function html_form_database 827 | ( 828 | $show_error = false 829 | ) { 830 | $output = ''; 831 | $output .= '' . "\n"; 832 | $output .= '' . "\n"; 833 | 834 | if ( $show_error === true ) { 835 | $output .= '
Database credentials invalid.
' . "\n"; 836 | } 837 | 838 | $output .= '
' . "\n"; 839 | $output .= '' . "\n"; 840 | $output .= '' . "\n"; 841 | $output .= '' . "\n"; 842 | $output .= '' . "\n"; 845 | $output .= '' . "\n"; 846 | $output .= 'We must connect to the database server (MySQL or MariaDB) to check the configuration' . "\n"; 847 | $output .= '
' . "\n"; 848 | $output .= '' . "\n"; 849 | $output .= '' . "\n"; 850 | 851 | echo $output; 852 | } 853 | 854 | public function test_form_connectivity() { 855 | $this->html_table_open( 'Connectivity tests', '', '', '' ); 856 | 857 | if ( isset( $_POST['test-connectivity'] ) ) { 858 | $result = $this->check_connectivity_http( 'api.wordpress.org', 80, '/core/version-check/1.7/' ); 859 | $this->html_table_row( 860 | 'Test HTTP on api.wordpress.org', 861 | ( $result === true ? 'OK' : 'KO' ), 862 | '', 863 | '', 864 | ( $result === true ? 'success' : 'error' ), 865 | 3 866 | ); 867 | 868 | $result = $this->check_connectivity_http( 'api.wordpress.org', 443, '/core/version-check/1.7/' ); 869 | $this->html_table_row( 870 | 'Test HTTPS on api.wordpress.org', 871 | ( $result === true ? 'OK' : 'KO' ), 872 | '', 873 | '', 874 | ( $result === true ? 'success' : 'error' ), 875 | 3 876 | ); 877 | 878 | $result = $this->check_connectivity_ssh( 'github.com' ); 879 | $this->html_table_row( 880 | 'Test SSH on github.com', 881 | ( $result === true ? 'OK' : 'KO' ), 882 | '', 883 | '', 884 | ( $result === true ? 'success' : 'error' ), 885 | 3 886 | ); 887 | 888 | $result = $this->check_connectivity_ssh( 'bitbucket.org' ); 889 | $this->html_table_row( 890 | 'Test SSH on bitbucket.org', 891 | ( $result === true ? 'OK' : 'KO' ), 892 | '', 893 | '', 894 | ( $result === true ? 'success' : 'error' ), 895 | 3 896 | ); 897 | 898 | $result = $this->check_connectivity_ssh( 'gitlab.com' ); 899 | $this->html_table_row( 900 | 'Test SSH on gitlab.com', 901 | ( $result === true ? 'OK' : 'KO' ), 902 | '', 903 | '', 904 | ( $result === true ? 'success' : 'error' ), 905 | 3 906 | ); 907 | } 908 | 909 | $output = ''; 910 | $output .= '' . "\n"; 911 | $output .= '' . "\n"; 912 | $output .= '
' . "\n"; 913 | $output .= '' . "\n"; 914 | $output .= 'Check if server can access to internet via web and SSH' . "\n"; 915 | $output .= '
' . "\n"; 916 | $output .= '' . "\n"; 917 | $output .= '' . "\n"; 918 | 919 | echo $output; 920 | 921 | $this->html_table_close(); 922 | } 923 | 924 | public function test_form_redis() { 925 | $this->html_table_open( 'Redis Configuration', '', '', '' ); 926 | 927 | // Test redis Server Version 928 | if ( $this->redis_link !== false && class_exists( 'Redis' ) ) { 929 | $redis_info = $this->redis_link->info(); 930 | 931 | if ( version_compare( $redis_info['redis_version'], $this->redis_version, '>=' ) ) { 932 | $this->html_table_row( 'Database Version', 933 | $this->redis_version, 934 | '> 5', 935 | $redis_info['redis_version'], 936 | 'success' ); 937 | } else { 938 | $this->html_table_row( 'Database Version', 939 | $this->redis_version, 940 | '> 5', 941 | $redis_info['redis_version'], 942 | 'error' ); 943 | } 944 | 945 | try { 946 | $this->redis_link->set( 'phpwpinfo', 'yes' ); 947 | $glueStatus = $this->redis_link->get( 'phpwpinfo' ); 948 | if ( $glueStatus ) { 949 | $testKey = 'phpwpinfo'; 950 | $output = "It's OK ! Glued with the Redis key value store:" . PHP_EOL; 951 | $output .= "1. Got value '{$glueStatus}' for key '{$testKey}'." . PHP_EOL; 952 | if ( $this->redis_link->del( 'phpwpinfo' ) ) { 953 | $output .= "2. And already removed the key/value pair again." . PHP_EOL; 954 | } 955 | 956 | $this->html_table_row( 'Redis self-test', 957 | $output, 958 | '', 959 | '', 960 | 'success', 961 | 3 ); 962 | } else { 963 | $output = "Not glued with the Redis key value store." . PHP_EOL; 964 | 965 | $this->html_table_row( 'Redis self-test', 966 | $output, 967 | '', 968 | '', 969 | 'error', 970 | 3 ); 971 | } 972 | } catch ( RedisException $e ) { 973 | $exceptionMessage = $e->getMessage(); 974 | $output = "Exception : {$exceptionMessage}. Not glued with the Redis key value store."; 975 | 976 | $this->html_table_row( 'Redis self-test', 977 | $output, 978 | '', 979 | '', 980 | 'error', 981 | 3 ); 982 | } 983 | } else { 984 | // Show redis Form 985 | $this->html_form_redis( $this->redis_infos === false ); 986 | 987 | $this->html_table_row( 'Redis version', 988 | $this->redis_version, 989 | '-', 990 | 'Not available, needs redis auth.', 991 | 'warning' ); 992 | } 993 | 994 | $this->html_table_close(); 995 | } 996 | 997 | /** 998 | * Form HTML for Redis Auth 999 | * 1000 | * @return void [description] 1001 | * 1002 | * @param boolean $show_error [description] 1003 | * 1004 | */ 1005 | public function html_form_redis( $show_error = false ) { 1006 | $output = ''; 1007 | $output .= '' . "\n"; 1008 | $output .= '' . "\n"; 1009 | 1010 | if ( $show_error === true ) { 1011 | $output .= '
Redis credentials invalid.'; 1012 | if ( isset( $_SESSION['redis-error-message'] ) ) { 1013 | $output .= ' (' . $_SESSION['redis-error-message'] . ')'; 1014 | } 1015 | 1016 | $output .= '
' . "\n"; 1017 | } 1018 | 1019 | $output .= '
' . "\n"; 1020 | $output .= '' . "\n"; 1021 | $output .= '' . "\n"; 1022 | $output .= '' . "\n"; 1025 | $output .= '' . "\n"; 1026 | $output .= 'We must connect to the Redis server to check the configuration' . "\n"; 1027 | $output .= '
' . "\n"; 1028 | $output .= '' . "\n"; 1029 | $output .= '' . "\n"; 1030 | 1031 | echo $output; 1032 | } 1033 | 1034 | /** 1035 | * Form for test email 1036 | * 1037 | * @return void [description] 1038 | */ 1039 | public function html_form_email() { 1040 | $output = ''; 1041 | $output .= '' . "\n"; 1042 | $output .= '' . "\n"; 1043 | 1044 | if ( isset( $_POST['test-email'], $_POST['mail'] ) ) { 1045 | if ( ! filter_var( $_POST['mail'], FILTER_VALIDATE_EMAIL ) ) { // Invalid 1046 | $output .= '
Email invalid.
' . "\n"; 1047 | } else {// Valid mail 1048 | $subject = 'Email test with PHP WP Info'; 1049 | $message = "Line 1\nLine 2\nLine 3\nGreat !"; 1050 | $additional_headers = ''; 1051 | $headers = 'X-Mailer: PHP/' . phpversion(); 1052 | 1053 | if ( isset( $_POST['mail_from'] ) && filter_var( $_POST['mail_from'], FILTER_VALIDATE_EMAIL ) ) { 1054 | $headers .= "\r\n" . 'From: ' . $_POST['mail_from'] . "\r\n" . 1055 | 'Reply-To: ' . $_POST['mail_from']; 1056 | 1057 | if ( isset( $_POST['mail_returnpath'] ) && '1' === $_POST['mail_returnpath'] ) { 1058 | $headers .= "\r\n" . 'Return-Path: ' . $_POST['mail_from']; 1059 | $additional_headers = '-f ' . $_POST['mail_from']; 1060 | } 1061 | } 1062 | 1063 | $mresult = mail( $_POST['mail'], $subject, $message, $headers, $additional_headers ); 1064 | if ( $mresult ) {// Valid send 1065 | $output .= '
Mail sent with success.
' . "\n"; 1066 | } else {// Error send 1067 | $output .= '
An error occured during mail sending.
' . "\n"; 1068 | } 1069 | } 1070 | } 1071 | 1072 | $output .= '
' . "\n"; 1073 | $output .= ' ' . "\n"; 1074 | $output .= ' ' . "\n"; 1075 | $output .= ' ' . "\n"; 1076 | $output .= ' ' . "\n"; 1077 | $output .= ' Send a test e-mail to check that the server is doing its job. You can leave the “mailfrom” field blank to let the server configuration “do its thing”.' . "\n"; 1078 | $output .= '
' . "\n"; 1079 | $output .= '' . "\n"; 1080 | $output .= '' . "\n"; 1081 | 1082 | echo $output; 1083 | } 1084 | 1085 | /** 1086 | * Stripslashes array 1087 | * 1088 | * @param $value 1089 | * 1090 | * @return array|string 1091 | */ 1092 | public function stripslashes_deep( $value ) { 1093 | return is_array( $value ) ? array_map( array( &$this, 'stripslashes_deep' ), $value ) : stripslashes( $value ); 1094 | } 1095 | 1096 | /** 1097 | * Detect current webserver 1098 | * 1099 | * @return string [description] 1100 | */ 1101 | private function _get_current_webserver() { 1102 | if ( stripos( $_SERVER['SERVER_SOFTWARE'], 'apache' ) !== false ) : 1103 | return 'Apache'; 1104 | elseif ( stripos( $_SERVER['SERVER_SOFTWARE'], 'LiteSpeed' ) !== false ) : 1105 | return 'Lite Speed'; 1106 | elseif ( stripos( $_SERVER['SERVER_SOFTWARE'], 'nginx' ) !== false ) : 1107 | return 'nginx'; 1108 | elseif ( stripos( $_SERVER['SERVER_SOFTWARE'], 'lighttpd' ) !== false ) : 1109 | return 'lighttpd'; 1110 | elseif ( stripos( $_SERVER['SERVER_SOFTWARE'], 'iis' ) !== false ) : 1111 | return 'Microsoft IIS'; 1112 | else : 1113 | return 'Not detected'; 1114 | endif; 1115 | } 1116 | 1117 | /** 1118 | * Method for get apaches modules with Apache modules or CGI with .HTACCESS 1119 | * 1120 | * @return array [description] 1121 | */ 1122 | private function _get_apache_modules() { 1123 | $apache_modules = ( is_callable( 'apache_get_modules' ) ? apache_get_modules() : false ); 1124 | 1125 | if ( $apache_modules === false && ( isset( $_SERVER['http_mod_env'] ) || isset( $_SERVER['REDIRECT_http_mod_env'] ) ) ) { 1126 | // Test with htaccess to get ENV values 1127 | $apache_modules = array( 'mod_env' ); 1128 | 1129 | if ( isset( $_SERVER['http_mod_rewrite'] ) || isset( $_SERVER['REDIRECT_http_mod_rewrite'] ) ) { 1130 | $apache_modules[] = 'mod_rewrite'; 1131 | } 1132 | if ( isset( $_SERVER['http_mod_deflate'] ) || isset( $_SERVER['REDIRECT_http_mod_deflate'] ) ) { 1133 | $apache_modules[] = 'mod_deflate'; 1134 | } 1135 | if ( isset( $_SERVER['http_mod_expires'] ) || isset( $_SERVER['REDIRECT_http_mod_expires'] ) ) { 1136 | $apache_modules[] = 'mod_expires'; 1137 | } 1138 | if ( isset( $_SERVER['http_mod_filter'] ) || isset( $_SERVER['REDIRECT_http_mod_filter'] ) ) { 1139 | $apache_modules[] = 'mod_filter'; 1140 | } 1141 | if ( isset( $_SERVER['http_mod_headers'] ) || isset( $_SERVER['REDIRECT_http_mod_headers'] ) ) { 1142 | $apache_modules[] = 'mod_headers'; 1143 | } 1144 | if ( isset( $_SERVER['http_mod_mime'] ) || isset( $_SERVER['REDIRECT_http_mod_mime'] ) ) { 1145 | $apache_modules[] = 'mod_mime'; 1146 | } 1147 | if ( isset( $_SERVER['http_mod_setenvif'] ) || isset( $_SERVER['REDIRECT_http_mod_setenvif'] ) ) { 1148 | $apache_modules[] = 'mod_setenvif'; 1149 | } 1150 | } 1151 | 1152 | return $apache_modules; 1153 | } 1154 | 1155 | /** 1156 | * Get humans values, take from https://php.net/manual/de/function.filesize.php 1157 | * 1158 | * @return string [description] 1159 | * 1160 | * @param $size 1161 | * 1162 | * @internal param int $bytes [description] 1163 | */ 1164 | private function _format_bytes( $size ) { 1165 | $units = array( ' B', ' KB', ' MB', ' GB', ' TB' ); 1166 | for ( $i = 0; $size >= 1024 && $i < 4; $i ++ ) { 1167 | $size /= 1024; 1168 | } 1169 | 1170 | return round( $size, 2 ) . $units[ $i ]; 1171 | } 1172 | 1173 | private function _variable_to_html( $variable ) { 1174 | if ( $variable === true ) { 1175 | return 'true'; 1176 | } 1177 | 1178 | if ( $variable === false ) { 1179 | return 'false'; 1180 | } 1181 | 1182 | if ( $variable === null ) { 1183 | return 'null'; 1184 | } 1185 | 1186 | if ( is_array( $variable ) ) { 1187 | $html = "\n"; 1188 | $html .= "\n"; 1189 | $html .= "\n"; 1190 | foreach ( $variable as $key => $value ) { 1191 | $value = $this->_variable_to_html( $value ); 1192 | $html .= "\n"; 1193 | } 1194 | $html .= "\n"; 1195 | $html .= '
KeyValue
$key$value
'; 1196 | 1197 | return $html; 1198 | } 1199 | 1200 | return (string) $variable; 1201 | } 1202 | 1203 | public function file_get_contents_url( $url ) { 1204 | if ( function_exists( 'curl_init' ) ) { 1205 | $curl = curl_init(); 1206 | 1207 | curl_setopt( $curl, CURLOPT_URL, $url ); 1208 | //The URL to fetch. This can also be set when initializing a session with curl_init(). 1209 | curl_setopt( $curl, CURLOPT_RETURNTRANSFER, true ); 1210 | //TRUE to return the transfer as a string of the return value of curl_exec() instead of outputting it out directly. 1211 | curl_setopt( $curl, CURLOPT_CONNECTTIMEOUT, 15 ); 1212 | //The number of seconds to wait while trying to connect. 1213 | 1214 | curl_setopt( $curl, 1215 | CURLOPT_USERAGENT, 1216 | 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322)' ); 1217 | //The contents of the "User-Agent: " header to be used in a HTTP request. 1218 | curl_setopt( $curl, CURLOPT_FAILONERROR, true ); 1219 | //To fail silently if the HTTP code returned is greater than or equal to 400. 1220 | curl_setopt( $curl, CURLOPT_FOLLOWLOCATION, true ); 1221 | //To follow any "Location: " header that the server sends as part of the HTTP header. 1222 | curl_setopt( $curl, CURLOPT_AUTOREFERER, true ); 1223 | //To automatically set the Referer: field in requests where it follows a Location: redirect. 1224 | curl_setopt( $curl, CURLOPT_TIMEOUT, 300 ); 1225 | //The maximum number of seconds to allow cURL functions to execute. 1226 | 1227 | //curl_setopt( $curl, CURLOPT_SSL_VERIFYPEER, false ); 1228 | //curl_setopt( $curl, CURLOPT_SSL_VERIFYHOST, false ); 1229 | 1230 | $contents = curl_exec( $curl ); 1231 | curl_close( $curl ); 1232 | 1233 | return $contents; 1234 | } 1235 | 1236 | return file_get_contents( $url ); 1237 | } 1238 | 1239 | public function rrmdir( $dir ) { 1240 | if ( is_dir( $dir ) ) { 1241 | $objects = scandir( $dir ); 1242 | foreach ( $objects as $object ) { 1243 | if ( $object !== '.' && $object !== '..' ) { 1244 | if ( filetype( $dir . '/' . $object ) === 'dir' ) { 1245 | $this->rrmdir( $dir . '/' . $object ); 1246 | } else { 1247 | unlink( $dir . '/' . $object ); 1248 | } 1249 | } 1250 | } 1251 | reset( $objects ); 1252 | rmdir( $dir ); 1253 | } 1254 | 1255 | return true; 1256 | } 1257 | 1258 | private function _check_request_redis() { 1259 | // Check GET for logout-db redis 1260 | if ( isset( $_GET['logout-redis'] ) && $_GET['logout-redis'] === 'true' ) { 1261 | // Flush old session if POST submit 1262 | unset( $_SESSION['credentials-redis'] ); 1263 | 1264 | header( 'Location: ' . $this->get_scheme() . $_SERVER['SERVER_NAME'] . $_SERVER['SCRIPT_NAME'], true ); 1265 | exit(); 1266 | } 1267 | 1268 | // Check POST for redis login 1269 | if ( isset( $_POST['redis-connection'] ) ) { 1270 | // Flush old session if POST submit 1271 | unset( $_SESSION['credentials-redis'], $_SESSION['redis-error-message'] ); 1272 | 1273 | // Cleanup form data 1274 | $this->redis_infos = $this->stripslashes_deep( $_POST['credentials-redis'] ); 1275 | 1276 | // Check remember checkbox 1277 | if ( isset( $_POST['remember'] ) ) { 1278 | $_SESSION['credentials-redis'] = $this->redis_infos; 1279 | } 1280 | } elseif ( isset( $_SESSION['credentials-redis'] ) ) { 1281 | $this->redis_infos = $_SESSION['credentials-redis']; 1282 | } 1283 | 1284 | // Check credentials-redis 1285 | if ( ! empty( $this->redis_infos ) && is_array( $this->redis_infos ) && class_exists( 'Redis' ) ) { 1286 | $host_parts = parse_url( $this->redis_infos['host'] ); 1287 | $host_parts['port'] = ( isset( $host_parts['port'] ) ) ? (int) $host_parts['port'] : 6379; 1288 | 1289 | $redis_args = [ 1290 | 'host' => $host_parts['host'], 1291 | 'port' => $host_parts['port'], 1292 | 'connectTimeout' => 5, 1293 | ]; 1294 | 1295 | if ( ! empty( $this->redis_infos['password'] ) ) { 1296 | $redis_args['auth'] = $this->redis_infos['password']; 1297 | } 1298 | 1299 | try { 1300 | $this->redis_link = new Redis( $redis_args ); 1301 | $this->redis_link->ping(); 1302 | } catch ( RedisException $e ) { 1303 | $_SESSION['redis-error-message'] = $e->getMessage(); 1304 | $this->redis_link = false; 1305 | } 1306 | 1307 | if ( $this->redis_link === false ) { 1308 | unset( $_SESSION['credentials-redis'] ); 1309 | $this->redis_infos = false; 1310 | } 1311 | } 1312 | 1313 | // Check GET for redis variables 1314 | if ( $this->redis_link !== false && isset( $_GET['redis-variables'] ) && $_GET['redis-variables'] === 'true' ) { 1315 | $redis_info = $this->redis_link->info(); 1316 | if ( empty( $redis_info ) ) { 1317 | echo 'No result found, nothing to print so am exiting'; 1318 | exit(); 1319 | } 1320 | 1321 | $this->get_header(); 1322 | echo $this->_variable_to_html( $redis_info ); 1323 | $this->get_footer(); 1324 | exit(); 1325 | } 1326 | } 1327 | 1328 | private function _check_request_database() { 1329 | // Check GET for logout-db database 1330 | if ( isset( $_GET['logout-db'] ) && $_GET['logout-db'] === 'true' ) { 1331 | // Flush old session if POST submit 1332 | unset( $_SESSION['credentials-db'] ); 1333 | 1334 | header( 'Location: ' . $this->get_scheme() . $_SERVER['SERVER_NAME'] . $_SERVER['SCRIPT_NAME'], true ); 1335 | exit(); 1336 | } 1337 | 1338 | // Check POST for database login 1339 | if ( isset( $_POST['database-connection'] ) ) { 1340 | // Flush old session if POST submit 1341 | unset( $_SESSION['credentials-db'] ); 1342 | 1343 | // Cleanup form data 1344 | $this->db_infos = $this->stripslashes_deep( $_POST['credentials-db'] ); 1345 | 1346 | // Check remember checkbox 1347 | if ( isset( $_POST['remember'] ) ) { 1348 | $_SESSION['credentials-db'] = $this->db_infos; 1349 | } 1350 | } elseif ( isset( $_SESSION['credentials-db'] ) ) { 1351 | $this->db_infos = $_SESSION['credentials-db']; 1352 | } 1353 | 1354 | // Check credentials-db 1355 | if ( ! empty( $this->db_infos ) && is_array( $this->db_infos ) && is_callable( 'mysqli_connect' ) ) { 1356 | try { 1357 | $this->db_link = mysqli_connect( $this->db_infos['host'], 1358 | $this->db_infos['user'], 1359 | $this->db_infos['password'] ); 1360 | } catch ( Exception $e ) { 1361 | $error = $e->getMessage(); 1362 | unset( $_SESSION['credentials-db'] ); 1363 | $this->db_infos = false; 1364 | } 1365 | } 1366 | 1367 | // Check GET for databse variables 1368 | if ( $this->db_link !== false && isset( $_GET['database-variables'] ) && $_GET['database-variables'] === 'true' ) { 1369 | $result = mysqli_query( $this->db_link, 'SHOW VARIABLES' ); 1370 | if ( ! $result ) { 1371 | echo "Could not successfully run query ( 'SHOW VARIABLES' ) from DB: " . mysqli_error( $this->db_link ); 1372 | exit(); 1373 | } 1374 | 1375 | if ( mysqli_num_rows( $result ) === 0 ) { 1376 | echo 'No rows found, nothing to print so am exiting'; 1377 | exit(); 1378 | } 1379 | 1380 | $output = array(); 1381 | while ( $row = mysqli_fetch_assoc( $result ) ) { 1382 | $output[ $row['Variable_name'] ] = $row['Value']; 1383 | } 1384 | $this->get_header(); 1385 | echo $this->_variable_to_html( $output ); 1386 | $this->get_footer(); 1387 | exit(); 1388 | } 1389 | } 1390 | 1391 | private function _check_request_adminer() { 1392 | // Check GET for Install Adminer 1393 | if ( isset( $_GET['adminer'] ) && $_GET['adminer'] === 'install' ) { 1394 | $code = $this->file_get_contents_url( 'https://www.adminer.org/latest-mysql-en.php' ); 1395 | if ( ! empty( $code ) ) { 1396 | $result = file_put_contents( __DIR__ . '/adminer.php', $code ); 1397 | if ( $result !== false ) { 1398 | header( 'Location: ' . $this->get_scheme() . $_SERVER['SERVER_NAME'] . '/adminer.php', true ); 1399 | exit(); 1400 | } 1401 | } 1402 | 1403 | die( 'Impossible to download and install Adminer with this script.' ); 1404 | } 1405 | 1406 | // Check GET for Uninstall Adminer 1407 | if ( isset( $_GET['adminer'] ) && $_GET['adminer'] === 'uninstall' ) { 1408 | if ( is_file( __DIR__ . '/adminer.php' ) ) { 1409 | $result = unlink( __DIR__ . '/adminer.php' ); 1410 | if ( $result !== false ) { 1411 | header( 'Location: ' . $this->get_scheme() . $_SERVER['SERVER_NAME'] . $_SERVER['SCRIPT_NAME'], 1412 | true ); 1413 | exit(); 1414 | } 1415 | } 1416 | 1417 | die( 'Impossible remove file and uninstall Adminer with this script.' ); 1418 | } 1419 | } 1420 | 1421 | public function _check_request_wordpress() { 1422 | // Check GET for Install wordpress 1423 | if ( isset( $_GET['wordpress'] ) && $_GET['wordpress'] === 'install' ) { 1424 | if ( ! is_file( __DIR__ . '/latest.zip' ) ) { 1425 | $code = $this->file_get_contents_url( 'https://wordpress.org/latest.zip' ); 1426 | if ( ! empty( $code ) ) { 1427 | $result = file_put_contents( __DIR__ . '/latest.zip', $code ); 1428 | if ( $result === false ) { 1429 | die( 'Impossible to write WordPress archive with this script.' ); 1430 | } 1431 | } else { 1432 | die( 'Impossible to download WordPress with this script. You can also send WordPress Zip archive via FTP and renme it latest.zip, the script will only try to decompress it.' ); 1433 | } 1434 | } 1435 | 1436 | if ( is_file( __DIR__ . '/latest.zip' ) ) { 1437 | $zip = new ZipArchive; 1438 | if ( $zip->open( __DIR__ . '/latest.zip' ) === true ) { 1439 | $zip->extractTo( __DIR__ . '/' ); 1440 | $zip->close(); 1441 | 1442 | unlink( __DIR__ . '/latest.zip' ); 1443 | } else { 1444 | unlink( __DIR__ . '/latest.zip' ); 1445 | die( 'Impossible to unzip WordPress with this script.' ); 1446 | } 1447 | } 1448 | } 1449 | 1450 | // Check GET for Uninstall WordPress 1451 | if ( isset( $_GET['wordpress'] ) && $_GET['wordpress'] === 'uninstall' ) { 1452 | if ( is_dir( __DIR__ . '/wordpress' ) ) { 1453 | $result = $this->rrmdir( __DIR__ . '/wordpress' ); 1454 | if ( $result !== false ) { 1455 | header( 'Location: ' . $this->get_scheme() . $_SERVER['SERVER_NAME'] . $_SERVER['SCRIPT_NAME'], 1456 | true ); 1457 | exit(); 1458 | } 1459 | } 1460 | 1461 | die( 'Impossible remove WordPress folder with this script.' ); 1462 | } 1463 | } 1464 | 1465 | /** 1466 | * Determines if a command exists on the current environment 1467 | * Source: https://stackoverflow.com/questions/12424787/how-to-check-if-a-shell-command-exists-from-php 1468 | * 1469 | * @return bool True if the command has been found ; otherwise, false. 1470 | * 1471 | * @param string $command The command to check 1472 | */ 1473 | private function _command_exists( $command ) { 1474 | if ( ! function_exists( 'proc_open' ) ) { 1475 | return false; 1476 | } 1477 | 1478 | $whereIsCommand = ( PHP_OS === 'WINNT' ) ? 'where' : 'which'; 1479 | 1480 | $process = proc_open( 1481 | "$whereIsCommand $command", 1482 | array( 1483 | 0 => array( 'pipe', 'r' ), //STDIN 1484 | 1 => array( 'pipe', 'w' ), //STDOUT 1485 | 2 => array( 'pipe', 'w' ), //STDERR 1486 | ), 1487 | $pipes 1488 | ); 1489 | 1490 | if ( $process !== false ) { 1491 | $stdout = stream_get_contents( $pipes[1] ); 1492 | //$stderr = stream_get_contents( $pipes[2] ); 1493 | fclose( $pipes[1] ); 1494 | fclose( $pipes[2] ); 1495 | proc_close( $process ); 1496 | 1497 | return $stdout !== ''; 1498 | } 1499 | 1500 | return false; 1501 | } 1502 | 1503 | private function get_scheme() { 1504 | if ( ( ! empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ) || (int) $_SERVER['SERVER_PORT'] === 443 ) { 1505 | return 'https://'; 1506 | } 1507 | 1508 | return 'http://'; 1509 | } 1510 | 1511 | /** 1512 | * @return mixed 1513 | * 1514 | * @param int $port 1515 | * @param string $path 1516 | * @param string $host 1517 | * 1518 | * @see : https://incarnate.github.io/curl-to-php/ 1519 | */ 1520 | private function check_connectivity_http( $host = "api.wordpress.org", $port = 80, $path = '/' ) { 1521 | if ( ! function_exists( 'curl_init' ) ) { 1522 | return false; 1523 | } 1524 | 1525 | $scheme = ( $port === 80 ) ? 'http://' : 'https://'; 1526 | 1527 | $ch = curl_init(); 1528 | curl_setopt( $ch, CURLOPT_URL, $scheme . $host . $path ); 1529 | curl_setopt( $ch, CURLOPT_FILETIME, true ); 1530 | curl_setopt( $ch, CURLOPT_NOBODY, true ); 1531 | curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true ); 1532 | curl_setopt( $ch, CURLOPT_HEADER, true ); 1533 | 1534 | $result = curl_exec( $ch ); 1535 | //$info = curl_getinfo( $ch ); 1536 | 1537 | if ( curl_errno( $ch ) ) { 1538 | //$result = 'Error:' . curl_error( $ch ); 1539 | return false; 1540 | } 1541 | 1542 | $http_code = curl_getinfo( $ch, CURLINFO_HTTP_CODE ); 1543 | 1544 | return ! ( $http_code > 204 ); 1545 | } 1546 | 1547 | /** 1548 | * @return bool 1549 | * 1550 | * @param int $port 1551 | * @param string $host 1552 | * 1553 | * @see : https://www.linuxquestions.org/questions/linux-server-73/ssh-connections-with-php-926003/ 1554 | */ 1555 | private function check_connectivity_ssh( $host = "github.com", $port = 22 ) { 1556 | try { 1557 | $fp = fsockopen( $host, $port, $errno, $errstr, 5 ); 1558 | if ( ! $fp ) { 1559 | return false; 1560 | } 1561 | 1562 | fclose( $fp ); 1563 | 1564 | return true; 1565 | } catch ( Exception $e ) { 1566 | return false; 1567 | } 1568 | } 1569 | } 1570 | 1571 | // Init render 1572 | phpwpinfo(); 1573 | --------------------------------------------------------------------------------