Skip to content

Instantly share code, notes, and snippets.

@Nicofuma
Last active August 29, 2015 14:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Nicofuma/88e2fd74fa7aa49f0b47 to your computer and use it in GitHub Desktop.
Save Nicofuma/88e2fd74fa7aa49f0b47 to your computer and use it in GitHub Desktop.
As you probably know, the 3.1 brings radical changes to the whole system of phpBB MOD. In fact what we call a MOD today no longer exist and will be replaced by a new extension system based on a simple concept:
[list][*][b][color=#008000]The extensions do not change the code of phpBB [/color] [/b], they are located in a separate folder (one per extension) and use a number of mechanisms to interact with the heart of phpBB.[/list]
But a new system that uses any new mechanisms can be difficult to understand, so I will try to present the general architecture of an extension and then explain how the different mechanisms and concepts used by the function extensions.
[list=1]
[*] [size=150] [color=#800080] [b] Templates [/b] [/color] [/size]
As you may know, with the 3.1 template engine change: instead of using a template engine developed internally used [b] Twig [/b](http://twig.sensiolabs.org) but actually it changes what?
And ben [u] it does not change anything [/u]. Of course it is possible to use all the features and tags Twig but it is not mandatory for all old tags phpBB are implemented and are sufficient to manage templates.
But there are still a few changes:
[list]
[*] [b] [size=130] [color=#BF4000] IE6 and IE7 are not supported! [/color] [/size] [/b]
[*] [b] [size=110] Legacy style [/size] [/b]
The legacy of style has been expanded: now the inheritance tree can be infinite, ie a style can inherit a style which itself inherits from another style and so on.
[b] [color=#0080BF] Note [/color] [/b] Only templates are inherited, not the css styles or images. Also to inherit the css you can use css instruction [c]import [/c]
[*] [b] [size=110] Some new tags for extensions [/size] [/b]
Some new tags appear for extensions, they are used to indicate new stylesheets or new scripts javascript must be loaded:
[list]
[*] [c] <!-- INCLUDECSS ../theme/chemin_vers_css.css --> [/c] for CSS
[b] [color=#0080BF] Note [/color] [/b] Currently there is a bug and should use this trick ([c]../theme/[/c]) for style sheets, but in the final version that should be resolved and we should be able to use [c]@vendor_ext/fichier.css[/c] as for js files.
[b] [color=#0080BF] Note [/color] [/b] The CSS must be placed in the [c]theme/[/c] style
[*] [c] <!-- INCLUDEJS @vendor_ext/fichier.js --> [/c] to a javascript script
[b] [color=#0080BF] Note [/color] [/b] The JavaScript files must be placed in the [c]template/[/c] style. [/list]
[b] [color=#FF0000] Warning [/color] [/b]
If you create your own style 0 or if you redefine [c] overall_header.html [/c] (or [c] simple_header.html [/c]) you would add the special tag [c] {$stylesheets} [/c] which indicates where the tags will be added for loading stylesheets added by extensions.
Similarly, if you redefine [c] overall_footer.html [/c] (or [c] simple_footer.html [/c]) you would add the special tag [c] ${SCRIPTS} [/c] which indicates location where tags will be added for loading javascript scripts added by extensions. [/list]
[*] [size=150] [color=#800080] [b] Dependency Injection [/b] [/color] [/size]
Any version 3.1 is based on a fundamental mechanism, namely dependency injection. The goal is not to have to worry about managing different dependencies of the feature you want to encode (eg database, the cache system or the current user can be dependencies).
[quote="Wikipedia"] The dependency injection (Dependency Injection) is a mechanism to implement the principle of inversion of control. It is to create dynamically (inject) the dependencies between different classes based on a description (metadata or configuration file) or programmatically. And dependencies between software components are not expressed in the code statically but determined dynamically at runtime. [/quote]
In practice this has been implemented using the [url=http://symfony.com/fr/doc/2.3/components/dependency_injection/introduction.html] component Symfony dependency injection [/url]. And all services provided by the phpBB are described Yaml format file [c] /config/services.yml [/c] and they can all be used by extensions.
In this file you can find three sections:
[list]
[*] [B] import [/b] to include the contents of another file.
[*] [B] parameters [/b] to define constants.
[*] [B] services [/b] to define services.
[/list]
The definition of a service is composed of three essential elements:
[list]
[*] A name (which must contain only alphanumeric, dash, dot and underscore)
[*] A class for this service
[*] A list of dependencies that can be constants (as is written) parameters or services (the parameter name surrounded by %) (service name prefixed with an @)
[b] [color=#0080BF] Note [/color] [/b] These dependencies are the list of parameters that will be sent to the class constructor for the service [/list]
example:
[code]
imports:
- {resource: tables.yml}
cache:
class: phpbb\cache\service
arguments:
- @cache.driver
- @config
- @dbal.conn
- %Core.root_path%
- %Core.php_ext%
parameters:
core.root_path: ./
core.php_ext: php
core.cache_dir: %core.root_path cache/%
[/code]
At this level it is important to note two things:
[list]
[*] It is possible to define multiple services with the same name, in this case only the latter definition is counted.
[*] Except for the point above, the order of service definition and parameters does not matter. [/List]
As for extensions, they can add new services (or redefine existing services) in file [c] /ext/<vendor>/<ext_name>/config/services.yml [/c]
[*] [size=150] [color=#800080] [b] General architecture of an extension [/b] [/color] [/size]
The extensions do not change any of the phpBB files, suddenly to function they must follow a specific architecture.
First of all an extension is identified by a name consisting of a [i] Vendor [/i] (eg Nicofuma) and a [i] name [/i] (eg ext1) and to be used it must necessarily be located in the [c] ext/<vendor>/<name>/[/i].
Then in this case, two files are required to be present:
[list]
[*] [C] ext.php [/c]
This file does nothing by default but you can define some methods that will be called during the different stages of activation and deactivation of the extension.
It should contain a class extending ext [c]\phpbb\extension\base[/c] and must be in the namespace [c]<vendor>\<name> [/c]
example:
[code=php] <?php
/**
*
* This file is share of the phpBB Forum Software package.
*
*copyright (C) phpBB Limited <https://www.phpbb.com>
license * GNU General Public License, version 2 (GPL 2.0)
*
* For full copyright and license information, please see
* The docs/Credits.txt file.
*
*/
namespace nicofuma\my_ext;
/**
* Extension class for my_ext
*/
class ext extends \phpbb\extension\base
{
} [/code]
[*] [C] composer.json [/c]
This file describes the extension and gives some information on it: https://wiki.phpbb.com/Extension_meta_data
[b] [color=#FF0000] Warning [/color] [/b] it is very important that the name in the [c] name [/c] is of the form [c]<vendor>/<name>[/c]
[b] [color=#0080BF] Note [/color] [/b] now this file is largely untapped, but eventually it will be used to manage all the dependencies of the extension and will be able to target a specific version of php and phpbb, but it will also require new external libraries or define another extension as a dependency. [/list]
In addition to these two required files you can add some optional files:
[list]
[*] [c] adm/style/ [/c] which contains the dedicated admin panel templates added by the extension
[*] [c] config/routing.yml [/c] will be defined where the different routes added by the extension (see the section on routing)
[*] [c] config/services.yml [/c] where different services are defined added by the extension (see section on dependency injection)
[*] [c] language/<lang>/ [/c] which contains all the language files defined by the extension (see Appendix dedicated)
[*] [c] migration/[/c] that contains the migration (changes in the database) for extension (see Appendix dedicated)
[*] [c] styles/<style>/template/ [/c] which contains templates added by the extension [/list]
Finally, there are two other cases it is advisable to define and use even if it is not mandatory:
[list]
[*] [c] controller/ [/c] in order to define the extension controllers (see the section on routing)
[*] [c] event/ [/c] in order to define event listeners (see the section on events) [/list]
[*] [size=150] [color=#800080] [b] Events template [/b] [/color] [/size]
As in 3.1 extension can not modify any file, he had to find a way to allow them to inject code in the template files, have the [b] events template [/b].
Specifically, an event template is composed of two elements:
[list]
[*] A placeholder, represented by a tag in any template
[code] <!-- EVENT event_name --> [/code]
[*] A set of files [c] ext/<vendor>/<ext_name>/<chemin_vers_le_template>/event/<event_name>.html [/c]
Part [i] <chemin_vers_le_template> [/i] can have different forms:
[code] styles/prosilver/template code for one specific prosilver
styles/all/template for a common code for all styles
adm/style for an event on the ACP [/code] [/list]
When compiling the template EVENT tag will be replaced by the contents of all files in the extensions.
[b] [color=#0080BF] Note [/color] [/b] Currently these files are included in a random order
[b] [color=#0080BF] Note [/color] [/b] Regarding JQuery UI, yet each extension is bothering to check if the required component is not already included. Will certainly be handled automatically for version 3.2, but for now how to be done is still under discussion.
[*] [size=150] [color=#800080] [b] Routing [/b] [/color] [/size]
An extension may need to add new pages (pages of content, form validation, json api etc ...) but the file extension must not and can not be accessed directly from the outside. So how to add the new pages?
For this we use the component [url=http://symfony.com/doc/2.3/components/routing/introduction.html] Routing [/url] Symfony that'll allow us to define a set of roads to link a callback php (a method of a class) to each of them.
[b] [color=#0080BF] Note [/color] [/b] Several roads may have the same callback and the callback can be different in different classes.
A route is composed of three essential elements:
[list] [*] A name
[*] A pattern that describes the concrete road (URL with different parameters)
[b] [color=#0080BF] Note [/color] [/b] A variable parameter is shown in brackets
[*] A callback which consists of the name of the service to retrieve an instance of the controller (the class that contains the method to call) followed by a [c]: [/c] and the name of the method to call
[b] [color=#0080BF] Note [/color] [/b] The controller is always defined using a service (see section on dependency injection)
[b] [color=#0080BF] Note [/color] [/b] This method takes as parameters that defines a variable parameter in the road. However, there can be another type parameter [c] Symfony\Component\HttpFoundation\Request [/c] if the request method then this parameter will contain the object corresponding to the current HTTP request.
[b] [color=#FF0000] Warning [/color] [/b] A controller should always return an instance of [c]Symfony\Component\HttpFoundation\Response [/c] or else raise an exception. And an extension should never call [c] trigger_error() [/c] (of course if you call a function that uses phpbb yet it is not your fault) or display something directly (except for unavoidable debug messages during development :)).
[b] [color=#0080BF] Note [/color] [/b] It is possible to define exclusive routes for GET or POST requests queries to defaults settings, pre-validate the value parameters etc ... ([url=http://symfony.com/doc/2.3/components/routing/introduction.html] see the documentation for Symfony [/url]) [/list]
[b] [color=#FF0000] Warning [/color] [/b] All roads defined by an extension must be in the file [c] /ext/<vendor>/<ext_name>/config/routing.yml [/c]
example:
[code]
my_route:
pattern: my/super/route/{parameter_1}
defaults: {_controller: vendor.ext_name.controller.mon_controlleur: my_route}
[/code]
Finally, when you will access a page [c] http://www.mon-forum.com/chemin/vers/le/forum/app.php [/c] the system will recover all that'll be between app. php and the question mark (or until the end of the URL if it is absent) and going to the store in what I now call the URL. Then it will compare the URL with the patterns of all registered road until you find one that fits perfectly. Then it will call the callback that has been registered with the road.
[b] [color=#0080BF] Note [/color] [/b] The roads are compared in the order of their declaration
[b] [color=#0080BF] Note [/color] [/b] If the pattern does not match any route a 404 error is returned.
Examples of URL:
[list] [*] to [c] http://www.mon-forum.com/chemi/vers/le/forum/app.php/my/super/route/10 [/c] URL is [c] /my/super/route/10 [/c]
[*] To [c] http://www.mon-forum.com/app.php/my/super/route/10?mon_parametre=15 [/c] is the URL [c]/my/super/route/10 [/c]
[*] To [c] http://www.mon-forum.com/app.php [/c] is the URL [c] / [/c]
[*] And if URL rewriting is activated, so [c] http://www.mon-forum.com/my/super/route/10?mon_parametre=15 [/c] is the URL [c]/my/super/route/10 [/c] [/list]
[size=120] [color=#BF0080] [b] @controller.helper [/b] [/color] [/size]
Service [c] controller.helper [/c] provided some practical methods for controllers:
[list] [*] [c] render($template_file, $page_title = '', $status_code = 200, $display_online_list = false) [/c]
Allows you to render a given template (calling functions [c] page_header() [/c] and [c] page_footer() [/c]).
The [c] $page_title [/c] and [c] $display_online_list [/c] went to [c] page_header() [/c] and [c] $status_code [/c] is the HTTP return code (if you want to return a 404 for example).
[*] [c] route($route, array $params = array (), $is_amp = true, $session_id = false) [/c]
Used to generate the url corresponding to a road.
[list] [*] [c] $route [/c] is the name of the road as defined in file [c] routing.yml` [/c]
[*] [c] $params [/c] is an associative array associating a value to all variable parameters
[*] [c] $is_amp [/c] indicates whether the character [c] & [/c] must be replaced by [c] &amp; [/c] in the url generated
[*] [c] $session_id [/c] to specify a custom during the call to session_id [c] append_sid() [/c] (which is called automatically) [/list]
[*] [c] error ($message, $code = 500) [/c]
Can display an error message easily (similar to [c] trigger_error() [/i] in version 3.0)
[list] [*] [c] $message [/i] is the message that will be displayed
[*] [c] $code [/c] is the HTTP error code [/list] [/list]
[*] [size=150] [color=#800080] [b] Events php [/b] [/color] [/size]
[*] [size=150] [color=#800080] [b] Schedule [/b] [/color] [/size]
[list=I] [*] [size=120] [color=#BF0080] [b] Language Files [/b] [/color] [/size]
[*] [size=120] [color=#BF0080] [b] Migration [/b] [/color] [/size]
[*] [size=120] [color=#BF0080] [b] permissions [/b] [/color] [/size]
[*] [size=120] [color=#BF0080] [b] Modules [/b] [/color] [/size]
[*] [size=120] [color=#BF0080] [b] Test [/b] [/color] [/size] [/list] [/list]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment