| 1 |
# Gutenberg PHP |
| 2 |
|
| 3 |
## File structure |
| 4 |
|
| 5 |
Gutenberg adds features to WordPress Core using PHP hooks and filters. Some |
| 6 |
features, once considered stable and useful, are merged into Core during a Core |
| 7 |
release. Some features remain in the plugin forever or are removed. |
| 8 |
|
| 9 |
To make it easier for contributors to know which features need to be merged to |
| 10 |
Core and which features can be deleted, Gutenberg uses the following file |
| 11 |
structure for its PHP code: |
| 12 |
|
| 13 |
- `lib/experimental` - Experimental features that exist only in the plugin. They |
| 14 |
are not ready to be merged to Core. |
| 15 |
- `lib/stable` - Stable features that exist only in the plugin. They could one |
| 16 |
day be merged to Core, but not yet. |
| 17 |
- `lib/compat/wordpress-X.Y` - Stable features that are intended to be merged to |
| 18 |
Core in the future X.Y release, or that were previously merged to Core in the |
| 19 |
X.Y release and remain in the plugin for backwards compatibility when running |
| 20 |
the plugin on older versions of WordPress. |
| 21 |
|
| 22 |
## Best practices |
| 23 |
|
| 24 |
### Prefer the `wp` prefix |
| 25 |
|
| 26 |
For features that may be merged to Core, it's best to use a `wp_` prefix for functions or a `WP_` prefix for classes. |
| 27 |
|
| 28 |
This applies to both experimental and stable features. |
| 29 |
|
| 30 |
Using the `wp_` prefix avoids us having to rename functions and classes from `gutenberg_` to `wp_` if the feature is merged to Core. |
| 31 |
|
| 32 |
Functions that are intended solely for the plugin, e.g., plugin infrastructure, should use the `gutenberg_` prefix. |
| 33 |
|
| 34 |
#### Feature that might be merged to Core |
| 35 |
|
| 36 |
```php |
| 37 |
function wp_get_navigation( $slug ) { ... } |
| 38 |
``` |
| 39 |
|
| 40 |
#### Plugin infrastructure that will never be merged to Core |
| 41 |
|
| 42 |
```php |
| 43 |
function gutenberg_get_navigation( $slug ) { ... } |
| 44 |
``` |
| 45 |
|
| 46 |
### Group PHP code by _feature_ |
| 47 |
|
| 48 |
Developers should organize PHP into files or folders by _feature_, not by _component_. |
| 49 |
|
| 50 |
When defining a function that will be hooked, developers should call `add_action` and `add_filter` immediately after the function declaration. |
| 51 |
|
| 52 |
These two practices make it easier for PHP code to start in one folder (e.g., `lib/experimental`) and eventually move to another using a simple `git mv`. |
| 53 |
|
| 54 |
#### Good |
| 55 |
|
| 56 |
```php |
| 57 |
// lib/experimental/navigation.php |
| 58 |
|
| 59 |
function wp_get_navigation( $slug ) { ... } |
| 60 |
|
| 61 |
function wp_register_navigation_cpt() { ... } |
| 62 |
|
| 63 |
add_action( 'init', 'wp_register_navigation_cpt' ); |
| 64 |
``` |
| 65 |
|
| 66 |
#### Not so good |
| 67 |
|
| 68 |
```php |
| 69 |
// lib/experimental/functions.php |
| 70 |
|
| 71 |
function wp_get_navigation( $slug ) { ... } |
| 72 |
|
| 73 |
// lib/experimental/post-types.php |
| 74 |
|
| 75 |
function wp_register_navigation_cpt() { ... } |
| 76 |
|
| 77 |
// lib/experimental/init.php |
| 78 |
add_action( 'init', 'wp_register_navigation_cpt' ); |
| 79 |
``` |
| 80 |
|
| 81 |
### Wrap functions and classes with `! function_exists` and `! class_exists` |
| 82 |
|
| 83 |
Developers should take care to not define functions and classes that are already defined. |
| 84 |
|
| 85 |
When writing new functions and classes, it's good practice to use `! function_exists` and `! class_exists`. |
| 86 |
|
| 87 |
If Core has defined a symbol once and then Gutenberg defines it a second time, fatal errors will occur. |
| 88 |
|
| 89 |
Wrapping functions and classes avoids such errors if the feature is merged to Core. |
| 90 |
|
| 91 |
#### Good |
| 92 |
|
| 93 |
```php |
| 94 |
// lib/experimental/navigation/navigation.php |
| 95 |
|
| 96 |
if ( ! function_exists( 'wp_get_navigation' ) ) { |
| 97 |
function wp_get_navigation( $slug ) { ... } |
| 98 |
} |
| 99 |
|
| 100 |
// lib/experimental/navigation/class-wp-navigation.php |
| 101 |
|
| 102 |
if ( class_exists( 'WP_Navigation' ) ) { |
| 103 |
return; |
| 104 |
} |
| 105 |
|
| 106 |
class WP_Navigation { ... } |
| 107 |
``` |
| 108 |
|
| 109 |
#### Not so good |
| 110 |
|
| 111 |
```php |
| 112 |
// lib/experimental/navigation/navigation.php |
| 113 |
|
| 114 |
function wp_get_navigation( $slug ) { ... } |
| 115 |
|
| 116 |
// lib/experimental/navigation/class-gutenberg-navigation.php |
| 117 |
|
| 118 |
class WP_Navigation { ... } |
| 119 |
``` |
| 120 |
|
| 121 |
Furthermore, a quick codebase search will also help you know if your new method is unique. |
| 122 |
|
| 123 |
### Note how your feature should look when merged to Core |
| 124 |
|
| 125 |
Developers should write a brief note about how their feature should be merged to Core, for example, which Core file or function should be patched. |
| 126 |
|
| 127 |
Notes can be included in the doc comment. |
| 128 |
|
| 129 |
This helps future developers know what to do when merging Gutenberg features into Core. |
| 130 |
|
| 131 |
#### Good |
| 132 |
|
| 133 |
```php |
| 134 |
/** |
| 135 |
* Returns a navigation object for the given slug. |
| 136 |
* |
| 137 |
* Should live in `wp-includes/navigation.php` when merged to Core. |
| 138 |
* |
| 139 |
* @param string $slug |
| 140 |
* |
| 141 |
* @return WP_Navigation |
| 142 |
*/ |
| 143 |
function wp_get_navigation( $slug ) { ... } |
| 144 |
``` |
| 145 |
|