VERSION
VERSION
Pico version
Pico
Pico is a stupidly simple, blazing fast, flat file CMS.
See http://picocms.org/ for more info.
$nativePlugins : array<mixed,\PicoPluginInterface>
List of loaded plugins using the current API version
__construct(string $rootDir, string $configDir, string $pluginsDir, string $themesDir, boolean $enableLocalPlugins = true)
Constructs a new Pico instance
To carry out all the processing in Pico, call \Pico::run().
string | $rootDir | root dir of this Pico instance |
string | $configDir | config dir of this Pico instance |
string | $pluginsDir | plugins dir of this Pico instance |
string | $themesDir | themes dir of this Pico instance |
boolean | $enableLocalPlugins | enables (TRUE; default) or disables (FALSE) loading plugins from the filesystem |
run() : string
Runs this Pico instance
Loads plugins, evaluates the config file, does URL routing, parses meta headers, processes Markdown, does Twig processing and returns the rendered contents.
thrown when a irrecoverable error occurs
rendered Pico contents
loadPlugin(\PicoPluginInterface|string $plugin) : \PicoPluginInterface
Manually loads a plugin
Manually loaded plugins MUST implement \PicoPluginInterface. They are simply appended to the plugins array without any additional checks, so you might get unexpected results, depending on when you're loading a plugin. You SHOULD NOT load plugins after a event has been triggered by Pico. In-depth knowledge of Pico's inner workings is strongly advised otherwise, and you MUST NOT rely on \PicoDeprecated to maintain backward compatibility in such cases.
If you e.g. load a plugin after the onPluginsLoaded
event, Pico
doesn't guarantee the plugin's order (\Pico::sortPlugins()).
Already triggered events won't get triggered on the manually loaded
plugin. Thus you SHOULD load plugins either before \Pico::run()
is called, or via the constructor of another plugin (i.e. the plugin's
__construct()
method; plugins are instanced in
\Pico::loadPlugins()).
This method triggers the onPluginManuallyLoaded
event.
\PicoPluginInterface|string | $plugin | either the class name of a plugin to instantiate or a plugin instance |
thrown when a plugin couldn't be loaded
instance of the loaded plugin
getPlugin(string $pluginName) : object
Returns the instance of a named plugin
Plugins SHOULD implement \PicoPluginInterface, but you MUST NOT rely on it. For more information see \PicoPluginInterface.
string | $pluginName | name of the plugin |
thrown when the plugin wasn't found
instance of the plugin
setConfig(array $config) : void
Sets Pico's config before calling Pico::run()
This method allows you to modify Pico's config without creating a {@path "config/config.yml"} or changing some of its variables before Pico starts processing.
You can call this method between \Pico::__construct() and \Pico::run() only. Options set with this method cannot be overwritten by {@path "config/config.yml"}.
array | $config | array with config variables |
thrown if Pico already started processing
getConfig(string $configName = null, mixed $default = null) : mixed
Returns either the value of the specified config variable or the config array
string | $configName | optional name of a config variable |
mixed | $default | optional default value to return when the named config variable doesn't exist |
if no name of a config variable has been supplied, the config array is returned; otherwise it returns either the value of the named config variable, or, if the named config variable doesn't exist, the provided default value or NULL
resolveFilePath(string $requestUrl) : string
Resolves a given file path to its corresponding content file
This method also prevents content_dir
breakouts using malicious
request URLs. We don't use realpath()
, because we neither want to
check for file existance, nor prohibit symlinks which intentionally
point to somewhere outside the content_dir
folder. It is STRONGLY
RECOMMENDED to use PHP's open_basedir
feature - always, not just
with Pico!
string | $requestUrl | path name (likely from a URL) to resolve |
path to the resolved content file
load404Content(string $file) : string
Returns the raw contents of the first found 404 file when traversing up from the directory the requested file is in
If no suitable 404.md
is found, fallback to a built-in error message.
string | $file | path to requested (but not existing) file |
raw contents of the 404 file
getMetaHeaders() : array<mixed,string>
Returns known meta headers
This method triggers the onMetaHeaders
event when the known meta
headers weren't assembled yet.
known meta headers; the array key specifies the YAML key to search for, the array value is later used to access the found value
parseFileMeta(string $rawContent, array<mixed,string> $headers) : array
Parses the file meta from raw file contents
Meta data MUST start on the first line of the file, either opened and
closed by ---
or C-style block comments (deprecated). The headers are
parsed by the YAML component of the Symfony project, keys are lowered.
If you're a plugin developer, you MUST register new headers during the
onMetaHeaders
event first. The implicit availability of headers is
for users and pure (!) theme developers ONLY.
string | $rawContent | the raw file contents |
array<mixed,string> | $headers | known meta headers |
thrown when the meta data is invalid
parsed meta data
prepareFileContent(string $rawContent, array $meta) : string
Applies some static preparations to the raw contents of a page, e.g. removing the meta header and replacing %.
..% placehodlers
This method calls the \Pico::substituteFileContent() method.
string | $rawContent | raw contents of a page |
array | $meta | meta data to use for %meta.*% replacement |
prepared Markdown contents
substituteFileContent(string $markdown, array $meta = array()) : string
Replaces all %.
..% placeholders in a page's contents
string | $markdown | Markdown contents of a page |
array | $meta | meta data to use for %meta.*% replacement |
substituted Markdown contents
getTwig() : \Twig_Environment|null
Returns the Twig template engine
This method triggers the onTwigRegistered
event when the Twig template
engine wasn't initiated yet. When initiating Twig, this method also
registers Pico's core Twig filters link
and content
as well as
Pico's \PicoTwigExtension Twig extension.
Twig template engine
getBaseUrl() : string
Returns the base URL of this Pico instance
Security Notice: You MUST configure Pico's base URL explicitly when using the base URL in contexts that are potentially vulnerable to HTTP Host Header Injection attacks (e.g. when generating emails).
the base url
getPageUrl(string $page, array|string $queryData = null, boolean $dropIndex = true) : string
Returns the URL to a given page
This method can be used in Twig templates by applying the link
filter
to a string representing a page identifier.
string | $page | identifier of the page to link to |
array|string | $queryData | either an array containing properties to create a URL-encoded query string from, or a already encoded string |
boolean | $dropIndex | when the last path component is "index", then passing TRUE (default) leads to removing this path component |
URL
getBaseThemeUrl() : string
Returns the URL of the themes folder of this Pico instance
We assume that the themes folder is a arbitrary deep sub folder of the
script's base path (i.e. the directory {@path "index.php"} is in resp.
the httpdocs
directory). Usually the script's base path is identical
to \Pico::$rootDir, but this may aberrate when Pico got installed
as a composer dependency. However, ultimately it allows us to use
\Pico::getBaseUrl() as origin of the theme URL. Otherwise Pico
falls back to the basename of \Pico::$themesDir (i.e. assuming
that Pico::$themesDir
is foo/bar/baz
, the base URL of the themes
folder will be baz/
; this ensures BC to Pico < 2.0). Pico's base URL
always gets prepended appropriately.
the URL of the themes folder
getUrlParameter(string $name, integer|string $filter = '', mixed|array $options = null, integer|string|array<mixed,integer>|array<mixed,string> $flags = null) : mixed
Filters a URL GET parameter with a specified filter
This method is just an alias for \Pico::filterVariable(), see
\Pico::filterVariable() for a detailed description. It can be
used in Twig templates by calling the url_param
function.
string | $name | name of the URL GET parameter to filter |
integer|string | $filter | the filter to apply |
mixed|array | $options | either a associative options array to be used by the filter or a scalar default value |
integer|string|array<mixed,integer>|array<mixed,string> | $flags | flags and flag strings to be used by the filter |
either the filtered data, FALSE if the filter fails, or NULL if the URL GET parameter doesn't exist and no default value is given
getFormParameter(string $name, integer|string $filter = '', mixed|array $options = null, integer|string|array<mixed,integer>|array<mixed,string> $flags = null) : mixed
Filters a HTTP POST parameter with a specified filter
This method is just an alias for \Pico::filterVariable(), see
\Pico::filterVariable() for a detailed description. It can be
used in Twig templates by calling the form_param
function.
string | $name | name of the HTTP POST parameter to filter |
integer|string | $filter | the filter to apply |
mixed|array | $options | either a associative options array to be used by the filter or a scalar default value |
integer|string|array<mixed,integer>|array<mixed,string> | $flags | flags and flag strings to be used by the filter |
either the filtered data, FALSE if the filter fails, or NULL if the HTTP POST parameter doesn't exist and no default value is given
getFiles(string $directory, string $fileExtension = '', integer $order = self::SORT_ASC) : array
Recursively walks through a directory and returns all containing files matching the specified file extension
string | $directory | start directory |
string | $fileExtension | return files with the given file extension only (optional) |
integer | $order | specify whether and how files should be sorted; use Pico::SORT_ASC for a alphabetical ascending order (this is the default behaviour), Pico::SORT_DESC for a descending order or Pico::SORT_NONE to leave the result unsorted |
list of found files
getFilesGlob(string $pattern, integer $order = self::SORT_ASC) : array
Returns all files in a directory matching a libc glob() pattern
string | $pattern | the pattern to search for; see PHP's glob() function for details |
integer | $order | specify whether and how files should be sorted; use Pico::SORT_ASC for a alphabetical ascending order (this is the default behaviour), Pico::SORT_DESC for a descending order or Pico::SORT_NONE to leave the result unsorted |
list of found files
triggerEvent(string $eventName, array $params = array()) : void
Triggers events on plugins using the current API version
Plugins using older API versions are handled by \PicoDeprecated. Please note that \PicoDeprecated also triggers custom events on plugins using older API versions, thus you can safely use this method to trigger custom events on all loaded plugins, no matter what API version - the event will be triggered in any case.
You MUST NOT trigger events of Pico's core with a plugin!
string | $eventName | name of the event to trigger |
array | $params | optional parameters to pass |
loadPlugins() : void
Loads plugins from vendor/pico-plugin.php and Pico::$pluginsDir
See \Pico::loadComposerPlugins() for details about plugins loaded
from vendor/pico-plugin.php
(i.e. plugins that were installed using
composer
), and \Pico::loadLocalPlugins() for details about
plugins installed to \Pico::$pluginsDir. Pico loads plugins from
the filesystem only if \Pico::$enableLocalPlugins is set to TRUE
(this is the default).
Pico always loads plugins from vendor/pico-plugin.php
first and
ignores conflicting plugins in \Pico::$pluginsDir.
Please note that Pico will change the processing order when needed to incorporate plugin dependencies. See \Pico::sortPlugins() for details.
thrown when a plugin couldn't be loaded
loadComposerPlugins(array<mixed,string> $pluginBlacklist = array()) : array<mixed,string>
Loads plugins from vendor/pico-plugin.php
This method loads all plugins installed using composer
and Pico's
picocms/pico-installer
installer by reading the pico-plugin.php
in
composer's vendor
dir.
array<mixed,string> | $pluginBlacklist | class names of plugins not to load |
installer names of the loaded plugins
loadLocalPlugins(array<mixed,string> $pluginBlacklist = array()) : void
Loads plugins from Pico::$pluginsDir in alphabetical order
Pico tries to load plugins from <plugin name>/<plugin name>.php
and
<plugin name>.php
only. Plugin names are treated case insensitive.
Pico will throw a RuntimeException if it can't load a plugin.
Plugin files MAY be prefixed by a number (e.g. 00-PicoDeprecated.php
)
to indicate their processing order. Plugins without a prefix will be
loaded last. If you want to use a prefix, you MUST NOT use the reserved
prefixes 00
to 09
. Prefixes are completely optional, however, you
SHOULD take the following prefix classification into consideration:
onPageRendered
eventarray<mixed,string> | $pluginBlacklist | class names of plugins not to load |
thrown when a plugin couldn't be loaded
sortPlugins() : void
Sorts all loaded plugins using a plugin dependency topology
Execution order matters: if plugin A depends on plugin B, it usually means that plugin B does stuff which plugin A requires. However, Pico loads plugins in alphabetical order, so events might get fired on plugin A before plugin B.
Hence plugins need to be sorted. Pico sorts plugins using a dependency topology, this means that it moves all plugins, on which a plugin depends, in front of that plugin. The order isn't touched apart from that, so they are still sorted alphabetically, as long as this doesn't interfere with the dependency topology. Circular dependencies are being ignored; their behavior is undefiend. Missing dependencies are being ignored until you try to enable the dependant plugin.
This method bases on Marc J. Schmidt's Topological Sort library in
version 1.1.0, licensed under the MIT license. It uses the ArraySort
implementation (class \MJS\TopSort\Implementations\ArraySort
).
loadConfig() : void
Loads the config.yml and any other *.yml from Pico::$configDir
After loading {@path "config/config.yml"}, Pico proceeds with any other
existing config/*.yml
file in alphabetical order. The file order is
crucial: Config values which have been set already, cannot be
overwritten by a succeeding file. This is also true for arrays, i.e.
when specifying test: { foo: bar }
in config/a.yml
and
test: { baz: 42 }
in config/b.yml
, {{ config.test.baz }}
will be
undefined!
evaluateRequestUrl() : void
Evaluates the requested URL
Pico uses the QUERY_STRING
routing method (e.g. /pico/?sub/page
)
to support SEO-like URLs out-of-the-box with any webserver. You can
still setup URL rewriting to basically remove the ?
from URLs.
However, URL rewriting requires some special configuration of your
webserver, but this should be "basic work" for any webmaster...
With Pico 1.0 you had to setup URL rewriting (e.g. using mod_rewrite
on Apache) in a way that rewritten URLs follow the QUERY_STRING
principles. Starting with version 2.0, Pico additionally supports the
REQUEST_URI
routing method, what allows you to simply rewrite all
requests to just index.php
. Pico then reads the requested page from
the REQUEST_URI
environment variable provided by the webserver.
Please note that QUERY_STRING
takes precedence over REQUEST_URI
.
Pico 0.9 and older required Apache with mod_rewrite
enabled, thus old
plugins, templates and contents may require you to enable URL rewriting
to work. If you're upgrading from Pico 0.9, you will probably have to
update your rewriting rules.
We recommend you to use the link
filter in templates to create
internal links, e.g. {{ "sub/page"|link }}
is equivalent to
{{ base_url }}/sub/page
and {{ base_url }}?sub/page
, depending on
enabled URL rewriting. In content files you can use the %base_url%
variable; e.g. %base_url%?sub/page
will be replaced accordingly.
Heads up! Pico always interprets the first parameter as name of the
requested page (provided that the parameter has no value). According to
that you MUST NOT call Pico with a parameter without value as first
parameter (e.g. http://example.com/pico/?someBooleanParam), otherwise
Pico interprets someBooleanParam
as name of the requested page. Use
/pico/?someBooleanParam=
or /pico/?index&someBooleanParam
instead.
readPages() : void
Reads the data of all pages known to Pico
The page data will be an array containing the following values:
Array key | Type | Description |
---|---|---|
id | string | relative path to the content file |
url | string | URL to the page |
title | string | title of the page (YAML header) |
description | string | description of the page (YAML header) |
author | string | author of the page (YAML header) |
time | string | timestamp derived from the Date header |
date | string | date of the page (YAML header) |
date_formatted | string | formatted date of the page |
hidden | bool | this page shouldn't be visible to the user |
raw_content | string | raw, not yet parsed contents of the page |
meta | string | parsed meta data of the page |
previous_page | &array | reference to the previous page |
next_page | &array | reference to the next page |
Please note that the previous_page
and next_page
keys won't be
available until the onCurrentPageDiscovered
event was triggered
(\Pico::discoverPageSiblings()).
filterVariable(mixed $variable, integer|string $filter = '', mixed|array $options = null, integer|string|array<mixed,integer>|array<mixed,string> $flags = null) : mixed
Filters a variable with a specified filter
This method basically wraps around PHP's filter_var()
function. It
filters data by either validating or sanitizing it. This is especially
useful when the data source contains unknown (or foreign) data, like
user supplied input. Validation is used to validate or check if the data
meets certain qualifications, but will not change the data itself.
Sanitization will sanitize the data, so it may alter it by removing
undesired characters. It doesn't actually validate the data! The
behaviour of most filters can optionally be tweaked by flags.
Heads up! Input validation is hard! Always validate your input data the most paranoid way you can imagine. Always prefer validation filters over sanitization filters; be very careful with sanitization filters, you might create cross-site scripting vulnerabilities!
mixed | $variable | value to filter |
integer|string | $filter | ID (int) or name (string) of the filter to apply; if omitted, the method will return FALSE |
mixed|array | $options | either a associative array
of options to be used by the filter (e.g. |
integer|string|array<mixed,integer>|array<mixed,string> | $flags | either a bitwise disjunction of flags or a string with the significant part of a flag constant (the constant name is the result of "FILTERFLAG" and the given string in ASCII-only uppercase); you may also pass an array of flags and flag strings (optional) |
with a validation filter, the method either returns the validated value or, provided that the value wasn't valid, the given default value or FALSE; with a sanitization filter, the method returns the sanitized value; if no value (i.e. NULL) was given, the method always returns either the provided default value or NULL