Merge branch 'master' of https://github.com/picocms/Pico into feature/bootstrap-theme
This commit is contained in:
commit
c4f9eb8d4e
14 changed files with 350 additions and 78 deletions
10
.gitignore
vendored
10
.gitignore
vendored
|
@ -10,15 +10,17 @@ desktop.ini
|
|||
.DS_Store
|
||||
._*
|
||||
|
||||
# Travis
|
||||
/build/phpdoc-*/
|
||||
/build/phpdoc-*.git/
|
||||
|
||||
# Composer
|
||||
/composer.lock
|
||||
/composer.phar
|
||||
/vendor
|
||||
|
||||
# phpDocumentor
|
||||
/_build/phpdoc/
|
||||
/_build/phpdoc.cache/
|
||||
/_build/phpdoc-*/
|
||||
/_build/phpdoc-*.git/
|
||||
|
||||
# User config
|
||||
/config/config.php
|
||||
|
||||
|
|
|
@ -6,9 +6,14 @@
|
|||
</description>
|
||||
|
||||
<!--
|
||||
Exclude build/ and vendor/ dirs as well as minified JavaScript files
|
||||
Run on current working directory by default
|
||||
-->
|
||||
<exclude-pattern type="relative">^build/</exclude-pattern>
|
||||
<file>.</file>
|
||||
|
||||
<!--
|
||||
Exclude _build/ and vendor/ dirs as well as minified JavaScript files
|
||||
-->
|
||||
<exclude-pattern type="relative">^_build/</exclude-pattern>
|
||||
<exclude-pattern type="relative">^vendor/</exclude-pattern>
|
||||
<exclude-pattern>*.min.js</exclude-pattern>
|
||||
|
32
.phpdoc.xml
Normal file
32
.phpdoc.xml
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<phpdoc>
|
||||
<title><![CDATA[Pico 1.0 API Documentation]]></title>
|
||||
<parser>
|
||||
<target>_build/phpdoc.cache</target>
|
||||
</parser>
|
||||
<transformer>
|
||||
<target>_build/phpdoc</target>
|
||||
</transformer>
|
||||
<transformations>
|
||||
<template name="clean"/>
|
||||
</transformations>
|
||||
<files>
|
||||
<directory>.</directory>
|
||||
<file>index.php</file>
|
||||
<file>index.php.dist</file>
|
||||
|
||||
<!-- exclude build environment -->
|
||||
<ignore>_build/*</ignore>
|
||||
|
||||
<!-- exclude user config -->
|
||||
<ignore>config/*</ignore>
|
||||
<file>config/config.php.template</file>
|
||||
|
||||
<!-- exclude all plugins -->
|
||||
<ignore>plugins/*</ignore>
|
||||
<file>plugins/DummyPlugin.php</file>
|
||||
|
||||
<!-- exclude vendor dir -->
|
||||
<ignore>vendor/*</ignore>
|
||||
</files>
|
||||
</phpdoc>
|
|
@ -17,10 +17,10 @@ install:
|
|||
- composer install
|
||||
|
||||
before_script:
|
||||
- export PATH="$TRAVIS_BUILD_DIR/build:$TRAVIS_BUILD_DIR/vendor/bin:$PATH"
|
||||
- export PATH="$TRAVIS_BUILD_DIR/_build:$TRAVIS_BUILD_DIR/vendor/bin:$PATH"
|
||||
|
||||
script:
|
||||
- phpcs --standard=phpcs.xml "$TRAVIS_BUILD_DIR"
|
||||
- phpcs --standard=.phpcs.xml "$TRAVIS_BUILD_DIR"
|
||||
|
||||
after_success:
|
||||
- deploy-phpdoc-branch.sh
|
||||
|
|
|
@ -7,8 +7,11 @@ Released: -
|
|||
```
|
||||
* [New] This is Picos first stable release! The Pico Community wants to thank
|
||||
all contributors and users which made this possible!
|
||||
* [New] Introducing the `PicoTwigExtension` Twig extension
|
||||
* [New] New `markdown` filter for Twig to parse markdown strings; Note: If you
|
||||
want to parse the contents of a page, use the `content` filter instead
|
||||
* [New] New `sort_by` filter to sort a array by a specified key or key path
|
||||
* [New] New `map` filter to get the values of the given key or key path
|
||||
* [New] New PHP version check in `index.php`
|
||||
* [Changed] Improve documentation
|
||||
* [Changed] Improve table styling in default theme
|
||||
|
|
|
@ -39,11 +39,11 @@ Pico uses the [PSR-2 Coding Standard](http://www.php-fig.org/psr/psr-2/) as defi
|
|||
|
||||
For historical reasons we don't use formal namespaces. Markdown files in the `content-sample` folder (the inline documentation) must follow a hard limit of 80 characters line length.
|
||||
|
||||
It is recommended to check your code using [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer) using the `PSR2` standard using the following command:
|
||||
It is recommended to check your code using [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer) using Pico's `.phpcs.xml` standard. Use the following command:
|
||||
|
||||
$ ./bin/phpcs --standard=PSR2 [file(s)]
|
||||
$ ./vendor/bin/phpcs --standard=.phpcs.xml [file]...
|
||||
|
||||
With this command you can specify a file or folder to limit which files it will check or omit that argument altogether, in which case the current directory is checked.
|
||||
With this command you can specify a file or folder to limit which files it will check or omit that argument altogether, in which case the current working directory is checked.
|
||||
|
||||
### Keep documentation in sync
|
||||
|
||||
|
|
|
@ -18,11 +18,13 @@ fi
|
|||
PHPDOC_ID="${TRAVIS_BRANCH//\//_}"
|
||||
|
||||
generate-phpdoc.sh \
|
||||
"$TRAVIS_BUILD_DIR" "$TRAVIS_BUILD_DIR/build/phpdoc-$PHPDOC_ID" \
|
||||
"$TRAVIS_BUILD_DIR/.phpdoc.xml" \
|
||||
"$TRAVIS_BUILD_DIR/_build/phpdoc.cache" \
|
||||
"$TRAVIS_BUILD_DIR/_build/phpdoc-$PHPDOC_ID" \
|
||||
"Pico 1.0 API Documentation ($TRAVIS_BRANCH branch)"
|
||||
[ $? -eq 0 ] || exit 1
|
||||
|
||||
deploy-phpdoc.sh \
|
||||
"$TRAVIS_REPO_SLUG" "heads/$TRAVIS_BRANCH @ $TRAVIS_COMMIT" "$TRAVIS_BUILD_DIR/build/phpdoc-$PHPDOC_ID" \
|
||||
"$TRAVIS_REPO_SLUG" "heads/$TRAVIS_BRANCH @ $TRAVIS_COMMIT" "$TRAVIS_BUILD_DIR/_build/phpdoc-$PHPDOC_ID" \
|
||||
"$TRAVIS_REPO_SLUG" "gh-pages" "phpDoc/$PHPDOC_ID"
|
||||
[ $? -eq 0 ] || exit 1
|
|
@ -5,11 +5,13 @@
|
|||
PHPDOC_ID="${TRAVIS_BRANCH//\//_}"
|
||||
|
||||
generate-phpdoc.sh \
|
||||
"$TRAVIS_BUILD_DIR" "$TRAVIS_BUILD_DIR/build/phpdoc-$PHPDOC_ID" \
|
||||
"$TRAVIS_BUILD_DIR/.phpdoc.xml" \
|
||||
"$TRAVIS_BUILD_DIR/_build/phpdoc.cache" \
|
||||
"$TRAVIS_BUILD_DIR/_build/phpdoc-$PHPDOC_ID" \
|
||||
"Pico 1.0 API Documentation ($TRAVIS_TAG)"
|
||||
[ $? -eq 0 ] || exit 1
|
||||
|
||||
deploy-phpdoc.sh \
|
||||
"$TRAVIS_REPO_SLUG" "tags/$TRAVIS_TAG" "$TRAVIS_BUILD_DIR/build/phpdoc-$PHPDOC_ID" \
|
||||
"$TRAVIS_REPO_SLUG" "tags/$TRAVIS_TAG" "$TRAVIS_BUILD_DIR/_build/phpdoc-$PHPDOC_ID" \
|
||||
"$TRAVIS_REPO_SLUG" "gh-pages" "phpDoc/$PHPDOC_ID"
|
||||
[ $? -eq 0 ] || exit 1
|
24
_build/generate-phpdoc.sh
Executable file
24
_build/generate-phpdoc.sh
Executable file
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
# parameters
|
||||
PHPDOC_CONFIG="$1"
|
||||
PHPDOC_CACHE_DIR="$2"
|
||||
PHPDOC_TARGET_DIR="$3"
|
||||
PHPDOC_TITLE="$4"
|
||||
|
||||
# print parameters
|
||||
echo "Generating phpDocs..."
|
||||
printf 'PHPDOC_CONFIG="%s"\n' "$PHPDOC_CONFIG"
|
||||
printf 'PHPDOC_CACHE_DIR="%s"\n' "$PHPDOC_CACHE_DIR"
|
||||
printf 'PHPDOC_TARGET_DIR="%s"\n' "$PHPDOC_TARGET_DIR"
|
||||
printf 'PHPDOC_TITLE="%s"\n' "$PHPDOC_TITLE"
|
||||
echo
|
||||
|
||||
# generate phpdoc
|
||||
phpdoc --config "$PHPDOC_CONFIG" \
|
||||
--cache-folder "$PHPDOC_CACHE_DIR" \
|
||||
--target "$PHPDOC_TARGET_DIR" \
|
||||
--title "$PHPDOC_TITLE"
|
||||
|
||||
echo
|
|
@ -1,24 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
# parameters
|
||||
PHPDOC_SOURCE_DIR="$1"
|
||||
PHPDOC_TARGET_DIR="$2"
|
||||
PHPDOC_TITLE="$3"
|
||||
|
||||
# print parameters
|
||||
echo "Generating phpDocs..."
|
||||
printf 'PHPDOC_SOURCE_DIR="%s"\n' "$PHPDOC_SOURCE_DIR"
|
||||
printf 'PHPDOC_TARGET_DIR="%s"\n' "$PHPDOC_TARGET_DIR"
|
||||
printf 'PHPDOC_TITLE="%s"\n' "$PHPDOC_TITLE"
|
||||
echo
|
||||
|
||||
# generate phpdoc
|
||||
phpdoc -d "$PHPDOC_SOURCE_DIR" \
|
||||
-i "$PHPDOC_SOURCE_DIR/build/" \
|
||||
-i "$PHPDOC_SOURCE_DIR/vendor/" \
|
||||
-i "$PHPDOC_SOURCE_DIR/plugins/" -f "$PHPDOC_SOURCE_DIR/plugins/DummyPlugin.php" \
|
||||
-t "$PHPDOC_TARGET_DIR" \
|
||||
--title "$PHPDOC_TITLE"
|
||||
|
||||
echo
|
|
@ -117,7 +117,7 @@ something like the following:
|
|||
This template will show a list of your articles, so you probably want to
|
||||
do something like this:
|
||||
```
|
||||
{% for page in pages %}
|
||||
{% for page in pages|sort_by("time")|reverse %}
|
||||
{% if page.id starts with "blog/" %}
|
||||
<div class="post">
|
||||
<h3><a href="{{ page.url }}">{{ page.title }}</a></h3>
|
||||
|
@ -127,16 +127,10 @@ something like the following:
|
|||
{% endif %}
|
||||
{% endfor %}
|
||||
```
|
||||
4. Let Pico sort pages by date by setting `$config['pages_order_by'] = 'date';`
|
||||
in your `config/config.php`. To use a descending order (newest articles
|
||||
first), also add `$config['pages_order'] = 'desc';`. The former won't affect
|
||||
pages without a `Date` meta header, but the latter does. To use ascending
|
||||
order for your page navigation again, add Twigs `reverse` filter to the
|
||||
navigation loop (`{% for page in pages|reverse %}...{% endfor %}`) in your
|
||||
themes `index.twig`.
|
||||
5. Make sure to exclude the blog articles from your page navigation. You can
|
||||
4. Make sure to exclude the blog articles from your page navigation. You can
|
||||
achieve this by adding `{% if not page starts with "blog/" %}...{% endif %}`
|
||||
to the navigation loop.
|
||||
to the navigation loop (`{% for page in pages|reverse %}...{% endfor %}`)
|
||||
in your themes `index.twig`.
|
||||
|
||||
## Customization
|
||||
|
||||
|
@ -162,7 +156,7 @@ HTML structure of the theme. Below are the Twig variables that are available
|
|||
to use in your theme. Please note that paths (e.g. `{{ base_dir }}`) and URLs
|
||||
(e.g. `{{ base_url }}`) don't have a trailing slash.
|
||||
|
||||
* `{{ config }}` - Conatins the values you set in `config/config.php`
|
||||
* `{{ config }}` - Contains the values you set in `config/config.php`
|
||||
(e.g. `{{ config.theme }}` becomes `default`)
|
||||
* `{{ base_dir }}` - The path to your Pico root directory
|
||||
* `{{ base_url }}` - The URL to your Pico site; use Twigs `link` filter to
|
||||
|
@ -211,6 +205,17 @@ Pages can be used like the following:
|
|||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
Additional to Twigs extensive list of filters, functions and tags, Pico also
|
||||
provides some useful additional filters to make theming easier. You can parse
|
||||
any Markdown string to HTML using the `markdown` filter. Arrays can be sorted
|
||||
by one of its keys or a arbitrary deep sub-key using the `sort_by` filter
|
||||
(e.g. `{% for page in pages|sort_by("meta:nav"|split(":")) %}...{% endfor %}`
|
||||
iterates through all pages, ordered by the `nav` meta header; please note the
|
||||
`"meta:nav"|split(":")` part of the example, which passes `['meta', 'nav']` to
|
||||
the filter describing a key path). You can return all values of a given key or
|
||||
key path of an array using the `map` filter (e.g. `{{ pages|map("title") }}`
|
||||
returns all page titles).
|
||||
|
||||
You can use different templates for different content files by specifying the
|
||||
`Template` meta header. Simply add e.g. `Template: blog-post` to a content file
|
||||
and Pico will use the `blog-post.twig` file in your theme folder to render
|
||||
|
|
39
lib/Pico.php
39
lib/Pico.php
|
@ -774,7 +774,7 @@ class Pico
|
|||
$meta[$fieldId] = $meta[$fieldName];
|
||||
unset($meta[$fieldName]);
|
||||
}
|
||||
} else {
|
||||
} elseif (!isset($meta[$fieldId])) {
|
||||
// guarantee array key existance
|
||||
$meta[$fieldId] = '';
|
||||
}
|
||||
|
@ -788,10 +788,7 @@ class Pico
|
|||
}
|
||||
} else {
|
||||
// guarantee array key existance
|
||||
foreach ($headers as $id => $field) {
|
||||
$meta[$id] = '';
|
||||
}
|
||||
|
||||
$meta = array_fill_keys(array_keys($headers), '');
|
||||
$meta['time'] = $meta['date_formatted'] = '';
|
||||
}
|
||||
|
||||
|
@ -1121,6 +1118,9 @@ class Pico
|
|||
/**
|
||||
* Registers the twig template engine
|
||||
*
|
||||
* This method also registers Picos core Twig filters `link` and `content`
|
||||
* as well as Picos {@link PicoTwigExtension} Twig extension.
|
||||
*
|
||||
* @see Pico::getTwig()
|
||||
* @return void
|
||||
*/
|
||||
|
@ -1129,23 +1129,15 @@ class Pico
|
|||
$twigLoader = new Twig_Loader_Filesystem($this->getThemesDir() . $this->getConfig('theme'));
|
||||
$this->twig = new Twig_Environment($twigLoader, $this->getConfig('twig_config'));
|
||||
$this->twig->addExtension(new Twig_Extension_Debug());
|
||||
$this->twig->addExtension(new PicoTwigExtension($this));
|
||||
|
||||
$this->registerTwigFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers Picos additional Twig filters
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function registerTwigFilter()
|
||||
{
|
||||
$pico = $this;
|
||||
|
||||
// link filter
|
||||
// register link filter
|
||||
$this->twig->addFilter(new Twig_SimpleFilter('link', array($this, 'getPageUrl')));
|
||||
|
||||
// content filter
|
||||
// register content filter
|
||||
// we pass the $pages array by reference to prevent multiple parser runs for the same page
|
||||
// this is the reason why we can't register this filter as part of PicoTwigExtension
|
||||
$pico = $this;
|
||||
$pages = &$this->pages;
|
||||
$this->twig->addFilter(new Twig_SimpleFilter('content', function ($page) use ($pico, &$pages) {
|
||||
if (isset($pages[$page])) {
|
||||
|
@ -1158,15 +1150,6 @@ class Pico
|
|||
}
|
||||
return null;
|
||||
}));
|
||||
|
||||
// markdown filter
|
||||
$this->twig->addFilter(new Twig_SimpleFilter('markdown', function ($markdown) use ($pico) {
|
||||
if ($pico->getParsedown() === null) {
|
||||
throw new LogicException("Unable to parse file contents: Parsedown instance wasn't registered yet");
|
||||
}
|
||||
|
||||
return $pico->getParsedown()->text($markdown);
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
238
lib/PicoTwigExtension.php
Normal file
238
lib/PicoTwigExtension.php
Normal file
|
@ -0,0 +1,238 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Picos Twig extension to implement additional filters
|
||||
*
|
||||
* @author Daniel Rudolf
|
||||
* @link http://picocms.org
|
||||
* @license http://opensource.org/licenses/MIT
|
||||
* @version 1.0
|
||||
*/
|
||||
class PicoTwigExtension extends Twig_Extension
|
||||
{
|
||||
/**
|
||||
* Current instance of Pico
|
||||
*
|
||||
* @see PicoTwigExtension::getPico()
|
||||
* @var Pico
|
||||
*/
|
||||
private $pico;
|
||||
|
||||
/**
|
||||
* Constructs a new instance of this Twig extension
|
||||
*
|
||||
* @param Pico $pico current instance of Pico
|
||||
*/
|
||||
public function __construct(Pico $pico)
|
||||
{
|
||||
$this->pico = $pico;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the extensions instance of Pico
|
||||
*
|
||||
* @see Pico
|
||||
* @return Pico the extensions instance of Pico
|
||||
*/
|
||||
public function getPico()
|
||||
{
|
||||
return $this->pico;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the extension
|
||||
*
|
||||
* @see Twig_ExtensionInterface::getName()
|
||||
* @return string the extension name
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'PicoTwigExtension';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Twig filters markdown, map and sort_by
|
||||
*
|
||||
* @see Twig_ExtensionInterface::getFilters()
|
||||
* @return Twig_SimpleFilter[] array of Picos Twig filters
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return array(
|
||||
'markdown' => new Twig_SimpleFilter('markdown', array($this, 'markdownFilter')),
|
||||
'map' => new Twig_SimpleFilter('map', array($this, 'mapFilter')),
|
||||
'sort_by' => new Twig_SimpleFilter('sort_by', array($this, 'sortByFilter')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a markdown string to HTML
|
||||
*
|
||||
* This method is registered as the Twig `markdown` filter. You can use it
|
||||
* to e.g. parse a meta variable (`{{ meta.description|markdown }}`).
|
||||
* Don't use it to parse the contents of a page, use the `content` filter
|
||||
* instead, what ensures the proper preparation of the contents.
|
||||
*
|
||||
* @param string $markdown markdown to parse
|
||||
* @return string parsed HTML
|
||||
*/
|
||||
public function markdownFilter($markdown)
|
||||
{
|
||||
if ($this->getPico()->getParsedown() === null) {
|
||||
throw new LogicException(
|
||||
'Unable to apply Twig "markdown" filter: '
|
||||
. 'Parsedown instance wasn\'t registered yet'
|
||||
);
|
||||
}
|
||||
|
||||
return $this->getPico()->getParsedown()->text($markdown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a array with the values of the given key or key path
|
||||
*
|
||||
* This method is registered as the Twig `map` filter. You can use this
|
||||
* filter to e.g. get all page titles (`{{ pages|map("title") }}`).
|
||||
*
|
||||
* @param array|Traversable $var variable to map
|
||||
* @param mixed $mapKeyPath key to map; either a scalar or a
|
||||
* array interpreted as key path (i.e. ['foo', 'bar'] will return all
|
||||
* $item['foo']['bar'] values)
|
||||
* @return array mapped values
|
||||
*/
|
||||
public function mapFilter($var, $mapKeyPath)
|
||||
{
|
||||
if (!is_array($var) && (!is_object($var) || !is_a($var, 'Traversable'))) {
|
||||
throw new Twig_Error_Runtime(sprintf(
|
||||
'The map filter only works with arrays or "Traversable", got "%s"',
|
||||
is_object($var) ? get_class($var) : gettype($var)
|
||||
));
|
||||
}
|
||||
|
||||
$result = array();
|
||||
foreach ($var as $key => $value) {
|
||||
$mapValue = $this->getKeyOfVar($value, $mapKeyPath);
|
||||
$result[$key] = ($mapValue !== null) ? $mapValue : $value;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts an array by one of its keys or a arbitrary deep sub-key
|
||||
*
|
||||
* This method is registered as the Twig `sort_by` filter. You can use this
|
||||
* filter to e.g. sort the pages array by a arbitrary meta value. Calling
|
||||
* `{{ pages|sort_by("meta:nav"|split(":")) }}` returns all pages sorted by
|
||||
* the meta value `nav`. Please note the `"meta:nav"|split(":")` part of
|
||||
* the example. The sorting algorithm will never assume equality of two
|
||||
* values, it will then fall back to the original order. The result is
|
||||
* always sorted in ascending order, apply Twigs `reverse` filter to
|
||||
* achieve a descending order.
|
||||
*
|
||||
* @param array|Traversable $var variable to sort
|
||||
* @param mixed $sortKeyPath key to use for sorting; either
|
||||
* a scalar or a array interpreted as key path (i.e. ['foo', 'bar']
|
||||
* will sort $var by $item['foo']['bar'])
|
||||
* @param string $fallback specify what to do with items
|
||||
* which don't contain the specified sort key; use "bottom" (default)
|
||||
* to move those items to the end of the sorted array, "top" to rank
|
||||
* them first, or "keep" to keep the original order of those items
|
||||
* @return array sorted array
|
||||
*/
|
||||
public function sortByFilter($var, $sortKeyPath, $fallback = 'bottom')
|
||||
{
|
||||
if (is_object($var) && is_a($var, 'Traversable')) {
|
||||
$var = iterator_to_array($var, true);
|
||||
} elseif (!is_array($var)) {
|
||||
throw new Twig_Error_Runtime(sprintf(
|
||||
'The sort_by filter only works with arrays or "Traversable", got "%s"',
|
||||
is_object($var) ? get_class($var) : gettype($var)
|
||||
));
|
||||
}
|
||||
if (($fallback !== 'top') && ($fallback !== 'bottom') && ($fallback !== 'keep')) {
|
||||
throw new Twig_Error_Runtime('The sort_by filter only supports the "top", "bottom" and "keep" fallbacks');
|
||||
}
|
||||
|
||||
$twigExtension = $this;
|
||||
$varKeys = array_keys($var);
|
||||
uksort($var, function ($a, $b) use ($twigExtension, $var, $varKeys, $sortKeyPath, $fallback, &$removeItems) {
|
||||
$aSortValue = $twigExtension->getKeyOfVar($var[$a], $sortKeyPath);
|
||||
$aSortValueNull = ($aSortValue === null);
|
||||
|
||||
$bSortValue = $twigExtension->getKeyOfVar($var[$b], $sortKeyPath);
|
||||
$bSortValueNull = ($bSortValue === null);
|
||||
|
||||
if ($aSortValueNull xor $bSortValueNull) {
|
||||
if ($fallback === 'top') {
|
||||
return ($aSortValueNull - $bSortValueNull) * -1;
|
||||
} elseif ($fallback === 'bottom') {
|
||||
return ($aSortValueNull - $bSortValueNull);
|
||||
}
|
||||
} elseif (!$aSortValueNull && !$bSortValueNull) {
|
||||
if ($aSortValue != $bSortValue) {
|
||||
return ($aSortValue > $bSortValue) ? 1 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
// never assume equality; fallback to original order
|
||||
$aIndex = array_search($a, $varKeys);
|
||||
$bIndex = array_search($b, $varKeys);
|
||||
return ($aIndex > $bIndex) ? 1 : -1;
|
||||
});
|
||||
|
||||
return $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of a variable item specified by a scalar key or a
|
||||
* arbitrary deep sub-key using a key path
|
||||
*
|
||||
* @param array|Traversable|ArrayAccess|object $var base variable
|
||||
* @param mixed $keyPath scalar key or a
|
||||
* array interpreted as key path (when passing e.g. ['foo', 'bar'],
|
||||
* the method will return $var['foo']['bar']) specifying the value
|
||||
* @return mixed the requested
|
||||
* value or NULL when the given key or key path didn't match
|
||||
*/
|
||||
public static function getKeyOfVar($var, $keyPath)
|
||||
{
|
||||
if (empty($keyPath)) {
|
||||
return null;
|
||||
} elseif (!is_array($keyPath)) {
|
||||
$keyPath = array($keyPath);
|
||||
}
|
||||
|
||||
foreach ($keyPath as $key) {
|
||||
if (is_object($var)) {
|
||||
if (is_a($var, 'ArrayAccess')) {
|
||||
// use ArrayAccess, see below
|
||||
} elseif (is_a($var, 'Traversable')) {
|
||||
$var = iterator_to_array($var);
|
||||
} elseif (isset($var->{$key})) {
|
||||
$var = $var->{$key};
|
||||
continue;
|
||||
} elseif (is_callable(array($var, 'get' . ucfirst($key)))) {
|
||||
try {
|
||||
$var = call_user_func(array($var, 'get' . ucfirst($key)));
|
||||
continue;
|
||||
} catch (BadMethodCallException $e) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} elseif (!is_array($var)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isset($var[$key])) {
|
||||
$var = $var[$key];
|
||||
continue;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return $var;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue