| 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 |
- `lib/compat/plugin` - Features for backwards compatibility for the plugin consumers. These files don't need to be merged to Core and should have a timeline for when they should be removed from the plugin. |
| 22 |
|
| 23 |
## Best practices |
| 24 |
|
| 25 |
### Prefer the `wp` prefix |
| 26 |
|
| 27 |
For features that may be merged to Core, it's best to use a `wp_` prefix for functions or a `WP_` prefix for classes. |
| 28 |
|
| 29 |
This applies to both experimental and stable features. |
| 30 |
|
| 31 |
Using the `wp_` prefix avoids us having to rename functions and classes from `gutenberg_` to `wp_` if the feature is merged to Core. |
| 32 |
|
| 33 |
Functions that are intended solely for the plugin, e.g., plugin infrastructure, should use the `gutenberg_` prefix. |
| 34 |
|
| 35 |
#### Feature that might be merged to Core |
| 36 |
|
| 37 |
```php |
| 38 |
function wp_get_navigation( $slug ) { ... } |
| 39 |
``` |
| 40 |
|
| 41 |
#### Plugin infrastructure that will never be merged to Core |
| 42 |
|
| 43 |
```php |
| 44 |
function gutenberg_get_navigation( $slug ) { ... } |
| 45 |
``` |
| 46 |
|
| 47 |
### Group PHP code by _feature_ |
| 48 |
|
| 49 |
Developers should organize PHP into files or folders by _feature_, not by _component_. |
| 50 |
|
| 51 |
When defining a function that will be hooked, developers should call `add_action` and `add_filter` immediately after the function declaration. |
| 52 |
|
| 53 |
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`. |
| 54 |
|
| 55 |
#### Good |
| 56 |
|
| 57 |
```php |
| 58 |
// lib/experimental/navigation.php |
| 59 |
|
| 60 |
function wp_get_navigation( $slug ) { ... } |
| 61 |
|
| 62 |
function wp_register_navigation_cpt() { ... } |
| 63 |
|
| 64 |
add_action( 'init', 'wp_register_navigation_cpt' ); |
| 65 |
``` |
| 66 |
|
| 67 |
#### Not so good |
| 68 |
|
| 69 |
```php |
| 70 |
// lib/experimental/functions.php |
| 71 |
|
| 72 |
function wp_get_navigation( $slug ) { ... } |
| 73 |
|
| 74 |
// lib/experimental/post-types.php |
| 75 |
|
| 76 |
function wp_register_navigation_cpt() { ... } |
| 77 |
|
| 78 |
// lib/experimental/init.php |
| 79 |
add_action( 'init', 'wp_register_navigation_cpt' ); |
| 80 |
``` |
| 81 |
|
| 82 |
### Wrap functions and classes with `! function_exists` and `! class_exists` |
| 83 |
|
| 84 |
Developers should take care to not define functions and classes that are already defined. |
| 85 |
|
| 86 |
When writing new functions and classes, it's good practice to use `! function_exists` and `! class_exists`. |
| 87 |
|
| 88 |
If Core has defined a symbol once and then Gutenberg defines it a second time, fatal errors will occur. |
| 89 |
|
| 90 |
Wrapping functions and classes avoids such errors if the feature is merged to Core. |
| 91 |
|
| 92 |
#### Good |
| 93 |
|
| 94 |
```php |
| 95 |
// lib/experimental/navigation/navigation.php |
| 96 |
|
| 97 |
if ( ! function_exists( 'wp_get_navigation' ) ) { |
| 98 |
function wp_get_navigation( $slug ) { ... } |
| 99 |
} |
| 100 |
|
| 101 |
// lib/experimental/navigation/class-wp-navigation.php |
| 102 |
|
| 103 |
if ( class_exists( 'WP_Navigation' ) ) { |
| 104 |
return; |
| 105 |
} |
| 106 |
|
| 107 |
class WP_Navigation { ... } |
| 108 |
``` |
| 109 |
|
| 110 |
#### Not so good |
| 111 |
|
| 112 |
```php |
| 113 |
// lib/experimental/navigation/navigation.php |
| 114 |
|
| 115 |
function wp_get_navigation( $slug ) { ... } |
| 116 |
|
| 117 |
// lib/experimental/navigation/class-gutenberg-navigation.php |
| 118 |
|
| 119 |
class WP_Navigation { ... } |
| 120 |
``` |
| 121 |
|
| 122 |
Furthermore, a quick codebase search will also help you know if your new method is unique. |
| 123 |
|
| 124 |
### Note how your feature should look when merged to Core |
| 125 |
|
| 126 |
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. |
| 127 |
|
| 128 |
Notes can be included in the doc comment. |
| 129 |
|
| 130 |
This helps future developers know what to do when merging Gutenberg features into Core. |
| 131 |
|
| 132 |
#### Good |
| 133 |
|
| 134 |
```php |
| 135 |
/** |
| 136 |
* Returns a navigation object for the given slug. |
| 137 |
* |
| 138 |
* Should live in `wp-includes/navigation.php` when merged to Core. |
| 139 |
* |
| 140 |
* @param string $slug |
| 141 |
* |
| 142 |
* @return WP_Navigation |
| 143 |
*/ |
| 144 |
function wp_get_navigation( $slug ) { ... } |
| 145 |
``` |
| 146 |
|