Note:

If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

User:Mark Johnson/Namespaces: Difference between revisions

From MoodleDocs
(Added description of proposed namepspacing convention)
(Added note about CSRF)
(10 intermediate revisions by the same user not shown)
Line 1: Line 1:
===Notes about using PHP namespaces in Moodle plugin development===
{{Work_in_progress}}
 
==Notes about using PHP namespaces in Moodle plugin development==


'''Goal:''' To take advantage of namespace features in PHP 5.3 to allow plugins to be written with less verbose function names, while still avoiding name collisions
'''Goal:''' To take advantage of namespace features in PHP 5.3 to allow plugins to be written with less verbose function names, while still avoiding name collisions
====Current Issue====
When writing a plugin, one creates a lib.php or locallib.php containing a set of functions for use within that plugin.  Each function name (and similarly, constant names and class names) must currently be prefixed in the format {plugintype}_{pluginname}_, to avoid a function that performs a similar task in a separate plugin having the same name, thus causing a name collision. 


This can result in overly verbose function names which inhibits both readability and ease of writing of code. For example, a function for printing tabs in an admin report named targetgrades would need to be called report_targetgrades_print_tabs() - the majority of this name is only there to avoid name collisions, and it otherwise redundant.
===About this document===
For the purposes of this document, the term "function" will be used to refer to any PHP function, constant or class defined in a Moodle plugin library (unless explicitly indicated otherwise), since namespaces apply to all 3 in the same way.
 
The exemplar plugin used for this exercise is an admin report used for calculating Target Grades.
 
===Current Issue===
When writing a plugin, one creates a lib.php or locallib.php containing a set of functions for use within that plugin.  Each function name must currently be prefixed in the format <tt>{plugintype}_{pluginname}_</tt>, to avoid a function that performs a similar task in a separate plugin having the same name, thus causing a name collision. 
 
This can result in overly verbose function names which inhibits both readability and ease of writing of code. For example, a function for printing tabs in an admin report named targetgrades would need to be called <tt>report_targetgrades_print_tabs()</tt> since there is already a global function called <tt>print_tabs()</tt>. The majority of this name is only there to avoid name collisions, and it otherwise redundant.
 
===Proposal===
I am going to upgrade one of my Moodle 1.9 blocks for use in Moodle 2.x. During this process I will alter the libraries to use namespaces, allowing functions, classes and constants to have shorter names while still avoiding name collisions.  In files including these libraries, I will use namespace aliases to allow the functions to be called using much shorter notation.
 
====Naming convention====
As mentioned above, the current naming convention for prefixing functions is <tt>plugintype_pluginname</tt>.  It makes logical sense to use a similar convention in the namespace hierachy - each plugin having a namespace of <tt>plugintype\pluginame</tt>.  For example, all admin reports would have their functions the <tt>report</tt> namespace, and the Target Grades report would have it's own sub-namespace, <tt>report\targetgrades</tt>.
 
====Potential issues====
This may cause issues with some plugin types.  For example, Activity modules require certain functions following the current naming convention to exist in lib.php. Placing these functions within a namespace would prevent them from working properly.
 
Some plugin types will include lib.php and its functions automatically. While this should not cause a problem for the majority of plugin types since the functions shouldn't be accessed unless explicitly requested in the code, it's a potential source of problems to be aware of.
 
===Steps for conversion===
All libraries used by the plugin needed to be put inside the plugin's namespace.  This was achieved by placing the following line at the top of the library files
<code php>namespace report\targetgrades;</code>
Note that the <tt>report</tt> namespace does not already have to have been defined for this to work.
 
Next, the <tt>report_targetgrade_</tt> prefix was removed from all function and class names in the libraries, as it is now redundant.
 
Any functions in the global namespace (i.e. built in PHP functions or Moodle functions) that are used in the library functions were prefixed with a backslash.  This doesn't appear to be strictly necessary with actual functions (as opposed to constants or classes, where it's definitely necessary), but was done for consistency.
 
In any documents that used the libraries, the files were included as usual using <tt>require_once</tt> at the start of the file. This includes the functions within their namespace, and makes that namespace available in the file.
This was followed immediately by
<code php>use report\targetgrades as tg;</code>
Which allows <tt>tg</tt> to be used an alias for the <tt>report\targetgrades</tt> namespace when calling functions.
 
Functions from the namespace, such as print_tabs, can now be called thusly:
<code php>tg\print_tabs();</code>


