Skip to content

Commit 84d64de

Browse files
authored
Merge pull request #305 from alleyinteractive/feature/issue-293/create-features-folder-structure
Issue-293: Create Structure for Features
2 parents 5906a02 + b01c6de commit 84d64de

File tree

3 files changed

+236
-1
lines changed

3 files changed

+236
-1
lines changed

plugin.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ function () {
6363
* Instantiate the plugin.
6464
*/
6565
function main(): void {
66-
// ...
66+
// This should be an array with keys set to feature classnames and arguments.
67+
$features = [
68+
// Add initial features here.
69+
];
70+
$features = apply_filters( 'create_wordpress_plugin_features', $features );
71+
Feature_Manager::add_features( $features );
6772
}
6873
main();

src/class-feature-manager.php

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
/**
3+
* Class to manage plugin features.
4+
*
5+
* @package create-wordpress-plugin
6+
*/
7+
8+
namespace Create_WordPress_Plugin;
9+
10+
use Alley\WP\Types\Feature;
11+
12+
/**
13+
* The Feature Manager class.
14+
*/
15+
class Feature_Manager {
16+
/**
17+
* Collected features.
18+
*
19+
* @var object[]
20+
*/
21+
private static array $features = [];
22+
23+
/**
24+
* Set up.
25+
*
26+
* @param array $features_to_create Array of feature classnames and arguments.
27+
*
28+
* @phpstan-param array{string: array{string?: mixed}}|array{} $features_to_create
29+
*/
30+
public static function add_features( array $features_to_create = [] ): void {
31+
foreach ( $features_to_create as $feature_class => $args ) {
32+
self::add_feature( $feature_class, $args );
33+
}
34+
}
35+
36+
/**
37+
* Return the plugin features.
38+
*
39+
* @return object[] The plugin features.
40+
*/
41+
public static function get_features(): array {
42+
return self::$features;
43+
}
44+
45+
/**
46+
* Add a feature to the plugin.
47+
*
48+
* @param string $feature_class The feature class to add.
49+
* @param array $args The arguments to pass to the feature constructor.
50+
*
51+
* @phpstan-param array{string?: mixed} $args
52+
*
53+
* @return object The instatiated feature that was added.
54+
* @throws \Exception If the feature class does not implement Feature.
55+
*/
56+
public static function add_feature( string $feature_class, array $args = [] ): object {
57+
if ( ! in_array( Feature::class, class_implements( $feature_class ) ?: [], true ) ) {
58+
throw new \Exception( 'Feature class must implement Feature interface.' );
59+
}
60+
$feature = new $feature_class( ...$args );
61+
$feature->boot(); // @phpstan-ignore-line
62+
self::$features[] = $feature;
63+
return $feature;
64+
}
65+
66+
/**
67+
* Get a feature by class name.
68+
*
69+
* @param string $feature_name The name of the feature to get.
70+
* @return object|null The feature or null if it doesn't exist.
71+
*/
72+
public static function get_feature( string $feature_name ): ?object {
73+
foreach ( self::$features as $feature ) {
74+
if ( get_class( $feature ) === $feature_name ) {
75+
return $feature;
76+
}
77+
}
78+
return null;
79+
}
80+
}

src/features/README.md

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# Features
2+
Features should be PHP classes that implement the [Alley\WP\Types\Feature interface](https://github.com/alleyinteractive/wp-type-extensions/blob/main/src/alley/wp/types/interface-feature.php).
3+
4+
Features should be located in the `src/features` directory of the plugin and have namespace `Create_WordPress_Plugin\Features;`
5+
6+
Files in the features directory will be autoloaded, but features will not be instantiated. Features are instantiated via the `Feature_Manager` static class.
7+
8+
The following variable is passed to the `Class_Hello` feature in each of the following examples. This shows how we can remove any business logic from the feature and pass it in when the feature is added.
9+
10+
```
11+
$lyrics = "Hello, Dolly
12+
Well, hello, Dolly
13+
It's so nice to have you back where you belong
14+
You're lookin' swell, Dolly
15+
I can tell, Dolly
16+
You're still glowin', you're still crowin'
17+
You're still goin' strong
18+
I feel the room swayin'
19+
While the band's playin'
20+
One of our old favorite songs from way back when
21+
So, take her wrap, fellas
22+
Dolly, never go away again";
23+
```
24+
25+
## There are two ways to add a feature:
26+
### Add a feature using the `add_features` method
27+
```
28+
$features = [
29+
'Create_WordPress_Plugin\Features\Hello' => [ 'lyrics' => $lyrics ],
30+
];
31+
32+
Feature_Manager::add_features( $features );
33+
```
34+
35+
> 💡 If you `apply_filters` to the features array before passing it to `add_features`, you can modify it with a filter.
36+
```
37+
$features = apply_filters( 'create_wordpress_plugin_features', $features );
38+
```
39+
40+
### Add a feature using the `add_feature` method
41+
```
42+
Feature_Manager::add_feature( 'Create_WordPress_Plugin\Features\Hello', [ 'lyrics' => $lyrics ] );
43+
```
44+
## Get the instance of an added feature with the `get_feature` method
45+
```
46+
$hello_feature = Feature_Manager::get_feature( 'Create_WordPress_Plugin\Features\Hello' );
47+
```
48+
## Once we have the instance, we can remove hooks from inside the instance
49+
```
50+
remove_action( 'admin_head', [ $hello_feature, 'dolly_css' ] );
51+
```
52+
53+
## Example feature class:
54+
This is a port of the infamous WordPress `hello.php` plugin to a feature. The lyrics would be passed in when the feature was called, as shown above.
55+
```
56+
<?php
57+
/**
58+
* Feature implementation of hello.php
59+
*
60+
* @package Create_WordPress_Plugin
61+
*/
62+
63+
namespace Create_WordPress_Plugin\Features;
64+
65+
use Alley\WP\Types\Feature;
66+
67+
/**
68+
* Hello class file
69+
*/
70+
final class Hello implements Feature {
71+
/**
72+
* Set up.
73+
*
74+
* @param string $lyrics The lyrics to Hello Dolly.
75+
*/
76+
public function __construct(
77+
private readonly string $lyrics,
78+
) {}
79+
80+
/**
81+
* Boot the feature.
82+
*/
83+
public function boot(): void {
84+
add_action( 'admin_notices', [ $this, 'hello_dolly' ] );
85+
add_action( 'admin_head', [ $this, 'dolly_css' ] );
86+
}
87+
88+
/**
89+
* Gets a random lyric from the lyric string.
90+
*
91+
* @return string
92+
*/
93+
public function hello_dolly_get_lyric(): string {
94+
// Here we split the lyrics into lines.
95+
$lyrics = explode( "\n", $this->lyrics );
96+
97+
// And then randomly choose a line.
98+
return wptexturize( $lyrics[ wp_rand( 0, count( $lyrics ) - 1 ) ] );
99+
}
100+
101+
/**
102+
* Echos the chosen line.
103+
*/
104+
public function hello_dolly(): void {
105+
$chosen = $this->hello_dolly_get_lyric();
106+
$lang = '';
107+
if ( 'en_' !== substr( get_user_locale(), 0, 3 ) ) {
108+
$lang = ' lang="en"';
109+
}
110+
111+
printf(
112+
'<p id="dolly"><span class="screen-reader-text">%s </span><span dir="ltr"%s>%s</span></p>',
113+
esc_html__( 'Quote from Hello Dolly song, by Jerry Herman:' ),
114+
esc_attr( $lang ),
115+
esc_html( $chosen )
116+
);
117+
}
118+
119+
/**
120+
* Output css to position the paragraph.
121+
*/
122+
public function dolly_css(): void {
123+
echo "
124+
<style type='text/css'>
125+
#dolly {
126+
float: right;
127+
padding: 5px 10px;
128+
margin: 0;
129+
font-size: 12px;
130+
line-height: 1.6666;
131+
}
132+
.rtl #dolly {
133+
float: left;
134+
}
135+
.block-editor-page #dolly {
136+
display: none;
137+
}
138+
@media screen and (max-width: 782px) {
139+
#dolly,
140+
.rtl #dolly {
141+
float: none;
142+
padding-left: 0;
143+
padding-right: 0;
144+
}
145+
}
146+
</style>
147+
";
148+
}
149+
}
150+
```

0 commit comments

Comments
 (0)