Subversion Repositories php-qbpwcf

Rev

Rev 3 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
3 liveuser 1
# Example: Plugin system
2
 
3
In this example I will show you how to create a generic plugin system with
4
événement where plugins can alter the behaviour of the app. The app is a blog.
5
Boring, I know. By using the EventEmitter it will be easy to extend this blog
6
with additional functionality without modifying the core system.
7
 
8
The blog is quite basic. Users are able to create blog posts when they log in.
9
The users are stored in a static config file, so there is no sign up process.
10
Once logged in they get a "new post" link which gives them a form where they
11
can create a new blog post with plain HTML. That will store the post in a
12
document database. The index lists all blog post titles by date descending.
13
Clicking on the post title will take you to the full post.
14
 
15
## Plugin structure
16
 
17
The goal of the plugin system is to allow features to be added to the blog
18
without modifying any core files of the blog.
19
 
20
The plugins are managed through a config file, `plugins.json`. This JSON file
21
contains a JSON-encoded list of class-names for plugin classes. This allows
22
you to enable and disable plugins in a central location. The initial
23
`plugins.json` is just an empty array:
24
```json
25
[]
26
```
27
 
28
A plugin class must implement the `PluginInterface`:
29
```php
30
interface PluginInterface
31
{
32
    function attachEvents(EventEmitterInterface $emitter);
33
}
34
```
35
 
36
The `attachEvents` method allows the plugin to attach any events to the
37
emitter. For example:
38
```php
39
class FooPlugin implements PluginInterface
40
{
41
    public function attachEvents(EventEmitterInterface $emitter)
42
    {
43
        $emitter->on('foo', function () {
44
            echo 'bar!';
45
        });
46
    }
47
}
48
```
49
 
50
The blog system creates an emitter instance and loads the plugins:
51
```php
52
$emitter = new EventEmitter();
53
 
54
$pluginClasses = json_decode(file_get_contents('plugins.json'), true);
55
foreach ($pluginClasses as $pluginClass) {
56
    $plugin = new $pluginClass();
57
    $pluginClass->attachEvents($emitter);
58
}
59
```
60
 
61
This is the base system. There are no plugins yet, and there are no events yet
62
either. That's because I don't know which extension points will be needed. I
63
will add them on demand.
64
 
65
## Feature: Markdown
66
 
67
Writing blog posts in HTML sucks! Wouldn't it be great if I could write them
68
in a nice format such as markdown, and have that be converted to HTML for me?
69
 
70
This feature will need two extension points. I need to be able to mark posts
71
as markdown, and I need to be able to hook into the rendering of the post body
72
and convert it from markdown to HTML. So the blog needs two new events:
73
`post.create` and `post.render`.
74
 
75
In the code that creates the post, I'll insert the `post.create` event:
76
```php
77
class PostEvent
78
{
79
    public $post;
80
 
81
    public function __construct(array $post)
82
    {
83
        $this->post = $post;
84
    }
85
}
86
 
87
$post = createPostFromRequest($_POST);
88
 
89
$event = new PostEvent($post);
90
$emitter->emit('post.create', [$event]);
91
$post = $event->post;
92
 
93
$db->save('post', $post);
94
```
95
 
96
This shows that you can wrap a value in an event object to make it mutable,
97
allowing listeners to change it.
98
 
99
The same thing for the `post.render` event:
100
```php
101
public function renderPostBody(array $post)
102
{
103
    $emitter = $this->emitter;
104
 
105
    $event = new PostEvent($post);
106
    $emitter->emit('post.render', [$event]);
107
    $post = $event->post;
108
 
109
    return $post['body'];
110
}
111
 
112
<h1><?= $post['title'] %></h1>
113
<p><?= renderPostBody($post) %></p>
114
```
115
 
116
Ok, the events are in place. It's time to create the first plugin, woohoo! I
117
will call this the `MarkdownPlugin`, so here's `plugins.json`:
118
```json
119
[
120
    "MarkdownPlugin"
121
]
122
```
123
 
124
The `MarkdownPlugin` class will be autoloaded, so I don't have to worry about
125
including any files. I just have to worry about implementing the plugin class.
126
The `markdown` function represents a markdown to HTML converter.
127
```php
128
class MarkdownPlugin implements PluginInterface
129
{
130
    public function attachEvents(EventEmitterInterface $emitter)
131
    {
132
        $emitter->on('post.create', function (PostEvent $event) {
133
            $event->post['format'] = 'markdown';
134
        });
135
 
136
        $emitter->on('post.render', function (PostEvent $event) {
137
            if (isset($event->post['format']) && 'markdown' === $event->post['format']) {
138
                $event->post['body'] = markdown($event->post['body']);
139
            }
140
        });
141
    }
142
}
143
```
144
 
145
There you go, the blog now renders posts as markdown. But all of the previous
146
posts before the addition of the markdown plugin are still rendered correctly
147
as raw HTML.
148
 
149
## Feature: Comments
150
 
151
TODO
152
 
153
## Feature: Comment spam control
154
 
155
TODO