From 4eb2e06b97e84687053807929b9bc5376662aa4b Mon Sep 17 00:00:00 2001 From: Kaspars Dambis Date: Mon, 12 May 2025 11:35:36 +0300 Subject: [PATCH 1/9] Implement sorting by parent and menu_order --- parser.php | 30 ++++++++++++++++++--- tools/parser/class-wp-api-client.php | 40 ++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/parser.php b/parser.php index a4dadef..4546149 100644 --- a/parser.php +++ b/parser.php @@ -55,6 +55,10 @@ $content_md = []; $toc = []; + // Collect for sorting. + $post_parents = []; + $post_order = []; + if ( isset( $cli_options['update'] ) ) { $json_files = $wp_developer->get_from_rest_api( $content_type ); } else { @@ -63,10 +67,17 @@ foreach ( $json_files as $json_file ) { $json = $wp_developer->get_json( $json_file ); - $toc[] = sprintf( '- [%s](#%s)', $json['title']['rendered'], $docs->get_anchor_id_for_url( $json['link'] ) ); - $content_md[] = $docs->get_post_json_as_markdown( $json ); + + $post_parents[ $json['id'] ] = $json['parent'] ?? 0; + $post_order[ $json['id'] ] = $json['menu_order'] ?? 0; + + $toc[ $json['id'] ] = sprintf( '- [%s](#%s)', $json['title']['rendered'], $docs->get_anchor_id_for_url( $json['link'] ) ); + $content_md[ $json['id'] ] = $docs->get_post_json_as_markdown( $json ); } + $toc = $wp_developer->sort_posts( $toc, $post_parents, $post_order ); + $content_md = $wp_developer->sort_posts( $content_md, $post_parents, $post_order ); + $handbooks_md[ $content_type ] = implode( "\n\n", [ @@ -126,12 +137,23 @@ $json_files = $wp_org->get_json_files_for_content_types( [ $content_type ] ); } + // Collect for sorting. + $post_parents = []; + $post_order = []; + foreach ( $json_files as $json_file ) { $json = $wp_org->get_json( $json_file ); - $toc[] = sprintf( '- [%s](#%s)', $json['title']['rendered'], $docs->get_anchor_id_for_url( $json['link'] ) ); - $content_md[] = $docs->get_post_json_as_markdown( $json ); + + $post_parents[ $json['id'] ] = $json['parent'] ?? 0; + $post_order[ $json['id'] ] = $json['menu_order'] ?? 0; + + $toc[ $json['id'] ] = sprintf( '- [%s](#%s)', $json['title']['rendered'], $docs->get_anchor_id_for_url( $json['link'] ) ); + $content_md[ $json['id'] ] = $docs->get_post_json_as_markdown( $json ); } + $toc = $wp_org->sort_posts( $toc, $post_parents, $post_order ); + $content_md = $wp_org->sort_posts( $content_md, $post_parents, $post_order ); + $content_md = implode( "\n\n", [ diff --git a/tools/parser/class-wp-api-client.php b/tools/parser/class-wp-api-client.php index 8c05567..c632689 100644 --- a/tools/parser/class-wp-api-client.php +++ b/tools/parser/class-wp-api-client.php @@ -426,4 +426,44 @@ public function get_url_status( array $urls ): array { return $responses; } + + public function sort_posts( array $posts_by_id, array $parent_ids, array $post_order ): array { + $sort_fn = function( $a_id, $b_id ) use ( $parent_ids, $post_order ) { + $a_parent = $parent_ids[ $a_id ] ?? 0; + $b_parent = $parent_ids[ $b_id ] ?? 0; + + // If one is a parent (0) and other isn't, parent goes first. + if ( $a_parent === 0 && $b_parent !== 0 ) { + return -1; + } elseif ( $a_parent !== 0 && $b_parent === 0 ) { + return 1; + } + + // If they have different parents, sort by parent. + if ( $a_parent !== $b_parent ) { + // Check if either is child of the other. + if ( $a_parent === $b_id ) { + return 1; // B is parent of A. + } elseif ( $b_parent === $a_id ) { + return -1; // A is parent of B. + } + + // Sort by parent weights + $a_parent_weight = $post_order[ $a_parent ] ?? 0; + $b_parent_weight = $post_order[ $b_parent ] ?? 0; + + return $a_parent_weight <=> $b_parent_weight; + } + + // If same parent (including both being top-level), sort by weights + $a_weight = $post_order[ $a_id ] ?? 0; + $b_weight = $post_order[ $b_id ] ?? 0; + + return $a_weight <=> $b_weight; + }; + + uksort( $posts_by_id, $sort_fn ); + + return $posts_by_id; + } } \ No newline at end of file From bd02a5322656a645abc0cc1e05e23651bb89c53f Mon Sep 17 00:00:00 2001 From: Kaspars Dambis Date: Mon, 12 May 2025 12:51:13 +0300 Subject: [PATCH 2/9] Add phpunit tests --- .gitignore | 3 +- composer.json | 6 + composer.lock | 1754 ++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 1760 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 42cd73d..19f18f7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -/vendor/ \ No newline at end of file +/vendor/ +.phpunit.result.cache \ No newline at end of file diff --git a/composer.json b/composer.json index d08a8e0..d25ca06 100644 --- a/composer.json +++ b/composer.json @@ -6,5 +6,11 @@ }, "require": { "league/html-to-markdown": "^5.1" + }, + "require-dev": { + "phpunit/phpunit": "^9" + }, + "scripts": { + "test": "phpunit" } } diff --git a/composer.lock b/composer.lock index f791267..fff4d7b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "55d22588d74c4cd2af8b00fa004ab06f", + "content-hash": "00fbfadc31c3b1484511beba960ff9c6", "packages": [ { "name": "league/html-to-markdown", @@ -96,7 +96,1757 @@ "time": "2023-07-12T21:21:09+00:00" } ], - "packages-dev": [], + "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:23:10+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.13.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c", + "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-04-29T12:36:36+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "447a020a1f875a434d62f2a401f53b82a396e494" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", + "reference": "447a020a1f875a434d62f2a401f53b82a396e494", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" + }, + "time": "2024-12-30T11:07:19+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.32", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.19.1 || ^5.1.0", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-text-template": "^2.0.4", + "sebastian/code-unit-reverse-lookup": "^2.0.3", + "sebastian/complexity": "^2.0.3", + "sebastian/environment": "^5.1.5", + "sebastian/lines-of-code": "^1.0.4", + "sebastian/version": "^3.0.2", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.6" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "9.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-22T04:23:01+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.6.23", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", + "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.5.0 || ^2", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.1", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=7.3", + "phpunit/php-code-coverage": "^9.2.32", + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.4", + "phpunit/php-timer": "^5.0.3", + "sebastian/cli-parser": "^1.0.2", + "sebastian/code-unit": "^1.0.8", + "sebastian/comparator": "^4.0.8", + "sebastian/diff": "^4.0.6", + "sebastian/environment": "^5.1.5", + "sebastian/exporter": "^4.0.6", + "sebastian/global-state": "^5.0.7", + "sebastian/object-enumerator": "^4.0.4", + "sebastian/resource-operations": "^3.0.4", + "sebastian/type": "^3.2.1", + "sebastian/version": "^3.0.2" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.6-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.23" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2025-05-02T06:40:34+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:27:43+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:19:30+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:30:58+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:03:51+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:33:00+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.7", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:35:11+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:20:34+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:07:39+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-14T16:00:52+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" + } + ], "aliases": [], "minimum-stability": "stable", "stability-flags": {}, From b380e7bd1074541db280706f873a84e0c61be2d7 Mon Sep 17 00:00:00 2001 From: Kaspars Dambis Date: Mon, 12 May 2025 12:51:27 +0300 Subject: [PATCH 3/9] Document version reasons --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 1d5e7e9..689ad1f 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,10 @@ Run `php parser.php --update` to fetch updated content over WP REST API (sorted - [ ] Consider including the `wp-parser-(function|class|hook|method)` content types. +## Notes + +- We're using PHPUnit 9 because test case filenames must match the class names [in later versions](https://github.com/sebastianbergmann/phpunit/issues/4621) (don't support `class-` prefixed used by WP). + ## Credits - Tooling created by [Kaspars](https://kaspars.net) ([@konstruktors](https://x.com/konstruktors)) and [contributors](https://github.com/kasparsd/wp-docs-md/graphs/contributors). From 3afa6ed5aed91b195e1e1393380d0fbdabf6347b Mon Sep 17 00:00:00 2001 From: Kaspars Dambis Date: Mon, 12 May 2025 13:25:05 +0300 Subject: [PATCH 4/9] Fix sorting algo --- phpunit.xml | 15 ++++ .../tools/parser/class-wp-api-client-test.php | 48 +++++++++++ tools/parser/class-wp-api-client.php | 84 ++++++++++++------- 3 files changed, 118 insertions(+), 29 deletions(-) create mode 100644 phpunit.xml create mode 100644 tests/tools/parser/class-wp-api-client-test.php diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..996d515 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,15 @@ + + + + + tests + + + + + tools/parser + + + \ No newline at end of file diff --git a/tests/tools/parser/class-wp-api-client-test.php b/tests/tools/parser/class-wp-api-client-test.php new file mode 100644 index 0000000..1761f19 --- /dev/null +++ b/tests/tools/parser/class-wp-api-client-test.php @@ -0,0 +1,48 @@ + 7, + 'top1' => 4, + '- top1-child1' => 2, + '-- top1-child1-child1' => 6, + '-- top1-child1-child2' => 1, + 'top2' => 5, + '- top2-child1' => 3, + '- top2-child2' => 0, + ]; + + $post_parents = [ + 7 => 0, + 4 => 0, + 2 => 4, + 6 => 2, + 1 => 2, + 5 => 0, + 3 => 5, + 0 => 5, + ]; + + $post_order = [ + 7 => 0, + 4 => 5, + 1 => 10, + 5 => 20, + 3 => -1, + ]; + + $sorted_posts = $client->sort_posts( array_flip( $posts ), $post_parents, $post_order ); + + $this->assertEquals( + array_keys( $posts ), + $sorted_posts + ); + } +} \ No newline at end of file diff --git a/tools/parser/class-wp-api-client.php b/tools/parser/class-wp-api-client.php index c632689..9bc173e 100644 --- a/tools/parser/class-wp-api-client.php +++ b/tools/parser/class-wp-api-client.php @@ -428,42 +428,68 @@ public function get_url_status( array $urls ): array { } public function sort_posts( array $posts_by_id, array $parent_ids, array $post_order ): array { - $sort_fn = function( $a_id, $b_id ) use ( $parent_ids, $post_order ) { - $a_parent = $parent_ids[ $a_id ] ?? 0; - $b_parent = $parent_ids[ $b_id ] ?? 0; + // Create a comparison function for sorting posts by weight + $compare_by_weight = function( $a_id, $b_id ) use ( $post_order ) { + $a_weight = $post_order[ $a_id ] ?? 0; + $b_weight = $post_order[ $b_id ] ?? 0; + + return $a_weight <=> $b_weight; + }; + + // Group posts into top-level and children collections + $top_level_posts = []; + $child_posts_by_parent = []; + + foreach ( array_keys( $posts_by_id ) as $post_id ) { + $parent_id = $parent_ids[ $post_id ] ?? 0; - // If one is a parent (0) and other isn't, parent goes first. - if ( $a_parent === 0 && $b_parent !== 0 ) { - return -1; - } elseif ( $a_parent !== 0 && $b_parent === 0 ) { - return 1; + if ( $parent_id === 0 ) { + $top_level_posts[] = $post_id; + } else { + $child_posts_by_parent[ $parent_id ][] = $post_id; } + } + + // Sort top-level posts by weight + usort( $top_level_posts, $compare_by_weight ); + + // Helper function to recursively build the sorted list + $collect_posts_with_children = function( $post_id ) use ( + &$collect_posts_with_children, + &$child_posts_by_parent, + $compare_by_weight, + $posts_by_id + ) { + // Start with the current post + $collected_posts = [ $posts_by_id[ $post_id ] ]; - // If they have different parents, sort by parent. - if ( $a_parent !== $b_parent ) { - // Check if either is child of the other. - if ( $a_parent === $b_id ) { - return 1; // B is parent of A. - } elseif ( $b_parent === $a_id ) { - return -1; // A is parent of B. - } + // Add all children (if any) in the correct order + if ( isset( $child_posts_by_parent[ $post_id ] ) ) { + // Sort children by weight + $children = $child_posts_by_parent[ $post_id ]; + usort( $children, $compare_by_weight ); - // Sort by parent weights - $a_parent_weight = $post_order[ $a_parent ] ?? 0; - $b_parent_weight = $post_order[ $b_parent ] ?? 0; - - return $a_parent_weight <=> $b_parent_weight; + // Add each child and its descendants + foreach ( $children as $child_id ) { + $collected_posts = array_merge( + $collected_posts, + $collect_posts_with_children( $child_id ) + ); + } } - // If same parent (including both being top-level), sort by weights - $a_weight = $post_order[ $a_id ] ?? 0; - $b_weight = $post_order[ $b_id ] ?? 0; - - return $a_weight <=> $b_weight; + return $collected_posts; }; + + // Build the final sorted list + $sorted_posts = []; + foreach ( $top_level_posts as $post_id ) { + $sorted_posts = array_merge( + $sorted_posts, + $collect_posts_with_children( $post_id ) + ); + } - uksort( $posts_by_id, $sort_fn ); - - return $posts_by_id; + return $sorted_posts; } } \ No newline at end of file From c9c97789c052f8b0fb6a891e3560ee38b93dd1d9 Mon Sep 17 00:00:00 2001 From: Kaspars Dambis Date: Mon, 12 May 2025 13:27:04 +0300 Subject: [PATCH 5/9] Keep them ordered --- docs/wp-adv-admin-handbook.md | 10888 +- docs/wp-apis-handbook.md | 9668 +- docs/wp-articles.md | 2780 +- docs/wp-blocks-handbook.md | 98946 +++++++++--------- docs/wp-handbooks.md | 173559 +++++++++++++++---------------- docs/wp-plugin-handbook.md | 14358 +-- docs/wp-rest-api-handbook.md | 11528 +- docs/wp-scf-handbook.md | 11630 +-- docs/wp-theme-handbook.md | 26363 +++-- docs/wp-wpcs-handbook.md | 470 +- 10 files changed, 180042 insertions(+), 180148 deletions(-) diff --git a/docs/wp-adv-admin-handbook.md b/docs/wp-adv-admin-handbook.md index e9c79f3..643f5aa 100644 --- a/docs/wp-adv-admin-handbook.md +++ b/docs/wp-adv-admin-handbook.md @@ -15,11 +15,13 @@ Table of Contents: - [Configuring Wildcard Subdomains](#advanced-administration/server/subdomains-wildcard) - [Emptying a Database Table](#advanced-administration/server/empty-database) - [Web servers](#advanced-administration/server/web-server) +- [Apache HTTPD / .htaccess](#advanced-administration/server/web-server/httpd) - [Nginx](#advanced-administration/server/web-server/nginx) - [Control Panels](#advanced-administration/server/control-panel) - [WordPress configuration](#advanced-administration/wordpress) - [Editing wp-config.php](#advanced-administration/wordpress/wp-config) - [Site Architecture (v1.5)](#advanced-administration/wordpress/site-architecture) +- [Post Formats](#advanced-administration/wordpress/post-formats) - [Cookies](#advanced-administration/wordpress/cookies) - [Update Services](#advanced-administration/wordpress/update-services) - [Editing Files](#advanced-administration/wordpress/edit-files) @@ -28,6 +30,7 @@ Table of Contents: - [Multilingual WordPress](#advanced-administration/wordpress/multilingual) - [oEmbed](#advanced-administration/wordpress/oembed) - [Loopbacks](#advanced-administration/wordpress/loopback) +- [Importing Content](#advanced-administration/wordpress/import) - [Common WordPress errors](#advanced-administration/wordpress/common-errors) - [Upgrading / Migration](#advanced-administration/upgrade) - [Updating WordPress using FTP](#advanced-administration/upgrade/ftp) @@ -41,38 +44,35 @@ Table of Contents: - [WordPress Multisite Domain Mapping](#advanced-administration/multisite/domain-mapping) - [Multisite Network Administration](#advanced-administration/multisite/administration) - [Network Admin](#advanced-administration/multisite/admin) +- [Network Admin Settings Screen](#advanced-administration/multisite/admin/settings) - [Migrate WordPress sites into WordPress Multisite](#advanced-administration/multisite/sites-multisite) - [Plugins](#advanced-administration/plugins) +- [File Editor Screen](#advanced-administration/plugins/editor-screen) - [Must Use Plugins](#advanced-administration/plugins/mu-plugins) - [Themes](#advanced-administration/themes) - [Security](#advanced-administration/security) - [Your password](#advanced-administration/security/logging-in) - [Two Step Authentication](#advanced-administration/security/mfa) - [Backups](#advanced-administration/security/backup) +- [Backing Up Your Database](#advanced-administration/security/backup/database) +- [Backing Up Your WordPress Files](#advanced-administration/security/backup/files) - [HTTPS](#advanced-administration/security/https) - [Brute Force Attacks](#advanced-administration/security/brute-force) - [Hardening WordPress](#advanced-administration/security/hardening) +- [Display Errors](#advanced-administration/security/hardening/display-errors) +- [Monitoring](#advanced-administration/security/monitoring) - [Performance / Optimization](#advanced-administration/performance) - [Cache](#advanced-administration/performance/cache) - [Optimization](#advanced-administration/performance/optimization) +- [PHP Optimization](#advanced-administration/performance/php) - [Debugging WordPress](#advanced-administration/debug) - [Debugging in WordPress](#advanced-administration/debug/debug-wordpress) - [Debugging a WordPress Network](#advanced-administration/debug/debug-network) - [Using Your Browser to Diagnose JavaScript Errors](#advanced-administration/debug/debug-javascript) - [Test Driving WordPress](#advanced-administration/debug/test-driving) +- [Version Control](#advanced-administration/debug/version-control) - [Resources for Building on WordPress](#advanced-administration/resources) -- [File Editor Screen](#advanced-administration/plugins/editor-screen) -- [Apache HTTPD / .htaccess](#advanced-administration/server/web-server/httpd) -- [Post Formats](#advanced-administration/wordpress/post-formats) -- [Importing Content](#advanced-administration/wordpress/import) -- [Network Admin Settings Screen](#advanced-administration/multisite/admin/settings) -- [Backing Up Your Database](#advanced-administration/security/backup/database) -- [Backing Up Your WordPress Files](#advanced-administration/security/backup/files) - [FAQ Troubleshooting](#advanced-administration/resources/faq) -- [Version Control](#advanced-administration/debug/version-control) -- [Monitoring](#advanced-administration/security/monitoring) -- [PHP Optimization](#advanced-administration/performance/php) -- [Display Errors](#advanced-administration/security/hardening/display-errors) # Advanced Administration Handbook @@ -1398,8918 +1398,9084 @@ See [Nginx](#advanced-administration/server/web-server/nginx). --- -# Nginx - -Source: https://developer.wordpress.org/advanced-administration/server/web-server/nginx/ - -While the LAMP stack (Linux + Apache + MySQL + PHP) is very popular for powering WordPress, it is also possible to use Nginx. WordPress supports Nginx, and some large WordPress sites, such as WordPress.com, are powered by Nginx. +# Apache HTTPD / .htaccess -When talking about Nginx, it is important to know that there are multiple ways to implement Nginx. It can be setup as a reverse-proxy in front of Apache, which is a very powerful setup that allows you to use all of the features and power of Apache, while benefiting from the speed of Nginx. Most websites that report using Nginx as the server (based on stats gathered from HTTP response headers), are actually Apache running with Nginx as the reverse proxy. (The HTTP response headers showing “Nginx” are being reported by the reverse-proxy, not the server itself.) +Source: https://developer.wordpress.org/advanced-administration/server/web-server/httpd/ -**This guide is referring to a standalone Nginx setup, where it is used as the primary server instead of Apache.** It should be noted that Nginx is not a completely interchangeable substitute for Apache. There are a few key differences affecting WordPress implementation that you need to be aware of before you proceed: +## .htaccess -- With Nginx there is no directory-level configuration file like Apache’s .htaccess or IIS’s web.config files. All configuration has to be done at the server level by an administrator, and WordPress cannot modify the configuration, like it can with Apache or IIS. -- Pretty Permalinks functionality is slightly different when running Nginx. -- Since Nginx does not have .htaccess-type capability and WordPress cannot automatically modify the server configuration for you, it cannot generate the rewrite rules for you. -- Without modifications to your install, “index.php” will be added to your Permalinks. (There are ways to mitigate this with plugins (see below) and/or adding custom code to your child theme’s functions.php.) -- However, if you do want to have some (limited) .htaccess capability, it is technically possible to do add by installing the [htscanner PECL extension for PHP](https://www.php.net/manual/en/book.htscanner.php). (However, this is not a perfect solution so be sure to test and debug thoroughly before using on a live site.) +The `.htaccess` is a distributed configuration file, and is how Apache handles configuration changes on a per-directory basis. -This guide is not going to cover how to install and configure Nginx, so this assumes that you have already installed Nginx and have a basic understanding of how to work with and debug it. +WordPress uses this file to manipulate how Apache serves files from its root directory, and subdirectories thereof. Most notably, WP modifies this file to be able to handle pretty permalinks. -## Generic and Multi-Site Support +This page may be used to restore a corrupted `.htaccess` file (e.g. a misbehaving plugin). -To make WordPress work with Nginx you have to configure the backend php-cgi. The options available are `fastcgi` or `php-fpm`. Here, php-fpm is being used because it is included with PHP 5.3+, so installing it is straight forward. +### Basic WP -The Nginx configuration has been broken up into five distinct files and is heavily commented to make each option easier to understand. The [author](https://wordpress.org/support/users/bigsite/) also made a best-effort attempting to follow “best practices” for nginx configurations. +``` +# BEGIN WordPress -### Main (generic) startup file +RewriteEngine On +RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] +RewriteBase / +RewriteRule ^index\.php$ - [L] +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule . /index.php [L] -This is equivalent to /etc/nginx/nginx.conf (or /etc/nginx/conf/nginx.conf if you’re using Arch Linux). +# END WordPress ``` -# Generic startup file. -user {user} {group}; -#usually equal to number of CPUs you have. run command "grep processor /proc/cpuinfo | wc -l" to find it -worker_processes auto; -worker_cpu_affinity auto; +### Multisite -error_log /var/log/nginx/error.log; -pid /var/run/nginx.pid; +#### WordPress 3.5 and up -# Keeps the logs free of messages about not being able to bind(). -#daemon off; +If you activated Multisite on WordPress 3.5 or later, use one of these. -events { - worker_connections 1024; -} +##### WordPress >=3.5 Subfolder Example -http { - #rewrite_log on; +``` +# BEGIN WordPress Multisite +# Using subfolder network type: https://wordpress.org/documentation/article/htaccess/#multisite - include mime.types; - default_type application/octet-stream; - access_log /var/log/nginx/access.log; - sendfile on; - #tcp_nopush on; - keepalive_timeout 3; - #tcp_nodelay on; - #gzip on; - #php max upload limit cannot be larger than this - client_max_body_size 13m; - index index.php index.html index.htm; +RewriteEngine On +RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] +RewriteBase / +RewriteRule ^index\.php$ - [L] - # Upstream to abstract backend connection(s) for PHP. - upstream php { - #this should match value of "listen" directive in php-fpm pool - server unix:/tmp/php-fpm.sock; - # server 127.0.0.1:9000; - } +# add a trailing slash to /wp-admin +RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L] - include sites-enabled/*; -} +RewriteCond %{REQUEST_FILENAME} -f [OR] +RewriteCond %{REQUEST_FILENAME} -d +RewriteRule ^ - [L] +RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L] +RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L] +RewriteRule . index.php [L] + +# END WordPress Multisite ``` -### Per Site configuration +##### WordPress >=3.5 SubDomain Example ``` -# Redirect everything to the main site. We use a separate server statement and NOT an if statement - see https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/ +# BEGIN WordPress Multisite +# Using subdomain network type: https://wordpress.org/documentation/article/htaccess/#multisite -server { - server_name _; - return 302 $scheme://example.com$request_uri; -} +RewriteEngine On +RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] +RewriteBase / +RewriteRule ^index\.php$ - [L] -server { - server_name example.com; - root /var/www/example.com; +# add a trailing slash to /wp-admin +RewriteRule ^wp-admin$ wp-admin/ [R=301,L] - index index.php; +RewriteCond %{REQUEST_FILENAME} -f [OR] +RewriteCond %{REQUEST_FILENAME} -d +RewriteRule ^ - [L] +RewriteRule ^(wp-(content|admin|includes).*) $1 [L] +RewriteRule ^(.*\.php)$ $1 [L] +RewriteRule . index.php [L] - include global/restrictions.conf; +# END WordPress Multisite - # Additional rules go here. +``` - # Only include one of the files below. - include global/wordpress.conf; - # include global/wordpress-ms-subdir.conf; - # include global/wordpress-ms-subdomain.conf; -} +#### WordPress 3.4 and below -``` +If you originally installed WordPress with 3.4 or older and activated Multisite then, you need to use one of these: -Splitting sections of the configuration into multiple files allows the same logic to be reused over and over. A ‘global’ subdirectory is used to add extra rules for general purpose use (either /etc/nginx/conf/global/ or /etc/nginx/global/ depending on how your nginx install is set up). +##### WordPress <=3.4 SubFolder Example -### Global restrictions file +WordPress 3.0 through 3.4.2 ``` -# Global restrictions configuration file. -# Designed to be included in any server {} block. -location = /favicon.ico { - log_not_found off; - access_log off; -} - -location = /robots.txt { - allow all; - log_not_found off; - access_log off; -} +# BEGIN WordPress Multisite +# Using subfolder network type: https://wordpress.org/documentation/article/htaccess/#multisite -# Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac). -# Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban) -location ~ /\. { - deny all; -} +RewriteEngine On +RewriteBase / +RewriteRule ^index\.php$ - [L] -# Deny access to any files with a .php extension in the uploads directory -# Works in sub-directory installs and also in multisite network -# Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban) -location ~* /(?:uploads|files)/.*\.php$ { - deny all; -} +# uploaded files +RewriteRule ^([_0-9a-zA-Z-]+/)?files/(.+) wp-includes/ms-files.php?file=$2 [L] -``` +# add a trailing slash to /wp-admin +RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L] -### General WordPress rules +RewriteCond %{REQUEST_FILENAME} -f [OR] +RewriteCond %{REQUEST_FILENAME} -d +RewriteRule ^ - [L] +RewriteRule ^[_0-9a-zA-Z-]+/(wp-(content|admin|includes).*) $1 [L] +RewriteRule ^[_0-9a-zA-Z-]+/(.*\.php)$ $1 [L] +RewriteRule . index.php [L] -For single site installations, here is the `global/wordpress.conf` file: +# END WordPress Multisite ``` -# WordPress single site rules. -# Designed to be included in any server {} block. -# Upstream to abstract backend connection(s) for php -upstream php { - server unix:/tmp/php-cgi.socket; - server 127.0.0.1:9000; -} -server { - ## Your website name goes here. - server_name domain.tld; - ## Your only path reference. - root /var/www/wordpress; - ## This should be in your http block and if it is, it's not needed here. - index index.php; +##### WordPress <=3.4 SubDomain Example - location = /favicon.ico { - log_not_found off; - access_log off; - } +``` +# BEGIN WordPress Multisite +# Using subdomain network type: https://wordpress.org/documentation/article/htaccess/#multisite - location = /robots.txt { - allow all; - log_not_found off; - access_log off; - } +RewriteEngine On +RewriteBase / +RewriteRule ^index\.php$ - [L] - location / { - # This is cool because no php is touched for static content. - # include the "?$args" part so non-default permalinks doesn't break when using query string - try_files $uri $uri/ /index.php?$args; - } +# uploaded files +RewriteRule ^files/(.+) wp-includes/ms-files.php?file=$1 [L] - location ~ \.php$ { - #NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini - include fastcgi.conf; - fastcgi_intercept_errors on; - fastcgi_pass php; - } +RewriteCond %{REQUEST_FILENAME} -f [OR] +RewriteCond %{REQUEST_FILENAME} -d +RewriteRule ^ - [L] +RewriteRule . index.php [L] - location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ { - expires max; - log_not_found off; - } -} +# END WordPress Multisite ``` -This is more up-to-date example for Nginx: https://www.nginx.com/resources/wiki/start/topics/recipes/wordpress/ +### General Examples -### WordPress Multisite +#### Options -For multisite installations, use one of the below sections for the `global/wordpress.conf` file, depending on the version of WordPress that was in use when multisite was *activated*, as well as the domain/subdirectory configuration. +Any options preceded by a **+** are added to the options currently in force, and any options preceded by a **–** are removed from the options currently in force. -#### WordPress 3.5 and up +Possible values for the [Options directive](https://httpd.apache.org/docs/trunk/mod/core.html#options) are any combination of: -If you activated Multisite on WordPress 3.5 or later, use one of these. +**None** -##### WordPress 3.5 and up Subdirectory Examples +All options are turned off. -``` -# WordPress multisite subdirectory config file for WP 3.5 and up. -server { - server_name example.com ; +**All** - root /var/www/example.com/htdocs; - index index.php; +All options except for MultiViews. This is the default setting. - if (!-e $request_filename) { - rewrite /wp-admin$ $scheme://$host$request_uri/ permanent; - rewrite ^(/[^/]+)?(/wp-.*) $2 last; - rewrite ^(/[^/]+)?(/.*\.php) $2 last; - } +**ExecCGI** - location / { - try_files $uri $uri/ /index.php?$args ; - } +Execution of CGI scripts using mod\_cgi is permitted. - location ~ \.php$ { - try_files $uri =404; - include fastcgi_params; - fastcgi_pass php; - } +**FollowSymLinks** - #add some rules for static content expiry-headers here -} +The server will follow symbolic links in this directory. -``` +**Includes** -##### WordPress 3.5 and up Subdomains Examples +Server-side includes provided by mod\_include are permitted. -``` -# WordPress multisite subdomain config file for WP 3.5 and up. -server { - server_name example.com *.example.com ; +**IncludesNOEXEC** - root /var/www/example.com/htdocs; - index index.php; +Server-side includes are permitted, but the #exec cmd and #exec cgi are disabled. - location / { - try_files $uri $uri/ /index.php?$args ; - } +**Indexes** - location ~ \.php$ { - try_files $uri =404; - include fastcgi_params; - fastcgi_pass php; - } +URL maps to a directory, and no DirectoryIndex, a formatted listing of the directory. - #add some rules for static content expiry-headers here -} +**MultiViews** + +Content negotiated “MultiViews” are allowed using mod\_negotiation. + +**SymLinksIfOwnerMatch** + +Only follow symbolic links where target is owned by the same user id as the link. + +This will disable all options, and then only enable FollowSymLinks, which is necessary for mod\_rewrite. + +``` +Options None +Options FollowSymLinks ``` -#### WordPress 3.4 and below +#### DirectoryIndex -If you originally activated Multisite with WordPress with 3.4 or older, you need to use one of these: +[DirectoryIndex Directive](https://httpd.apache.org/docs/trunk/mod/mod_dir.html#directoryindex) sets the file that Apache will serve if a directory is requested. -##### WordPress <=3.4 Subdirectory Examples +Several URLs may be given, in which case the server will return the first one that it finds. ``` -# WordPress multisite subdirectory config file for WP 3.4 and below. +DirectoryIndex index.php index.html /index.php -map $uri $blogname{ - ~^(?P/[^/]+/)files/(.*) $blogpath ; -} +``` -map $blogname $blogid{ - default -999; +#### DefaultLanguage - #Ref: https://wordpress.org/extend/plugins/nginx-helper/ - #include /var/www/wordpress/wp-content/plugins/nginx-helper/map.conf ; -} +[DefaultLanguage Directive](https://httpd.apache.org/docs/trunk/mod/mod_mime.html#defaultlanguage) will cause all files that do not already have a specific language tag associated with it will use this. -server { - server_name example.com ; +``` +DefaultLanguage en - root /var/www/example.com/htdocs; - index index.php; +``` - location ~ ^(/[^/]+/)?files/(.+) { - try_files /wp-content/blogs.dir/$blogid/files/$2 /wp-includes/ms-files.php?file=$2 ; - access_log off; log_not_found off; expires max; - } +#### Default Charset - #avoid php readfile() - location ^~ /blogs.dir { - internal; - alias /var/www/example.com/htdocs/wp-content/blogs.dir ; - access_log off; log_not_found off; expires max; - } +Set the default character encoding sent in the HTTP header. See [Setting charset information in .htaccess](https://www.w3.org/International/questions/qa-htaccess-charset) - if (!-e $request_filename) { - rewrite /wp-admin$ $scheme://$host$request_uri/ permanent; - rewrite ^(/[^/]+)?(/wp-.*) $2 last; - rewrite ^(/[^/]+)?(/.*\.php) $2 last; - } +``` +AddDefaultCharset UTF-8 - location / { - try_files $uri $uri/ /index.php?$args ; - } +``` - location ~ \.php$ { - try_files $uri =404; - include fastcgi_params; - fastcgi_pass php; - } +**Set Charset for Specific Files** - #add some rules for static content expiry-headers here -} +``` +AddType 'text/html; charset=UTF-8' .html ``` -NGINX provides 2 special directive: X-Accel-Redirect and map. Using these 2 directives, one can eliminate performance hit for static-file serving on WordPress multisite network. +**Set for specific files** -##### WordPress <=3.4 Subdomains Examples +``` +AddCharset UTF-8 .html ``` -# WordPress multisite subdomain config file for WP 3.4 and below. -map $http_host $blogid { - default -999; - #Ref: https://wordpress.org/extend/plugins/nginx-helper/ - #include /var/www/wordpress/wp-content/plugins/nginx-helper/map.conf ; +#### ServerSignature -} +The [ServerSignature Directive](https://httpd.apache.org/docs/trunk/mod/core.html#serversignature) allows the configuration of a trailing footer line under server-generated documents. Optionally add a line containing the server version and virtual host name to server-generated pages (internal error documents, FTP directory listings, mod\_status and mod\_info output etc., but not CGI generated documents or custom error documents). -server { - server_name example.com *.example.com ; +**On** - root /var/www/example.com/htdocs; - index index.php; +adds a line with the server version number and ServerName of the serving virtual host - location / { - try_files $uri $uri/ /index.php?$args ; - } +**Off** - location ~ \.php$ { - try_files $uri =404; - include fastcgi_params; - fastcgi_pass php; - } +suppresses the footer line - #WPMU Files - location ~ ^/files/(.*)$ { - try_files /wp-content/blogs.dir/$blogid/$uri /wp-includes/ms-files.php?file=$1 ; - access_log off; log_not_found off; expires max; - } +**Email** - #WPMU x-sendfile to avoid php readfile() - location ^~ /blogs.dir { - internal; - alias /var/www/example.com/htdocs/wp-content/blogs.dir; - access_log off; log_not_found off; expires max; - } +creates a “mailto:” reference to the ServerAdmin of the referenced document - #add some rules for static content expiry-headers here -} +``` +SetEnv SERVER_ADMIN admin@site.com +ServerSignature Email ``` -Ref: https://www.nginx.com/resources/wiki/start/topics/recipes/wordpress/ +#### Force Files to be Downloaded -### HTTPS in Nginx +The below will cause any requests for files ending in the specified extensions to not be displayed in the browser but instead force a “Save As” dialog so the client can download. -Enabling HTTPS in Nginx is relatively simple. +``` +AddType application/octet-stream .avi .mpg .mov .pdf .xls .mp4 ``` -server { - # listens both on IPv4 and IPv6 on 443 and enables HTTPS and HTTP/2 support. - # HTTP/2 is available in nginx 1.9.5 and above. - listen *:443 ssl; - listen [::]:443 ssl; - http2 on; - # indicate locations of SSL key files. - ssl_certificate /srv/www/ssl/ssl.crt; - ssl_certificate_key /srv/www/ssl/ssl.key; - ssl_dhparam /srv/www/master/ssl/dhparam.pem; +#### HTTP Compression - # indicate the server name - server_name example.com *.example.com; +The [AddOutputFilter Directive](https://httpd.apache.org/docs/trunk/mod/mod_mime.html#addoutputfilter) maps the filename extension extension to the filters which will process responses from the server before they are sent to the client. This is in addition to any filters defined elsewhere, including `SetOutputFilter` and `AddOutputFilterByType`. This mapping is merged over any already in force, overriding any mappings that already exist for the same extension. - # Enable HSTS. This forces SSL on clients that respect it, most modern browsers. The includeSubDomains flag is optional. - add_header Strict-Transport-Security "max-age=31536000; includeSubDomains"; +See also [Enable Compression](https://developers.google.com/speed/docs/insights/EnableCompression) - # Set caches, protocols, and accepted ciphers. This config will merit an A+ SSL Labs score as of Sept 2015. - ssl_session_cache shared:SSL:20m; - ssl_session_timeout 10m; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - ssl_prefer_server_ciphers on; - ssl_ciphers 'ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5'; -} +``` +AddOutputFilterByType DEFLATE text/html text/plain text/xml application/xml application/xhtml+xml text/javascript text/css application/x-javascript +BrowserMatch ^Mozilla/4 gzip-only-text/html +BrowserMatch ^Mozilla/4\.0[678] no-gzip +BrowserMatch \bMSIE !no-gzip !gzip-only-text/html ``` -Mozilla offers an [excellent SSL config generation tool](https://mozilla.github.io/server-side-tls/ssl-config-generator/) as well. +**Force Compression for certain files** -### WP Super Cache Rules +``` +SetOutputFilter DEFLATE ``` -# WP Super Cache rules. -# Designed to be included from a 'wordpress-ms-...' configuration file. -set $cache_uri $request_uri; +#### Send Custom HTTP Headers -# POST requests and urls with a query string should always go to PHP -if ($request_method = POST) { - set $cache_uri 'null cache'; -} +The [Header Directive](https://httpd.apache.org/docs/trunk/mod/mod_headers.html#header) lets you send HTTP headers for every request, or just specific files. You can view a sites HTTP Headers using [Firebug](https://getfirebug.com/), [Chrome Dev Tools](https://developer.chrome.com/docs/devtools/), [Wireshark](https://www.wireshark.org/) or [Advanced HTTP Request / Response Headers](https://www.askapache.com/online-tools/http-headers-tool/). -if ($query_string != "") { - set $cache_uri 'null cache'; -} +``` +Header set X-Pingback "https://example.com/xmlrpc.php" +Header set Content-Language "en-US" -# Don't cache uris containing the following segments -if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") { - set $cache_uri 'null cache'; -} +``` -# Don't use the cache for logged in users or recent commenters -if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in") { - set $cache_uri 'null cache'; -} +#### Unset HTTP Headers -# START MOBILE -# Mobile browsers section to server them non-cached version. COMMENTED by default as most modern wordpress themes including twenty-eleven are responsive. Uncomment config lines in this section if you want to use a plugin like WP-Touch -# if ($http_x_wap_profile) { - # set $cache_uri 'null cache'; -#} +This will unset HTTP headers, using **always** will try extra hard to remove them. -#if ($http_profile) { - # set $cache_uri 'null cache'; -#} +``` +Header unset Pragma +Header always unset WP-Super-Cache +Header always unset X-Pingback -#if ($http_user_agent ~* (2.0\ MMP|240x320|400X240|AvantGo|BlackBerry|Blazer|Cellphone|Danger|DoCoMo|Elaine/3.0|EudoraWeb|Googlebot-Mobile|hiptop|IEMobile|KYOCERA/WX310K|LG/U990|MIDP-2.|MMEF20|MOT-V|NetFront|Newt|Nintendo\ Wii|Nitro|Nokia|Opera\ Mini|Palm|PlayStation\ Portable|portalmmm|Proxinet|ProxiNet|SHARP-TQ-GX10|SHG-i900|Small|SonyEricsson|Symbian\ OS|SymbianOS|TS21i-10|UP.Browser|UP.Link|webOS|Windows\ CE|WinWAP|YahooSeeker/M1A1-R2D2|iPhone|iPod|Android|BlackBerry9530|LG-TU915\ Obigo|LGE\ VX|webOS|Nokia5800)) { - # set $cache_uri 'null cache'; -#} +``` -#if ($http_user_agent ~* (w3c\ |w3c-|acs-|alav|alca|amoi|audi|avan|benq|bird|blac|blaz|brew|cell|cldc|cmd-|dang|doco|eric|hipt|htc_|inno|ipaq|ipod|jigs|kddi|keji|leno|lg-c|lg-d|lg-g|lge-|lg/u|maui|maxo|midp|mits|mmef|mobi|mot-|moto|mwbp|nec-|newt|noki|palm|pana|pant|phil|play|port|prox|qwap|sage|sams|sany|sch-|sec-|send|seri|sgh-|shar|sie-|siem|smal|smar|sony|sph-|symb|t-mo|teli|tim-|tosh|tsm-|upg1|upsi|vk-v|voda|wap-|wapa|wapi|wapp|wapr|webc|winw|winw|xda\ |xda-)) { - # set $cache_uri 'null cache'; -#} -#END MOBILE - -# Use cached or actual file if they exists, otherwise pass request to WordPress -location / { - try_files /wp-content/cache/supercache/$http_host/$cache_uri/index.html $uri $uri/ /index.php?$args ; -} - -``` +#### Password Protect Login -**Experimental modifications:** +This is very useful for protecting the `wp-login.php` file. You can use this [Advanced Htpasswd/Htdigest file creator](https://www.askapache.com/online-tools/htpasswd-generator/). -If you are using HTTPS, the latest development version of WP Super Cache may use a different directory structure to differentiate between HTTP and HTTPS. try\_files line may look like below: +**Basic Authentication** ``` -location / { - try_files /wp-content/cache/supercache/$http_host/$cache_uri/index-https.html $uri $uri/ /index.php?$args ; -} +AuthType Basic +AuthName "Password Protected" +AuthUserFile /full/absolute/path/to/.htpasswd +Require valid-user +Satisfy All ``` -### W3 Total Cache Rules - -W3 Total Cache uses different directory structure for disk-based cache storage depending on WordPress configuration. - -Cache validation checks will remain common as shown below: +**Digest Authentication** ``` -#W3 TOTAL CACHE CHECK -set $cache_uri $request_uri; +AuthType Digest +AuthName "Password Protected" +AuthDigestDomain /wp-login.php https://example.com/wp-login.php +AuthUserFile /full/absolute/path/to/.htpasswd +Require valid-user +Satisfy All -# POST requests and urls with a query string should always go to PHP -if ($request_method = POST) { - set $cache_uri 'null cache'; -} -if ($query_string != "") { - set $cache_uri 'null cache'; -} +``` -# Don't cache uris containing the following segments -if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") { - set $cache_uri 'null cache'; -} +#### Require Specific IP -# Don't use the cache for logged in users or recent commenters -if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in") { - set $cache_uri 'null cache'; -} -#ADD mobile rules from WP SUPER CACHE section above +This is a way to only allow access for IP addresses listed. Note usage of RequireAny instead of RequireAll. -#APPEND A CODE BLOCK FROM BELOW... +``` + + Require ip 192.0.2.123 + Require ip 2001:0DB8:1111:2222:3333:4444:5555:6666 + ``` -**FOR Normal WordPress (without Multisite)** +#### Protect Sensitive Files -Use following: +This denies all web access to your wp-config file, htaccess/htpasswd and WordPress debug.log. On installed site, consider adding install.php as well. ``` -# Use cached or actual file if they exists, otherwise pass request to WordPress -location / { - try_files /wp-content/w3tc/pgcache/$cache_uri/_index.html $uri $uri/ /index.php?$args ; -} + + Require all denied + ``` -**FOR Multisite with subdirectories** -Use the following: +#### Require SSL + +This will force SSL, and require the exact hostname or else it will redirect to the SSL version. Useful in a `/wp-admin/.htaccess` file. ``` -if ( $request_uri ~* "^/([_0-9a-zA-Z-]+)/.*" ){ - set $blog $1; -} +SSLOptions +StrictRequire +SSLRequireSSL +SSLRequire %{HTTP_HOST} eq "www.example.com" +ErrorDocument 403 https://www.example.com -set $blog "${blog}."; +``` -if ( $blog = "blog." ){ - set $blog ""; -} +### External Resources -# Use cached or actual file if they exists, otherwise pass request to WordPress -location / { - try_files /wp-content/w3tc-$blog$host/pgcache$cache_uri/_index.html $uri $uri/ /index.php?$args ; -} +- [Official Apache HTTP Server Tutorial: .htaccess files](https://httpd.apache.org/docs/trunk/howto/htaccess.html) +- [Official Htaccess Directive Quick Reference](https://httpd.apache.org/docs/trunk/mod/quickreference.html) +- [Htaccess Tutorial](https://www.askapache.com/htaccess/) +- [Google PageSpeed for Developers](https://developers.google.com/speed/docs/insights/rules) +- [Stupid Htaccess Tricks](https://perishablepress.com/stupid-htaccess-tricks/) +- [Advanced Mod\_Rewrite](https://www.askapache.com/htaccess/crazy-advanced-mod_rewrite-tutorial/) -``` +### See also -**FOR Multisite with Subdomains/Domain-mapping** -Use following: +- [htaccess for subdirectories](https://codex.wordpress.org/htaccess%20for%20subdirectories) +- [Using Permalinks](https://wordpress.org/documentation/article/customize-permalinks/) +- [Changing File Permissions](https://wordpress.org/documentation/article/changing-file-permissions/) +- [UNIX Shell Skills](https://codex.wordpress.org/UNIX%20Shell%20Skills) +- [Rewrite API](https://codex.wordpress.org/Rewrite%20API) -``` -location / { - try_files /wp-content/w3tc-$host/pgcache/$cache_uri/_index.html $uri $uri/ /index.php?$args; -} +## Changelog -``` +- 2023-04-25: Original content from [htaccess](https://wordpress.org/documentation/article/htaccess/). -Notes +--- -- Nginx can handle gzip & browser cache automatically so better leave that part to nginx. -- W3 Total Cache Minify rules will work with above config without any issues. +# Nginx -## Nginx fastcgi\_cache +Source: https://developer.wordpress.org/advanced-administration/server/web-server/nginx/ -Nginx can perform caching on its own end to reduce load on your server. When you want to use Nginx’s built-in fastcgi\_cache, you better compile nginx with [fastcgi\_cache\_purge](https://github.com/FRiCKLE/ngx_cache_purge) module. It will help nginx purge cache for a page when it gets edited. On the WordPress side, you need to install a plugin like [Nginx Helper](https://wordpress.org/plugins/nginx-helper/) to utilize fastcgi\_cache\_purge feature. +While the LAMP stack (Linux + Apache + MySQL + PHP) is very popular for powering WordPress, it is also possible to use Nginx. WordPress supports Nginx, and some large WordPress sites, such as WordPress.com, are powered by Nginx. -Config will look like below: +When talking about Nginx, it is important to know that there are multiple ways to implement Nginx. It can be setup as a reverse-proxy in front of Apache, which is a very powerful setup that allows you to use all of the features and power of Apache, while benefiting from the speed of Nginx. Most websites that report using Nginx as the server (based on stats gathered from HTTP response headers), are actually Apache running with Nginx as the reverse proxy. (The HTTP response headers showing “Nginx” are being reported by the reverse-proxy, not the server itself.) -**Define a Nginx cache zone in http{…} block, outside server{…} block** +**This guide is referring to a standalone Nginx setup, where it is used as the primary server instead of Apache.** It should be noted that Nginx is not a completely interchangeable substitute for Apache. There are a few key differences affecting WordPress implementation that you need to be aware of before you proceed: -``` -#move next 3 lines to /etc/nginx/nginx.conf if you want to use fastcgi_cache across many sites -fastcgi_cache_path /var/run/nginx-cache levels=1:2 keys_zone=WORDPRESS:500m inactive=60m; -fastcgi_cache_key "$scheme$request_method$host$request_uri"; -fastcgi_cache_use_stale error timeout invalid_header http_500; +- With Nginx there is no directory-level configuration file like Apache’s .htaccess or IIS’s web.config files. All configuration has to be done at the server level by an administrator, and WordPress cannot modify the configuration, like it can with Apache or IIS. +- Pretty Permalinks functionality is slightly different when running Nginx. +- Since Nginx does not have .htaccess-type capability and WordPress cannot automatically modify the server configuration for you, it cannot generate the rewrite rules for you. +- Without modifications to your install, “index.php” will be added to your Permalinks. (There are ways to mitigate this with plugins (see below) and/or adding custom code to your child theme’s functions.php.) +- However, if you do want to have some (limited) .htaccess capability, it is technically possible to do add by installing the [htscanner PECL extension for PHP](https://www.php.net/manual/en/book.htscanner.php). (However, this is not a perfect solution so be sure to test and debug thoroughly before using on a live site.) -``` +This guide is not going to cover how to install and configure Nginx, so this assumes that you have already installed Nginx and have a basic understanding of how to work with and debug it. -**For WordPress site config, in server{..} block add a cache check block as follow** +## Generic and Multi-Site Support -``` -#fastcgi_cache start -set $no_cache 0; +To make WordPress work with Nginx you have to configure the backend php-cgi. The options available are `fastcgi` or `php-fpm`. Here, php-fpm is being used because it is included with PHP 5.3+, so installing it is straight forward. -# POST requests and urls with a query string should always go to PHP -if ($request_method = POST) { - set $no_cache 1; -} -if ($query_string != "") { - set $no_cache 1; -} +The Nginx configuration has been broken up into five distinct files and is heavily commented to make each option easier to understand. The [author](https://wordpress.org/support/users/bigsite/) also made a best-effort attempting to follow “best practices” for nginx configurations. -# Don't cache uris containing the following segments -if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") { - set $no_cache 1; -} +### Main (generic) startup file -# Don't use the cache for logged in users or recent commenters -if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") { - set $no_cache 1; -} +This is equivalent to /etc/nginx/nginx.conf (or /etc/nginx/conf/nginx.conf if you’re using Arch Linux). ``` +# Generic startup file. +user {user} {group}; -**Then make changes to PHP handling block** +#usually equal to number of CPUs you have. run command "grep processor /proc/cpuinfo | wc -l" to find it +worker_processes auto; +worker_cpu_affinity auto; -Just add this to the following php block. Note the line fastcgi\_cache\_valid 200 60m; which tells nginx only to cache 200 responses(normal pages), which means that redirects are not cached. This is important for multilanguage sites where, if not implemented, nginx would cache the main url in one language instead of redirecting users to their respective content according to their language. +error_log /var/log/nginx/error.log; +pid /var/run/nginx.pid; -``` -fastcgi_cache_bypass $no_cache; -fastcgi_no_cache $no_cache; +# Keeps the logs free of messages about not being able to bind(). +#daemon off; -fastcgi_cache WORDPRESS; -fastcgi_cache_valid 200 60m; +events { + worker_connections 1024; +} -``` +http { + #rewrite_log on; -Such that it becomes something like this + include mime.types; + default_type application/octet-stream; + access_log /var/log/nginx/access.log; + sendfile on; + #tcp_nopush on; + keepalive_timeout 3; + #tcp_nodelay on; + #gzip on; + #php max upload limit cannot be larger than this + client_max_body_size 13m; + index index.php index.html index.htm; -``` -location ~ [^/]\.php(/|$) { - fastcgi_split_path_info ^(.+?\.php)(/.*)$; - if (!-f $document_root$fastcgi_script_name) { - return 404; + # Upstream to abstract backend connection(s) for PHP. + upstream php { + #this should match value of "listen" directive in php-fpm pool + server unix:/tmp/php-fpm.sock; + # server 127.0.0.1:9000; } - # This is a robust solution for path info security issue and works with "cgi.fix_pathinfo = 1" in /etc/php.ini (default) - - include fastcgi.conf; - fastcgi_index index.php; - # fastcgi_intercept_errors on; - fastcgi_pass php; - - fastcgi_cache_bypass $no_cache; - fastcgi_no_cache $no_cache; - fastcgi_cache WORDPRESS; - fastcgi_cache_valid 200 60m; + include sites-enabled/*; } ``` -**Finally add a location for conditional purge** +### Per Site configuration ``` -location ~ /purge(/.*) { - # Uncomment the following two lines to allow purge only from the webserver - # allow 127.0.0.1; - # deny all; +# Redirect everything to the main site. We use a separate server statement and NOT an if statement - see https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/ - fastcgi_cache_purge WORDPRESS "$scheme$request_method$host$1"; +server { + server_name _; + return 302 $scheme://example.com$request_uri; } -``` +server { + server_name example.com; + root /var/www/example.com; -If you get an ‘unknown directive “fastcgi\_cache\_purge”‘ error check that your Nginx installation has fastcgi\_cache\_purge module. + index index.php; -## Better Performance for Static Files in Multisite (WP <= 3.4) + include global/restrictions.conf; -By default, on multisite networks activated prior to 3.5, a static file request brings php into picture i.e. `ms-files.php` file. You can get much better performance using Nginx `Map{..}` directive. + # Additional rules go here. -In Nginx config for your site, above `server{..}` block, add a section as follows: + # Only include one of the files below. + include global/wordpress.conf; + # include global/wordpress-ms-subdir.conf; + # include global/wordpress-ms-subdomain.conf; +} ``` -map $http_host $blogid { - default 0; - example.com 1; - site1.example.com 2; - site1.com 2; -} +Splitting sections of the configuration into multiple files allows the same logic to be reused over and over. A ‘global’ subdirectory is used to add extra rules for general purpose use (either /etc/nginx/conf/global/ or /etc/nginx/global/ depending on how your nginx install is set up). + +### Global restrictions file ``` +# Global restrictions configuration file. +# Designed to be included in any server {} block. +location = /favicon.ico { + log_not_found off; + access_log off; +} -It is just a list of site-names and blog-ids. You can use [Nginx helper](https://wordpress.org/extend/plugins/nginx-helper) to get such a list of site-name/blog-id pairs. This plugin will also generate a `map.conf` file which you can directly include in the `map{}` section like this: +location = /robots.txt { + allow all; + log_not_found off; + access_log off; +} -``` -map $http_host $blogid { - default 0; +# Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac). +# Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban) +location ~ /\. { + deny all; +} - include /path/to/map.conf ; +# Deny access to any files with a .php extension in the uploads directory +# Works in sub-directory installs and also in multisite network +# Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban) +location ~* /(?:uploads|files)/.*\.php$ { + deny all; } ``` -After creating a `map{..}` section, you just need to make one more change in your Nginx config so requests for /files/ will be first processed using nginx `map{..}`: +### General WordPress rules + +For single site installations, here is the `global/wordpress.conf` file: ``` -location ~ ^/files/(.*)$ { - try_files /wp-content/blogs.dir/$blogid/$uri /wp-includes/ms-files.php?file=$1 ; - access_log off; log_not_found off; expires max; +# WordPress single site rules. +# Designed to be included in any server {} block. +# Upstream to abstract backend connection(s) for php +upstream php { + server unix:/tmp/php-cgi.socket; + server 127.0.0.1:9000; } -``` +server { + ## Your website name goes here. + server_name domain.tld; + ## Your only path reference. + root /var/www/wordpress; + ## This should be in your http block and if it is, it's not needed here. + index index.php; -Notes + location = /favicon.ico { + log_not_found off; + access_log off; + } -- Whenever a new site is created, deleted or an extra domain is mapped to an existing site, Nginx helper will update map.conf file automatically but you will still need to reload Nginx config manually. You can do that anytime later. Till then, only files for new sites will be served using php-fpm. -- This method does not generate any symbolic links. So, there will be no issues with accidental deletes or backup scripts that follow symbolic links. -- For large networks, this will scale-up nicely as there will be a single map.conf file. + location = /robots.txt { + allow all; + log_not_found off; + access_log off; + } -A couple of final but important notes: This whole setup assumes that the root of the site is the blog and that all files that will be referenced reside on the host. If you put the blog in a subdirectory such as /blog, then the rules will have to be modified. Perhaps someone can take these rules and make it possible to, for instance, use a: + location / { + # This is cool because no php is touched for static content. + # include the "?$args" part so non-default permalinks doesn't break when using query string + try_files $uri $uri/ /index.php?$args; + } -``` -set $wp_subdir "/blog"; + location ~ \.php$ { + #NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini + include fastcgi.conf; + fastcgi_intercept_errors on; + fastcgi_pass php; + } -``` + location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ { + expires max; + log_not_found off; + } +} -directive in the main ‘server’ block and have it automagically apply to the generic WP rules. +``` -## Warning +This is more up-to-date example for Nginx: https://www.nginx.com/resources/wiki/start/topics/recipes/wordpress/ -A typo in [Global restrictions file](#advanced-administration/server/web-server/nginx) can create loopholes. To test if your “uploads” directory is really protected, create a PHP file with some content (example: ), upload it to “uploads” directory (or one of its sub-directories), then try to access (execute) it from your browser. +### WordPress Multisite -## Resources +For multisite installations, use one of the below sections for the `global/wordpress.conf` file, depending on the version of WordPress that was in use when multisite was *activated*, as well as the domain/subdirectory configuration. -### Reference +#### WordPress 3.5 and up -- [nginx + php-fpm + PHP APC + WordPress multisite (subdirectory) + WP Super Cache](https://wordpress.org/support/topic/nginx-php-fpm-php-apc-wordpress-multisite-subdirectory-wp-super-cache/). +If you activated Multisite on WordPress 3.5 or later, use one of these. -### External Links +##### WordPress 3.5 and up Subdirectory Examples -- [Nginx WordPress wiki page](https://www.nginx.com/resources/wiki/start/topics/recipes/wordpress/) -- [LEMP guides on Linode’s Library](https://www.linode.com/docs/guides/web-servers/lemp/) -- [Various guides about Nginx on Linode’s Library](https://www.linode.com/docs/guides/web-servers/nginx/) -- [Lightning fast WordPress with Php-fpm and Nginx](https://www.sitepoint.com/lightning-fast-wordpress-with-php-fpm-and-nginx/) -- [Virtual Hosts Examples](https://wiki.nginx.org/VirtualHostExample) -- [List of 20+ WordPress-Nginx Tutorials for common situations](https://rtcamp.com/wordpress-nginx/tutorials/) -- [An introduction to Nginx configuration](https://blog.martinfjordvald.com/nginx-primer/) -- [A comprehensive blog series on hosting WordPress yourself using Nginx](https://deliciousbrains.com/hosting-wordpress-setup-secure-virtual-server/) -- [WordPress Installation CentminMod](https://centminmod.com/nginx_configure_wordpress.html) -- [Nginx WordPress Installation Guide](https://thecustomizewindows.com/2015/12/nginx-wordpress-installation-guide-steps/) +``` +# WordPress multisite subdirectory config file for WP 3.5 and up. +server { + server_name example.com ; -### Scripts & Tools + root /var/www/example.com/htdocs; + index index.php; -For WordPress Nginx scripted installation [CentminMod](https://centminmod.com/nginx_configure_wordpress.html) can be used for CentOS. + if (!-e $request_filename) { + rewrite /wp-admin$ $scheme://$host$request_uri/ permanent; + rewrite ^(/[^/]+)?(/wp-.*) $2 last; + rewrite ^(/[^/]+)?(/.*\.php) $2 last; + } -### Securing Nginx + location / { + try_files $uri $uri/ /index.php?$args ; + } -- [Securing Nginx and PHP](http://kbeezie.com/securing-nginx-php/) -- [Setting up PHP-FastCGI and nginx? Don’t trust the tutorials: check your configuration!](https://nealpoole.com/blog/2011/04/setting-up-php-fastcgi-and-nginx-dont-trust-the-tutorials-check-your-configuration/) + location ~ \.php$ { + try_files $uri =404; + include fastcgi_params; + fastcgi_pass php; + } -## Changelog + #add some rules for static content expiry-headers here +} -- 2022-10-25: Original content from [Nginx](https://wordpress.org/documentation/article/nginx/). +``` ---- +##### WordPress 3.5 and up Subdomains Examples -# Control Panels +``` +# WordPress multisite subdomain config file for WP 3.5 and up. +server { + server_name example.com *.example.com ; -Source: https://developer.wordpress.org/advanced-administration/server/control-panel/ + root /var/www/example.com/htdocs; + index index.php; -## WP Toolkit for cPanel & WHM and Plesk + location / { + try_files $uri $uri/ /index.php?$args ; + } -WP Toolkit is a single management interface that allows you to install, configure, and manage WordPress® easily. WP Toolkit can install, configure, and manage WordPress versions 4.9 or later. + location ~ \.php$ { + try_files $uri =404; + include fastcgi_params; + fastcgi_pass php; + } -cPanel & WHM versions 102 and above install WP Toolkit by default. WP Toolkit is available in Plesk if the server administrator has installed the WP Toolkit extension. + #add some rules for static content expiry-headers here +} -## cPanel & WHM +``` -This tutorial describes how to install WordPress in the cPanel & WHM control panel using WP Toolkit. For other options to install WordPress in cPanel & WHM, read cPanel’s [How to Install WordPress with cPanel](https://docs.cpanel.net/knowledge-base/third-party/how-to-install-wordpress-with-cpanel/) documentation. +#### WordPress 3.4 and below -To install WordPress via WP Toolkit, perform the following actions: +If you originally activated Multisite with WordPress with 3.4 or older, you need to use one of these: -1. Log in to the cPanel interface with the information provided by your hosting company. -2. Select *WP Toolkit* from the lefthand menu bar. The *WP Toolkit* interface will appear. -3. Click *Install WordPress* to create a new WordPress installation. +##### WordPress <=3.4 Subdirectory Examples -### Install WordPress +``` +# WordPress multisite subdirectory config file for WP 3.4 and below. -In the *Install WordPress* interface, click *Install* to use the default settings. Or provide the following optional information to customize your installation, then click *Install*: +map $uri $blogname{ + ~^(?P/[^/]+/)files/(.*) $blogpath ; +} -- The installation directory. By default, the installation uses the account’s `public_html` directory. -- The title for the website. -- The plugin or theme set. -- The language of the website. -- The version of WordPress to install. By default, the installation uses the current WordPress version. -- The WordPress Administrator username and password. -- The database name, table prefix, username and password. -- The automatic update settings for the WordPress software, plugins, and themes. +map $blogname $blogid{ + default -999; -Click *Install Plugins* if you want to install plugins and themes, or click *No, thanks* to optionally install them later. + #Ref: https://wordpress.org/extend/plugins/nginx-helper/ + #include /var/www/wordpress/wp-content/plugins/nginx-helper/map.conf ; +} -## Plesk +server { + server_name example.com ; -This tutorial provides step-by-step examples of installing Plesk WP Toolkit using the Plesk Web Installer. + root /var/www/example.com/htdocs; + index index.php; -### Using Web Installer + location ~ ^(/[^/]+/)?files/(.+) { + try_files /wp-content/blogs.dir/$blogid/files/$2 /wp-includes/ms-files.php?file=$2 ; + access_log off; log_not_found off; expires max; + } -Install Plesk using Web Installer please open your browser and go to [get.plesk.com](https://get.plesk.com/). For Plesk installation, it is required a fresh Linux server with access to the Internet. You can install Plesk on any supported Linux-based OS. + #avoid php readfile() + location ^~ /blogs.dir { + internal; + alias /var/www/example.com/htdocs/wp-content/blogs.dir ; + access_log off; log_not_found off; expires max; + } -![Image_1](https://i0.wp.com/user-images.githubusercontent.com/19301688/189542599-4fce4d63-8060-416e-9fdf-f21ae62c87e1.png?ssl=1) + if (!-e $request_filename) { + rewrite /wp-admin$ $scheme://$host$request_uri/ permanent; + rewrite ^(/[^/]+)?(/wp-.*) $2 last; + rewrite ^(/[^/]+)?(/.*\.php) $2 last; + } -Provide your server’s IP address or hostname, enter your root password, or just add a private key. + location / { + try_files $uri $uri/ /index.php?$args ; + } -- Accept the terms of End-User License Agreement and click Install button. -- Relax and wait for some time to let the installation be finalized. -- Click on the Login link. No worries about “secure connection warnings”, just make an exception. + location ~ \.php$ { + try_files $uri =404; + include fastcgi_params; + fastcgi_pass php; + } -### Installing WordPress + #add some rules for static content expiry-headers here +} -![image_2](https://i0.wp.com/user-images.githubusercontent.com/19301688/189542665-78f52a1c-e92b-4d70-bb5d-899ac02cc57e.png?ssl=1) +``` -- For an express installation, click Install (Quick). The latest version of WordPress will be installed, and the default settings will be used. The new instance will be available via HTTPS if SSL/TLS support is enabled for the domain. -- If you want to change the default installation settings, click Install (Custom). This enables you to set up the administrator user, select the desired WordPress version, specify the database name, select auto-update settings, and more. +NGINX provides 2 special directive: X-Accel-Redirect and map. Using these 2 directives, one can eliminate performance hit for static-file serving on WordPress multisite network. -### Managing WordPress Instances +##### WordPress <=3.4 Subdomains Examples -Go to WordPress to see all your WordPress instances. WP Toolkit groups information about each instance in blocks we call cards. +``` +# WordPress multisite subdomain config file for WP 3.4 and below. +map $http_host $blogid { + default -999; -![image_3](https://i0.wp.com/user-images.githubusercontent.com/19301688/189542692-5d6f38b5-1b32-4de8-8f40-2abe9a5d1d86.png?ssl=1) + #Ref: https://wordpress.org/extend/plugins/nginx-helper/ + #include /var/www/wordpress/wp-content/plugins/nginx-helper/map.conf ; -A card shows a screenshot of your website and features several controls that give you easy access to frequently used tools. The screenshot changes in real time to reflect the changes you make to your website. For example, if you switch the maintenance mode on or change the WordPress theme, the screenshot of the website will change immediately. +} -### Tools +server { + server_name example.com *.example.com ; -In the “Tools” section, click to access the following WP Toolkit features: + root /var/www/example.com/htdocs; + index index.php; -![image_4](https://i0.wp.com/user-images.githubusercontent.com/19301688/189542713-abf476de-fcbd-4113-9975-1c2961765190.png?ssl=1) + location / { + try_files $uri $uri/ /index.php?$args ; + } -- “Sync” to synchronize the content of your website with another one. -- “Clone” to make a full copy of your website. -- “Manage Files” to manage the website’s files in File Manager. -- “Back Up/Restore” to create a backup of your website and restore it if necessary. - -The controls below give you easy access to the following settings and tools: + location ~ \.php$ { + try_files $uri =404; + include fastcgi_params; + fastcgi_pass php; + } -- “Search engine indexing” shows your website in search results of search engines. -- “Caching (nginx)” speeds up the website load time and reduces server load. -- “Debugging” helps you debug a website that is not ready for viewing and being tested or developed. -- “Maintenance mode” hides your website’s content from visitors. -- “Password Protection” specifies the password you will use to log in to WordPress from Plesk. + #WPMU Files + location ~ ^/files/(.*)$ { + try_files /wp-content/blogs.dir/$blogid/$uri /wp-includes/ms-files.php?file=$1 ; + access_log off; log_not_found off; expires max; + } -## Changelog + #WPMU x-sendfile to avoid php readfile() + location ^~ /blogs.dir { + internal; + alias /var/www/example.com/htdocs/wp-content/blogs.dir; + access_log off; log_not_found off; expires max; + } -- 2023-04-25: Removed outdated manual instructions from cPanel section and combined common WP Toolkit info for cPanel and Plesk. -- 2023-01-26: Original copied from [Using cPanel](https://wordpress.org/documentation/article/using-cpanel/). -- 2022-09-11: Original copied for Plesk. + #add some rules for static content expiry-headers here +} ---- +``` -# WordPress configuration +Ref: https://www.nginx.com/resources/wiki/start/topics/recipes/wordpress/ -Source: https://developer.wordpress.org/advanced-administration/wordpress/ +### HTTPS in Nginx -## Changelog +Enabling HTTPS in Nginx is relatively simple. -- 2022-08-16: Nothing here, yet. +``` +server { + # listens both on IPv4 and IPv6 on 443 and enables HTTPS and HTTP/2 support. + # HTTP/2 is available in nginx 1.9.5 and above. + listen *:443 ssl; + listen [::]:443 ssl; + http2 on; ---- + # indicate locations of SSL key files. + ssl_certificate /srv/www/ssl/ssl.crt; + ssl_certificate_key /srv/www/ssl/ssl.key; + ssl_dhparam /srv/www/master/ssl/dhparam.pem; -# Editing wp-config.php + # indicate the server name + server_name example.com *.example.com; -Source: https://developer.wordpress.org/advanced-administration/wordpress/wp-config/ + # Enable HSTS. This forces SSL on clients that respect it, most modern browsers. The includeSubDomains flag is optional. + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains"; -One of the most important files in your WordPress installation is the `wp-config.php` file. This file is located in the root of your WordPress file directory and contains your website’s base configuration details, such as database connection information. + # Set caches, protocols, and accepted ciphers. This config will merit an A+ SSL Labs score as of Sept 2015. + ssl_session_cache shared:SSL:20m; + ssl_session_timeout 10m; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_prefer_server_ciphers on; + ssl_ciphers 'ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5'; +} -When you first download WordPress, the `wp-config.php` file isn’t included. The WordPress setup process will create a `wp-config.php` file for you based on the information you provide in the [installation](#advanced-administration/before-install/howto-install) process. +``` -It is unlikely that a non-developer would have to edit the wp-config.php file, in the case you are acting on trouble shooting steps provided by a technical person or by your webhost, this [page](#advanced-administration/wordpress/wp-config) should help. +Mozilla offers an [excellent SSL config generation tool](https://mozilla.github.io/server-side-tls/ssl-config-generator/) as well. -# wp-config.php +### WP Super Cache Rules -TEMPORALLY NOTE: this may link for the simple part, to: -\* #advanced-administration/wordpress/wp-config -\* #advanced-administration/debug/debug-wordpress +``` +# WP Super Cache rules. +# Designed to be included from a 'wordpress-ms-...' configuration file. -## Advanced Options +set $cache_uri $request_uri; -The following sections may contain advanced information and some changes might result in unforeseen issues. Please make sure you practice [regular backups](#advanced-administration/security/backup) and know how to restore them before modifying these settings. +# POST requests and urls with a query string should always go to PHP +if ($request_method = POST) { + set $cache_uri 'null cache'; +} -### table\_prefix +if ($query_string != "") { + set $cache_uri 'null cache'; +} -The **$table\_prefix** is the value placed in the front of your database tables. Change the value if you want to use something other than **wp\_** for your database prefix. Typically this is changed if you are [installing multiple WordPress blogs](#advanced-administration/before-install/multiple-instances) in the same database, as is done with the multisite feature. +# Don't cache uris containing the following segments +if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") { + set $cache_uri 'null cache'; +} -It is possible to have multiple installations in one database if you give each a unique prefix. Keep security in mind if you choose to do this. +# Don't use the cache for logged in users or recent commenters +if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in") { + set $cache_uri 'null cache'; +} -``` -$table_prefix = 'example123_'; // Only numbers, letters, and underscores please! +# START MOBILE +# Mobile browsers section to server them non-cached version. COMMENTED by default as most modern wordpress themes including twenty-eleven are responsive. Uncomment config lines in this section if you want to use a plugin like WP-Touch +# if ($http_x_wap_profile) { + # set $cache_uri 'null cache'; +#} -``` +#if ($http_profile) { + # set $cache_uri 'null cache'; +#} -### WP\_SITEURL +#if ($http_user_agent ~* (2.0\ MMP|240x320|400X240|AvantGo|BlackBerry|Blazer|Cellphone|Danger|DoCoMo|Elaine/3.0|EudoraWeb|Googlebot-Mobile|hiptop|IEMobile|KYOCERA/WX310K|LG/U990|MIDP-2.|MMEF20|MOT-V|NetFront|Newt|Nintendo\ Wii|Nitro|Nokia|Opera\ Mini|Palm|PlayStation\ Portable|portalmmm|Proxinet|ProxiNet|SHARP-TQ-GX10|SHG-i900|Small|SonyEricsson|Symbian\ OS|SymbianOS|TS21i-10|UP.Browser|UP.Link|webOS|Windows\ CE|WinWAP|YahooSeeker/M1A1-R2D2|iPhone|iPod|Android|BlackBerry9530|LG-TU915\ Obigo|LGE\ VX|webOS|Nokia5800)) { + # set $cache_uri 'null cache'; +#} -WP\_SITEURL allows the WordPress address (URL) to be defined. The value defined is the address where your WordPress core files reside. It should include the https:// part too. Do not put a slash “**/**” at the end. Setting this value in `wp-config.php` overrides the [wp\_options table](https://codex.wordpress.org/Database_Description#Table:_wp_options) value for **siteurl**. Adding this in can reduce the number of database calls when loading your site. **Note:** This will **not** change the database stored value. The URL will revert to the old database value if this line is ever removed from `wp-config`. [Use the **RELOCATE** constant](#advanced-administration/upgrade/migrating) to change the **siteurl** value in the database. +#if ($http_user_agent ~* (w3c\ |w3c-|acs-|alav|alca|amoi|audi|avan|benq|bird|blac|blaz|brew|cell|cldc|cmd-|dang|doco|eric|hipt|htc_|inno|ipaq|ipod|jigs|kddi|keji|leno|lg-c|lg-d|lg-g|lge-|lg/u|maui|maxo|midp|mits|mmef|mobi|mot-|moto|mwbp|nec-|newt|noki|palm|pana|pant|phil|play|port|prox|qwap|sage|sams|sany|sch-|sec-|send|seri|sgh-|shar|sie-|siem|smal|smar|sony|sph-|symb|t-mo|teli|tim-|tosh|tsm-|upg1|upsi|vk-v|voda|wap-|wapa|wapi|wapp|wapr|webc|winw|winw|xda\ |xda-)) { + # set $cache_uri 'null cache'; +#} +#END MOBILE -If WordPress is installed into a directory called “wordpress” for the [domain](https://en.wikipedia.org/wiki/Domain_name_system) example.com, define `WP_SITEURL` like this: +# Use cached or actual file if they exists, otherwise pass request to WordPress +location / { + try_files /wp-content/cache/supercache/$http_host/$cache_uri/index.html $uri $uri/ /index.php?$args ; +} ``` -define( 'WP_SITEURL', 'https://example.com/wordpress' ); -``` +**Experimental modifications:** -Dynamically set WP\_SITEURL based on $\_SERVER\[‘HTTP\_HOST’\] +If you are using HTTPS, the latest development version of WP Super Cache may use a different directory structure to differentiate between HTTP and HTTPS. try\_files line may look like below: ``` -define( 'WP_SITEURL', 'https://' . $_SERVER['HTTP_HOST'] . '/path/to/wordpress' ); +location / { + try_files /wp-content/cache/supercache/$http_host/$cache_uri/index-https.html $uri $uri/ /index.php?$args ; +} ``` -**Note:** HTTP\_HOST is created dynamically by PHP based on the value of the HTTP HOST Header in the request, thus possibly allowing for [file inclusion vulnerabilities](https://en.wikipedia.org/wiki/File_inclusion_vulnerability). SERVER\_NAME may also be created dynamically. However, when Apache is configured as UseCanonicalName “on”, SERVER\_NAME is set by the server configuration, instead of dynamically. In that case, it is safer to user SERVER\_NAME than HTTP\_HOST. +### W3 Total Cache Rules -Dynamically set `WP_SITEURL` based on `$_SERVER['SERVER_NAME']` +W3 Total Cache uses different directory structure for disk-based cache storage depending on WordPress configuration. -``` -define( 'WP_SITEURL', 'https://' . $_SERVER['SERVER_NAME'] . '/path/to/wordpress' ); +Cache validation checks will remain common as shown below: ``` +#W3 TOTAL CACHE CHECK +set $cache_uri $request_uri; -### Blog address (URL) +# POST requests and urls with a query string should always go to PHP +if ($request_method = POST) { + set $cache_uri 'null cache'; +} +if ($query_string != "") { + set $cache_uri 'null cache'; +} -Similar to WP\_SITEURL, WP\_HOME *overrides the [wp\_options table](https://codex.wordpress.org/Database_Description#Table:_wp_options) value for* home *but does not change it in the database.* **home** is the address you want people to type in their browser to reach your WordPress blog. It should include the https:// part and should not have a slash “**/**” at the end. Adding this in can reduce the number of database calls when loading your site. +# Don't cache uris containing the following segments +if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") { + set $cache_uri 'null cache'; +} -``` -define( 'WP_HOME', 'https://example.com/wordpress' ); +# Don't use the cache for logged in users or recent commenters +if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in") { + set $cache_uri 'null cache'; +} +#ADD mobile rules from WP SUPER CACHE section above + +#APPEND A CODE BLOCK FROM BELOW... ``` -If you are using the technique described in [Giving WordPress Its Own Directory](#advanced-administration/server/wordpress-in-directory) then follow the example below. Remember, you will also be placing an `index.php` in your web-root directory if you use a setting like this. +**FOR Normal WordPress (without Multisite)** + +Use following: ``` -define( 'WP_HOME', 'https://example.com' ); +# Use cached or actual file if they exists, otherwise pass request to WordPress +location / { + try_files /wp-content/w3tc/pgcache/$cache_uri/_index.html $uri $uri/ /index.php?$args ; +} ``` -Dynamically set `WP_HOME` based on `$_SERVER['HTTP_HOST']` +**FOR Multisite with subdirectories** +Use the following: ``` -define( 'WP_HOME', 'https://' . $_SERVER['HTTP_HOST'] . '/path/to/wordpress' ); +if ( $request_uri ~* "^/([_0-9a-zA-Z-]+)/.*" ){ + set $blog $1; +} -``` +set $blog "${blog}."; -### Moving wp-content folder +if ( $blog = "blog." ){ + set $blog ""; +} -You can move the `wp-content` directory, which holds your themes, plugins, and uploads, outside of the WordPress application directory. +# Use cached or actual file if they exists, otherwise pass request to WordPress +location / { + try_files /wp-content/w3tc-$blog$host/pgcache$cache_uri/_index.html $uri $uri/ /index.php?$args ; +} -Set WP\_CONTENT\_DIR to the full **local path** of this directory (no trailing slash), e.g. +``` + +**FOR Multisite with Subdomains/Domain-mapping** +Use following: ``` -define( 'WP_CONTENT_DIR', dirname(__FILE__) . '/blog/wp-content' ); +location / { + try_files /wp-content/w3tc-$host/pgcache/$cache_uri/_index.html $uri $uri/ /index.php?$args; +} ``` -Set WP\_CONTENT\_URL to the full **URL** of this directory (no trailing slash), e.g. +Notes -``` -define( 'WP_CONTENT_URL', 'https://example/blog/wp-content' ); +- Nginx can handle gzip & browser cache automatically so better leave that part to nginx. +- W3 Total Cache Minify rules will work with above config without any issues. -``` +## Nginx fastcgi\_cache -### Moving plugin folder +Nginx can perform caching on its own end to reduce load on your server. When you want to use Nginx’s built-in fastcgi\_cache, you better compile nginx with [fastcgi\_cache\_purge](https://github.com/FRiCKLE/ngx_cache_purge) module. It will help nginx purge cache for a page when it gets edited. On the WordPress side, you need to install a plugin like [Nginx Helper](https://wordpress.org/plugins/nginx-helper/) to utilize fastcgi\_cache\_purge feature. -Set WP\_PLUGIN\_DIR to the full **local path** of this directory (no trailing slash), e.g. +Config will look like below: + +**Define a Nginx cache zone in http{…} block, outside server{…} block** ``` -define( 'WP_PLUGIN_DIR', dirname(__FILE__) . '/blog/wp-content/plugins' ); +#move next 3 lines to /etc/nginx/nginx.conf if you want to use fastcgi_cache across many sites +fastcgi_cache_path /var/run/nginx-cache levels=1:2 keys_zone=WORDPRESS:500m inactive=60m; +fastcgi_cache_key "$scheme$request_method$host$request_uri"; +fastcgi_cache_use_stale error timeout invalid_header http_500; ``` -Set WP\_PLUGIN\_URL to the full **URI** of this directory (no trailing slash), e.g. +**For WordPress site config, in server{..} block add a cache check block as follow** ``` -define( 'WP_PLUGIN_URL', 'https://example/blog/wp-content/plugins' ); +#fastcgi_cache start +set $no_cache 0; -``` +# POST requests and urls with a query string should always go to PHP +if ($request_method = POST) { + set $no_cache 1; +} +if ($query_string != "") { + set $no_cache 1; +} -If you have compatibility issues with plugins Set PLUGINDIR to the full **local path** of this directory (no trailing slash), e.g. +# Don't cache uris containing the following segments +if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") { + set $no_cache 1; +} -``` -define( 'PLUGINDIR', dirname(__FILE__) . '/blog/wp-content/plugins' ); +# Don't use the cache for logged in users or recent commenters +if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") { + set $no_cache 1; +} ``` -### Moving themes folder +**Then make changes to PHP handling block** -You cannot move the themes folder because its path is hardcoded relative to the `wp-content` folder: +Just add this to the following php block. Note the line fastcgi\_cache\_valid 200 60m; which tells nginx only to cache 200 responses(normal pages), which means that redirects are not cached. This is important for multilanguage sites where, if not implemented, nginx would cache the main url in one language instead of redirecting users to their respective content according to their language. ``` -$theme_root = WP_CONTENT_DIR . '/themes'; +fastcgi_cache_bypass $no_cache; +fastcgi_no_cache $no_cache; + +fastcgi_cache WORDPRESS; +fastcgi_cache_valid 200 60m; ``` -However, you can register additional theme directories using [register\_theme\_directory](#reference/functions/register_theme_directory). +Such that it becomes something like this -See how to [move the wp-content](#advanced-administration/wordpress/wp-config) folder. For more details how the themes folder is determined, see `wp-includes/theme.php`. +``` +location ~ [^/]\.php(/|$) { + fastcgi_split_path_info ^(.+?\.php)(/.*)$; + if (!-f $document_root$fastcgi_script_name) { + return 404; + } + # This is a robust solution for path info security issue and works with "cgi.fix_pathinfo = 1" in /etc/php.ini (default) -### Moving uploads folder + include fastcgi.conf; + fastcgi_index index.php; + # fastcgi_intercept_errors on; + fastcgi_pass php; -Set UPLOADS to: + fastcgi_cache_bypass $no_cache; + fastcgi_no_cache $no_cache; -``` -define( 'UPLOADS', 'blog/wp-content/uploads' ); + fastcgi_cache WORDPRESS; + fastcgi_cache_valid 200 60m; +} ``` -This path can not be absolute. It is always relative to ABSPATH, therefore does not require a leading slash. +**Finally add a location for conditional purge** -### Modify AutoSave Interval +``` +location ~ /purge(/.*) { + # Uncomment the following two lines to allow purge only from the webserver + # allow 127.0.0.1; + # deny all; -When editing a post, WordPress uses Ajax to auto-save revisions to the post as you edit. You may want to increase this setting for longer delays in between auto-saves, or decrease the setting to make sure you never lose changes. The default is 60 seconds. + fastcgi_cache_purge WORDPRESS "$scheme$request_method$host$1"; +} ``` -define( 'AUTOSAVE_INTERVAL', 180 ); // Seconds -``` +If you get an ‘unknown directive “fastcgi\_cache\_purge”‘ error check that your Nginx installation has fastcgi\_cache\_purge module. -### Post Revisions +## Better Performance for Static Files in Multisite (WP <= 3.4) -WordPress, by default, will save copies of each edit made to a post or page, allowing the possibility of reverting to a previous version of that post or page. The saving of revisions can be disabled, or a maximum number of revisions per post or page can be specified. +By default, on multisite networks activated prior to 3.5, a static file request brings php into picture i.e. `ms-files.php` file. You can get much better performance using Nginx `Map{..}` directive. -#### Disable Post Revisions +In Nginx config for your site, above `server{..}` block, add a section as follows: -If you do **not** set this value, WordPress defaults WP\_POST\_REVISIONS to *true* (enable post revisions). If you want to disable the awesome revisions feature, use this setting: +``` +map $http_host $blogid { + default 0; + + example.com 1; + site1.example.com 2; + site1.com 2; +} ``` -define( 'WP_POST_REVISIONS', false ); + +It is just a list of site-names and blog-ids. You can use [Nginx helper](https://wordpress.org/extend/plugins/nginx-helper) to get such a list of site-name/blog-id pairs. This plugin will also generate a `map.conf` file which you can directly include in the `map{}` section like this: ``` +map $http_host $blogid { + default 0; -Note: Some users could not get this to function until moving the command to the first line under the initial block comment in `wp-config.php`. + include /path/to/map.conf ; +} -#### Specify the Number of Post Revisions +``` -If you want to specify a maximum number of revisions that WordPress stores, change *false* to an integer/number (*e.g.*, 3 or 12). +After creating a `map{..}` section, you just need to make one more change in your Nginx config so requests for /files/ will be first processed using nginx `map{..}`: ``` -define( 'WP_POST_REVISIONS', 3 ); +location ~ ^/files/(.*)$ { + try_files /wp-content/blogs.dir/$blogid/$uri /wp-includes/ms-files.php?file=$1 ; + access_log off; log_not_found off; expires max; +} ``` -Note: Some users could not get this to function until moving the command to the first line under the initial block comment in `wp-config.php`. +Notes -### Set Cookie Domain +- Whenever a new site is created, deleted or an extra domain is mapped to an existing site, Nginx helper will update map.conf file automatically but you will still need to reload Nginx config manually. You can do that anytime later. Till then, only files for new sites will be served using php-fpm. +- This method does not generate any symbolic links. So, there will be no issues with accidental deletes or backup scripts that follow symbolic links. +- For large networks, this will scale-up nicely as there will be a single map.conf file. -The domain set in the cookies for WordPress can be specified for those with unusual domain setups. For example, if subdomains are used to serve static content, you can set the cookie domain to only your non-static domain to prevent WordPress cookies from being sent with each request to static content on your subdomain . +A couple of final but important notes: This whole setup assumes that the root of the site is the blog and that all files that will be referenced reside on the host. If you put the blog in a subdirectory such as /blog, then the rules will have to be modified. Perhaps someone can take these rules and make it possible to, for instance, use a: ``` -define( 'COOKIE_DOMAIN', 'www.example.com' ); +set $wp_subdir "/blog"; ``` -### Enable Multisite / Network Ability +directive in the main ‘server’ block and have it automagically apply to the generic WP rules. -WP\_ALLOW\_MULTISITE is a feature enable multisite functionality. If this setting is absent from `wp-config.php` it defaults to false. +## Warning -``` -define( 'WP_ALLOW_MULTISITE', true ); +A typo in [Global restrictions file](#advanced-administration/server/web-server/nginx) can create loopholes. To test if your “uploads” directory is really protected, create a PHP file with some content (example: ), upload it to “uploads” directory (or one of its sub-directories), then try to access (execute) it from your browser. -``` +## Resources -### Redirect Nonexistent Blogs +### Reference -NOBLOGREDIRECT can be used to redirect the browser if the visitor tries to access a nonexistent subdomain or a subfolder. +- [nginx + php-fpm + PHP APC + WordPress multisite (subdirectory) + WP Super Cache](https://wordpress.org/support/topic/nginx-php-fpm-php-apc-wordpress-multisite-subdirectory-wp-super-cache/). -``` -define( 'NOBLOGREDIRECT', 'https://example.com' ); +### External Links -``` +- [Nginx WordPress wiki page](https://www.nginx.com/resources/wiki/start/topics/recipes/wordpress/) +- [LEMP guides on Linode’s Library](https://www.linode.com/docs/guides/web-servers/lemp/) +- [Various guides about Nginx on Linode’s Library](https://www.linode.com/docs/guides/web-servers/nginx/) +- [Lightning fast WordPress with Php-fpm and Nginx](https://www.sitepoint.com/lightning-fast-wordpress-with-php-fpm-and-nginx/) +- [Virtual Hosts Examples](https://wiki.nginx.org/VirtualHostExample) +- [List of 20+ WordPress-Nginx Tutorials for common situations](https://rtcamp.com/wordpress-nginx/tutorials/) +- [An introduction to Nginx configuration](https://blog.martinfjordvald.com/nginx-primer/) +- [A comprehensive blog series on hosting WordPress yourself using Nginx](https://deliciousbrains.com/hosting-wordpress-setup-secure-virtual-server/) +- [WordPress Installation CentminMod](https://centminmod.com/nginx_configure_wordpress.html) +- [Nginx WordPress Installation Guide](https://thecustomizewindows.com/2015/12/nginx-wordpress-installation-guide-steps/) -### Fatal Error Handler +### Scripts & Tools -WordPress 5.2 introduced [Recovery Mode](https://make.wordpress.org/core/2019/04/16/fatal-error-recovery-mode-in-5-2/) which displays error message instead of white screen when plugins causes fatal error. +For WordPress Nginx scripted installation [CentminMod](https://centminmod.com/nginx_configure_wordpress.html) can be used for CentOS. -*The site is experiencing technical difficulties. Please check your site admin email inbox for instructions.* +### Securing Nginx -White screens and PHP error messages are not displayed to users any more. But in a development environment, if you want to enable WP\_DEBUG\_DISPLAY, you have to disable recovery mode by set true to WP\_DISABLE\_FATAL\_ERROR\_HANDLER. +- [Securing Nginx and PHP](http://kbeezie.com/securing-nginx-php/) +- [Setting up PHP-FastCGI and nginx? Don’t trust the tutorials: check your configuration!](https://nealpoole.com/blog/2011/04/setting-up-php-fastcgi-and-nginx-dont-trust-the-tutorials-check-your-configuration/) -``` -define( 'WP_DISABLE_FATAL_ERROR_HANDLER', true ); // 5.2 and later -define( 'WP_DEBUG', true ); -define( 'WP_DEBUG_DISPLAY', true ); +## Changelog -``` +- 2022-10-25: Original content from [Nginx](https://wordpress.org/documentation/article/nginx/). -### WP\_DEBUG +--- -The [WP\_DEBUG](#advanced-administration/debug/debug-wordpress) option controls the reporting of some errors and warnings and enables use of the WP\_DEBUG\_DISPLAY and WP\_DEBUG\_LOG settings. The default boolean value is false. +# Control Panels -``` -define( 'WP_DISABLE_FATAL_ERROR_HANDLER', true ); // 5.2 and later -define( 'WP_DEBUG', true ); +Source: https://developer.wordpress.org/advanced-administration/server/control-panel/ -``` +## WP Toolkit for cPanel & WHM and Plesk -[Database errors are printed only if WP\_DEBUG is set to true](https://trac.wordpress.org/ticket/5473). Database errors are handled by the [wpdb](#reference/classes/wpdb) class and are not affected by [PHP’s error settings](https://www.php.net/errorfunc). +WP Toolkit is a single management interface that allows you to install, configure, and manage WordPress® easily. WP Toolkit can install, configure, and manage WordPress versions 4.9 or later. -Setting WP\_DEBUG to true also raises the [error reporting level](https://www.php.net/manual/en/errorfunc.configuration.php#ini.error-reporting) to E\_ALL and activates warnings when deprecated functions or files are used; otherwise, WordPress sets the error reporting level to E\_ALL ^ E\_NOTICE ^ E\_USER\_NOTICE. +cPanel & WHM versions 102 and above install WP Toolkit by default. WP Toolkit is available in Plesk if the server administrator has installed the WP Toolkit extension. -### WP\_ENVIRONMENT\_TYPE +## cPanel & WHM -The WP\_ENVIRONMENT\_TYPE option controls the environment type for a site: `local`, `development`, `staging`, and `production`. +This tutorial describes how to install WordPress in the cPanel & WHM control panel using WP Toolkit. For other options to install WordPress in cPanel & WHM, read cPanel’s [How to Install WordPress with cPanel](https://docs.cpanel.net/knowledge-base/third-party/how-to-install-wordpress-with-cpanel/) documentation. -The values of environment types are processed in the following order with each sequential method overriding any previous values: the WP\_ENVIRONMENT\_TYPE [PHP environment variable](https://www.php.net/manual/en/reserved.variables.environment.php) and the WP\_ENVIRONMENT\_TYPE constant. +To install WordPress via WP Toolkit, perform the following actions: -For both methods, if the value of an environment type provided is not in the list of allowed environment types, the default `production` value will be returned. +1. Log in to the cPanel interface with the information provided by your hosting company. +2. Select *WP Toolkit* from the lefthand menu bar. The *WP Toolkit* interface will appear. +3. Click *Install WordPress* to create a new WordPress installation. -The simplest way to set the value is probably through defining the constant: +### Install WordPress -``` -define( 'WP_ENVIRONMENT_TYPE', 'staging' ); +In the *Install WordPress* interface, click *Install* to use the default settings. Or provide the following optional information to customize your installation, then click *Install*: -``` +- The installation directory. By default, the installation uses the account’s `public_html` directory. +- The title for the website. +- The plugin or theme set. +- The language of the website. +- The version of WordPress to install. By default, the installation uses the current WordPress version. +- The WordPress Administrator username and password. +- The database name, table prefix, username and password. +- The automatic update settings for the WordPress software, plugins, and themes. -Note: When `development` is returned by [wp\_get\_environment\_type()](#reference/functions/wp_get_environment_type), WP\_DEBUG will be set to `true` if it is not defined in the `wp-config.php` file of the site. +Click *Install Plugins* if you want to install plugins and themes, or click *No, thanks* to optionally install them later. -### SCRIPT\_DEBUG +## Plesk -[SCRIPT\_DEBUG](#advanced-administration/debug/debug-wordpress) is a related constant that will force WordPress to use the “dev” versions of scripts and stylesheets in `wp-includes/js`, `wp-includes/css`, `wp-admin/js`, and `wp-admin/css` will be loaded instead of the `.min.css` and `.min.js` versions. If you are planning on modifying some of WordPress’ built-in JavaScript or Cascading Style Sheets, you should add the following code to your config file: +This tutorial provides step-by-step examples of installing Plesk WP Toolkit using the Plesk Web Installer. -``` -define( 'SCRIPT_DEBUG', true ); +### Using Web Installer -``` +Install Plesk using Web Installer please open your browser and go to [get.plesk.com](https://get.plesk.com/). For Plesk installation, it is required a fresh Linux server with access to the Internet. You can install Plesk on any supported Linux-based OS. -### Disable Javascript Concatenation +![Image_1](https://i0.wp.com/user-images.githubusercontent.com/19301688/189542599-4fce4d63-8060-416e-9fdf-f21ae62c87e1.png?ssl=1) -To result in faster administration screens, all JavaScript files are [concatenated](https://en.wikipedia.org/wiki/Concatenation) into one URL. If JavaScript is failing to work in an administration screen, you can try disabling this feature: +Provide your server’s IP address or hostname, enter your root password, or just add a private key. -``` -define( 'CONCATENATE_SCRIPTS', false ); +- Accept the terms of End-User License Agreement and click Install button. +- Relax and wait for some time to let the installation be finalized. +- Click on the Login link. No worries about “secure connection warnings”, just make an exception. -``` +### Installing WordPress -### Configure Error Logging +![image_2](https://i0.wp.com/user-images.githubusercontent.com/19301688/189542665-78f52a1c-e92b-4d70-bb5d-899ac02cc57e.png?ssl=1) -Configuring error logging can be a bit tricky. First of all, default PHP error log and display settings are set in the php.ini file, which you may or may not have access to. If you do, they should be set to the desired settings for live PHP pages served to the public. It’s strongly recommended that no error messages are displayed to the public and instead routed to an error log. Further more, error logs should not be located in the publicly accessible portion of your server. Sample recommended php.ini error settings: +- For an express installation, click Install (Quick). The latest version of WordPress will be installed, and the default settings will be used. The new instance will be available via HTTPS if SSL/TLS support is enabled for the domain. +- If you want to change the default installation settings, click Install (Custom). This enables you to set up the administrator user, select the desired WordPress version, specify the database name, select auto-update settings, and more. -``` -error_reporting = 4339 -display_errors = Off -display_startup_errors = Off -log_errors = On -error_log = /home/example.com/logs/php_error.log -log_errors_max_len = 1024 -ignore_repeated_errors = On -ignore_repeated_source = Off -html_errors = Off +### Managing WordPress Instances -``` +Go to WordPress to see all your WordPress instances. WP Toolkit groups information about each instance in blocks we call cards. -**About Error Reporting 4339** This is a custom value that only logs issues that affect the functioning of your site, and ignores things like notices that may not even be errors. See [PHP Error Constants](https://www.php.net/manual/en/errorfunc.constants.php) for the meaning of each binary position for 1000011110011, which is the binary number equal to 4339. The far left 1 means report any E\_RECOVERABLE\_ERROR. The next 0 means do not report E\_STRICT, (which is thrown when sloppy but functional coding is used) and so on. Feel free to determine your own custom error reporting number to use in place of 4339. +![image_3](https://i0.wp.com/user-images.githubusercontent.com/19301688/189542692-5d6f38b5-1b32-4de8-8f40-2abe9a5d1d86.png?ssl=1) -Obviously, you will want different settings for your development environment. If your staging copy is on the same server, or you don’t have access to `php.ini`, you will need to override the default settings at run time. It’s a matter of personal preference whether you prefer errors to go to a log file, or you prefer to be notified immediately of any error, or perhaps both. Here’s an example that reports all errors immediately that you could insert into your `wp-config.php` file: +A card shows a screenshot of your website and features several controls that give you easy access to frequently used tools. The screenshot changes in real time to reflect the changes you make to your website. For example, if you switch the maintenance mode on or change the WordPress theme, the screenshot of the website will change immediately. -``` -@ini_set( 'log_errors', 'Off' ); -@ini_set( 'display_errors', 'On' ); -define( 'WP_DISABLE_FATAL_ERROR_HANDLER', true ); // 5.2 and later -define( 'WP_DEBUG', true ); -define( 'WP_DEBUG_LOG', false ); -define( 'WP_DEBUG_DISPLAY', true ); +### Tools -``` +In the “Tools” section, click to access the following WP Toolkit features: -Because `wp-config.php` is loaded for every page view not loaded from a cache file, it is an excellent location to set `php.ini` settings that control your PHP installation. This is useful if you don’t have access to a `php.ini` file, or if you just want to change some settings on the fly. One exception is ‘error\_reporting’. When WP\_DEBUG is defined as true, ‘error\_reporting’ will be set to E\_ALL by WordPress regardless of anything you try to set in wp-config.php. If you really have a need to set ‘error\_reporting’ to something else, it must be done after `wp-settings.php` is loaded, such as in a plugin file. +![image_4](https://i0.wp.com/user-images.githubusercontent.com/19301688/189542713-abf476de-fcbd-4113-9975-1c2961765190.png?ssl=1) -If you turn on error logging, remember to delete the file afterwards, as it will often be in a publicly accessible location, where anyone could gain access to your log. +- “Sync” to synchronize the content of your website with another one. +- “Clone” to make a full copy of your website. +- “Manage Files” to manage the website’s files in File Manager. +- “Back Up/Restore” to create a backup of your website and restore it if necessary. -Here is an example that turns PHP error\_logging on and logs them to a specific file. If WP\_DEBUG is defined to true, the errors will also be saved to this file. Just place this above any *require\_once* or *include* commands. +The controls below give you easy access to the following settings and tools: -``` -@ini_set( 'log_errors', 'On' ); -@ini_set( 'display_errors', 'Off' ); -@ini_set( 'error_log', '/home/example.com/logs/php_error.log' ); -/* That's all, stop editing! Happy blogging. */ +- “Search engine indexing” shows your website in search results of search engines. +- “Caching (nginx)” speeds up the website load time and reduces server load. +- “Debugging” helps you debug a website that is not ready for viewing and being tested or developed. +- “Maintenance mode” hides your website’s content from visitors. +- “Password Protection” specifies the password you will use to log in to WordPress from Plesk. -``` +## Changelog -Another example of logging errors, as suggested by Mike Little on the [wp-hackers email list](https://lists.automattic.com/pipermail/wp-hackers/2010-September/034830.html): +- 2023-04-25: Removed outdated manual instructions from cPanel section and combined common WP Toolkit info for cPanel and Plesk. +- 2023-01-26: Original copied from [Using cPanel](https://wordpress.org/documentation/article/using-cpanel/). +- 2022-09-11: Original copied for Plesk. -``` -/** - * This will log all errors notices and warnings to a file called debug.log in - * wp-content (if Apache does not have write permission, you may need to create - * the file first and set the appropriate permissions (i.e. use 666) ) - */ -define( 'WP_DEBUG', true ); -define( 'WP_DEBUG_LOG', true ); -define( 'WP_DEBUG_DISPLAY', false ); -@ini_set( 'display_errors', 0 ); +--- -``` +# WordPress configuration -A refined version from Mike Little on the [Manchester WordPress User Group](https://groups.google.com/g/manchester-wordpress-user-group/c/tHJxMGhcnZs/m/dn-8yjYIq9wJ): +Source: https://developer.wordpress.org/advanced-administration/wordpress/ -``` -/** - * This will log all errors notices and warnings to a file called debug.log in - * wp-content only when WP_DEBUG is true. if Apache does not have write permission, - * you may need to create the file first and set the appropriate permissions (i.e. use 666). - */ -define( 'WP_DEBUG', true ); // Or false -if ( WP_DEBUG ) { - define( 'WP_DEBUG_LOG', true ); - define( 'WP_DEBUG_DISPLAY', false ); - @ini_set( 'display_errors', 0 ); -} - -``` - -Confusing the issue is that WordPress has three (3) constants that look like they could do the same thing. First off, remember that if WP\_DEBUG is false, it and the other two WordPress DEBUG constants do not do anything. The PHP directives, whatever they are, will prevail. Except for ‘error\_reporting’, WordPress will set this to 4983 if WP\_DEBUG is defined as false. Second, even if WP\_DEBUG is true, the other constants only do something if they too are set to true. If they are set to false, the PHP directives remain unchanged. For example, if your `php.ini` file has the directive (‘display\_errors’ = ‘On’); but you have the statement define( ‘WP\_DEBUG\_DISPLAY’, false ); in your `wp-config.php` file, errors will still be displayed on screen even though you tried to prevent it by setting WP\_DEBUG\_DISPLAY to false because that is the PHP configured behavior. This is why it’s very important to set the PHP directives to what you need in case any of the related WP constants are set to false. To be safe, explicitly set/define both types. More detailed descriptions of the WP constants is available at [Debugging in WordPress](#advanced-administration/debug/debug-wordpress). - -For your public, production WordPress installation, you might consider placing the following in your `wp-config.php` file, even though it may be partly redundant: +## Changelog -``` -@ini_set( 'log_errors', 'On' ); -@ini_set( 'display_errors', 'Off' ); -define( 'WP_DISABLE_FATAL_ERROR_HANDLER', false ); // 5.2 and later -define( 'WP_DEBUG', false ); -define( 'WP_DEBUG_LOG', false ); -define( 'WP_DEBUG_DISPLAY', false ); +- 2022-08-16: Nothing here, yet. -``` +--- -The default debug log file is `/wp-content/debug.log`. Placing error logs in publicly accessible locations is a security risk. Ideally, your log files should be placed above you site’s public root directory. If you can’t do this, at the very least, set the log file permissions to 600 and add this entry to the `.htaccess` file in the root directory of your WordPress installation: +# Editing wp-config.php -``` - - Order allow,deny - Deny from all - +Source: https://developer.wordpress.org/advanced-administration/wordpress/wp-config/ -``` +One of the most important files in your WordPress installation is the `wp-config.php` file. This file is located in the root of your WordPress file directory and contains your website’s base configuration details, such as database connection information. -This prevents anyone from accessing the file via HTTP. You can always view the log file by retrieving it from your server via FTP. +When you first download WordPress, the `wp-config.php` file isn’t included. The WordPress setup process will create a `wp-config.php` file for you based on the information you provide in the [installation](#advanced-administration/before-install/howto-install) process. -### Increasing memory allocated to PHP +It is unlikely that a non-developer would have to edit the wp-config.php file, in the case you are acting on trouble shooting steps provided by a technical person or by your webhost, this [page](#advanced-administration/wordpress/wp-config) should help. -**WP\_MEMORY\_LIMIT** option allows you to specify the maximum amount of memory that can be consumed by PHP. This setting may be necessary in the event you receive a message such as “Allowed memory size of xxxxxx bytes exhausted”. +# wp-config.php -This setting increases PHP Memory only for WordPress, not other applications. By default, WordPress will attempt to increase memory allocated to PHP to 40MB (code is at the beginning of `/wp-includes/default-constants.php`) for single site and 64MB for multisite, so the setting in `wp-config.php` should reflect something higher than 40MB or 64MB depending on your setup. +TEMPORALLY NOTE: this may link for the simple part, to: +\* #advanced-administration/wordpress/wp-config +\* #advanced-administration/debug/debug-wordpress -WordPress will automatically check if PHP has been allocated less memory than the entered value before utilizing this function. For example, if PHP has been allocated 64MB, there is no need to set this value to 64M as WordPress will automatically use all 64MB if need be. +## Advanced Options -Note: Some hosts do not allow for increasing the PHP memory limit automatically. In that event, contact your host to increase the PHP memory limit. Also, many hosts set the PHP limit at 8MB. +The following sections may contain advanced information and some changes might result in unforeseen issues. Please make sure you practice [regular backups](#advanced-administration/security/backup) and know how to restore them before modifying these settings. -Adjusting the WordPress memory limit potentially creates problems as well. You might end up hiding the root of the issue for it to happen later down the line as you add in more plugins or functionalities. +### table\_prefix -If you are facing Out of Memory issues even with an elevated memory limit, you should properly debug your installation. Chances are you have too many memory intensive functions tied to a specific action and should move these functions to a cronjob. +The **$table\_prefix** is the value placed in the front of your database tables. Change the value if you want to use something other than **wp\_** for your database prefix. Typically this is changed if you are [installing multiple WordPress blogs](#advanced-administration/before-install/multiple-instances) in the same database, as is done with the multisite feature. -Increase PHP Memory to 64MB +It is possible to have multiple installations in one database if you give each a unique prefix. Keep security in mind if you choose to do this. ``` -define( 'WP_MEMORY_LIMIT', '64M' ); +$table_prefix = 'example123_'; // Only numbers, letters, and underscores please! ``` -Increase PHP Memory to 96MB +### WP\_SITEURL + +WP\_SITEURL allows the WordPress address (URL) to be defined. The value defined is the address where your WordPress core files reside. It should include the https:// part too. Do not put a slash “**/**” at the end. Setting this value in `wp-config.php` overrides the [wp\_options table](https://codex.wordpress.org/Database_Description#Table:_wp_options) value for **siteurl**. Adding this in can reduce the number of database calls when loading your site. **Note:** This will **not** change the database stored value. The URL will revert to the old database value if this line is ever removed from `wp-config`. [Use the **RELOCATE** constant](#advanced-administration/upgrade/migrating) to change the **siteurl** value in the database. + +If WordPress is installed into a directory called “wordpress” for the [domain](https://en.wikipedia.org/wiki/Domain_name_system) example.com, define `WP_SITEURL` like this: ``` -define( 'WP_MEMORY_LIMIT', '96M' ); +define( 'WP_SITEURL', 'https://example.com/wordpress' ); ``` -Administration tasks require may require memory than usual operation. When in the administration area, the memory can be increased or decreased from the WP\_MEMORY\_LIMIT by defining WP\_MAX\_MEMORY\_LIMIT. +Dynamically set WP\_SITEURL based on $\_SERVER\[‘HTTP\_HOST’\] ``` -define( 'WP_MAX_MEMORY_LIMIT', '128M' ); +define( 'WP_SITEURL', 'https://' . $_SERVER['HTTP_HOST'] . '/path/to/wordpress' ); ``` -Note: this has to be put before wp-settings.php inclusion. - -### Cache +**Note:** HTTP\_HOST is created dynamically by PHP based on the value of the HTTP HOST Header in the request, thus possibly allowing for [file inclusion vulnerabilities](https://en.wikipedia.org/wiki/File_inclusion_vulnerability). SERVER\_NAME may also be created dynamically. However, when Apache is configured as UseCanonicalName “on”, SERVER\_NAME is set by the server configuration, instead of dynamically. In that case, it is safer to user SERVER\_NAME than HTTP\_HOST. -The **WP\_CACHE** setting, if true, includes the `wp-content/advanced-cache.php` script, when executing `wp-settings.php`. +Dynamically set `WP_SITEURL` based on `$_SERVER['SERVER_NAME']` ``` -define( 'WP_CACHE', true ); +define( 'WP_SITEURL', 'https://' . $_SERVER['SERVER_NAME'] . '/path/to/wordpress' ); ``` -### Custom User and Usermeta Tables +### Blog address (URL) -**CUSTOM\_USER\_TABLE** and **CUSTOM\_USER\_META\_TABLE** are used to designate that the user and usermeta tables normally utilized by WordPress are not used, instead these values/tables are used to store your user information. +Similar to WP\_SITEURL, WP\_HOME *overrides the [wp\_options table](https://codex.wordpress.org/Database_Description#Table:_wp_options) value for* home *but does not change it in the database.* **home** is the address you want people to type in their browser to reach your WordPress blog. It should include the https:// part and should not have a slash “**/**” at the end. Adding this in can reduce the number of database calls when loading your site. ``` -define( 'CUSTOM_USER_TABLE', $table_prefix.'my_users' ); -define( 'CUSTOM_USER_META_TABLE', $table_prefix.'my_usermeta' ); +define( 'WP_HOME', 'https://example.com/wordpress' ); ``` -Note: Even if ‘CUSTOM\_USER\_META\_TABLE’ is manually set, a usermeta table is still created for each database with the corresponding permissions for each instance. By default, the WordPress installer will add permissions for the first user (ID #1). You also need to manage permissions to each of the site via a plugin or custom function. If this isn’t setup you will experience permission errors and log-in issues. - -CUSTOM\_USER\_TABLE is easiest to adopt during initial Setup your first instance of WordPress. The define statements of the `wp-config.php` on the first instance point to where `wp_users` data will be stored by default. After the first site setup, copying the working `wp-config.php` to your next instance will only require a change the `$table_prefix` variable. Do not use an e-mail address that is already in use by your original install. Once you have finished the setup process log in with the auto generated admin account and password. Next, promote your normal account to the administrator level and Log out of admin. Log back in as yourself, delete the admin account and promote the other user accounts as is needed. - -### Language and Language Directory +If you are using the technique described in [Giving WordPress Its Own Directory](#advanced-administration/server/wordpress-in-directory) then follow the example below. Remember, you will also be placing an `index.php` in your web-root directory if you use a setting like this. -WordPress [Version 4.0](https://wordpress.org/documentation/wordpress-version/version-4-0/) allows you to change the language in your WordPress [Administration Screens](https://wordpress.org/documentation/article/administration-screens/). To change the language in the admin settings screen. Go to [Settings](https://wordpress.org/documentation/article/administration-screens/#settings-configuration-settings) > [General](https://wordpress.org/documentation/article/settings-general-screen/) and select Site Language. +``` +define( 'WP_HOME', 'https://example.com' ); -#### WordPress v3.9.6 and below +``` -**WPLANG** defines the name of the language translation (.mo) file. **WP\_LANG\_DIR** defines what directory the WPLANG .mo file resides. If WP\_LANG\_DIR is not defined WordPress looks first to wp-content/languages and then `wp-includes/languages` for the .mo defined by WPLANG file. +Dynamically set `WP_HOME` based on `$_SERVER['HTTP_HOST']` ``` -define( 'WPLANG', 'de_DE' ); -define( 'WP_LANG_DIR', dirname(__FILE__) . 'wordpress/languages' ); +define( 'WP_HOME', 'https://' . $_SERVER['HTTP_HOST'] . '/path/to/wordpress' ); ``` -To find out the WPLANG language code, please [refer here](https://make.wordpress.org/polyglots/teams/). The code in WP Local column is what you need. - -### Save queries for analysis +### Moving wp-content folder -The **SAVEQUERIES** definition saves the database queries to an array and that array can be displayed to help analyze those queries. The information saves each query, what function called it, and how long that query took to execute. **Note:** This will have a performance impact on your site, so make sure to turn this off when you aren’t debugging. +You can move the `wp-content` directory, which holds your themes, plugins, and uploads, outside of the WordPress application directory. -First, add this to the `wp-config.php` file: +Set WP\_CONTENT\_DIR to the full **local path** of this directory (no trailing slash), e.g. ``` -define( 'SAVEQUERIES', true ); +define( 'WP_CONTENT_DIR', dirname(__FILE__) . '/blog/wp-content' ); ``` -Then in the footer of your theme put this: +Set WP\_CONTENT\_URL to the full **URL** of this directory (no trailing slash), e.g. ``` -if ( current_user_can( 'administrator' ) ) { - global $wpdb; - echo ""; - print_r( $wpdb->queries ); - echo ""; -} +define( 'WP_CONTENT_URL', 'https://example/blog/wp-content' ); ``` -Alternatively, consider using [Query Monitor](https://wordpress.org/plugins/query-monitor/) - -### Override of default file permissions +### Moving plugin folder -The **FS\_CHMOD\_DIR** and **FS\_CHMOD\_FILE** define statements allow override of default file permissions. These two variables were developed in response to the problem of the core update function failing with hosts running under [suexec](https://en.wikipedia.org/wiki/SuEXEC). If a host uses restrictive file permissions (e.g. 400) for all user files, and refuses to access files which have group or world permissions set, these definitions could solve the problem. +Set WP\_PLUGIN\_DIR to the full **local path** of this directory (no trailing slash), e.g. ``` -define( 'FS_CHMOD_DIR', ( 0755 & ~ umask() ) ); -define( 'FS_CHMOD_FILE', ( 0644 & ~ umask() ) ); +define( 'WP_PLUGIN_DIR', dirname(__FILE__) . '/blog/wp-content/plugins' ); ``` -Example to provide setgid: +Set WP\_PLUGIN\_URL to the full **URI** of this directory (no trailing slash), e.g. ``` -define( 'FS_CHMOD_DIR', ( 02755 & ~umask() ) ); +define( 'WP_PLUGIN_URL', 'https://example/blog/wp-content/plugins' ); ``` -Note: ‘**0755′** and ‘**02755**‘ are octal values. Octal values must be prefixed with a 0 and are not delineated with single quotes (‘). See Also: [Changing File Permissions](#advanced-administration/server/file-permissions) - -### WordPress Upgrade Constants - -**Note: Define as few of the below constants as needed to correct your update issues.** - -The most common causes of needing to define these are: +If you have compatibility issues with plugins Set PLUGINDIR to the full **local path** of this directory (no trailing slash), e.g. -Host running with a special installation setup involving symlinks. You may need to define the path-related constants (FTP\_BASE, FTP\_CONTENT\_DIR, and FTP\_PLUGIN\_DIR). Often defining simply the base will be enough. +``` +define( 'PLUGINDIR', dirname(__FILE__) . '/blog/wp-content/plugins' ); -Certain PHP installations shipped with a PHP FTP extension which is incompatible with certain FTP servers. Under these rare situations, you may need to define FS\_METHOD to “ftpsockets”. +``` -The following are valid constants for WordPress updates: +### Moving themes folder -- **FS\_METHOD** forces the filesystem method. It should only be “direct”, “ssh2”, “ftpext”, or “ftpsockets”. Generally, you should only change this if you are experiencing update problems. If you change it and it doesn’t help, **change it back/remove it**. Under most circumstances, setting it to ‘ftpsockets’ will work if the automatically chosen method does not. - - **(Primary Preference) “direct”** forces it to use Direct File I/O requests from within PHP, this is fraught with opening up security issues on poorly configured hosts, This is chosen automatically when appropriate. - - **(Secondary Preference) “ssh2”** is to force the usage of the SSH PHP Extension if installed - - **(3rd Preference) “ftpext”** is to force the usage of the FTP PHP Extension for FTP Access, and finally - - **(4th Preference) “ftpsockets”** utilises the PHP Sockets Class for FTP Access. -- **FTP\_BASE** is the full path to the “base”(ABSPATH) folder of the WordPress installation. -- **FTP\_CONTENT\_DIR** is the full path to the wp-content folder of the WordPress installation. -- **FTP\_PLUGIN\_DIR** is the full path to the plugins folder of the WordPress installation. -- **FTP\_PUBKEY** is the full path to your SSH public key. -- **FTP\_PRIKEY** is the full path to your SSH private key. -- **FTP\_USER** is either user FTP or SSH username. Most likely these are the same, but use the appropriate one for the type of update you wish to do. -- **FTP\_PASS** is the password for the username entered for **FTP\_USER**. If you are using SSH public key authentication this can be omitted. -- **FTP\_HOST** is the hostname:port combination for your SSH/FTP server. The default FTP port is 21 and the default SSH port is 22. These do not need to be mentioned. -- **FTP\_SSL** TRUE for SSL-connection *if supported by the underlying transport* (not available on all servers). This is for “Secure FTP” not for SSH SFTP. +You cannot move the themes folder because its path is hardcoded relative to the `wp-content` folder: ``` -define( 'FS_METHOD', 'ftpext' ); -define( 'FTP_BASE', '/path/to/wordpress/' ); -define( 'FTP_CONTENT_DIR', '/path/to/wordpress/wp-content/' ); -define( 'FTP_PLUGIN_DIR ', '/path/to/wordpress/wp-content/plugins/' ); -define( 'FTP_PUBKEY', '/home/username/.ssh/id_rsa.pub' ); -define( 'FTP_PRIKEY', '/home/username/.ssh/id_rsa' ); -define( 'FTP_USER', 'username' ); -define( 'FTP_PASS', 'password' ); -define( 'FTP_HOST', 'ftp.example.org' ); -define( 'FTP_SSL', false ); +$theme_root = WP_CONTENT_DIR . '/themes'; ``` -Some configurations should set FTP\_HOST to localhost to avoid 503 problems when trying to update plugins or WP itself. - -#### Enabling SSH Upgrade Access +However, you can register additional theme directories using [register\_theme\_directory](#reference/functions/register_theme_directory). -There are two ways to upgrade using SSH2. +See how to [move the wp-content](#advanced-administration/wordpress/wp-config) folder. For more details how the themes folder is determined, see `wp-includes/theme.php`. -The first is to use the [SSH SFTP Updater Support plugin](https://wordpress.org/plugins/ssh-sftp-updater-support/). The second is to use the built-in SSH2 upgrader, which requires the pecl SSH2 extension be installed. +### Moving uploads folder -To install the pecl SSH2 extension you will need to issue a command similar to the following or talk to your web hosting provider to get this installed: +Set UPLOADS to: ``` -pecl install ssh2 +define( 'UPLOADS', 'blog/wp-content/uploads' ); ``` -After installing the pecl ssh2 extension you will need to modify your PHP configuration to automatically load this extension. +This path can not be absolute. It is always relative to ABSPATH, therefore does not require a leading slash. -pecl is provided by the pear package in most linux distributions. To install pecl in Redhat/Fedora/CentOS: +### Modify AutoSave Interval -``` -yum -y install php-pear +When editing a post, WordPress uses Ajax to auto-save revisions to the post as you edit. You may want to increase this setting for longer delays in between auto-saves, or decrease the setting to make sure you never lose changes. The default is 60 seconds. ``` - -To install pecl in Debian/Ubuntu: +define( 'AUTOSAVE_INTERVAL', 180 ); // Seconds ``` -apt-get install php-pear -``` +### Post Revisions -It is recommended to use a private key that is not pass-phrase protected. There have been numerous reports that pass phrase protected private keys do not work properly. If you decide to try a pass phrase protected private key you will need to enter the pass phrase for the private key as FTP\_PASS, or entering it in the “Password” field in the presented credential field when installing updates. +WordPress, by default, will save copies of each edit made to a post or page, allowing the possibility of reverting to a previous version of that post or page. The saving of revisions can be disabled, or a maximum number of revisions per post or page can be specified. -### Alternative Cron +#### Disable Post Revisions -There might be reason to use an alternative Cron with WP. Most commonly this is done if scheduled posts are not getting published as predicted. This alternative method uses a redirection approach. The users’ browser get a redirect when the cron needs to run, so that they come back to the site immediately while cron continues to run in the connection they just dropped. This method has certain risks, since it depends on a non-native WordPress service. +If you do **not** set this value, WordPress defaults WP\_POST\_REVISIONS to *true* (enable post revisions). If you want to disable the awesome revisions feature, use this setting: ``` -define( 'ALTERNATE_WP_CRON', true ); +define( 'WP_POST_REVISIONS', false ); ``` -### Disable Cron and Cron Timeout +Note: Some users could not get this to function until moving the command to the first line under the initial block comment in `wp-config.php`. -Disable cron entirely by setting DISABLE\_WP\_CRON to true. +#### Specify the Number of Post Revisions -``` -define( 'DISABLE_WP_CRON', true ); +If you want to specify a maximum number of revisions that WordPress stores, change *false* to an integer/number (*e.g.*, 3 or 12). ``` - -Make sure a cron process cannot run more than once every WP\_CRON\_LOCK\_TIMEOUT seconds. +define( 'WP_POST_REVISIONS', 3 ); ``` -define( 'WP_CRON_LOCK_TIMEOUT', 60 ); -``` +Note: Some users could not get this to function until moving the command to the first line under the initial block comment in `wp-config.php`. -### Additional Defined Constants +### Set Cookie Domain -Here are additional constants that can be defined. These probably shouldn’t be set unless other methodologies have been attempted first. The Cookie definitions can be particularly useful if you have an unusual domain setup. +The domain set in the cookies for WordPress can be specified for those with unusual domain setups. For example, if subdomains are used to serve static content, you can set the cookie domain to only your non-static domain to prevent WordPress cookies from being sent with each request to static content on your subdomain . ``` -define( 'COOKIEPATH', preg_replace( '|https?://[^/]+|i', '', get_option( 'home' ) . '/' ) ); -define( 'SITECOOKIEPATH', preg_replace( '|https?://[^/]+|i', '', get_option( 'siteurl' ) . '/' ) ); -define( 'ADMIN_COOKIE_PATH', SITECOOKIEPATH . 'wp-admin' ); -define( 'PLUGINS_COOKIE_PATH', preg_replace( '|https?://[^/]+|i', '', WP_PLUGIN_URL ) ); -define( 'TEMPLATEPATH', get_template_directory() ); -define( 'STYLESHEETPATH', get_stylesheet_directory() ); +define( 'COOKIE_DOMAIN', 'www.example.com' ); ``` -### Empty Trash +### Enable Multisite / Network Ability -This constant controls the number of days before WordPress permanently deletes posts, pages, attachments, and comments, from the trash bin. The default is 30 days: +WP\_ALLOW\_MULTISITE is a feature enable multisite functionality. If this setting is absent from `wp-config.php` it defaults to false. ``` -define( 'EMPTY_TRASH_DAYS', 30 ); // 30 days +define( 'WP_ALLOW_MULTISITE', true ); ``` -To disable trash set the number of days to zero. +### Redirect Nonexistent Blogs + +NOBLOGREDIRECT can be used to redirect the browser if the visitor tries to access a nonexistent subdomain or a subfolder. ``` -define( 'EMPTY_TRASH_DAYS', 0 ); // Zero days +define( 'NOBLOGREDIRECT', 'https://example.com' ); ``` -**Note:** WordPress will not ask for confirmation when someone clicks on “Delete Permanently” using this setting. +### Fatal Error Handler -### Automatic Database Optimizing +WordPress 5.2 introduced [Recovery Mode](https://make.wordpress.org/core/2019/04/16/fatal-error-recovery-mode-in-5-2/) which displays error message instead of white screen when plugins causes fatal error. -There is automatic database repair support, which you can enable by adding the following define to your `wp-config.php` file. +*The site is experiencing technical difficulties. Please check your site admin email inbox for instructions.* -Note: This should only be enabled if needed and disabled once the issue is solved. When enabled, a user does not need to be logged in to access the functionality, since its main intent is to repair a corrupted database and users can often not login when the database is corrupt. +White screens and PHP error messages are not displayed to users any more. But in a development environment, if you want to enable WP\_DEBUG\_DISPLAY, you have to disable recovery mode by set true to WP\_DISABLE\_FATAL\_ERROR\_HANDLER. ``` -define( 'WP_ALLOW_REPAIR', true ); +define( 'WP_DISABLE_FATAL_ERROR_HANDLER', true ); // 5.2 and later +define( 'WP_DEBUG', true ); +define( 'WP_DEBUG_DISPLAY', true ); ``` -The script can be found at `{$your_site}/wp-admin/maint/repair.php`. - -### DO\_NOT\_UPGRADE\_GLOBAL\_TABLES - -A **DO\_NOT\_UPGRADE\_GLOBAL\_TABLES** define prevents [dbDelta()](#reference/functions/dbdelta) and the upgrade functions from doing expensive queries against global tables. +### WP\_DEBUG -Sites that have large global tables (particularly users and usermeta), as well as sites that share user tables with bbPress and other WordPress installs, can prevent the upgrade from changing those tables during upgrade by defining **DO\_NOT\_UPGRADE\_GLOBAL\_TABLES** to true. Since an ALTER, or an unbounded DELETE or UPDATE, can take a long time to complete, large sites usually want to avoid these being run as part of the upgrade so they can handle it themselves. Further, if installations are sharing user tables between multiple bbPress and WordPress installs you may to want one site to be the upgrade master. +The [WP\_DEBUG](#advanced-administration/debug/debug-wordpress) option controls the reporting of some errors and warnings and enables use of the WP\_DEBUG\_DISPLAY and WP\_DEBUG\_LOG settings. The default boolean value is false. ``` -define( 'DO_NOT_UPGRADE_GLOBAL_TABLES', true ); +define( 'WP_DISABLE_FATAL_ERROR_HANDLER', true ); // 5.2 and later +define( 'WP_DEBUG', true ); ``` -### View All Defined Constants +[Database errors are printed only if WP\_DEBUG is set to true](https://trac.wordpress.org/ticket/5473). Database errors are handled by the [wpdb](#reference/classes/wpdb) class and are not affected by [PHP’s error settings](https://www.php.net/errorfunc). -PHP has a function that returns an array of all the currently defined constants with their values. +Setting WP\_DEBUG to true also raises the [error reporting level](https://www.php.net/manual/en/errorfunc.configuration.php#ini.error-reporting) to E\_ALL and activates warnings when deprecated functions or files are used; otherwise, WordPress sets the error reporting level to E\_ALL ^ E\_NOTICE ^ E\_USER\_NOTICE. -``` -print_r( @get_defined_constants() ); +### WP\_ENVIRONMENT\_TYPE -``` +The WP\_ENVIRONMENT\_TYPE option controls the environment type for a site: `local`, `development`, `staging`, and `production`. -### Disable the Plugin and Theme File Editor +The values of environment types are processed in the following order with each sequential method overriding any previous values: the WP\_ENVIRONMENT\_TYPE [PHP environment variable](https://www.php.net/manual/en/reserved.variables.environment.php) and the WP\_ENVIRONMENT\_TYPE constant. -Occasionally you may wish to disable the plugin or theme file editor to prevent overzealous users from being able to edit sensitive files and potentially crash the site. Disabling these also provides an additional layer of security if a hacker gains access to a well-privileged user account. +For both methods, if the value of an environment type provided is not in the list of allowed environment types, the default `production` value will be returned. + +The simplest way to set the value is probably through defining the constant: ``` -define( 'DISALLOW_FILE_EDIT', true ); +define( 'WP_ENVIRONMENT_TYPE', 'staging' ); ``` -**Note**: The functionality of some plugins may be affected by the use of `current_user_can('edit_plugins')` in their code. Plugin authors should avoid checking for this capability, or at least check if this constant is set and display an appropriate error message. Be aware that if a plugin is not working this may be the cause. +Note: When `development` is returned by [wp\_get\_environment\_type()](#reference/functions/wp_get_environment_type), WP\_DEBUG will be set to `true` if it is not defined in the `wp-config.php` file of the site. -### Disable Plugin and Theme Update and Installation +### SCRIPT\_DEBUG -This will block users being able to use the plugin and theme installation/update functionality from the WordPress admin area. Setting this constant also disables the Plugin and Theme File editor (i.e. you don’t need to set DISALLOW\_FILE\_MODS and DISALLOW\_FILE\_EDIT, as on its own DISALLOW\_FILE\_MODS will have the same effect). +[SCRIPT\_DEBUG](#advanced-administration/debug/debug-wordpress) is a related constant that will force WordPress to use the “dev” versions of scripts and stylesheets in `wp-includes/js`, `wp-includes/css`, `wp-admin/js`, and `wp-admin/css` will be loaded instead of the `.min.css` and `.min.js` versions. If you are planning on modifying some of WordPress’ built-in JavaScript or Cascading Style Sheets, you should add the following code to your config file: ``` -define( 'DISALLOW_FILE_MODS', true ); +define( 'SCRIPT_DEBUG', true ); ``` -### Require SSL for Admin and Logins - -**Note:** WordPress [Version 4.0](https://wordpress.org/documentation/wordpress-version/version-4-0/) deprecated FORCE\_SSL\_LOGIN. Please use FORCE\_SSL\_ADMIN. +### Disable Javascript Concatenation -FORCE\_SSL\_ADMIN is for when you want to secure logins and the admin area so that both passwords and cookies are never sent in the clear. See also [HTTPS](#advanced-administration/security/https) for more details. +To result in faster administration screens, all JavaScript files are [concatenated](https://en.wikipedia.org/wiki/Concatenation) into one URL. If JavaScript is failing to work in an administration screen, you can try disabling this feature: ``` -define( 'FORCE_SSL_ADMIN', true ); +define( 'CONCATENATE_SCRIPTS', false ); ``` -### Block External URL Requests +### Configure Error Logging -Block external URL requests by defining WP\_HTTP\_BLOCK\_EXTERNAL as true and this will only allow localhost and your blog to make requests. The constant WP\_ACCESSIBLE\_HOSTS will allow additional hosts to go through for requests. The format of the WP\_ACCESSIBLE\_HOSTS constant is a comma separated list of hostnames to allow, wildcard domains are supported, eg `*.wordpress.org` will allow for all subdomains of wordpress.org to be contacted. +Configuring error logging can be a bit tricky. First of all, default PHP error log and display settings are set in the php.ini file, which you may or may not have access to. If you do, they should be set to the desired settings for live PHP pages served to the public. It’s strongly recommended that no error messages are displayed to the public and instead routed to an error log. Further more, error logs should not be located in the publicly accessible portion of your server. Sample recommended php.ini error settings: ``` -define( 'WP_HTTP_BLOCK_EXTERNAL', true ); -define( 'WP_ACCESSIBLE_HOSTS', 'api.wordpress.org,*.github.com' ); +error_reporting = 4339 +display_errors = Off +display_startup_errors = Off +log_errors = On +error_log = /home/example.com/logs/php_error.log +log_errors_max_len = 1024 +ignore_repeated_errors = On +ignore_repeated_source = Off +html_errors = Off ``` -### Disable WordPress Auto Updates +**About Error Reporting 4339** This is a custom value that only logs issues that affect the functioning of your site, and ignores things like notices that may not even be errors. See [PHP Error Constants](https://www.php.net/manual/en/errorfunc.constants.php) for the meaning of each binary position for 1000011110011, which is the binary number equal to 4339. The far left 1 means report any E\_RECOVERABLE\_ERROR. The next 0 means do not report E\_STRICT, (which is thrown when sloppy but functional coding is used) and so on. Feel free to determine your own custom error reporting number to use in place of 4339. -There might be reason for a site to not auto-update, such as customizations or host supplied updates. It can also be done before a major release to allow time for testing on a development or staging environment before allowing the update on a production site. +Obviously, you will want different settings for your development environment. If your staging copy is on the same server, or you don’t have access to `php.ini`, you will need to override the default settings at run time. It’s a matter of personal preference whether you prefer errors to go to a log file, or you prefer to be notified immediately of any error, or perhaps both. Here’s an example that reports all errors immediately that you could insert into your `wp-config.php` file: ``` -define( 'AUTOMATIC_UPDATER_DISABLED', true ); +@ini_set( 'log_errors', 'Off' ); +@ini_set( 'display_errors', 'On' ); +define( 'WP_DISABLE_FATAL_ERROR_HANDLER', true ); // 5.2 and later +define( 'WP_DEBUG', true ); +define( 'WP_DEBUG_LOG', false ); +define( 'WP_DEBUG_DISPLAY', true ); ``` -### Disable WordPress Core Updates +Because `wp-config.php` is loaded for every page view not loaded from a cache file, it is an excellent location to set `php.ini` settings that control your PHP installation. This is useful if you don’t have access to a `php.ini` file, or if you just want to change some settings on the fly. One exception is ‘error\_reporting’. When WP\_DEBUG is defined as true, ‘error\_reporting’ will be set to E\_ALL by WordPress regardless of anything you try to set in wp-config.php. If you really have a need to set ‘error\_reporting’ to something else, it must be done after `wp-settings.php` is loaded, such as in a plugin file. -The easiest way to manipulate core updates is with the WP\_AUTO\_UPDATE\_CORE constant: +If you turn on error logging, remember to delete the file afterwards, as it will often be in a publicly accessible location, where anyone could gain access to your log. -**Disable all core updates:** +Here is an example that turns PHP error\_logging on and logs them to a specific file. If WP\_DEBUG is defined to true, the errors will also be saved to this file. Just place this above any *require\_once* or *include* commands. ``` -define( 'WP_AUTO_UPDATE_CORE', false ); +@ini_set( 'log_errors', 'On' ); +@ini_set( 'display_errors', 'Off' ); +@ini_set( 'error_log', '/home/example.com/logs/php_error.log' ); +/* That's all, stop editing! Happy blogging. */ ``` -**Enable all core updates, including minor and major:** +Another example of logging errors, as suggested by Mike Little on the [wp-hackers email list](https://lists.automattic.com/pipermail/wp-hackers/2010-September/034830.html): ``` -define( 'WP_AUTO_UPDATE_CORE', true ); +/** + * This will log all errors notices and warnings to a file called debug.log in + * wp-content (if Apache does not have write permission, you may need to create + * the file first and set the appropriate permissions (i.e. use 666) ) + */ +define( 'WP_DEBUG', true ); +define( 'WP_DEBUG_LOG', true ); +define( 'WP_DEBUG_DISPLAY', false ); +@ini_set( 'display_errors', 0 ); ``` -**Enable core updates for minor releases (default):** +A refined version from Mike Little on the [Manchester WordPress User Group](https://groups.google.com/g/manchester-wordpress-user-group/c/tHJxMGhcnZs/m/dn-8yjYIq9wJ): ``` -define( 'WP_AUTO_UPDATE_CORE', 'minor' ); +/** + * This will log all errors notices and warnings to a file called debug.log in + * wp-content only when WP_DEBUG is true. if Apache does not have write permission, + * you may need to create the file first and set the appropriate permissions (i.e. use 666). + */ +define( 'WP_DEBUG', true ); // Or false +if ( WP_DEBUG ) { + define( 'WP_DEBUG_LOG', true ); + define( 'WP_DEBUG_DISPLAY', false ); + @ini_set( 'display_errors', 0 ); +} ``` -Reference: [Disabling Auto Updates in WordPress 3.7](https://make.wordpress.org/core/2013/10/25/the-definitive-guide-to-disabling-auto-updates-in-wordpress-3-7/) - -### Cleanup Image Edits +Confusing the issue is that WordPress has three (3) constants that look like they could do the same thing. First off, remember that if WP\_DEBUG is false, it and the other two WordPress DEBUG constants do not do anything. The PHP directives, whatever they are, will prevail. Except for ‘error\_reporting’, WordPress will set this to 4983 if WP\_DEBUG is defined as false. Second, even if WP\_DEBUG is true, the other constants only do something if they too are set to true. If they are set to false, the PHP directives remain unchanged. For example, if your `php.ini` file has the directive (‘display\_errors’ = ‘On’); but you have the statement define( ‘WP\_DEBUG\_DISPLAY’, false ); in your `wp-config.php` file, errors will still be displayed on screen even though you tried to prevent it by setting WP\_DEBUG\_DISPLAY to false because that is the PHP configured behavior. This is why it’s very important to set the PHP directives to what you need in case any of the related WP constants are set to false. To be safe, explicitly set/define both types. More detailed descriptions of the WP constants is available at [Debugging in WordPress](#advanced-administration/debug/debug-wordpress). -By default, WordPress creates a new set of images every time you edit an image and when you restore the original, it leaves all the edits on the server. Defining IMAGE\_EDIT\_OVERWRITE as true changes this behavior. Only one set of image edits are ever created and when you restore the original, the edits are removed from the server. +For your public, production WordPress installation, you might consider placing the following in your `wp-config.php` file, even though it may be partly redundant: ``` -define( 'IMAGE_EDIT_OVERWRITE', true ); +@ini_set( 'log_errors', 'On' ); +@ini_set( 'display_errors', 'Off' ); +define( 'WP_DISABLE_FATAL_ERROR_HANDLER', false ); // 5.2 and later +define( 'WP_DEBUG', false ); +define( 'WP_DEBUG_LOG', false ); +define( 'WP_DEBUG_DISPLAY', false ); ``` -## Double Check Before Saving +The default debug log file is `/wp-content/debug.log`. Placing error logs in publicly accessible locations is a security risk. Ideally, your log files should be placed above you site’s public root directory. If you can’t do this, at the very least, set the log file permissions to 600 and add this entry to the `.htaccess` file in the root directory of your WordPress installation: -***Be sure to check for leading and/or trailing spaces around any of the above values you entered, and DON’T delete the single quotes!*** +``` + + Order allow,deny + Deny from all + -Before you save the file, be sure to **double-check** that you have not accidentally deleted any of the single quotes around the parameter values. Be sure there is nothing after the closing PHP tag in the file. The last thing in the file should be **?>** and nothing else. No spaces. +``` -To save the file, choose **File > Save As > wp-config.php** and save the file in the root of your WordPress install. Upload the file to your web server and you’re ready to install WordPress! +This prevents anyone from accessing the file via HTTP. You can always view the log file by retrieving it from your server via FTP. -## Changelog +### Increasing memory allocated to PHP -- 2022-10-25: Fix content and links. -- 2022-09-04: Original content from [wp-config.php](#apis/wp-config-php); ticket [Github](https://github.com/WordPress/Documentation-Issue-Tracker/issues/349). -- 2023-01-20: Add content to the start from [documentation](https://wordpress.org/documentation/article/editing-wp-config-php/) ticket [Github](https://github.com/WordPress/Advanced-administration-handbook/issues/89) +**WP\_MEMORY\_LIMIT** option allows you to specify the maximum amount of memory that can be consumed by PHP. This setting may be necessary in the event you receive a message such as “Allowed memory size of xxxxxx bytes exhausted”. ---- +This setting increases PHP Memory only for WordPress, not other applications. By default, WordPress will attempt to increase memory allocated to PHP to 40MB (code is at the beginning of `/wp-includes/default-constants.php`) for single site and 64MB for multisite, so the setting in `wp-config.php` should reflect something higher than 40MB or 64MB depending on your setup. -# Site Architecture (v1.5) +WordPress will automatically check if PHP has been allocated less memory than the entered value before utilizing this function. For example, if PHP has been allocated 64MB, there is no need to set this value to 64M as WordPress will automatically use all 64MB if need be. -Source: https://developer.wordpress.org/advanced-administration/wordpress/site-architecture/ +Note: Some hosts do not allow for increasing the PHP memory limit automatically. In that event, contact your host to increase the PHP memory limit. Also, many hosts set the PHP limit at 8MB. -The following is a description of the general site architecture for WordPress 1.5. WordPress theme developers are encouraged but not required to maintain much of the core site architecture of XHTML tags and CSS selectors. Therefore, you can just consider this to be a general outline, because your theme may be different. +Adjusting the WordPress memory limit potentially creates problems as well. You might end up hiding the root of the issue for it to happen later down the line as you add in more plugins or functionalities. -## Template Driven Pages +If you are facing Out of Memory issues even with an elevated memory limit, you should properly debug your installation. Chances are you have too many memory intensive functions tied to a specific action and should move these functions to a cronjob. -Before we get to the [core structure](#Core_Structure) of the WordPress page architecture, you need to understand that WordPress uses [template files](https://codex.wordpress.org/Templates) to generate the final page “look” and content. For example, when viewing the front page of your WordPress site, you are actually viewing several template files: +Increase PHP Memory to 64MB -- index.php -- header.php -- sidebar.php -- footer.php +``` +define( 'WP_MEMORY_LIMIT', '64M' ); -When you view a single post page, you might be viewing the following template files: +``` -- single.php -- header.php -- sidebar.php -- footer.php -- comments.php +Increase PHP Memory to 96MB -On a multi-post page like categories, archives, and search, you might be viewing any combination of the following template files: +``` +define( 'WP_MEMORY_LIMIT', '96M' ); -- index.php -- category.php -- 404.php -- search.php -- header.php -- sidebar.php -- footer.php +``` -As much as possible in the following architecture specifications, we’ve specified which CSS selectors belong in which template files. +Administration tasks require may require memory than usual operation. When in the administration area, the memory can be increased or decreased from the WP\_MEMORY\_LIMIT by defining WP\_MAX\_MEMORY\_LIMIT. -## Core Structure +``` +define( 'WP_MAX_MEMORY_LIMIT', '128M' ); -The core structure of a WordPress site represents the main containers that hold the page’s content. The core structure of a WordPress site features, at a minimum: +``` -- Header -- Sidebar/Menu -- Content -- Footer +Note: this has to be put before wp-settings.php inclusion. -These are the main containers in which the most important parts of the page are “contained.” Remember, the core structure is like a set of building blocks, where each unit is dependent upon the other units. If you change one, you have to change the others. +### Cache -**Classic Theme** +The **WP\_CACHE** setting, if true, includes the `wp-content/advanced-cache.php` script, when executing `wp-settings.php`. ``` - -
-

