Creating Snippets

From MODx Wiki
Revision as of 07:18, 22 January 2009 by Everettg 99 (Talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search


* This is a long and beefy document... for a simple overview of what Snippets are and how to use them, please check out the Snippets Overview.

Here you can see some guidelines that are thought to be best practices for add-on developers. You don't have to follow these to the letter, but it's good to be aware of them.

Contents

Reading MODx Information from Within Snippets

Reading System Settings

System settings are represented by [(this)] type of tag; You can read system settings using the following construct:

$fmPath = $modx->config['filemanager_path'];

Retrieving Information about the Current Document

See the page Document page for information about getting information about the current document.

To get the ID of the current document

$id = $modx->documentIdentifier;

Never use:

$id = $modx->getDocumentIdentifier('id');  // Never use this!

as your snippet will cause Friendly URLs to stop working for pages that use your snippet.

To get other information about the current document

Use the MODx API:documentObject.

See also, this page on Understanding the Document Object

Code Tips

Use single quotes instead of double quotes

When you use single quotes, your text is parsed faster by php core.

echo "MODx"; // bad
echo 'MODx'; // good


Use concatenation instead of putting variables into double quoted string

When you define a string in double quotes, it's possible to put $variables inside that string and they will be parsed by php core. This however lowers the readability of your code and it's also faster to use concatenation.

echo "My name is $name"; // bad
echo 'My name is '.$name; // good

All variables and functions and classes should begin with the initials of your resource name

This prevents naming collision and is also good practice in general.

Variables:

$lang; // bad
$rnLang; // good, (when resource name is ResourceName)

Function names (if not inside a class):

function countRows() { ... } // bad
function rnCountRows() { ... } // good

Class names:

class RowClass { ... } // bad
class RnRowClass { ... } // good

Name variables so that their content format can be guessed

It is good to name variables so that their content format can be guessed from the variable name. It improves code readability. For example:

$country = "Japan"; // bad
$rnStrCountry = "Japan"; // good, 'Str' tells that it's a String
$rn_str_country = "Japan"; // also good
 
$count = 1; // bad
$rnNrCount = 1; // good, 'Nr' tells that it's a number
$rn_int_count = 1; // also good
 
$rows = array(); // bad
$rnArrayRows = array(); // good, 'Array' tells that it's an Array
$nr_ar_rows; // good

Name functions so that their procedure and possible return value format can be guessed

Function names should imply what they do and do they return something and possibly the return value format.

function name($userid) { return $name; } // bad
function rnGetStrName($rnNrUserId) { return $rnStrName; } // good, a function that returns a name in String
function rnIsValidMember($rnNrUserId) { return true; } // good, a function that returns a boolean

Wrap functions inside !function_exists conditional if the resource is needed to run more than once on a page

If you call a snippet twice on a page, it will throw up a parsing error saying something like "Cannot redeclare functionName()". So, if you need it to be possible to run your snippet multiple times on a page, you need to wrap it inside a !function_exists conditional block.

function rnCountRows() { ... } // bad
 
if(!function_exists(rnCountRows)) { // good
    function rnCountRows() { ... } 
}

You can also write all your functions and store them in a file on your webserver under /assets/snippets/[yoursnippetname]/ and use include_once to include all your functions.

Declare or initialize variables before using them

This protects against variable injecting.

echo $rnStrCountry; // bad
$rnStrCountry = 'Japan'; echo $rnStrCountry; // good
$rnStrCountry = ''; echo $rnStrCountry; // good

Variables and objects created during the execution of the resource should be destroyed

Destroying the variables and objects that you create in your snippet after use is good practice. This frees resources, such as memory on the server. Free memory is always good.

$rnStrCountry = 'Japan'; // end of ressource // bad
$rnStrCountry = 'Japan'; unset($rnStrCountry); // good

Unset is good to do at the end of the resource execution - only to variables that are not used after that. Though, if you load a big resource into the memory in the middle of your code, it's good to remove it too after you're done with it and not wait until the end of resource execution.

Validate snippet parameter values as well as possible

You should always validate the user input for parameter values as well as possible.

// Parameters : param=$timestamp;
$rnTimeStamp = $param; // bad
if (isTimestamp($param)) $rnTimeStamp = $param; // good

Snippet parameters should always have a default value

The configurable parameters that a snippet has should always have default values. This is to avoid errors if some parameters are not initialized and also to make the usage of the snippet as simple as possible from an end-user's point of view. If some parameters cannot have default values, they should be checked in the snippet code and if a value is not set, an error message should be returned to the user.

$rnStrName = $param; // bad
$rnStrName = isset($param) ? $param : 'default value'; // good

Snippets and plugins should not contain presentation layer code

Snippets and plugins should not contain any presentation layer code, e.g. the output (X)HTML should not be hardcoded into the snippet logic code. Instead you should use MODx templating abilities to insert output into a placeholder, or get the output template from a chunk. This has many advantages - your resource will be more flexible for end users, code readability is improved, translation is simplified, etc..

Bad:

$output = '<p>'.$rnStrName.'</p>';
return $output;

Good

// in page template, user has: <p>[+rnStrName+]</p>
$modx->setPlaceholder('rnStrName', $rnStrName);
return;

Better:

// Use a chunk, and within this chunk, use placeholders to output any dynamic data.
// What we're doing here is first to set the placeholders, and then we're returning the chunk with the edited placeholders.
 
// Set placeholders:
$modx->setPlaceholder('name', $varName);		
$modx->setPlaceholder('lastvisit', $varLastvisit);
 
// Return the chunk
return $modx->getChunk('NameOfChunk');

See the setPlaceholder API function for more information ([1]).

Tip! If you're more experienced with MODx and PHP, please take a look at the parseChunk API function ([2]), which is probably the best way to handle chunks and placeholders.

Use strftime() to format date instead of date()

You should use strftime() instead of date() to formate the date output. This improves the internationalization of the output value, strftime() is affected by set_local() where as date() is not.

// bad 
$rnStrFormat = 'F j, Y, g:i a'; 
$rnStrToday = date($rnStrFormat); // March 10, 2006, 1:23 pm
 
// good
// assuming that user has set his locale some where, for example in confic.inc.php
$locale = setlocale(LC_ALL, 'de_DE.UTF-8');
 
$rnStrFormat = '%B %d, %Y, %H:%S';
$rnStrToday = strftime($rnStrFormat); // März 23, 2006, 13:23

Only configuration settings in snippet code, use php include to include logic code

If your snippet code has many lines, use external files for the logic code. It's good to organize the code in the external files too, for example logic in one file, functions in other, etc.

It's good practice to have as little as possible of your code for user to paste in the textarea because in this way, it prevents the user from breaking the code. Now days when more and more server hosts are also using mod_security, it can cause problems for the user to save the snippet code into MODx when mod_security prevents some strings to be in POST data.

Also, use include_once() instead of include(), require() and require_once() to prevent the files from being included many times if snippet is run multiple times on a page. You must use $modx->config['base_path'] in the path to the include file to support enviroments that have open_basedir restriction in effect.

//bad
$rnNrCount = 10;
for ($rnIndex = 0; $rnIndex < $rnNrCount; $rnIndex++) {
 ... etc, many lines of logic code ...
}
 
//good
$rnStrName = isset($rnStrName) ? $rnStrName : 'default value';
include_once($modx->config['base_path'].'assets/snippets/yoursnippet/yourcode.inc.php');

Create descriptive headers to your code

It's good practice to have descriptive headers in php comments in your code that state:

  • Resource name
  • Author name
  • Author nick (eg MODx forum username, if exists)
  • Version
  • MODx version compatibility info
  • Date of release
  • Short description

You should always also provide the following documentation for your resource:

  • Long description
  • Examples of use
  • List of parameters and their explanations (if any)
  • List of modifications (Short info about bugfixes, new functionality, rewrites, etc.)
  • List of chunk templates that are used, short explanations and default contents (if any)
  • List of template placeholders (if any)
  • List of files that are needed for the resource to work (Eg. dependencies for other resources, installation paths, needed permissions etc.)
  • Todo list of planned features (if any)

Comment your code as much as possible

It's good to comment your code as much as possible to increase readability and help collaborative work.


Objectify Your Code

If you're writing a lot of common functions, put them into a class! Don't create a huge list of functions with no bearing, when you could be writing solid Object-Oriented Programming.

For example, a good snippet would have the config parameters in the snippet TPL, include its actual logic in an external PHP file, and be run via a class instantiation and method calls. i.e.:

  1. <?php
  2. require_once($modx->config['base_path']."assets/snippets/paintboard/paintboard.inc.php");
  3.  
  4. $PaintBoard = new PaintBoard(array(
  5. 'width' => 400,
  6. 'height' => 300,
  7. 'title' => 'My Canvas',
  8. ));
  9.  
  10. $modx->setPlaceholder('pbCanvas',$Paintboard->render());
  11. ?>

where the constructor of the class takes a config array, sets it up, and then the render() method renders the PaintBoard.

The paintboard.inc.php class can look like this (PHP5 demo):

  1. <?php
  2. class PaintBoard {
  3. function __construct($config) {
  4. // do logic based on array here...
  5. }
  6.  
  7. public function render() {
  8. // draw code here...
  9. return $output;
  10. }
  11.  
  12. private function createColorPalette() {
  13. // code here
  14. }
  15.  
  16. // .. more functions ..
  17. }
  18. ?>

The benefits to this are simple: the main logic code is separated from the connector (the snippet code), and the output is put in a placeholder. Thus, you get MVC (Model-View-Controller) programming style, which is optimal.

It also prevents you from overwriting PHP variables used in other snippets or MODx situations. Not to mention, this cleans up the code big time.

Do not use short PHP open tags in your external script files

To have your resource supported on many different server environments, avoid using short php open tags.

<? .... ?> //bad
<?php .... ?> //good

See Also

Snippets Overview

Personal tools