phpBB

Development Wiki

Difference between revisions of "Developing Extensions"

From phpBB Development Wiki

m (Getting Started)
(Front-facing Files)
Line 21: Line 21:
  
 
== Front-facing Files ==
 
== Front-facing Files ==
While, some extensions perform only background tasks or simply modify a pre-existent page, some extensions may require the inclusion of a front-facing file. For example, a blog extension needs to display one or more entries on its own page. This is achieved by adding a class that extends the '''phpbb_extension_controller''' base class. For our example, we will use an extension named '''foobar''' made by  vendor named '''example'''.
+
While, some extensions perform only background tasks or simply modify a pre-existent page, other extensions may require the inclusion of a front-facing file, which the user is able to access to view information and perform tasks related to the extension. For example, a blog extension needs to display one or more blog entries. To achieve this, we use classes, which are called "controllers". All controllers are mapped with their own routes in the config/routing.yml file. There is one in the main phpBB config directory, but extensions wishing to include a controller must have their own config directory and routing.yml file.
  
=== The Controller ===
+
=== Routing ===
In order for your extension to have a user-accessible page, you must create a front controller class, which is responsible for controlling what a user sees when he accesses your extension's front end. You will need to create a new file in the root of your extension directory (i.e. '''./ext/example/foobar/''') called '''controller.php'''. This file must contain a new class called '''phpbb_ext_example_foobar_controller''' (This follows the phpBB class naming guidelines, driving the class name from the directory structure and the file in which the class is contained). Your class '''must''' extend '''phpbb_extension_controller'''. The only required method within your controller class is a public '''handle()''' method (as is defined in the phpbb_extension_controller_interface, which is implemented by the phpbb_extension_controller abstract class). The '''handle()''' method receives no arguments, and serves as your class's constructor. ''Your controller should not have its own __construct() method.''<br />
+
The first step in creating your front-facing pages is to decide what routes you will need. For instance, a blog extension might want the route /blog for its main page, and /blog/{id} for viewing an individual blog article. The route definition is contained in your extension's routing.yml file, in the config directory.
===== Accessing Common phpBB Variables =====
+
The .yml extension signifies that the file uses the YAML (Yet Another Markup Language) syntax. A routing.yml file containing a single route definition might look like this:
The following phpBB variables are automatically inherited as class properties by your controller class when it extends phpbb_extension_controller: $template, $user, $db, $request, $config, $phpbb_root_path, $phpEx
+
<br /><br />
+
To access these within your class, use the $this-> prefix. For example, the $user object can be accessed as $this->user. '''Note:''' These class properties are set within the phpbb_extension_controller::__construct() method. As such, you should use handle() as your constructor method, rather than overwriting the parent constructor.
+
===== Fig. 1 - Example Controller Class =====
+
<php><?php
+
  
class phpbb_ext_example_foobar_controller extends phpbb_extension_controller
+
<php>
{
+
blog_front:
    /**
+
     pattern: blog
    * Extension front handler method. This is called automatically when your extension is accessed
+
     defaults: { _controller: blog_service:controller_method }
    * through index.php?ext=example/foobar
+
</php>
    * @return null
+
What that says is that a route named "blog_front" will match the pattern "blog" in the URL. When that URL is matched, the method "controller_method", which is a method of the class defined by the service "blog_service", is run (@todo: link to services documentation). This method ''must'' return a Response object. (@todo: explain more about this, including use of the helper class.)
     */
+
    public function handle()
+
     {
+
        // We defined the phpBB objects in __construct() and can use them in the rest of our class like this
+
        echo 'Welcome, ' . $this->user->data['username'];
+
  
        // So to output stuff to the page, let's call our function display()
+
You can also specify that you want to ask for a blog ID so that the user can view a specific blog.
        $this->display();
+
    }
+
  
     /**
+
<php>
     * Display method - This is not a required method, but just for example.
+
blog_view:
     *
+
     pattern: blog/{id}
    * @return null
+
     defaults: { _controller: blog_service:view_article, id: 0 }
    */
+
     requirements:
    private function display()
+
        id: \d+
    {
+
</php>
        // The following takes two arguments:
+
The {id} in the pattern is called a "slug". The value directly following "blog/" in the URL will be placed into a variable called $id and passed to the controller method. As such, your controller method ''MUST'' have a parameter called $id. The "id: \d+" line is a match requirement. \d+ is regex for "one or more numbers". Because that requirement is in place, "blog/foo" and "blog/number1" would not match the pattern, but "blog/1" and "blog/10003423423490" would.
        // 1) which extension language folder we're using (it's not smart enough to use its own automatically)
+
Note that the "id: 0" bit in the defaults simply states that if no value is given for id (i.e. the route is "blog"), 0 is passed as the default value to the controller parameter $id.
        // 2) what language file to use
+
        $this->user->add_lang_ext('example/foobar', 'foobar');
+
  
        // foobar_body.html is in ./ext/foobar/example/styles/prosilver/template/foobar_body.html
+
=== The Controller ===
        $this->template->set_filenames(array(
+
The controller itself is simply a method that returns a Response object. The way things are set up in phpBB, the method must be tied to a class that is defined as a service. The method must contain at least as many parameters as there are slugs route pattern. In other words, if the route pattern is "blog/{id}", the method must at least contain an $id parameter. The method may not contain any more required parameters than there are slugs in the URL. In other words, if your method signature requires a parameter $foo without providing a default value, and neither a slug nor a default are given by the route, an error will be thrown. The only exception is when a parameter type hints "Symfony\HttpFoundation\Request". In this case, the Symfony Request object will be automatically injected into the method by the controller framework. Note that the Symfony Request object is separate from the phpbb_request object. It is recommended that you only use the Symfony Request object if you absolutely need to.
                'body' => 'foobar_body.html'
+
        ));
+
 
+
        // And we assign template variables the same as before as well
+
        $this->template->assign_var('MESSAGE', 'Yes, this is hard-coded language, which should still be avoided in virtually all cases.');
+
  
        // And now to output the page.
+
==== Accessing Common phpBB Variables ====
        page_header($user->lang('WELCOME_TO_FOOBAR'));
+
Because controllers are methods within a service, they can gain access to the commonly used phpBB objects (such as phpbb_template, phpbb_user, phpbb_db_driver, etc.) by specifying them in the service definition and then assigning them to class properties. Then, instead of using a locally scoped $db variable, you would instead use something like $this->db to access the database object.
        page_footer();
+
    }
+
}</php>
+
You can also include other public and private methods to call within the handle() method.
+
  
 
=== Invoking ===
 
=== Invoking ===
  
To load your page, visit domain.com/forum/index.php?ext=foobar
+
Currently, all routing passes through the "app.php" file. For example, a route with the pattern "foo/bar/baz" would be accessed by the URL <pre>www.domain.com/app.php?controller=foo/bar/baz</pre>. The goal for future versions is to shorten that to <pre>www.domain.com/app.php/foo/bar/baz</pre> and even <pre>www.domain.com/foo/bar/baz</pre>
  
 
[[Category:Extensions]]
 
[[Category:Extensions]]

Revision as of 16:56, 7 January 2013

phpBB 3.1 "Ascraeus" introduces extensions as another way to add new community-contributed features into a phpBB installation. Extensions are different from MODs in that they do not make any file modifications to the core phpBB files. Instead, extensions are entirely contained in their own directories within the ./ext/ directory in the phpBB root.

Getting Started

For an example extension package, please view the following repository: https://github.com/naderman/phpbb3-example-ext

Extensions should be located in the phpBB directory as follows: phpBB/ext/<vendor>/<ext>

<vendor> is the author or group of authors of the extension. Note that vendor and extension directory names may ONLY contain numbers and letters. Underscores, dashes, and other characters are NOT permitted. It is perfectly fine to have an extension named iamanextension.

composer.json

This file is the metadata file containing specific information about your extension. For information on what information goes into the file, please view extension_meta_data.

ext.php

Each extension should have a file called ext.php at phpBB/ext/<vendor>/<ext>/ext.php. Here is an example file from an example extesnion phpBB/ext/example/foobar/ext.php:

class phpbb_ext_example_foobar_ext extends phpbb_extension_base
{
}

The class is required in order for phpBB to identify your extension as an extension. It also may contain any special (un)installation commands in the methods enable_step(), disable_step() and purge_step(). As it is, these methods are defined in phpbb_extension_base, which this class extends, but you can overwrite them to give special instructions for those cases.

Front-facing Files

While, some extensions perform only background tasks or simply modify a pre-existent page, other extensions may require the inclusion of a front-facing file, which the user is able to access to view information and perform tasks related to the extension. For example, a blog extension needs to display one or more blog entries. To achieve this, we use classes, which are called "controllers". All controllers are mapped with their own routes in the config/routing.yml file. There is one in the main phpBB config directory, but extensions wishing to include a controller must have their own config directory and routing.yml file.

Routing

The first step in creating your front-facing pages is to decide what routes you will need. For instance, a blog extension might want the route /blog for its main page, and /blog/{id} for viewing an individual blog article. The route definition is contained in your extension's routing.yml file, in the config directory. The .yml extension signifies that the file uses the YAML (Yet Another Markup Language) syntax. A routing.yml file containing a single route definition might look like this:

blog_front:
    
patternblog
    defaults
: { _controllerblog_service:controller_method }

What that says is that a route named "blog_front" will match the pattern "blog" in the URL. When that URL is matched, the method "controller_method", which is a method of the class defined by the service "blog_service", is run (@todo: link to services documentation). This method must return a Response object. (@todo: explain more about this, including use of the helper class.)

You can also specify that you want to ask for a blog ID so that the user can view a specific blog.

blog_view:
    
patternblog/{id}
    
defaults: { _controllerblog_service:view_articleid}
    
requirements:
        
id: \d+

The {id} in the pattern is called a "slug". The value directly following "blog/" in the URL will be placed into a variable called $id and passed to the controller method. As such, your controller method MUST have a parameter called $id. The "id: \d+" line is a match requirement. \d+ is regex for "one or more numbers". Because that requirement is in place, "blog/foo" and "blog/number1" would not match the pattern, but "blog/1" and "blog/10003423423490" would. Note that the "id: 0" bit in the defaults simply states that if no value is given for id (i.e. the route is "blog"), 0 is passed as the default value to the controller parameter $id.

The Controller

The controller itself is simply a method that returns a Response object. The way things are set up in phpBB, the method must be tied to a class that is defined as a service. The method must contain at least as many parameters as there are slugs route pattern. In other words, if the route pattern is "blog/{id}", the method must at least contain an $id parameter. The method may not contain any more required parameters than there are slugs in the URL. In other words, if your method signature requires a parameter $foo without providing a default value, and neither a slug nor a default are given by the route, an error will be thrown. The only exception is when a parameter type hints "Symfony\HttpFoundation\Request". In this case, the Symfony Request object will be automatically injected into the method by the controller framework. Note that the Symfony Request object is separate from the phpbb_request object. It is recommended that you only use the Symfony Request object if you absolutely need to.

Accessing Common phpBB Variables

Because controllers are methods within a service, they can gain access to the commonly used phpBB objects (such as phpbb_template, phpbb_user, phpbb_db_driver, etc.) by specifying them in the service definition and then assigning them to class properties. Then, instead of using a locally scoped $db variable, you would instead use something like $this->db to access the database object.

Invoking

Currently, all routing passes through the "app.php" file. For example, a route with the pattern "foo/bar/baz" would be accessed by the URL
www.domain.com/app.php?controller=foo/bar/baz
. The goal for future versions is to shorten that to
www.domain.com/app.php/foo/bar/baz
and even
www.domain.com/foo/bar/baz