Creating Snippets
From MODx Wiki
* 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.
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.:
<?php require_once($modx->config['base_path']."assets/snippets/paintboard/paintboard.inc.php"); 'width' => 400, 'height' => 300, 'title' => 'My Canvas', )); $modx->setPlaceholder('pbCanvas',$Paintboard->render()); ?>
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):
<?php class PaintBoard { function __construct($config) { // do logic based on array here... } public function render() { // draw code here... return $output; } private function createColorPalette() { // code here } // .. more functions .. } ?>
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
