Compression

Compression is a light weight class that will load a CSS stylesheet, bind and translate given variables, compress and remove white space and cache the output for future use.

Version: 3.3.1
Requires: PHP 5.2
Tested On: PHP 5.3
Commit Hash: 508c6ee8b9
Released: Apr 22nd, 15:21



Decoda

Version: 3.3.1 (logs)
Package: Lightweight Markup Parser
Category: PHP
Views: 9,100
Permalink - Tinylink

Decoda is a lightweight class that extracts and parses a custom markup language; based on the concept of BB code. Decoda supports all the basic HTML tags and manages special features for making links and emails auto-clickable, using shorthand emails and links, and finally allowing the user to add their own code tags.

Decoda is a play on words for: Decoding Markup.

Class Features:
  • Parses custom code to valid (X)HTML markup
  • Setting to make links and emails auto-clickable
  • Setting to use shorthand text for links and emails
  • Provides Filters to parse markup and custom code
  • Provides Hooks to execute during the parsing cycle
  • Provides functionality to render complex markup using a template system
  • Can censor offensive words
  • Can convert smiley faces into images
  • Basic support for localized messages
  • Supports a wide range of tags
  • Fixes incorrectly nested tags by removing the broken/unclosed tags
  • Logs errors for validation
Related Entries:

Top

1 - Installation


For each string or block of text that you want parsed, you would need to initiate a new Decoda object by passing the string variable into the class constructor. We create a new object for each string so that we may alter its output, and not conflict for other strings that may be on the page.

// Include the class
include 'decoda/Decoda.php';
 
// String to be parsed
$string = 'Hello, my name is [b]Miles Johnson[/b], you may visit my website at [url]http://milesj.me/[/url].';
 
// Load the text and parse
$code = new Decoda($string);
echo $code->parse();


And that is it! In the following sections, you will learn about the powerful Filter and Hook system.
Top

2 - Configuration


Changing the Brackets
By default, all tags are wrapped in square brackets. Use setBrackets() to change them.

$code->setBrackets('{', '}');


Changing the Translations
Decoda comes with a built-in translation dictionary, which is used to translate words like "mail" and "quote". To see the list of supported locales, open up the config/messages.json file, and then use setLocale() to change it.

$code->setLocale('de-de');


Shorthand Links
If you would like a hyperlink to display its shorthand variant (displaying the word link or mail, instead of the text/url/email), you would call the method setShorthand().

$code->setShorthand(true);


Using XHTML
By default all strings are parsed as HTML, if you would like to use XHTML, call setXhtml();

$code->setXhtml(true);


Whitelisting Tags
To only parse specific tags, pass an array of whitelisted tags.

$code->whitelist('b', 'i', 'u');


Disable Parsing
To disable parsing all together, use disable().

$code->disable(true);
Top

3 - Using Filters


Filters are a very powerful and flexible system that adds support for tag analysis and parsing. Filters are a packaging of similar tags into a single class, which contains a mapping of rules for how each tag behaves. You can view all the available filters in the filters/ folder. By default no filters are installed, to add them use addFilter().

// Only email and URL parsing
$code = new Decoda($string);
$code->addFilter(new EmailFilter());
$code->addFilter(new UrlFilter());


If you want to remove certain filters from being applied, for example, you don't want to parse quotes, you can call removeFilter(). The argument should be the name of the filter class without "Filter".

$code->removeFilter('Quote');


You can also disable filters by calling disableFilters(), which will turn off all tag parsing. Doing this actually removes all filters, so you can't un-disable them unless you add them again.

$code->disableFilters();


If you want to enable all filters, use defaults().

$code->defaults();
Top

4 - Using Hooks


Hooks are a versatile system that allow you to hook into the parsing cycle to inject and alter the output. Hooks currently support beforeParse() and afterParse(). You can view all the available hooks in the hooks/ folder. By default no hooks are installed, to add them use addHook().

// Only emoticons
$code = new Decoda($string);
$code->addHook(new EmoticonHook());


If you want to remove certain hooks, use removeHook(). The argument should be the name of the class without "Hook".

$code->removeHook('Censor');


You can also disable hooks by calling disableHooks(). Doing this actually removes all hooks, so you can't un-disable them unless you add them again.

$code->disableHooks();


If you want to enable all hooks, use defaults().

$code->defaults();
Top

5 - Creating Filters


You can create your own filters to parse any kind of tag you want. Simply create a new filter class and name it accordingly, for example AudioFilter. The class will need a property called $_tags that will contain a mapping of all tags and their rules. The following rules are available.

