Initial commit: apps.supersam24.ru
This commit is contained in:
commit
e955e1da36
6566 files changed
+1271095
No files matched your search
+25
@@ -0,0 +1,25 @@
|
||||
language: php
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- language-pack-nl
|
||||
php:
|
||||
- 5.6
|
||||
- 7.0
|
||||
- 7.1
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
install:
|
||||
- composer install
|
||||
- wget https://scrutinizer-ci.com/ocular.phar -O "$HOME/ocular.phar"
|
||||
|
||||
script:
|
||||
- vendor/bin/phpunit --coverage-clover cache/logs/clover.xml
|
||||
|
||||
after_success:
|
||||
- php "$HOME/ocular.phar" code-coverage:upload --format=php-clover cache/logs/clover.xml
|
||||
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
Copyright (c) Arnold Daniels <arnold@jasny.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
Jasny Twig Extensions
|
||||
=======================
|
||||
|
||||
[](https://travis-ci.org/jasny/twig-extensions)
|
||||
[](https://scrutinizer-ci.com/g/jasny/twig-extensions/?branch=master)
|
||||
[](https://scrutinizer-ci.com/g/jasny/twig-extensions/?branch=master)
|
||||
[](https://insight.sensiolabs.com/projects/20fdb2a4-7565-441b-8920-48a7c09113a1)
|
||||
[](https://packagist.org/packages/jasny/twig-extensions)
|
||||
[](https://packagist.org/packages/jasny/twig-extensions)
|
||||
|
||||
A number of useful filters for Twig.
|
||||
|
||||
## Installation
|
||||
|
||||
Jasny's Twig Extensions can be easily installed using [composer](http://getcomposer.org/)
|
||||
|
||||
composer require jasny/twig-extensions
|
||||
|
||||
## Usage
|
||||
|
||||
```php
|
||||
$twig = new Twig_Environment($loader, $options);
|
||||
$twig->addExtension(new Jasny\Twig\DateExtension());
|
||||
$twig->addExtension(new Jasny\Twig\PcreExtension());
|
||||
$twig->addExtension(new Jasny\Twig\TextExtension());
|
||||
$twig->addExtension(new Jasny\Twig\ArrayExtension());
|
||||
```
|
||||
|
||||
To use in a symfony project [register the extensions as a service](http://symfony.com/doc/current/cookbook/templating/twig_extension.html#register-an-extension-as-a-service).
|
||||
|
||||
```yaml
|
||||
services:
|
||||
twig.extension.date:
|
||||
class: Jasny\Twig\DateExtension
|
||||
tags:
|
||||
- { name: twig.extension }
|
||||
|
||||
twig.extension.pcre:
|
||||
class: Jasny\Twig\PcreExtension
|
||||
tags:
|
||||
- { name: twig.extension }
|
||||
|
||||
twig.extension.text:
|
||||
class: Jasny\Twig\TextExtension
|
||||
tags:
|
||||
- { name: twig.extension }
|
||||
|
||||
twig.extension.array:
|
||||
class: Jasny\Twig\ArrayExtension
|
||||
tags:
|
||||
- { name: twig.extension }
|
||||
```
|
||||
|
||||
|
||||
## Date extension
|
||||
|
||||
Format a date base on the current locale. Requires the [intl extension](http://www.php.net/intl).
|
||||
|
||||
* localdate - Format the date value as a string based on the current locale
|
||||
* localtime - Format the time value as a string based on the current locale
|
||||
* localdatetime - Format the date/time value as a string based on the current locale
|
||||
* age - Get the age (in years) based on a date
|
||||
* duration - Get the duration string from seconds
|
||||
|
||||
```php
|
||||
Locale::setDefault(LC_ALL, "en_US"); // vs "nl_NL"
|
||||
```
|
||||
|
||||
```
|
||||
{{"now"|localdate('long')}} <!-- July 12, 2013 --> <!-- 12 juli 2013 -->
|
||||
{{"now"|localtime('short')}} <!-- 5:53 PM --> <!-- 17:53 -->
|
||||
{{"2013-10-01 23:15:00"|localdatetime}} <!-- 10/01/2013 11:15 PM --> <!-- 01-10-2013 23:15 -->
|
||||
{{"22-08-1981"|age}} <!-- 35 -->
|
||||
{{ 3600|duration }} <!-- 1h -->
|
||||
```
|
||||
|
||||
|
||||
## PCRE
|
||||
|
||||
Exposes [PCRE](http://www.php.net/pcre) to Twig.
|
||||
|
||||
* preg_quote - Quote regular expression characters
|
||||
* preg_match - Perform a regular expression match
|
||||
* preg_get - Perform a regular expression match and returns the matched group
|
||||
* preg_get_all - Perform a regular expression match and return the group for all matches
|
||||
* preg_grep - Perform a regular expression match and return an array of entries that match the pattern
|
||||
* preg_replace - Perform a regular expression search and replace
|
||||
* preg_filter - Perform a regular expression search and replace, returning only matched subjects.
|
||||
* preg_split - Split text into an array using a regular expression
|
||||
|
||||
```
|
||||
{% if client.email|preg_match('/^.+@.+\.\w+$/) %}Email: {{ client.email }}{% endif %}
|
||||
Website: {{ client.website|preg_replace('~^https?://~')
|
||||
First name: {{ client.fullname|preg_get('/^\S+/') }}
|
||||
<ul>
|
||||
{% for item in items|preg_split('/\s+/')|grep_filter('/-test$/', 'invert') %}
|
||||
<li>{{ item }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
```
|
||||
|
||||
|
||||
## Text ##
|
||||
|
||||
Convert text to HTML + string functions
|
||||
|
||||
* paragraph - Add HTML paragraph and line breaks to text
|
||||
* line - Get a single line of text
|
||||
* less - Cut of text on a pagebreak
|
||||
* truncate - Cut of text if it's to long
|
||||
* linkify - Turn all URLs in clickable links (also supports Twitter @user and #subject)
|
||||
|
||||
|
||||
## Array ##
|
||||
|
||||
Brings PHP's array functions to Twig
|
||||
|
||||
* sum - Calculate the sum of values in an array
|
||||
* product - Calculate the product of values in an array
|
||||
* values - Return all the values of an array
|
||||
* as_array - Cast an object to an associated array
|
||||
* html_attr - Turn an array into an HTML attribute string
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "jasny/twig-extensions",
|
||||
"description": "A set of useful Twig filters",
|
||||
"keywords": ["templating", "PCRE", "preg", "regex", "date", "time", "datetime", "text", "array"],
|
||||
"license": "MIT",
|
||||
"homepage": "http://github.com/jasny/twig-extensions#README",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Arnold Daniels",
|
||||
"email": "arnold@jasny.net",
|
||||
"homepage": "http://www.jasny.net"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/jasny/twig-extensions/issues",
|
||||
"source": "https://github.com/jasny/twig-extensions"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.0.0 | >=5.6.0",
|
||||
"twig/twig": "^2.0 | ^1.12"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-intl": "Required for the use of the LocalDate Twig extension",
|
||||
"ext-pcre": "Required for the use of the PCRE Twig extension"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Jasny\\Twig\\": "src/"
|
||||
}
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-intl": "*",
|
||||
"ext-pcre": "*",
|
||||
"jasny/php-code-quality": "^2.1",
|
||||
"phpunit/phpunit": "^5.0"
|
||||
},
|
||||
"autoload-dev": {
|
||||
"classmap": [
|
||||
"tests/support/"
|
||||
]
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit
|
||||
colors="true"
|
||||
bootstrap="vendor/autoload.php"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite>
|
||||
<directory>tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
<logging>
|
||||
<log type="coverage-text" target="php://stdout"/>
|
||||
</logging>
|
||||
</phpunit>
|
||||
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace Jasny\Twig;
|
||||
|
||||
/**
|
||||
* Expose PHP's array functions to Twig
|
||||
*/
|
||||
class ArrayExtension extends \Twig_Extension
|
||||
{
|
||||
/**
|
||||
* Return extension name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'jasny/array';
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for Twig
|
||||
* @ignore
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return [
|
||||
new \Twig_SimpleFilter('sum', [$this, 'sum']),
|
||||
new \Twig_SimpleFilter('product', [$this, 'product']),
|
||||
new \Twig_SimpleFilter('values', [$this, 'values']),
|
||||
new \Twig_SimpleFilter('as_array', [$this, 'asArray']),
|
||||
new \Twig_SimpleFilter('html_attr', [$this, 'htmlAttributes']),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate the sum of values in an array
|
||||
*
|
||||
* @param array $array
|
||||
* @return int
|
||||
*/
|
||||
public function sum($array)
|
||||
{
|
||||
return isset($array) ? array_sum((array)$array) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the product of values in an array
|
||||
*
|
||||
* @param array $array
|
||||
* @return int
|
||||
*/
|
||||
public function product($array)
|
||||
{
|
||||
return isset($array) ? array_product((array)$array) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the values of an array or object
|
||||
*
|
||||
* @param array|object $array
|
||||
* @return array
|
||||
*/
|
||||
public function values($array)
|
||||
{
|
||||
return isset($array) ? array_values((array)$array) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast value to an array
|
||||
*
|
||||
* @param object|mixed $value
|
||||
* @return array
|
||||
*/
|
||||
public function asArray($value)
|
||||
{
|
||||
return is_object($value) ? get_object_vars($value) : (array)$value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast an array to an HTML attribute string
|
||||
*
|
||||
* @param mixed $array
|
||||
* @return string
|
||||
*/
|
||||
public function htmlAttributes($array)
|
||||
{
|
||||
if (!isset($array)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$str = "";
|
||||
foreach ($array as $key => $value) {
|
||||
if (!isset($value) || $value === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($value === true) {
|
||||
$value = $key;
|
||||
}
|
||||
|
||||
$str .= ' ' . $key . '="' . addcslashes($value, '"') . '"';
|
||||
}
|
||||
|
||||
return trim($str);
|
||||
}
|
||||
}
|
||||
+347
@@ -0,0 +1,347 @@
|
||||
<?php
|
||||
|
||||
namespace Jasny\Twig;
|
||||
|
||||
/**
|
||||
* Format a date based on the current locale in Twig
|
||||
*/
|
||||
class DateExtension extends \Twig_Extension
|
||||
{
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if (!extension_loaded('intl')) {
|
||||
throw new \Exception("The Date Twig extension requires the 'intl' PHP extension."); // @codeCoverageIgnore
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return extension name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'jasny/date';
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for Twig to get all the filters.
|
||||
*
|
||||
* @return \Twig_SimpleFilter[]
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return [
|
||||
new \Twig_SimpleFilter('localdate', [$this, 'localDate']),
|
||||
new \Twig_SimpleFilter('localtime', [$this, 'localTime']),
|
||||
new \Twig_SimpleFilter('localdatetime', [$this, 'localDateTime']),
|
||||
new \Twig_SimpleFilter('duration', [$this, 'duration']),
|
||||
new \Twig_SimpleFilter('age', [$this, 'age']),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn a value into a DateTime object
|
||||
*
|
||||
* @param string|int|\DateTime $date
|
||||
* @return \DateTime
|
||||
*/
|
||||
protected function valueToDateTime($date)
|
||||
{
|
||||
if (!$date instanceof \DateTime) {
|
||||
$date = is_int($date) ? \DateTime::createFromFormat('U', $date) : new \DateTime((string)$date);
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configured intl date formatter.
|
||||
*
|
||||
* @param string|null $dateFormat
|
||||
* @param string|null $timeFormat
|
||||
* @param string $calendar
|
||||
* @return \IntlDateFormatter
|
||||
*/
|
||||
protected function getDateFormatter($dateFormat, $timeFormat, $calendar)
|
||||
{
|
||||
$datetype = isset($dateFormat) ? $this->getFormat($dateFormat) : null;
|
||||
$timetype = isset($timeFormat) ? $this->getFormat($timeFormat) : null;
|
||||
|
||||
$calendarConst = $calendar === 'traditional' ? \IntlDateFormatter::TRADITIONAL : \IntlDateFormatter::GREGORIAN;
|
||||
|
||||
$pattern = $this->getDateTimePattern(
|
||||
isset($datetype) ? $datetype : $dateFormat,
|
||||
isset($timetype) ? $timetype : $timeFormat,
|
||||
$calendarConst
|
||||
);
|
||||
|
||||
return new \IntlDateFormatter(\Locale::getDefault(), $datetype, $timetype, null, $calendarConst, $pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the date/time value as a string based on the current locale
|
||||
*
|
||||
* @param string|false $format 'short', 'medium', 'long', 'full', 'none' or false
|
||||
* @return int|null
|
||||
*/
|
||||
protected function getFormat($format)
|
||||
{
|
||||
if ($format === false) {
|
||||
$format = 'none';
|
||||
}
|
||||
|
||||
$types = [
|
||||
'none' => \IntlDateFormatter::NONE,
|
||||
'short' => \IntlDateFormatter::SHORT,
|
||||
'medium' => \IntlDateFormatter::MEDIUM,
|
||||
'long' => \IntlDateFormatter::LONG,
|
||||
'full' => \IntlDateFormatter::FULL
|
||||
];
|
||||
|
||||
return isset($types[$format]) ? $types[$format] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the date/time pattern.
|
||||
*
|
||||
* @param int|string $datetype
|
||||
* @param int|string $timetype
|
||||
* @param int $calendar
|
||||
* @return string
|
||||
*/
|
||||
protected function getDateTimePattern($datetype, $timetype, $calendar = \IntlDateFormatter::GREGORIAN)
|
||||
{
|
||||
if (is_int($datetype) && is_int($timetype)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->getDatePattern(
|
||||
isset($datetype) ? $datetype : \IntlDateFormatter::SHORT,
|
||||
isset($timetype) ? $timetype : \IntlDateFormatter::SHORT,
|
||||
$calendar
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the formatter to create a date and/or time pattern
|
||||
*
|
||||
* @param int|string $datetype
|
||||
* @param int|string $timetype
|
||||
* @param int $calendar
|
||||
* @return \IntlDateFormatter
|
||||
*/
|
||||
protected function getDatePatternFormatter($datetype, $timetype, $calendar = \IntlDateFormatter::GREGORIAN)
|
||||
{
|
||||
return \IntlDateFormatter::create(
|
||||
\Locale::getDefault(),
|
||||
is_int($datetype) ? $datetype : \IntlDateFormatter::NONE,
|
||||
is_int($timetype) ? $timetype : \IntlDateFormatter::NONE,
|
||||
\IntlTimeZone::getGMT(),
|
||||
$calendar
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the date and/or time pattern
|
||||
* Default date pattern is short date pattern with 4 digit year.
|
||||
*
|
||||
* @param int|string $datetype
|
||||
* @param int|string $timetype
|
||||
* @param int $calendar
|
||||
* @return string
|
||||
*/
|
||||
protected function getDatePattern($datetype, $timetype, $calendar = \IntlDateFormatter::GREGORIAN)
|
||||
{
|
||||
$createPattern =
|
||||
(is_int($datetype) && $datetype !== \IntlDateFormatter::NONE) ||
|
||||
(is_int($timetype) && $timetype !== \IntlDateFormatter::NONE);
|
||||
|
||||
$pattern = $createPattern ? $this->getDatePatternFormatter($datetype, $timetype, $calendar)->getPattern() : '';
|
||||
|
||||
return trim(
|
||||
(is_string($datetype) ? $datetype . ' ' : '') .
|
||||
preg_replace('/\byy?\b/', 'yyyy', $pattern) .
|
||||
(is_string($timetype) ? ' ' . $timetype : '')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the date and/or time value as a string based on the current locale
|
||||
*
|
||||
* @param \DateTime|int|string $value
|
||||
* @param string $dateFormat null, 'short', 'medium', 'long', 'full' or pattern
|
||||
* @param string $timeFormat null, 'short', 'medium', 'long', 'full' or pattern
|
||||
* @param string $calendar 'gregorian' or 'traditional'
|
||||
* @return string
|
||||
*/
|
||||
protected function formatLocal($value, $dateFormat, $timeFormat, $calendar = 'gregorian')
|
||||
{
|
||||
if (!isset($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$date = $this->valueToDateTime($value);
|
||||
$formatter = $this->getDateFormatter($dateFormat, $timeFormat, $calendar);
|
||||
|
||||
return $formatter->format($date->getTimestamp());
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the date value as a string based on the current locale
|
||||
*
|
||||
* @param DateTime|int|string $date
|
||||
* @param string $format null, 'short', 'medium', 'long', 'full' or pattern
|
||||
* @param string $calendar 'gregorian' or 'traditional'
|
||||
* @return string
|
||||
*/
|
||||
public function localDate($date, $format = null, $calendar = 'gregorian')
|
||||
{
|
||||
return $this->formatLocal($date, $format, false, $calendar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the time value as a string based on the current locale
|
||||
*
|
||||
* @param DateTime|int|string $date
|
||||
* @param string $format 'short', 'medium', 'long', 'full' or pattern
|
||||
* @param string $calendar 'gregorian' or 'traditional'
|
||||
* @return string
|
||||
*/
|
||||
public function localTime($date, $format = 'short', $calendar = 'gregorian')
|
||||
{
|
||||
return $this->formatLocal($date, false, $format, $calendar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the date/time value as a string based on the current locale
|
||||
*
|
||||
* @param DateTime|int|string $date
|
||||
* @param string $format date format, pattern or ['date'=>format, 'time'=>format)
|
||||
* @param string $calendar 'gregorian' or 'traditional'
|
||||
* @return string
|
||||
*/
|
||||
public function localDateTime($date, $format = null, $calendar = 'gregorian')
|
||||
{
|
||||
if (is_array($format) || $format instanceof \stdClass || !isset($format)) {
|
||||
$formatDate = isset($format['date']) ? $format['date'] : null;
|
||||
$formatTime = isset($format['time']) ? $format['time'] : 'short';
|
||||
} else {
|
||||
$formatDate = $format;
|
||||
$formatTime = false;
|
||||
}
|
||||
|
||||
return $this->formatLocal($date, $formatDate, $formatTime, $calendar);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Split duration into seconds, minutes, hours, days, weeks and years.
|
||||
*
|
||||
* @param int $seconds
|
||||
* @return array
|
||||
*/
|
||||
protected function splitDuration($seconds, $max)
|
||||
{
|
||||
if ($max < 1 || $seconds < 60) {
|
||||
return [$seconds];
|
||||
}
|
||||
|
||||
$minutes = floor($seconds / 60);
|
||||
$seconds = $seconds % 60;
|
||||
if ($max < 2 || $minutes < 60) {
|
||||
return [$seconds, $minutes];
|
||||
}
|
||||
|
||||
$hours = floor($minutes / 60);
|
||||
$minutes = $minutes % 60;
|
||||
if ($max < 3 || $hours < 24) {
|
||||
return [$seconds, $minutes, $hours];
|
||||
}
|
||||
|
||||
$days = floor($hours / 24);
|
||||
$hours = $hours % 24;
|
||||
if ($max < 4 || $days < 7) {
|
||||
return [$seconds, $minutes, $hours, $days];
|
||||
}
|
||||
|
||||
$weeks = floor($days / 7);
|
||||
$days = $days % 7;
|
||||
if ($max < 5 || $weeks < 52) {
|
||||
return [$seconds, $minutes, $hours, $days, $weeks];
|
||||
}
|
||||
|
||||
$years = floor($weeks / 52);
|
||||
$weeks = $weeks % 52;
|
||||
return [$seconds, $minutes, $hours, $days, $weeks, $years];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate duration from seconds.
|
||||
* One year is seen as exactly 52 weeks.
|
||||
*
|
||||
* Use null to skip a unit.
|
||||
*
|
||||
* @param int $value Time in seconds
|
||||
* @param array $units Time units (seconds, minutes, hours, days, weeks, years)
|
||||
* @param string $separator
|
||||
* @return string
|
||||
*/
|
||||
public function duration($value, $units = ['s', 'm', 'h', 'd', 'w', 'y'], $separator = ' ')
|
||||
{
|
||||
if (!isset($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
list($seconds, $minutes, $hours, $days, $weeks, $years) =
|
||||
$this->splitDuration($value, count($units) - 1) + array_fill(0, 6, null);
|
||||
|
||||
$duration = '';
|
||||
if (isset($years) && isset($units[5])) {
|
||||
$duration .= $separator . $years . $units[5];
|
||||
}
|
||||
|
||||
if (isset($weeks) && isset($units[4])) {
|
||||
$duration .= $separator . $weeks . $units[4];
|
||||
}
|
||||
|
||||
if (isset($days) && isset($units[3])) {
|
||||
$duration .= $separator . $days . $units[3];
|
||||
}
|
||||
|
||||
if (isset($hours) && isset($units[2])) {
|
||||
$duration .= $separator . $hours . $units[2];
|
||||
}
|
||||
|
||||
if (isset($minutes) && isset($units[1])) {
|
||||
$duration .= $separator . $minutes . $units[1];
|
||||
}
|
||||
|
||||
if (isset($seconds) && isset($units[0])) {
|
||||
$duration .= $separator . $seconds . $units[0];
|
||||
}
|
||||
|
||||
return trim($duration, $separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the age (in years) based on a date.
|
||||
*
|
||||
* @param DateTime|string $value
|
||||
* @return int
|
||||
*/
|
||||
public function age($value)
|
||||
{
|
||||
if (!isset($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$date = $this->valueToDateTime($value);
|
||||
|
||||
return $date->diff(new \DateTime())->format('%y');
|
||||
}
|
||||
}
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
|
||||
namespace Jasny\Twig;
|
||||
|
||||
/**
|
||||
* Expose the PCRE functions to Twig.
|
||||
*
|
||||
* @see http://php.net/manual/en/book.pcre.php
|
||||
*/
|
||||
class PcreExtension extends \Twig_Extension
|
||||
{
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if (!extension_loaded('pcre')) {
|
||||
throw new \Exception("The Twig PCRE extension requires the PCRE extension."); // @codeCoverageIgnore
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return extension name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'jasny/pcre';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return [
|
||||
new \Twig_SimpleFilter('preg_quote', [$this, 'quote']),
|
||||
new \Twig_SimpleFilter('preg_match', [$this, 'match']),
|
||||
new \Twig_SimpleFilter('preg_get', [$this, 'get']),
|
||||
new \Twig_SimpleFilter('preg_get_all', [$this, 'getAll']),
|
||||
new \Twig_SimpleFilter('preg_grep', [$this, 'grep']),
|
||||
new \Twig_SimpleFilter('preg_replace', [$this, 'replace']),
|
||||
new \Twig_SimpleFilter('preg_filter', [$this, 'filter']),
|
||||
new \Twig_SimpleFilter('preg_split', [$this, 'split']),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check that the regex doesn't use the eval modifier
|
||||
*
|
||||
* @param string $pattern
|
||||
* @throws \LogicException
|
||||
*/
|
||||
protected function assertNoEval($pattern)
|
||||
{
|
||||
$pos = strrpos($pattern, $pattern[0]);
|
||||
$modifiers = substr($pattern, $pos + 1);
|
||||
|
||||
if (strpos($modifiers, 'e') !== false) {
|
||||
throw new \Twig_Error_Runtime("Using the eval modifier for regular expressions is not allowed");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Quote regular expression characters.
|
||||
*
|
||||
* @param string $value
|
||||
* @param string $delimiter
|
||||
* @return string
|
||||
*/
|
||||
public function quote($value, $delimiter = '/')
|
||||
{
|
||||
if (!isset($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return preg_quote($value, $delimiter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a regular expression match.
|
||||
*
|
||||
* @param string $value
|
||||
* @param string $pattern
|
||||
* @return boolean
|
||||
*/
|
||||
public function match($value, $pattern)
|
||||
{
|
||||
if (!isset($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return preg_match($pattern, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a regular expression match and return a matched group.
|
||||
*
|
||||
* @param string $value
|
||||
* @param string $pattern
|
||||
* @return string
|
||||
*/
|
||||
public function get($value, $pattern, $group = 0)
|
||||
{
|
||||
if (!isset($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return preg_match($pattern, $value, $matches) && isset($matches[$group]) ? $matches[$group] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a regular expression match and return the group for all matches.
|
||||
*
|
||||
* @param string $value
|
||||
* @param string $pattern
|
||||
* @return array
|
||||
*/
|
||||
public function getAll($value, $pattern, $group = 0)
|
||||
{
|
||||
if (!isset($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return preg_match_all($pattern, $value, $matches, PREG_PATTERN_ORDER) && isset($matches[$group])
|
||||
? $matches[$group] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a regular expression match and return an array of entries that match the pattern
|
||||
*
|
||||
* @param array $values
|
||||
* @param string $pattern
|
||||
* @param string $flags Optional 'invert' to return entries that do not match the given pattern.
|
||||
* @return array
|
||||
*/
|
||||
public function grep($values, $pattern, $flags = '')
|
||||
{
|
||||
if (!isset($values)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (is_string($flags)) {
|
||||
$flags = $flags === 'invert' ? PREG_GREP_INVERT : 0;
|
||||
}
|
||||
|
||||
return preg_grep($pattern, $values, $flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a regular expression search and replace.
|
||||
*
|
||||
* @param string $value
|
||||
* @param string $pattern
|
||||
* @param string $replacement
|
||||
* @param int $limit
|
||||
* @return string
|
||||
*/
|
||||
public function replace($value, $pattern, $replacement = '', $limit = -1)
|
||||
{
|
||||
$this->assertNoEval($pattern);
|
||||
|
||||
if (!isset($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return preg_replace($pattern, $replacement, $value, $limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a regular expression search and replace, returning only matched subjects.
|
||||
*
|
||||
* @param string $value
|
||||
* @param string $pattern
|
||||
* @param string $replacement
|
||||
* @param int $limit
|
||||
* @return string
|
||||
*/
|
||||
public function filter($value, $pattern, $replacement = '', $limit = -1)
|
||||
{
|
||||
$this->assertNoEval($pattern);
|
||||
|
||||
if (!isset($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return preg_filter($pattern, $replacement, $value, $limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Split text into an array using a regular expression.
|
||||
*
|
||||
* @param string $value
|
||||
* @param string $pattern
|
||||
* @return array
|
||||
*/
|
||||
public function split($value, $pattern)
|
||||
{
|
||||
if (!isset($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return preg_split($pattern, $value);
|
||||
}
|
||||
}
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
namespace Jasny\Twig;
|
||||
|
||||
/**
|
||||
* Text functions for Twig.
|
||||
*/
|
||||
class TextExtension extends \Twig_Extension
|
||||
{
|
||||
/**
|
||||
* Return extension name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'jasny/text';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return [
|
||||
new \Twig_SimpleFilter('paragraph', [$this, 'paragraph'], ['pre_escape' => 'html', 'is_safe' => ['html']]),
|
||||
new \Twig_SimpleFilter('line', [$this, 'line']),
|
||||
new \Twig_SimpleFilter('less', [$this, 'less'], ['pre_escape' => 'html', 'is_safe' => ['html']]),
|
||||
new \Twig_SimpleFilter('truncate', [$this, 'truncate'], ['pre_escape' => 'html', 'is_safe' => ['html']]),
|
||||
new \Twig_SimpleFilter('linkify', [$this, 'linkify'], ['pre_escape' => 'html', 'is_safe' => ['html']])
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add paragraph and line breaks to text.
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public function paragraph($value)
|
||||
{
|
||||
if (!isset($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return '<p>' . preg_replace(['~\n(\s*)\n\s*~', '~(?<!</p>)\n\s*~'], ["</p>\n\$1<p>", "<br>\n"], trim($value)) .
|
||||
'</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single line
|
||||
*
|
||||
* @param string $value
|
||||
* @param int $line Line number (starts at 1)
|
||||
* @return string
|
||||
*/
|
||||
public function line($value, $line = 1)
|
||||
{
|
||||
if (!isset($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$lines = explode("\n", $value);
|
||||
|
||||
return isset($lines[$line - 1]) ? $lines[$line - 1] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cut of text on a pagebreak.
|
||||
*
|
||||
* @param string $value
|
||||
* @param string $replace
|
||||
* @param string $break
|
||||
* @return string
|
||||
*/
|
||||
public function less($value, $replace = '...', $break = '<!-- pagebreak -->')
|
||||
{
|
||||
if (!isset($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$pos = stripos($value, $break);
|
||||
return $pos === false ? $value : substr($value, 0, $pos) . $replace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cut of text if it's to long.
|
||||
*
|
||||
* @param string $value
|
||||
* @param int $length
|
||||
* @param string $replace
|
||||
* @return string
|
||||
*/
|
||||
public function truncate($value, $length, $replace = '...')
|
||||
{
|
||||
if (!isset($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return strlen($value) <= $length ? $value : substr($value, 0, $length - strlen($replace)) . $replace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Linkify a HTTP(S) link.
|
||||
*
|
||||
* @param string $protocol 'http' or 'https'
|
||||
* @param string $text
|
||||
* @param array $links OUTPUT
|
||||
* @param string $attr
|
||||
* @param string $mode
|
||||
* @return string
|
||||
*/
|
||||
protected function linkifyHttp($protocol, $text, array &$links, $attr, $mode)
|
||||
{
|
||||
$regexp = $mode != 'all'
|
||||
? '~(?:(https?)://([^\s<>]+)|(?<!\w@)\b(www\.[^\s<>]+?\.[^\s<>]+))(?<![\.,:;\?!\'"\|])~i'
|
||||
: '~(?:(https?)://([^\s<>]+)|(?<!\w@)\b([^\s<>@]+?\.[^\s<>]+)(?<![\.,:]))~i';
|
||||
|
||||
return preg_replace_callback($regexp, function ($match) use ($protocol, &$links, $attr) {
|
||||
if ($match[1]) $protocol = $match[1];
|
||||
$link = $match[2] ?: $match[3];
|
||||
|
||||
return '<' . array_push($links, '<a' . $attr . ' href="' . $protocol . '://' . $link . '">'
|
||||
. rtrim($link, '/') . '</a>') . '>';
|
||||
}, $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Linkify a mail link.
|
||||
*
|
||||
* @param string $text
|
||||
* @param array $links OUTPUT
|
||||
* @param string $attr
|
||||
* @return string
|
||||
*/
|
||||
protected function linkifyMail($text, array &$links, $attr)
|
||||
{
|
||||
$regexp = '~([^\s<>]+?@[^\s<>]+?\.[^\s<>]+)(?<![\.,:;\?!\'"\|])~';
|
||||
|
||||
return preg_replace_callback($regexp, function ($match) use (&$links, $attr) {
|
||||
return '<' . array_push($links, '<a' . $attr . ' href="mailto:' . $match[1] . '">' . $match[1] . '</a>')
|
||||
. '>';
|
||||
}, $text);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Linkify a link.
|
||||
*
|
||||
* @param string $protocol
|
||||
* @param string $text
|
||||
* @param array $links OUTPUT
|
||||
* @param string $attr
|
||||
* @param string $mode
|
||||
* @return string
|
||||
*/
|
||||
protected function linkifyOther($protocol, $text, array &$links, $attr, $mode)
|
||||
{
|
||||
if (strpos($protocol, ':') === false) {
|
||||
$protocol .= in_array($protocol, ['ftp', 'tftp', 'ssh', 'scp']) ? '://' : ':';
|
||||
}
|
||||
|
||||
$regexp = $mode != 'all'
|
||||
? '~' . preg_quote($protocol, '~') . '([^\s<>]+)(?<![\.,:;\?!\'"\|])~i'
|
||||
: '~([^\s<>]+)(?<![\.,:])~i';
|
||||
|
||||
return preg_replace_callback($regexp, function ($match) use ($protocol, &$links, $attr) {
|
||||
return '<' . array_push($links, '<a' . $attr . ' href="' . $protocol . $match[1] . '">' . $match[1]
|
||||
. '</a>') . '>';
|
||||
}, $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn all URLs in clickable links.
|
||||
*
|
||||
* @param string $value
|
||||
* @param array $protocols 'http'/'https', 'mail' and also 'ftp', 'scp', 'tel', etc
|
||||
* @param array $attributes HTML attributes for the link
|
||||
* @param string $mode normal or all
|
||||
* @return string
|
||||
*/
|
||||
public function linkify($value, $protocols = ['http', 'mail'], array $attributes = [], $mode = 'normal')
|
||||
{
|
||||
if (!isset($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Link attributes
|
||||
$attr = '';
|
||||
foreach ($attributes as $key => $val) {
|
||||
$attr .= ' ' . $key . '="' . htmlentities($val) . '"';
|
||||
}
|
||||
|
||||
$links = [];
|
||||
|
||||
// Extract existing links and tags
|
||||
$text = preg_replace_callback('~(<a .*?>.*?</a>|<.*?>)~i', function ($match) use (&$links) {
|
||||
return '<' . array_push($links, $match[1]) . '>';
|
||||
}, $value);
|
||||
|
||||
// Extract text links for each protocol
|
||||
foreach ((array)$protocols as $protocol) {
|
||||
switch ($protocol) {
|
||||
case 'http':
|
||||
case 'https': $text = $this->linkifyHttp($protocol, $text, $links, $attr, $mode); break;
|
||||
case 'mail': $text = $this->linkifyMail($text, $links, $attr); break;
|
||||
default: $text = $this->linkifyOther($protocol, $text, $links, $attr, $mode); break;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert all link
|
||||
return preg_replace_callback('/<(\d+)>/', function ($match) use (&$links) {
|
||||
return $links[$match[1] - 1];
|
||||
}, $text);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace Jasny\Twig;
|
||||
|
||||
use Jasny\Twig\ArrayExtension;
|
||||
use Jasny\Twig\TestHelper;
|
||||
|
||||
/**
|
||||
* @covers Jasny\Twig\ArrayExtension
|
||||
*/
|
||||
class ArrayExtensionTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
use TestHelper;
|
||||
|
||||
protected function getExtension()
|
||||
{
|
||||
return new ArrayExtension();
|
||||
}
|
||||
|
||||
|
||||
public function testSum()
|
||||
{
|
||||
$data = [1, 2, 3, 4];
|
||||
|
||||
$this->assertRender('10', '{{ data|sum }}', compact('data'));
|
||||
}
|
||||
|
||||
public function testProduct()
|
||||
{
|
||||
$data = [1, 2, 3, 4];
|
||||
|
||||
$this->assertRender('24', '{{ data|product }}', compact('data'));
|
||||
}
|
||||
|
||||
public function testValues()
|
||||
{
|
||||
$data = (object)['foo' => 1, 'bar' => 2, 'zoo' => 3];
|
||||
|
||||
$this->assertRender('1-2-3', '{{ data|values|join("-") }}', compact('data'));
|
||||
}
|
||||
|
||||
|
||||
public function testAsArrayWithObject()
|
||||
{
|
||||
$data = (object)['foo' => 1, 'bar' => 2, 'zoo' => 3];
|
||||
|
||||
$this->assertRender('foo-bar-zoo', '{{ data|as_array|keys|join("-") }}', compact('data'));
|
||||
}
|
||||
|
||||
public function testAsArrayWithString()
|
||||
{
|
||||
$data = 'foo';
|
||||
|
||||
$this->assertRender('foo', '{{ data|as_array|join("-") }}', compact('data'));
|
||||
}
|
||||
|
||||
public function testHtmlAttr()
|
||||
{
|
||||
$data = ['href' => 'foo.html', 'class' => 'big small', 'checked' => true, 'disabled' => false];
|
||||
|
||||
$this->assertRender(
|
||||
'href="foo.html" class="big small" checked="checked"',
|
||||
'{{ data|html_attr|raw }}',
|
||||
compact('data')
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function filterProvider()
|
||||
{
|
||||
return [
|
||||
['sum'],
|
||||
['product'],
|
||||
['values'],
|
||||
['as_array'],
|
||||
['html_attr']
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider filterProvider
|
||||
*
|
||||
* @param string $filter
|
||||
*/
|
||||
public function testWithNull($filter)
|
||||
{
|
||||
$this->assertRender('-', '{{ null|' . $filter . '("//")|default("-") }}');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
namespace Jasny\Twig;
|
||||
|
||||
use Jasny\Twig\DateExtension;
|
||||
use Jasny\Twig\TestHelper;
|
||||
|
||||
/**
|
||||
* @covers Jasny\Twig\DateExtension
|
||||
*/
|
||||
class DateExtensionTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
use TestHelper;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
date_default_timezone_set('UTC');
|
||||
\Locale::setDefault("en_EN");
|
||||
}
|
||||
|
||||
protected function getExtension()
|
||||
{
|
||||
return new DateExtension();
|
||||
}
|
||||
|
||||
|
||||
public function localDateTimeProvider()
|
||||
{
|
||||
return [
|
||||
['9/20/2015', '20-09-2015', "{{ '20-09-2015'|localdate }}"],
|
||||
['September 20, 2015', '20 september 2015', "{{ '20-09-2015'|localdate('long') }}"],
|
||||
['9/20/15', "20-09-15", "{{ '20-09-2015'|localdate('short') }}"],
|
||||
['Sunday, September 20, 2015', "zondag 20 september 2015", "{{ '20-09-2015'|localdate('full') }}"],
|
||||
['20|09|2015', "20|09|2015", "{{ '20-09-2015'|localdate('dd|MM|yyyy') }}"],
|
||||
|
||||
['11:14 PM', "23:14", "{{ '23:14:12'|localtime }}"],
|
||||
['11:14:12 PM GMT', "23:14:12 GMT", "{{ '23:14:12'|localtime('long') }}"],
|
||||
['11:14 PM', "23:14", "{{ '23:14:12'|localtime('short') }}"],
|
||||
['11:14:12 PM GMT', "23:14:12 GMT", "{{ '23:14:12'|localtime('full') }}"],
|
||||
['23|14|12', "23|14|12", "{{ '23:14:12'|localtime('HH|mm|ss') }}"],
|
||||
|
||||
// NOTE: a `replace` is used to remove the comma, which seems to be inconsistant accross environments.
|
||||
['9/20/2015 11:14 PM', '20-09-2015 23:14', "{{ '20-09-2015 23:14:12'|localdatetime|replace({',': ''}) }}"],
|
||||
['20|23', '20|23', "{{ '20-09-2015 23:14:12'|localdatetime('dd|HH') }}"],
|
||||
[
|
||||
'9/20/15 11:14:12 PM GMT',
|
||||
'20-09-15 23:14:12 GMT',
|
||||
"{{ '20-09-2015 23:14:12'|localdatetime({date: 'short', time: 'full'})|replace({',': ''}) }}"
|
||||
],
|
||||
[
|
||||
'20150920 11:14:12 PM GMT',
|
||||
'20150920 23:14:12 GMT',
|
||||
"{{ '20-09-2015 23:14:12'|localdatetime({date: 'yyyyMMdd', time: 'full'}) }}"
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider localDateTimeProvider
|
||||
*
|
||||
* @param string $en
|
||||
* @param string $nl
|
||||
* @param string $template
|
||||
*/
|
||||
public function testLocalDateTimeEn($en, $nl, $template)
|
||||
{
|
||||
if (!\Locale::setDefault("en_EN")) {
|
||||
return $this->markAsSkipped("Unable to set locale to 'en_EN'");
|
||||
}
|
||||
|
||||
$this->assertRender($en, $template);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider localDateTimeProvider
|
||||
*
|
||||
* @param string $en
|
||||
* @param string $nl
|
||||
* @param string $template
|
||||
*/
|
||||
public function testLocalDateTimeNL($en, $nl, $template)
|
||||
{
|
||||
if (!\Locale::setDefault("nl_NL")) {
|
||||
return $this->markAsSkipped("Unable to set locale to 'nl_NL'");
|
||||
}
|
||||
|
||||
$this->assertRender($nl, $template);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function durationProvider()
|
||||
{
|
||||
return [
|
||||
['31s', "{{ 31|duration }}"],
|
||||
['17m 31s', "{{ 1051|duration }}"],
|
||||
['3h 17m 31s', "{{ 11851|duration }}"],
|
||||
['2d 3h 17m 31s', "{{ 184651|duration }}"],
|
||||
['3w 2d 3h 17m 31s', "{{ 1999051|duration }}"],
|
||||
['1y 3w 2d 3h 17m 31s', "{{ 33448651|duration }}"],
|
||||
|
||||
['17 minute(s)', "{{ 1051|duration([null, ' minute(s)', ' hour(s)', ' day(s)']) }}"],
|
||||
['3 hour(s)', "{{ 11851|duration([null, null, ' hour(s)']) }}"],
|
||||
['2 day(s)', "{{ 184651|duration([null, null, null, ' day(s)']) }}"],
|
||||
['3 week(s)', "{{ 1999051|duration([null, null, null, null, ' week(s)']) }}"],
|
||||
['1 year(s)', "{{ 33448651|duration([null, null, null, null, null, ' year(s)']) }}"],
|
||||
|
||||
['3u:17m', "{{ 11851|duration([null, 'm', 'u'], ':') }}"],
|
||||
['3:17h', "{{ 11851|duration([null, '', ''], ':') }}h"],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider durationProvider
|
||||
*
|
||||
* @param string $expect
|
||||
* @param string $template
|
||||
*/
|
||||
public function testDuration($expect, $template)
|
||||
{
|
||||
$this->assertRender($expect, $template);
|
||||
}
|
||||
|
||||
|
||||
public function ageProvider()
|
||||
{
|
||||
$time = time() - (((32 * 365) + 100) * 24 * 3600);
|
||||
$date = date('Y-m-d', $time);
|
||||
|
||||
return [
|
||||
['32', "{{ $time|age }}"],
|
||||
['32', "{{ '$date'|age }}"]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider ageProvider
|
||||
*
|
||||
* @param string $expect
|
||||
* @param string $template
|
||||
*/
|
||||
public function testAge($expect, $template)
|
||||
{
|
||||
$this->assertRender($expect, $template);
|
||||
}
|
||||
|
||||
|
||||
public function filterProvider()
|
||||
{
|
||||
return [
|
||||
['localdate'],
|
||||
['localtime'],
|
||||
['localdatetime'],
|
||||
['duration'],
|
||||
['age']
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider filterProvider
|
||||
*
|
||||
* @param string $filter
|
||||
*/
|
||||
public function testWithNull($filter)
|
||||
{
|
||||
$this->assertRender('-', '{{ null|' . $filter . '("//")|default("-") }}');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace Jasny\Twig;
|
||||
|
||||
use Jasny\Twig\PcreExtension;
|
||||
use Jasny\Twig\TestHelper;
|
||||
|
||||
/**
|
||||
* @covers Jasny\Twig\PcreExtension
|
||||
*/
|
||||
class PcreExtensionTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
use TestHelper;
|
||||
|
||||
protected function getExtension()
|
||||
{
|
||||
return new PcreExtension();
|
||||
}
|
||||
|
||||
|
||||
public function testGetName()
|
||||
{
|
||||
$this->assertEquals('jasny/pcre', $this->getExtension()->getName());
|
||||
}
|
||||
|
||||
|
||||
public function testQuote()
|
||||
{
|
||||
$this->assertRender('foo\(\)', '{{ "foo()"|preg_quote }}');
|
||||
}
|
||||
|
||||
public function testQuoteDelimiter()
|
||||
{
|
||||
$this->assertRender('foo\@bar', '{{ "foo@bar"|preg_quote("@") }}');
|
||||
}
|
||||
|
||||
public function testPregMatch()
|
||||
{
|
||||
$this->assertRender('YES', '{% if "foo"|preg_match("/oo/") %}YES{% else %}NO{% endif %}');
|
||||
}
|
||||
|
||||
public function testPregMatchNo()
|
||||
{
|
||||
$this->assertRender('NO', '{% if "fod"|preg_match("/oo/") %}YES{% else %}NO{% endif %}');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Twig_Error_Runtime
|
||||
*/
|
||||
public function testPregMatchError()
|
||||
{
|
||||
$this->render('{% if "fod"|preg_match("/o//o/") %}YES{% else %}NO{% endif %}');
|
||||
}
|
||||
|
||||
|
||||
public function testPregGet()
|
||||
{
|
||||
$this->assertRender('d', '{{ "food"|preg_get("/oo(.)/", 1) }}');
|
||||
}
|
||||
|
||||
public function testPregGetDefault()
|
||||
{
|
||||
$this->assertRender('ood', '{{ "food"|preg_get("/oo(.)/") }}');
|
||||
}
|
||||
|
||||
|
||||
public function testPregGetAll()
|
||||
{
|
||||
$this->assertRender('d|t|m', '{{ "food woot should doom"|preg_get_all("/oo(.)/", 1)|join("|") }}');
|
||||
}
|
||||
|
||||
public function testPregGetAllDefault()
|
||||
{
|
||||
$this->assertRender('ood|oot|oom', '{{ "food woot doom"|preg_get_all("/oo(.)/")|join("|") }}');
|
||||
}
|
||||
|
||||
|
||||
public function testPregGrep()
|
||||
{
|
||||
$this->assertRender(
|
||||
'world|how|you',
|
||||
'{{ ["hello", "sweet", "world", "how", "are", "you"]|preg_grep("/o./")|join("|") }}'
|
||||
);
|
||||
}
|
||||
|
||||
public function testPregGrepInvert()
|
||||
{
|
||||
$this->assertRender(
|
||||
'hello|sweet|are',
|
||||
'{{ ["hello", "sweet", "world", "how", "are", "you"]|preg_grep("/o./", "invert")|join("|") }}'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function testReplace()
|
||||
{
|
||||
$this->assertRender(
|
||||
'the quick brawen faxe jumped aveer the lazy dage',
|
||||
'{{ "the quick brown fox jumped over the lazy dog"|preg_replace("/o(\\\\w)/", "a$1e") }}'
|
||||
);
|
||||
}
|
||||
|
||||
public function testReplaceLimit()
|
||||
{
|
||||
$this->assertRender(
|
||||
'the quick brawen faxe jumped over the lazy dog',
|
||||
'{{ "the quick brown fox jumped over the lazy dog"|preg_replace("/o(\\\\w)/", "a$1e", 2) }}'
|
||||
);
|
||||
}
|
||||
|
||||
public function testReplaceWithArray()
|
||||
{
|
||||
$this->assertRender(
|
||||
'hello|sweet|wareld|hawe|are|yaue',
|
||||
'{{ ["hello", "sweet", "world", "how", "are", "you"]|preg_replace("/o(.)/", "a$1e")|join("|") }}'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Twig_Error_Runtime
|
||||
*/
|
||||
public function testReplaceAssertNoEval()
|
||||
{
|
||||
$this->render('{{ "foo"|preg_replace("/o/ei", "strtoupper") }}');
|
||||
}
|
||||
|
||||
|
||||
public function testFilter()
|
||||
{
|
||||
$this->assertRender(
|
||||
'wareld|hawe|yaue',
|
||||
'{{ ["hello", "sweet", "world", "how", "are", "you"]|preg_filter("/o(.)/", "a$1e")|join("|") }}'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Twig_Error_Runtime
|
||||
*/
|
||||
public function testFilterAssertNoEval()
|
||||
{
|
||||
$this->render('{{ "foo"|preg_filter("/o/ei", "strtoupper") }}');
|
||||
}
|
||||
|
||||
|
||||
public function testSplit()
|
||||
{
|
||||
$this->assertRender(
|
||||
'the quick br|n f| jumped |er the lazy d|',
|
||||
'{{ "the quick brown fox jumped over the lazy dog"|preg_split("/o(\\\\w)/", "a$1e")|join("|") }}'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function filterProvider()
|
||||
{
|
||||
return [
|
||||
['preg_quote'],
|
||||
['preg_match'],
|
||||
['preg_get'],
|
||||
['preg_get_all'],
|
||||
['preg_grep'],
|
||||
['preg_replace'],
|
||||
['preg_filter'],
|
||||
['preg_split']
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider filterProvider
|
||||
*
|
||||
* @param string $filter
|
||||
*/
|
||||
public function testWithNull($filter)
|
||||
{
|
||||
$this->assertRender('-', '{{ null|' . $filter . '("//")|default("-") }}');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
namespace Jasny\Twig;
|
||||
|
||||
use Jasny\Twig\TextExtension;
|
||||
use Jasny\Twig\TestHelper;
|
||||
|
||||
/**
|
||||
* @covers Jasny\Twig\TextExtension
|
||||
*/
|
||||
class TextExtensionTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
use TestHelper;
|
||||
|
||||
protected function getExtension()
|
||||
{
|
||||
return new TextExtension();
|
||||
}
|
||||
|
||||
|
||||
public function testParagraph()
|
||||
{
|
||||
$this->assertRender("<p>foo<br>\nbar</p>\n<p>monkey</p>", "{{ 'foo\nbar\n\nmonkey'|paragraph() }}");
|
||||
}
|
||||
|
||||
|
||||
public function testLine()
|
||||
{
|
||||
$this->assertRender("foo", "{{ 'foo\nbar\nbaz'|line() }}");
|
||||
}
|
||||
|
||||
public function testLineTwo()
|
||||
{
|
||||
$this->assertRender("bar", "{{ 'foo\nbar\nbaz'|line(2) }}");
|
||||
}
|
||||
|
||||
public function testLineToHigh()
|
||||
{
|
||||
$this->assertRender("", "{{ 'foo\nbar\nbaz'|line(100) }}");
|
||||
}
|
||||
|
||||
|
||||
public function testLess()
|
||||
{
|
||||
$this->assertRender("foo...", "{{ 'foo<!-- pagebreak -->baz'|less() }}");
|
||||
}
|
||||
|
||||
public function testLessCustom()
|
||||
{
|
||||
$this->assertRender("foo..", "{{ 'fooXbarXbaz'|less('..', 'X') }}");
|
||||
}
|
||||
|
||||
public function testLessNoPageBreak()
|
||||
{
|
||||
$this->assertRender("foo bar", "{{ 'foo bar'|less }}");
|
||||
}
|
||||
|
||||
|
||||
public function testTruncate()
|
||||
{
|
||||
$this->assertRender("foo...", "{{ 'foo bar baz'|truncate(6) }}");
|
||||
}
|
||||
|
||||
public function testTruncateCustom()
|
||||
{
|
||||
$this->assertRender("foo ..", "{{ 'foo bar baz'|truncate(6, '..') }}");
|
||||
}
|
||||
|
||||
public function testTruncateToHigh()
|
||||
{
|
||||
$this->assertRender("foo bar baz", "{{ 'foo bar baz'|truncate(100) }}");
|
||||
}
|
||||
|
||||
|
||||
public function testLinkify()
|
||||
{
|
||||
$this->assertRender(
|
||||
'<a href="http://www.example.com">www.example.com</a>, color.bar and '
|
||||
. '<a href="mailto:john@example.com">john@example.com</a>',
|
||||
'{{ "www.example.com, color.bar and john@example.com"|linkify }}'
|
||||
);
|
||||
}
|
||||
|
||||
public function testLinkifyAll()
|
||||
{
|
||||
$this->assertRender(
|
||||
'<a href="http://www.example.com">www.example.com</a>, <a href="http://color.bar">color.bar</a> and '
|
||||
. '<a href="mailto:john@example.com">john@example.com</a>',
|
||||
'{{ "www.example.com, color.bar and john@example.com"|linkify(["http", "mail"], [], "all") }}'
|
||||
);
|
||||
}
|
||||
|
||||
public function testLinkifyHttps()
|
||||
{
|
||||
$this->assertRender(
|
||||
'<a href="https://www.example.com">www.example.com</a>',
|
||||
'{{ "www.example.com"|linkify("https") }}'
|
||||
);
|
||||
}
|
||||
|
||||
public function testLinkifyMail()
|
||||
{
|
||||
$this->assertRender(
|
||||
'<a href="mailto:john@example.com">john@example.com</a> and '
|
||||
. '<a href="mailto:jeff@example.com">jeff@example.com</a>',
|
||||
'{{ "john@example.com and jeff@example.com"|linkify }}'
|
||||
);
|
||||
}
|
||||
|
||||
public function testLinkifyFtp()
|
||||
{
|
||||
$this->assertRender(
|
||||
'<a href="ftp://www.example.com">www.example.com</a>',
|
||||
'{{ "ftp://www.example.com"|linkify("ftp") }}'
|
||||
);
|
||||
}
|
||||
|
||||
public function testLinkifyFtpAll()
|
||||
{
|
||||
$this->assertRender(
|
||||
'<a href="ftp://www.example.com">www.example.com</a>',
|
||||
'{{ "www.example.com"|linkify("ftp", [], "all") }}'
|
||||
);
|
||||
}
|
||||
|
||||
public function testLinkifyOther()
|
||||
{
|
||||
$this->assertRender(
|
||||
'<a href="foo:abc.def.hif">abc.def.hif</a>',
|
||||
'{{ "foo:abc.def.hif"|linkify("foo") }}'
|
||||
);
|
||||
}
|
||||
|
||||
public function testLinkifyOtherAll()
|
||||
{
|
||||
$this->assertRender(
|
||||
'<a href="foo:abc.def.hif">abc.def.hif</a>',
|
||||
'{{ "abc.def.hif"|linkify("foo", [], "all") }}'
|
||||
);
|
||||
}
|
||||
|
||||
public function testLinkifyWithAttributes()
|
||||
{
|
||||
$this->assertRender(
|
||||
'<a foo="bar" color="blue" href="http://www.example.com">www.example.com</a> and '
|
||||
. '<a foo="bar" color="blue" href="mailto:john@example.com">john@example.com</a>',
|
||||
'{{ "www.example.com and john@example.com"|linkify(["http", "mail"], {foo: "bar", color: "blue"}) }}'
|
||||
);
|
||||
}
|
||||
|
||||
public function testLinkifyWithExistingLink()
|
||||
{
|
||||
$this->assertRender(
|
||||
'<a href="http://www.example.com">www.example.com</a> and '
|
||||
. '<a href="http://www.example.net">www.example.net</a>',
|
||||
'{{ "<a href=\\"http://www.example.com\\">www.example.com</a> and www.example.net"|linkify }}'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function filterProvider()
|
||||
{
|
||||
return [
|
||||
['paragraph'],
|
||||
['line'],
|
||||
['less'],
|
||||
['truncate'],
|
||||
['linkify']
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider filterProvider
|
||||
*
|
||||
* @param string $filter
|
||||
*/
|
||||
public function testWithNull($filter)
|
||||
{
|
||||
$this->assertRender('-', '{{ null|' . $filter . '("//")|default("-") }}');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Jasny\Twig;
|
||||
|
||||
/**
|
||||
* Helper methods for unit tests
|
||||
*/
|
||||
trait TestHelper
|
||||
{
|
||||
/**
|
||||
* Get the tested extension
|
||||
*
|
||||
* @return \Twig_Extension
|
||||
*/
|
||||
abstract protected function getExtension();
|
||||
|
||||
/**
|
||||
* Build the Twig environment for the template
|
||||
*
|
||||
* @param string $template
|
||||
* @return \Twig_Environment
|
||||
*/
|
||||
protected function buildEnv($template)
|
||||
{
|
||||
$loader = new \Twig_Loader_Array([
|
||||
'template' => $template,
|
||||
]);
|
||||
$twig = new \Twig_Environment($loader);
|
||||
|
||||
$twig->addExtension($this->getExtension());
|
||||
|
||||
return $twig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render template
|
||||
*
|
||||
* @param string $template
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
protected function render($template, array $data = [])
|
||||
{
|
||||
$twig = $this->buildEnv($template);
|
||||
$result = $twig->render('template', $data);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render template and assert equals
|
||||
*
|
||||
* @param string $expected
|
||||
* @param string $template
|
||||
* @param array $data
|
||||
*/
|
||||
protected function assertRender($expected, $template, array $data = [])
|
||||
{
|
||||
$result = $this->render($template, $data);
|
||||
|
||||
$this->assertEquals($expected, (string)$result);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user