-
- -

-
- +define( 'WP_CACHE', true ); ``` -**Default Theme** +### Custom User and Usermeta Tables -``` - -
- -
- - -
- +**CUSTOM\_USER\_TABLE** and **CUSTOM\_USER\_META\_TABLE** are used to designate that the user and usermeta tables normally utilized by WordPress are not used, instead these values/tables are used to store your user information. ``` +define( 'CUSTOM_USER_TABLE', $table_prefix.'my_users' ); +define( 'CUSTOM_USER_META_TABLE', $table_prefix.'my_usermeta' ); -Please note that, while both themes make use of a sidebar, the first theme refers to it as a menu, while the other theme refers to it as a sidebar. - -Perhaps the main difference between the core structures of these two themes is the use of the header and footer. For the Classic Theme, the header is in an h1 tag, and the footer is enclosed in a paragraph tag. Meanwhile in the Default Theme, the header has been placed in a div called header, while the footer has been placed in a footer div. - -Both themes feature a container that encompasses or “wraps” itself around the entire page. This container (often used in combination with the body HTML tag) allows for more definitive control of the entire structure. Depending on the WordPress theme being used, this container can also be referred to as: - -- page -- wrap -- rap +``` -Some themes may add a second, third, or even fourth sidebar, creating a column effect. They may also include additional wrappers around the entire page or around specific containers. However, in all cases, the basic core structure essentially remains the same. +Note: Even if ‘CUSTOM\_USER\_META\_TABLE’ is manually set, a usermeta table is still created for each database with the corresponding permissions for each instance. By default, the WordPress installer will add permissions for the first user (ID #1). You also need to manage permissions to each of the site via a plugin or custom function. If this isn’t setup you will experience permission errors and log-in issues. -### The Modular Template Files +CUSTOM\_USER\_TABLE is easiest to adopt during initial Setup your first instance of WordPress. The define statements of the `wp-config.php` on the first instance point to where `wp_users` data will be stored by default. After the first site setup, copying the working `wp-config.php` to your next instance will only require a change the `$table_prefix` variable. Do not use an e-mail address that is already in use by your original install. Once you have finished the setup process log in with the auto generated admin account and password. Next, promote your normal account to the administrator level and Log out of admin. Log back in as yourself, delete the admin account and promote the other user accounts as is needed. -Based on the premise of building blocks, WordPress themes divide the core structure into individual blocks called [template files](https://codex.wordpress.org/Templates). These are the template files: +### Language and Language Directory -- Header – header.php -- Sidebar/Menu – sidebar.php -- Content – index.php, single.php, page.php, category.php, author.php, search.php, etc. -- Footer – footer.php +WordPress [Version 4.0](https://wordpress.org/documentation/wordpress-version/version-4-0/) allows you to change the language in your WordPress [Administration Screens](https://wordpress.org/documentation/article/administration-screens/). To change the language in the admin settings screen. Go to [Settings](https://wordpress.org/documentation/article/administration-screens/#settings-configuration-settings) > [General](https://wordpress.org/documentation/article/settings-general-screen/) and select Site Language. -In each of these template files, it is possible to use the body div as an all-encompassing container for content. +#### WordPress v3.9.6 and below -When viewing a web page that uses a particular WordPress theme, the specific template files generated are dependent upon the user’s request. If a user clicks on a category tag, the category template will be used. If the user views a [page](https://wordpress.org/documentation/article/create-pages/), the page template will be used. +**WPLANG** defines the name of the language translation (.mo) file. **WP\_LANG\_DIR** defines what directory the WPLANG .mo file resides. If WP\_LANG\_DIR is not defined WordPress looks first to wp-content/languages and then `wp-includes/languages` for the .mo defined by WPLANG file. -When these core template files are loaded in combination with the [WordPress Loop](https://codex.wordpress.org/The_Loop) and queries, a variety of templates can be generated. This allows web page developers to create individual and unique styles for each specific template. +``` +define( 'WPLANG', 'de_DE' ); +define( 'WP_LANG_DIR', dirname(__FILE__) . 'wordpress/languages' ); -## Interior Structures +``` -Within these core structural containers are smaller building blocks which hold the specific content within the parent container. WordPress themes can feature a variety of these, but we are going to concentrate on the two themes that come with WordPress. (Most WordPress theme templates are based on these two themes.) +To find out the WPLANG language code, please [refer here](https://make.wordpress.org/polyglots/teams/). The code in WP Local column is what you need. -### Header +### Save queries for analysis -The header is the structure that traditionally sits at the top of a web page. It contains the title of the website. It may also be referred to as a masthead, head, title, or banner. In all WordPress themes, the header is found within the header.php template file. +The **SAVEQUERIES** definition saves the database queries to an array and that array can be displayed to help analyze those queries. The information saves each query, what function called it, and how long that query took to execute. **Note:** This will have a performance impact on your site, so make sure to turn this off when you aren’t debugging. -The Classic Theme features the simplest header code: +First, add this to the `wp-config.php` file: ``` -

+define( 'SAVEQUERIES', true ); ``` -The Default Theme has a more complex header code: +Then in the footer of your theme put this: ``` - +if ( current_user_can( 'administrator' ) ) { + global $wpdb; + echo ""; + print_r( $wpdb->queries ); + echo ""; +} ``` -While the styles for the Classic Theme are found within the theme’s style.css file, styles for the Default Theme are found both within the style.css file and the of the header.php [template file](https://codex.wordpress.org/Templates). Working with these styles is extensively covered in the article [Designing Headers](https://codex.wordpress.org/Designing_Headers). - -### Content +Alternatively, consider using [Query Monitor](https://wordpress.org/plugins/query-monitor/) -The content container in WordPress plays a critical role, because it holds the [WordPress Loop](https://codex.wordpress.org/The_Loop). The WordPress Loop processes each post that will be displayed on the current page. These posts are then formatted according to how they match specific criteria within the Loop tags. +### Override of default file permissions -The Classic Theme has the simplest content structure: +The **FS\_CHMOD\_DIR** and **FS\_CHMOD\_FILE** define statements allow override of default file permissions. These two variables were developed in response to the problem of the core update function failing with hosts running under [suexec](https://en.wikipedia.org/wiki/SuEXEC). If a host uses restrictive file permissions (e.g. 400) for all user files, and refuses to access files which have group or world permissions set, these definitions could solve the problem. ``` -
-

Date

-
-

Post Title

-
Post Meta Data
-
-

Welcome to WordPress.

-
- -
-
+define( 'FS_CHMOD_DIR', ( 0755 & ~ umask() ) ); +define( 'FS_CHMOD_FILE', ( 0644 & ~ umask() ) ); ``` -The Classic Theme hosts containers for the Date, Title, Post Meta Data, Post Content, and Feedback (number of comments). It also showcases a powerful feature: the ability to individually style a single post’s look. +Example to provide setgid: ``` -
+define( 'FS_CHMOD_DIR', ( 02755 & ~umask() ) ); ``` -The post CSS class selector applies the post styles to this container. It is important to note that the post class selector also has an ID which is generated automatically by WordPress. Here is an example of the code that can be used to display a class selector’s ID: +Note: ‘**0755′** and ‘**02755**‘ are octal values. Octal values must be prefixed with a 0 and are not delineated with single quotes (‘). See Also: [Changing File Permissions](#advanced-administration/server/file-permissions) -``` -
+### WordPress Upgrade Constants -``` +**Note: Define as few of the below constants as needed to correct your update issues.** -The use of the template tag [the\_ID()](#reference/functions/the_ID) displays the ID number for the post. This unique identifier can be used for internal page links as well as for styles. For instance, an individual post could have a style for post-1, as well as for post-2. While it is a bit excessive to feature a style for every post, there may be a post or two that you need to have look a little different. Some plugins may use this identifier to automatically change the look of different posts, too. +The most common causes of needing to define these are: -The content container for the Default Theme features a **multi-post view** (e.g. for the front page, categories, archives, and searches) as well as a **single post view** for single posts. The multi-post view looks like this: +Host running with a special installation setup involving symlinks. You may need to define the path-related constants (FTP\_BASE, FTP\_CONTENT\_DIR, and FTP\_PLUGIN\_DIR). Often defining simply the base will be enough. + +Certain PHP installations shipped with a PHP FTP extension which is incompatible with certain FTP servers. Under these rare situations, you may need to define FS\_METHOD to “ftpsockets”. + +The following are valid constants for WordPress updates: + +- **FS\_METHOD** forces the filesystem method. It should only be “direct”, “ssh2”, “ftpext”, or “ftpsockets”. Generally, you should only change this if you are experiencing update problems. If you change it and it doesn’t help, **change it back/remove it**. Under most circumstances, setting it to ‘ftpsockets’ will work if the automatically chosen method does not. + - **(Primary Preference) “direct”** forces it to use Direct File I/O requests from within PHP, this is fraught with opening up security issues on poorly configured hosts, This is chosen automatically when appropriate. + - **(Secondary Preference) “ssh2”** is to force the usage of the SSH PHP Extension if installed + - **(3rd Preference) “ftpext”** is to force the usage of the FTP PHP Extension for FTP Access, and finally + - **(4th Preference) “ftpsockets”** utilises the PHP Sockets Class for FTP Access. +- **FTP\_BASE** is the full path to the “base”(ABSPATH) folder of the WordPress installation. +- **FTP\_CONTENT\_DIR** is the full path to the wp-content folder of the WordPress installation. +- **FTP\_PLUGIN\_DIR** is the full path to the plugins folder of the WordPress installation. +- **FTP\_PUBKEY** is the full path to your SSH public key. +- **FTP\_PRIKEY** is the full path to your SSH private key. +- **FTP\_USER** is either user FTP or SSH username. Most likely these are the same, but use the appropriate one for the type of update you wish to do. +- **FTP\_PASS** is the password for the username entered for **FTP\_USER**. If you are using SSH public key authentication this can be omitted. +- **FTP\_HOST** is the hostname:port combination for your SSH/FTP server. The default FTP port is 21 and the default SSH port is 22. These do not need to be mentioned. +- **FTP\_SSL** TRUE for SSL-connection *if supported by the underlying transport* (not available on all servers). This is for “Secure FTP” not for SSH SFTP. ``` -
-
-

Post Title

- Date -
-

Post Content.

-
- -
- -
+define( 'FS_METHOD', 'ftpext' ); +define( 'FTP_BASE', '/path/to/wordpress/' ); +define( 'FTP_CONTENT_DIR', '/path/to/wordpress/wp-content/' ); +define( 'FTP_PLUGIN_DIR ', '/path/to/wordpress/wp-content/plugins/' ); +define( 'FTP_PUBKEY', '/home/username/.ssh/id_rsa.pub' ); +define( 'FTP_PRIKEY', '/home/username/.ssh/id_rsa' ); +define( 'FTP_USER', 'username' ); +define( 'FTP_PASS', 'password' ); +define( 'FTP_HOST', 'ftp.example.org' ); +define( 'FTP_SSL', false ); ``` -There is a lot going on here. Let’s break it down. +Some configurations should set FTP\_HOST to localhost to avoid 503 problems when trying to update plugins or WP itself. -### Breakdown of Content Example +#### Enabling SSH Upgrade Access -``` -
+There are two ways to upgrade using SSH2. -``` +The first is to use the [SSH SFTP Updater Support plugin](https://wordpress.org/plugins/ssh-sftp-updater-support/). The second is to use the built-in SSH2 upgrader, which requires the pecl SSH2 extension be installed. -The **multi-post view** features a content container with a class called narrowcolumn, while the **single post view** features a class called widecolumn. The sidebar for the single post view is not generated on that page, allowing the post to be viewed across the width of the entire content area. +To install the pecl SSH2 extension you will need to issue a command similar to the following or talk to your web hosting provider to get this installed: ``` -
+pecl install ssh2 ``` -Like the Classic Theme, this division sets up the style for post and the identifier for post-X, with X representing the post’s unique ID number. This allows the user to customize the specific post’s look. +After installing the pecl ssh2 extension you will need to modify your PHP configuration to automatically load this extension. + +pecl is provided by the pear package in most linux distributions. To install pecl in Redhat/Fedora/CentOS: ``` -

Post Title

+yum -y install php-pear ``` -This encompasses the post’s title code, styled by the `

` tag. +To install pecl in Debian/Ubuntu: ``` -Date +apt-get install php-pear ``` -The date code is surrounded and styled by the small tag. +It is recommended to use a private key that is not pass-phrase protected. There have been numerous reports that pass phrase protected private keys do not work properly. If you decide to try a pass phrase protected private key you will need to enter the pass phrase for the private key as FTP\_PASS, or entering it in the “Password” field in the presented credential field when installing updates. + +### Alternative Cron + +There might be reason to use an alternative Cron with WP. Most commonly this is done if scheduled posts are not getting published as predicted. This alternative method uses a redirection approach. The users’ browser get a redirect when the cron needs to run, so that they come back to the site immediately while cron continues to run in the connection they just dropped. This method has certain risks, since it depends on a non-native WordPress service. ``` -
+define( 'ALTERNATE_WP_CRON', true ); ``` -The post content is styled with a combination of the styles within the entry CSS selectors, and the paragraph tag. +### Disable Cron and Cron Timeout + +Disable cron entirely by setting DISABLE\_WP\_CRON to true. ``` - +define( 'DISABLE_WP_CRON', true ); ``` -The [Post Meta Data Section](https://codex.wordpress.org/Post_Meta_Data_Section) contains data details about the post, such as the date, time, and categories the post belongs to. +Make sure a cron process cannot run more than once every WP\_CRON\_LOCK\_TIMEOUT seconds. ``` - +define( 'EMPTY_TRASH_DAYS', 0 ); // Zero days ``` -While individual sections of the comments feature styling reference, the Classic Theme has no general comment division or group style reference, one could be easily added. +**Note:** WordPress will not ask for confirmation when someone clicks on “Delete Permanently” using this setting. -**\#comments h2** +### Automatic Database Optimizing -Styles the title at the top of the comments list which says “Comments 4 Leave a Comment”, with the latter part of the sentence in a link that jumps to `

Leave a comment

`. +There is automatic database repair support, which you can enable by adding the following define to your `wp-config.php` file. -**\#comment-n** +Note: This should only be enabled if needed and disabled once the issue is solved. When enabled, a user does not need to be logged in to access the functionality, since its main intent is to repair a corrupted database and users can often not login when the database is corrupt. -Comments are given a unique ID number, signified here by the letter n. This allows them to be styled individually. +``` +define( 'WP_ALLOW_REPAIR', true ); -**\#comments ol** +``` -This begins the **ordered list** of the comments, counting down from one, and sets the overall style of the comments list. +The script can be found at `{$your_site}/wp-admin/maint/repair.php`. -**\#comments li** +### DO\_NOT\_UPGRADE\_GLOBAL\_TABLES -Style reference for each comment on the list. +A **DO\_NOT\_UPGRADE\_GLOBAL\_TABLES** define prevents [dbDelta()](#reference/functions/dbdelta) and the upgrade functions from doing expensive queries against global tables. -**\#comments p** +Sites that have large global tables (particularly users and usermeta), as well as sites that share user tables with bbPress and other WordPress installs, can prevent the upgrade from changing those tables during upgrade by defining **DO\_NOT\_UPGRADE\_GLOBAL\_TABLES** to true. Since an ALTER, or an unbounded DELETE or UPDATE, can take a long time to complete, large sites usually want to avoid these being run as part of the upgrade so they can handle it themselves. Further, if installations are sharing user tables between multiple bbPress and WordPress installs you may to want one site to be the upgrade master. -This paragraph tag styles the actual comments on the comment list. +``` +define( 'DO_NOT_UPGRADE_GLOBAL_TABLES', true ); -**\#comment cite** +``` -This use of the cite controls the look of the commenter’s name. It usually states “Name says:” in the comments list. +### View All Defined Constants -**\#comments h2** or **\#postcomment** +PHP has a function that returns an array of all the currently defined constants with their values. -The h2 heading can be styled two ways, as #comments h2 or #postcomment. The latter is used by the “Leave a Comment” link from the top of the comments section, too. +``` +print_r( @get_defined_constants() ); -**\#commentform** +``` -Style reference for the overall “form” for inputting comments. Each input area has it’s own ID. +### Disable the Plugin and Theme File Editor -**\#author** +Occasionally you may wish to disable the plugin or theme file editor to prevent overzealous users from being able to edit sensitive files and potentially crash the site. Disabling these also provides an additional layer of security if a hacker gains access to a well-privileged user account. -ID reference for the comment author’s input area. +``` +define( 'DISALLOW_FILE_EDIT', true ); -**\#comments small** +``` -The `` tag is used in several places in the Classic Theme. This usage surrounds the text in the **comment submit form** and the text for the **list of tags** that can be used in the comment. +**Note**: The functionality of some plugins may be affected by the use of `current_user_can('edit_plugins')` in their code. Plugin authors should avoid checking for this capability, or at least check if this constant is set and display an appropriate error message. Be aware that if a plugin is not working this may be the cause. -**\#email** +### Disable Plugin and Theme Update and Installation -ID reference for the comment author’s email. - -**\#url** - -ID reference for the comment author’s URL. - -**\#comment** +This will block users being able to use the plugin and theme installation/update functionality from the WordPress admin area. Setting this constant also disables the Plugin and Theme File editor (i.e. you don’t need to set DISALLOW\_FILE\_MODS and DISALLOW\_FILE\_EDIT, as on its own DISALLOW\_FILE\_MODS will have the same effect). -ID reference for the comment input textarea. It does not style the final generated comment, just the input box. +``` +define( 'DISALLOW_FILE_MODS', true ); -**\#comment #submit** +``` -There are two submit buttons in the Classic Theme, for search and comment submissions. This is the submit comment button. +### Require SSL for Admin and Logins -##### Default Theme Comments +**Note:** WordPress [Version 4.0](https://wordpress.org/documentation/wordpress-version/version-4-0/) deprecated FORCE\_SSL\_LOGIN. Please use FORCE\_SSL\_ADMIN. -The Default Theme comments feature a loop query within the comments.php and comments-popup.php which changes some of the information depending upon if comments are open, closed, and any present. If the comments are open or closed and no comments have been made, this information will be displayed within the `

` tag. +FORCE\_SSL\_ADMIN is for when you want to secure logins and the admin area so that both passwords and cookies are never sent in the clear. See also [HTTPS](#advanced-administration/security/https) for more details. ``` -

One Response to "Hello world!"

-
    -
  1. - - Mr WordPress - Says:
    - -

    Hi, this is a comment.

    -
  2. -
-

Leave a Reply

-
-

- - -

-

- - -

-

- - -

-

- XHTML: You can use these tags:.... -

-

- -

-

- - -

-
-
+define( 'FORCE_SSL_ADMIN', true ); ``` -While individual sections of the comments feature styling reference, the Default Theme has no general comment division or group style reference, though one could be easily added. - -**h3 #comments** +### Block External URL Requests -Styles the `

` tag for the “number of responses to the post” heading. +Block external URL requests by defining WP\_HTTP\_BLOCK\_EXTERNAL as true and this will only allow localhost and your blog to make requests. The constant WP\_ACCESSIBLE\_HOSTS will allow additional hosts to go through for requests. The format of the WP\_ACCESSIBLE\_HOSTS constant is a comma separated list of hostnames to allow, wildcard domains are supported, eg `*.wordpress.org` will allow for all subdomains of wordpress.org to be contacted. -**\#commentlist ol** +``` +define( 'WP_HTTP_BLOCK_EXTERNAL', true ); +define( 'WP_ACCESSIBLE_HOSTS', 'api.wordpress.org,*.github.com' ); -Styles the “ordered list” of the comments list. +``` -**.alt li** and **\#comment-n** +### Disable WordPress Auto Updates -The comment list items have two style references. The first one is the class alt and the second is the comment ID number signified here by the letter n. This allows them to be styled individually. +There might be reason for a site to not auto-update, such as customizations or host supplied updates. It can also be done before a major release to allow time for testing on a development or staging environment before allowing the update on a production site. -**cite** +``` +define( 'AUTOMATIC_UPDATER_DISABLED', true ); -The tag cite frames the “Name says:” and link to the comment author’s URL. +``` -**.commentmetadata small** +### Disable WordPress Core Updates -The `` tag has a class of commentmetadata which allows the date and time of the post to be styled. +The easiest way to manipulate core updates is with the WP\_AUTO\_UPDATE\_CORE constant: -**ol #commentlist p** +**Disable all core updates:** -Styles the paragraph within the ordered list of comments. +``` +define( 'WP_AUTO_UPDATE_CORE', false ); -**\#respond h3** +``` -Styles the heading for “Leave a Reply”. +**Enable all core updates, including minor and major:** -**\#commentform** +``` +define( 'WP_AUTO_UPDATE_CORE', true ); -Style reference for the overall “form” for inputting comments. Each input area has it’s own ID. +``` -**\#author** +**Enable core updates for minor releases (default):** -ID reference for the comment author’s input area. +``` +define( 'WP_AUTO_UPDATE_CORE', 'minor' ); -**\#comments small** +``` -The `` tag is used in several places in the Classic Theme. This usage surrounds the text in the **comment submit form *and the text for the*** *list of tags’* that can be used in the comment. +Reference: [Disabling Auto Updates in WordPress 3.7](https://make.wordpress.org/core/2013/10/25/the-definitive-guide-to-disabling-auto-updates-in-wordpress-3-7/) -**\#email** +### Cleanup Image Edits -ID reference for the comment author’s email. +By default, WordPress creates a new set of images every time you edit an image and when you restore the original, it leaves all the edits on the server. Defining IMAGE\_EDIT\_OVERWRITE as true changes this behavior. Only one set of image edits are ever created and when you restore the original, the edits are removed from the server. -**\#url** +``` +define( 'IMAGE_EDIT_OVERWRITE', true ); -ID reference for the comment author’s URL. +``` -**\#comment** +## Double Check Before Saving -ID reference for the comment input textarea. It does not style the final generated comment, just the input box. +***Be sure to check for leading and/or trailing spaces around any of the above values you entered, and DON’T delete the single quotes!*** -**\#comment #submit** +Before you save the file, be sure to **double-check** that you have not accidentally deleted any of the single quotes around the parameter values. Be sure there is nothing after the closing PHP tag in the file. The last thing in the file should be **?>** and nothing else. No spaces. -There are two submit buttons in the Classic Theme, for search and comment submissions. This is the submit comment button. +To save the file, choose **File > Save As > wp-config.php** and save the file in the root of your WordPress install. Upload the file to your web server and you’re ready to install WordPress! -##### Popup Comments +## Changelog -The Classic and Default Themes’ `comments-popup.php` template file is essentially the same. They use the layout for the [Classic Theme comment structure](#Default_Theme_Comments). While the Classic Theme uses `

` headings and the Default Theme uses `

` headings for the title headings in their comments, in the comments-popup.php template file, they both use the `

` heading tag. +- 2022-10-25: Fix content and links. +- 2022-09-04: Original content from [wp-config.php](#apis/wp-config-php); ticket [Github](https://github.com/WordPress/Documentation-Issue-Tracker/issues/349). +- 2023-01-20: Add content to the start from [documentation](https://wordpress.org/documentation/article/editing-wp-config-php/) ticket [Github](https://github.com/WordPress/Advanced-administration-handbook/issues/89) -``` - -

-

Comments

- ....Classic Theme comment section..... - ...Classic Theme footer.... +--- -``` +# Site Architecture (v1.5) -The body tag sets the style for the overall page with `#commentspopup`. The `

` heading begins the comments section. +Source: https://developer.wordpress.org/advanced-administration/wordpress/site-architecture/ -If you make modifications to the structure of the tags within the header and footer of the overall Theme, ensure those structural changes are applied to the comments popup template, especially if you will be [releasing the Theme to the public](https://codex.wordpress.org/Designing_Themes_for_Public_Release). +The following is a description of the general site architecture for WordPress 1.5. WordPress theme developers are encouraged but not required to maintain much of the core site architecture of XHTML tags and CSS selectors. Therefore, you can just consider this to be a general outline, because your theme may be different. -### Sidebar +## Template Driven Pages -As you saw with the Default Theme, the sidebar can be visible or not, depending upon the template file in use. The sidebar, in general, can be simple or complex. WordPress Themes often feature information within the sidebar in **nested lists**. There is a step-by-step guide for the sidebar at [Customizing Your Sidebar](https://codex.wordpress.org/Customizing_Your_Sidebar) and more information on [Styling Lists with CSS](https://codex.wordpress.org/Styling_Lists_with_CSS), too. +Before we get to the [core structure](#Core_Structure) of the WordPress page architecture, you need to understand that WordPress uses [template files](https://codex.wordpress.org/Templates) to generate the final page “look” and content. For example, when viewing the front page of your WordPress site, you are actually viewing several template files: -In general, the WordPress sidebar features titles of the various sections within a list, with the section items in a nested list below the title. +- index.php +- header.php +- sidebar.php +- footer.php -The Classic Theme sidebar looks like this, with the links removed for simplification: +When you view a single post page, you might be viewing the following template files: -``` - +- single.php +- header.php +- sidebar.php +- footer.php +- comments.php -``` +On a multi-post page like categories, archives, and search, you might be viewing any combination of the following template files: -Most of these are self-explanatory. Each set of links has its own CSS selector: [Pages](https://wordpress.org/documentation/article/create-pages/), categories, archives, search, and meta. +- index.php +- category.php +- 404.php +- search.php +- header.php +- sidebar.php +- footer.php -#### Pages and Link Categories +As much as possible in the following architecture specifications, we’ve specified which CSS selectors belong in which template files. -The [Pages](https://wordpress.org/documentation/article/create-pages/) and [Links](https://codex.wordpress.org/Links_Manager) category, labeled “Blogroll”, uses the [](#reference/functions/get_links_list) and [](#reference/functions/wp_list_pages) template tags which automatically generates a heading. +## Core Structure -For the **Links** category, it generates an h2 heading for that set of links. This means you can style the menu h2 heading to look differently from the rest of the headings, or, if you want them to all look the same, make sure that the menu h2 style *matches* the rest of the category styles which are not automatically generated. +The core structure of a WordPress site represents the main containers that hold the page’s content. The core structure of a WordPress site features, at a minimum: -The **Pages** template tag generates pagenav as the heading and then identifies the pages in a new way. As a general list viewed on multi-post and single post views, the Page list items feature a class=”page\_item” to style those links. When viewing an individual Page, that Page’s link will change to class=”current\_page\_item”, which can then be styled to look differently from the rest of the Page links. +- Header +- Sidebar/Menu +- Content +- Footer -#### Categories, Archives, and Meta +These are the main containers in which the most important parts of the page are “contained.” Remember, the core structure is like a set of building blocks, where each unit is dependent upon the other units. If you change one, you have to change the others. -The other sidebar section titles, *categories*, *archives*, *meta*, and others, do not use template tags which generate their own titles. These are set inside of PHP statements which “print” the text on the page. While these could be put inside of [heading tags](https://codex.wordpress.org/Designing_Headings), WordPress uses the `_e()` function to display or `echo` the text titles while also marking the text as a possible target for language translation. If you will be [developing your theme](https://codex.wordpress.org/Theme_Development) for [public release](https://codex.wordpress.org/Designing_Themes_for_Public_Release), using the echo functions is highly recommended. +**Classic Theme** -You can style these individually or all the same. Some Themes, like the Default Theme, put all these in `

` headings so the list headings will all look the same. Therefore, they may or may not use style references for each section. You may add them if you need them to change the look of each section of links. +``` + +
+

+
+ +

+
+ -#### Search Form +``` -The search form is found within the searchform.php. It may be found in different locations within the sidebar. To style the overall search form, use the search ID. Here is a list of the individual areas of the search form which may be styled by default. You may add style classes to gain more control over the look of your search form. +**Default Theme** ``` - + +
+ +
+ + +
+ ``` -**\#search** - -The overall style for the search form. - -**\#search label** - -Used to style the label tag, if necessary. - -**\#searchform** - -Used to style the form itself. - -**\#search div** +Please note that, while both themes make use of a sidebar, the first theme refers to it as a menu, while the other theme refers to it as a sidebar. -This unlabeled div is a child container of the parent container search and maybe styled from within that selector. +Perhaps the main difference between the core structures of these two themes is the use of the header and footer. For the Classic Theme, the header is in an h1 tag, and the footer is enclosed in a paragraph tag. Meanwhile in the Default Theme, the header has been placed in a div called header, while the footer has been placed in a footer div. -**\#searchform input** +Both themes feature a container that encompasses or “wraps” itself around the entire page. This container (often used in combination with the body HTML tag) allows for more definitive control of the entire structure. Depending on the WordPress theme being used, this container can also be referred to as: -To style the input area for the search, this selector combination will work. +- page +- wrap +- rap -**\#searchsubmit** +Some themes may add a second, third, or even fourth sidebar, creating a column effect. They may also include additional wrappers around the entire page or around specific containers. However, in all cases, the basic core structure essentially remains the same. -*Used by the Default Theme*, this selector may be used to style the **search** or **submit** button. +### The Modular Template Files -The search form area, input, and button can be styled in many ways, or left with the default input and “button” look. +Based on the premise of building blocks, WordPress themes divide the core structure into individual blocks called [template files](https://codex.wordpress.org/Templates). These are the template files: -#### Meta Feed Links +- Header – header.php +- Sidebar/Menu – sidebar.php +- Content – index.php, single.php, page.php, category.php, author.php, search.php, etc. +- Footer – footer.php -The Meta links may be shown as text or icons representing the various links. The XHTML and CSS validation links may use the W3 icons. The various Feeds can also be represented as icons. Or left as text. It’s up to you. Use of the feeds within your sidebar with text or icons is covered by the article [WordPress Feeds](https://wordpress.org/documentation/article/wordpress-feeds/). +In each of these template files, it is possible to use the body div as an all-encompassing container for content. -### Footer +When viewing a web page that uses a particular WordPress theme, the specific template files generated are dependent upon the user’s request. If a user clicks on a category tag, the category template will be used. If the user views a [page](https://wordpress.org/documentation/article/create-pages/), the page template will be used. -The footer is found within the footer.php template file. In both the Default and Classic Themes, the footer contains little information. +When these core template files are loaded in combination with the [WordPress Loop](https://codex.wordpress.org/The_Loop) and queries, a variety of templates can be generated. This allows web page developers to create individual and unique styles for each specific template. -**Classic Theme** +## Interior Structures -``` -

- - Powered by WordPress -

-

+Within these core structural containers are smaller building blocks which hold the specific content within the parent container. WordPress themes can feature a variety of these, but we are going to concentrate on the two themes that come with WordPress. (Most WordPress theme templates are based on these two themes.) -``` +### Header -The footer’s content is styled with the credit class and the paragraph and cite tags. +The header is the structure that traditionally sits at the top of a web page. It contains the title of the website. It may also be referred to as a masthead, head, title, or banner. In all WordPress themes, the header is found within the header.php template file. -The tag displays the number of mysql queries used on the page and the time it took for the page to load, in HTML commented code. It is there for the administrator’s convenience and use. It is only visible within the page’s source code. If you would like to display this visible on the page, remove the [comments](https://codex.wordpress.org/Commenting_Code). It’s look will be influenced by the credit class style of the paragraph tag. On the template file, it looks like this: +The Classic Theme features the simplest header code: ``` - +

``` -**Default Theme** +The Default Theme has a more complex header code: ``` -