key			- (string) Decoda tag
tag			- (string) HTML replacement tag
template		- (string) Template file to use for rendering
pattern			- (string) Regex pattern that the content or default attribute must pass
type			- (constant) Type of HTML element: block or inline
allowed			- (constant) What types of elements are allowed to be nested
attributes		- (array) Custom attributes to parse out of the Decoda markup
map			- (array) Map parsed attributes to different names
html			- (array) Custom HTML attributes to append to the parsed tag
lineBreaks		- (boolean) Convert linebreaks within the content body
autoClose		- (boolean) HTML tag is self closing
preserveTags		- (boolean) Will not convert nested Decoda markup within this tag
escapeContent		- (boolean) Escape HTML entities within the content body
escapeAttributes	- (boolean) Escape HTML entities within the parsed attributes
maxChildDepth		- (integer) Max depth for nested children of the same tag (-1 to disable)
parent			- (array) List of Decoda keys that this tag can only be a direct child of
children		- (array) List of Decoda keys for all the tags that can only be a direct descendant


Of all the rules, only the following are required: key, tag, type, allowed. The key should be the name of the tag used in the markup, example: [audio] will have the key of audio. Tag would be the HTML tag to replace it with and type will determine if the tag is a block or inline element. Block elements can have nested inline and block elements, while inline can only nest other inlines. Let's now add our audio example:

class AudioFilter extends DecodaFilter {
 
	protected $_tags = array(
		'audio' => array(
			'tag' => 'audio',
			'type' => self::TYPE_BLOCK,
			'allowed' => self::TYPE_NONE,
			'template' => 'audio',
			'pattern' => '/^(.*?)\.(ogg|mp3)$/is',
			'attributes' => array(
				'default' => '/ogg|mp3/i',
				'autoplay' => '/true|false/i',
				'controls' => '/true|false/i'
			),
			'map' => array(
				'default' => 'type'
			)
		)
	);
 
}


The defined rules basically state that we want to parse any [audio] tags, that have a default attribute value of ogg/mp3, can have an autoplay attribute, must have the content end in ogg/mp3, may not have any other nested tags and will use "audio" as a template. The rules would match the following:

[audio="mp3"]song.mp3[/audio]
[audio="ogg"]song.ogg[/audio]
[audio="ogg" autoplay="true"]song.ogg[/audio]


This would then render using the templates/audio.php template. All parsed out $attributes are available as variables in the template. The $type variable is actually the remapped default attribute, which was defined in the audio rules.

<audio autoplay="<?php echo (isset($autoplay) && $autoplay == 'true') ? 'autoplay' : ''; ?>" controls="<?php echo (isset($controls) && $controls == 'true') ? 'controls' : ''; ?>">
	<source src="<?php echo $content; ?>" type="audio/<?php echo $type; ?>" />
</audio>


And thats pretty much how the filter system works. To get a more advanced understand of how certain rules work, or the best scenario to use them, take a look at all the current filters.
Top

6 - Creating Hooks


To add a new hook, create a new class and name it accordingly, for example GrammarHook. The class takes two optional methods, beforeParse() and afterParse(), both of which take a 1st argument which contains the currently parsed string. Both methods also need to return the string for the parser to work correctly.

class GrammarHook extends DecodaHook {
 
	public function beforeParse($content) {
		// Do something
		return $content;
	}
 
	public function afterParse($content) {
		// Do something again
		return $content;
	}
}


That's all it takes to create your own hooks. For more advanced examples, take a look at the current hooks.
Top

7 - Validating Input


Decoda comes built in with a system to detect incorrectly nested tags, broken tags or incorrectly nested scope types. After parse() has been executed, you can use the getErrors() method to grab all the errors for the last parse. The method accepts a single argument which restricts what type of errors should be returned.

$code->getErrors(); // all 3 errors
$code->getErrors(Decoda::ERROR_NESTING); // incorrect nesting order
$code->getErrors(Decoda::ERROR_CLOSING); // no closing tags
$code->getErrors(Decoda::ERROR_SCOPE); // tags nested within invalid types


The method will return an array of tags that have failed, so that you can output some kind of error message to the user and block the data being saved to the database. The array changes per error type, so be sure to loop over each correctly. Something like the following should suffice:

$nesting = array(); 
$closing = array();
$scope = array();
 
foreach ($code->getErrors() as $error) {
	switch ($error['type']) {
		case Decoda::ERROR_NESTING:	$nesting[] = $error['tag']; break;
		case Decoda::ERROR_CLOSING:	$closing[] = $error['tag']; break;
		case Decoda::ERROR_SCOPE:	$scope[] = $error['child'] . ' in ' . $error['parent']; break;
	}
}
 
if (!empty($nesting)) {
	$errors[] = sprintf('The following tags have been nested in the wrong order: %s', implode(', ', $nesting));
}
 
if (!empty($closing)) {
	$errors[] = sprintf('The following tags have no closing tag: %s', implode(', ', $closing));
}
 
if (!empty($scope)) {
	$errors[] = sprintf('The following tags can not be placed within a specific tag: %s', implode(', ', $scope));
}
Read the whole documentation? Download the script now and try it yourself! Return to the Top