====Proposal====
===Conclusions===
I am going to upgrade one of my Moodle 1.9 blocks for use in Moodle 2.x. During this process I will alter the libraries to use namespaces, allowing functions, classes and constants to have shorter names while still avoiding name collisions.  In files including these libraries, I will use namespace aliases to allow the functions/classes/constants to be called using much shorter notation.
* Using namespaces achieves the stated goal of improving readability and ease of writing code by shortening function names, especially when aliases are used as demonstrated.
* Using existing naming conventions for the namespaces is logical and makes the process easier to understand for developers not used to using namespaces.
* Namespaces will not be suitable for all libraries due to some plugins requiring the current naming conventions for some functions. However, even in these cases all non-standard functions could be namespaced to achieve the same advantages.
* Defining a Moodle Form in a namespace will cause problems - code for preventing CSRF falls afoul of the \ character meaning that the hidden field containing the forms name doesn't match the full namespaced name of the form's class, which in turn prevents the form submitting.


=====Naming convention=====
===Code===
As mentioned above, the current naming convention for prefixing functions is <tt>plugintype_pluginname</tt>.  It makes logical sense to use a similar convention in the namespace hierachy - each plugin having a namespace of <tt>plugintype\pluginame</tt>.  For example, all admin reports would have their functions the <tt>report</tt> namespace, and the Target Grades report described above would have it's own sub-namespace, <tt>report\targetgrades</tt>.
Once completed, the plugin will be published on GitHub as an example of a Moodle plugin using namespaces.

Revision as of 12:34, 7 November 2011

Note: This page is a work-in-progress. Feedback and suggested improvements are welcome. Please join the discussion on moodle.org or use the talk.


Notes about using PHP namespaces in Moodle plugin development

Goal: To take advantage of namespace features in PHP 5.3 to allow plugins to be written with less verbose function names, while still avoiding name collisions

About this document

For the purposes of this document, the term "function" will be used to refer to any PHP function, constant or class defined in a Moodle plugin library (unless explicitly indicated otherwise), since namespaces apply to all 3 in the same way.

The exemplar plugin used for this exercise is an admin report used for calculating Target Grades.

Current Issue

When writing a plugin, one creates a lib.php or locallib.php containing a set of functions for use within that plugin. Each function name must currently be prefixed in the format {plugintype}_{pluginname}_, to avoid a function that performs a similar task in a separate plugin having the same name, thus causing a name collision.

This can result in overly verbose function names which inhibits both readability and ease of writing of code. For example, a function for printing tabs in an admin report named targetgrades would need to be called report_targetgrades_print_tabs() since there is already a global function called print_tabs(). The majority of this name is only there to avoid name collisions, and it otherwise redundant.

Proposal

I am going to upgrade one of my Moodle 1.9 blocks for use in Moodle 2.x. During this process I will alter the libraries to use namespaces, allowing functions, classes and constants to have shorter names while still avoiding name collisions. In files including these libraries, I will use namespace aliases to allow the functions to be called using much shorter notation.

Naming convention

As mentioned above, the current naming convention for prefixing functions is plugintype_pluginname. It makes logical sense to use a similar convention in the namespace hierachy - each plugin having a namespace of plugintype\pluginame. For example, all admin reports would have their functions the report namespace, and the Target Grades report would have it's own sub-namespace, report\targetgrades.

Potential issues

This may cause issues with some plugin types. For example, Activity modules require certain functions following the current naming convention to exist in lib.php. Placing these functions within a namespace would prevent them from working properly.

Some plugin types will include lib.php and its functions automatically. While this should not cause a problem for the majority of plugin types since the functions shouldn't be accessed unless explicitly requested in the code, it's a potential source of problems to be aware of.

Steps for conversion

All libraries used by the plugin needed to be put inside the plugin's namespace. This was achieved by placing the following line at the top of the library files namespace report\targetgrades; Note that the report namespace does not already have to have been defined for this to work.

Next, the report_targetgrade_ prefix was removed from all function and class names in the libraries, as it is now redundant.

Any functions in the global namespace (i.e. built in PHP functions or Moodle functions) that are used in the library functions were prefixed with a backslash. This doesn't appear to be strictly necessary with actual functions (as opposed to constants or classes, where it's definitely necessary), but was done for consistency.

In any documents that used the libraries, the files were included as usual using require_once at the start of the file. This includes the functions within their namespace, and makes that namespace available in the file. This was followed immediately by use report\targetgrades as tg; Which allows tg to be used an alias for the report\targetgrades namespace when calling functions.

Functions from the namespace, such as print_tabs, can now be called thusly: tg\print_tabs();

Conclusions

  • Using namespaces achieves the stated goal of improving readability and ease of writing code by shortening function names, especially when aliases are used as demonstrated.
  • Using existing naming conventions for the namespaces is logical and makes the process easier to understand for developers not used to using namespaces.
  • Namespaces will not be suitable for all libraries due to some plugins requiring the current naming conventions for some functions. However, even in these cases all non-standard functions could be namespaced to achieve the same advantages.
  • Defining a Moodle Form in a namespace will cause problems - code for preventing CSRF falls afoul of the \ character meaning that the hidden field containing the forms name doesn't match the full namespaced name of the form's class, which in turn prevents the form submitting.

Code

Once completed, the plugin will be published on GitHub as an example of a Moodle plugin using namespaces.