Browse Source
* origin/master: (104 commits) Fixed typo in query builder and reverted changes to DbCache.php added note about enabling APC cache for CLI more assertions for cache test fixed dbcache multiget fixed Html test under Windows (line endings) fixed DB quoting typo create a webapp in test bootstrap updated expected exception message Added a basic app. initial implementation of asset command. script WIP script command WIP Fixed bug in yiic.php. Refactoring AssetConverter. adjusted default app layout refactoring and documentation for asset/script management. Finished asset converter. removed unnecessary code, added comment about displaying errors better handling of errors during rendering an error safer exception rendering turn asset manager into a getter. ...tags/2.0.0-beta
Carsten Brandt
12 years ago
140 changed files with 8890 additions and 5864 deletions
@ -0,0 +1,9 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
defined('YII_DEBUG') or define('YII_DEBUG', true); |
||||||
|
|
||||||
|
require(__DIR__ . '/../framework/yii.php'); |
||||||
|
|
||||||
|
$config = require(__DIR__ . '/protected/config/main.php'); |
||||||
|
$application = new yii\web\Application($config); |
||||||
|
$application->run(); |
@ -0,0 +1,15 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
return array( |
||||||
|
'id' => 'hello', |
||||||
|
'basePath' => dirname(__DIR__), |
||||||
|
'components' => array( |
||||||
|
'cache' => array( |
||||||
|
'class' => 'yii\caching\FileCache', |
||||||
|
), |
||||||
|
'user' => array( |
||||||
|
'class' => 'yii\web\User', |
||||||
|
'identityClass' => 'app\models\User', |
||||||
|
) |
||||||
|
), |
||||||
|
); |
@ -0,0 +1,22 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
class SiteController extends \yii\web\Controller |
||||||
|
{ |
||||||
|
public function actionIndex() |
||||||
|
{ |
||||||
|
echo $this->render('index'); |
||||||
|
} |
||||||
|
|
||||||
|
public function actionLogin() |
||||||
|
{ |
||||||
|
$user = app\models\User::findIdentity(100); |
||||||
|
Yii::$app->getUser()->login($user); |
||||||
|
Yii::$app->getResponse()->redirect(array('site/index')); |
||||||
|
} |
||||||
|
|
||||||
|
public function actionLogout() |
||||||
|
{ |
||||||
|
Yii::$app->getUser()->logout(); |
||||||
|
Yii::$app->getResponse()->redirect(array('site/index')); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace app\models; |
||||||
|
|
||||||
|
class User extends \yii\base\Object implements \yii\web\Identity |
||||||
|
{ |
||||||
|
public $id; |
||||||
|
public $name; |
||||||
|
public $authKey; |
||||||
|
|
||||||
|
private static $users = array( |
||||||
|
'100' => array( |
||||||
|
'id' => '100', |
||||||
|
'authKey' => 'test100key', |
||||||
|
'name' => 'admin', |
||||||
|
), |
||||||
|
'101' => array( |
||||||
|
'id' => '101', |
||||||
|
'authKey' => 'test101key', |
||||||
|
'name' => 'demo', |
||||||
|
), |
||||||
|
); |
||||||
|
|
||||||
|
public static function findIdentity($id) |
||||||
|
{ |
||||||
|
return isset(self::$users[$id]) ? new self(self::$users[$id]) : null; |
||||||
|
} |
||||||
|
|
||||||
|
public function getId() |
||||||
|
{ |
||||||
|
return $this->id; |
||||||
|
} |
||||||
|
|
||||||
|
public function getAuthKey() |
||||||
|
{ |
||||||
|
return $this->authKey; |
||||||
|
} |
||||||
|
|
||||||
|
public function validateAuthKey($authKey) |
||||||
|
{ |
||||||
|
return $this->authKey === $authKey; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* @var $this \yii\base\View |
||||||
|
* @var $content string |
||||||
|
*/ |
||||||
|
use yii\helpers\Html; |
||||||
|
?> |
||||||
|
<!DOCTYPE html> |
||||||
|
<html> |
||||||
|
<?php $this->beginPage(); ?> |
||||||
|
<head> |
||||||
|
<title><?php echo Html::encode($this->title); ?></title>
|
||||||
|
<?php $this->head(); ?> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<h1>Welcome</h1> |
||||||
|
<?php $this->beginBody(); ?> |
||||||
|
<?php echo $content; ?> |
||||||
|
<?php $this->endBody(); ?> |
||||||
|
</body> |
||||||
|
<?php $this->endPage(); ?> |
||||||
|
</html> |
@ -0,0 +1,17 @@ |
|||||||
|
<?php |
||||||
|
/** @var $this \yii\base\View */ |
||||||
|
|
||||||
|
use yii\helpers\Html; |
||||||
|
|
||||||
|
$this->title = 'Hello World'; |
||||||
|
|
||||||
|
$user = Yii::$app->getUser(); |
||||||
|
if ($user->isGuest) { |
||||||
|
echo Html::a('login', array('login')); |
||||||
|
} else { |
||||||
|
echo "You are logged in as " . $user->identity->name . "<br/>"; |
||||||
|
echo Html::a('logout', array('logout')); |
||||||
|
} |
||||||
|
?> |
||||||
|
|
||||||
|
|
@ -0,0 +1,20 @@ |
|||||||
|
#!/usr/bin/env php |
||||||
|
<?php |
||||||
|
/** |
||||||
|
* build script file. |
||||||
|
* |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
// fcgi doesn't have STDIN defined by default |
||||||
|
defined('STDIN') or define('STDIN', fopen('php://stdin', 'r')); |
||||||
|
|
||||||
|
require(__DIR__ . '/../framework/yii.php'); |
||||||
|
|
||||||
|
$id = 'yiic-build'; |
||||||
|
$basePath = __DIR__; |
||||||
|
|
||||||
|
$application = new yii\console\Application($id, $basePath); |
||||||
|
$application->run(); |
@ -0,0 +1,23 @@ |
|||||||
|
@echo off |
||||||
|
|
||||||
|
rem ------------------------------------------------------------- |
||||||
|
rem build script for Windows. |
||||||
|
rem |
||||||
|
rem This is the bootstrap script for running build on Windows. |
||||||
|
rem |
||||||
|
rem @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
rem @link http://www.yiiframework.com/ |
||||||
|
rem @copyright 2008 Yii Software LLC |
||||||
|
rem @license http://www.yiiframework.com/license/ |
||||||
|
rem @version $Id$ |
||||||
|
rem ------------------------------------------------------------- |
||||||
|
|
||||||
|
@setlocal |
||||||
|
|
||||||
|
set BUILD_PATH=%~dp0 |
||||||
|
|
||||||
|
if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe |
||||||
|
|
||||||
|
%PHP_COMMAND% "%BUILD_PATH%build" %* |
||||||
|
|
||||||
|
@endlocal |
@ -0,0 +1,276 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<!-- |
||||||
|
/** |
||||||
|
* Phing build file for Yii. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright 2008-2009 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
--> |
||||||
|
<project name="yii" basedir="." default="help"> |
||||||
|
<!-- task definitions --> |
||||||
|
<taskdef name="yii-init-build" classname="YiiInitTask" classpath="tasks" /> |
||||||
|
<!-- |
||||||
|
<taskdef name="yii-pear" classname="YiiPearTask" classpath="tasks"/> |
||||||
|
--> |
||||||
|
|
||||||
|
<!-- init yii.version, yii.revision and yii.winbuild --> |
||||||
|
<yii-init-build /> |
||||||
|
|
||||||
|
<!-- these are required external commands --> |
||||||
|
<property name="php" value="php" /> <!-- PHP parser --> |
||||||
|
<property name="hhc" value="hhc" /> <!-- compile phpdoc into CHM --> |
||||||
|
<property name="pdflatex" value="pdflatex" /> <!-- generates PDF from LaTex --> |
||||||
|
|
||||||
|
<property name="pkgname" value="${phing.project.name}-${yii.version}.${yii.revision}"/> |
||||||
|
<property name="docname" value="${phing.project.name}-docs-${yii.version}.${yii.revision}"/> |
||||||
|
<property name="pearname" value="${phing.project.name}-${yii.release}.tgz" /> |
||||||
|
|
||||||
|
<!-- directory definitions --> |
||||||
|
<property name="build.base.dir" value="release"/> |
||||||
|
<property name="build.dist.dir" value="${build.base.dir}/dist"/> |
||||||
|
<property name="build.src.dir" value="${build.base.dir}/${pkgname}"/> |
||||||
|
<property name="build.pear.src.dir" value="${build.src.dir}/framework" /> |
||||||
|
<property name="build.doc.dir" value="${build.base.dir}/${docname}"/> |
||||||
|
<property name="build.web.dir" value="${build.base.dir}/web"/> |
||||||
|
|
||||||
|
<tstamp> |
||||||
|
<format property="DATE" pattern="%b %e %Y" /> |
||||||
|
</tstamp> |
||||||
|
|
||||||
|
<if> |
||||||
|
<equals arg1="${yii.winbuild}" arg2="true"/> |
||||||
|
<then> |
||||||
|
<property name="build" value="build"/> |
||||||
|
</then> |
||||||
|
<else> |
||||||
|
<property name="build" value="php build"/> |
||||||
|
</else> |
||||||
|
</if> |
||||||
|
|
||||||
|
<!-- source files in the framework --> |
||||||
|
<fileset dir=".." id="framework"> |
||||||
|
<exclude name="**/.gitignore"/> |
||||||
|
<exclude name="**/*.bak"/> |
||||||
|
<exclude name="**/*~"/> |
||||||
|
<include name="framework/**/*"/> |
||||||
|
<include name="requirements/**/*"/> |
||||||
|
<include name="demos/**/*"/> |
||||||
|
<include name="CHANGELOG"/> |
||||||
|
<include name="UPGRADE"/> |
||||||
|
<include name="LICENSE"/> |
||||||
|
<include name="README"/> |
||||||
|
</fileset> |
||||||
|
|
||||||
|
<!-- doc files --> |
||||||
|
<fileset dir="../docs" id="docs"> |
||||||
|
<exclude name="**/.gitignore"/> |
||||||
|
<exclude name="**/*.bak"/> |
||||||
|
<exclude name="**/*~"/> |
||||||
|
<include name="guide/**/*"/> |
||||||
|
<include name="blog/**/*"/> |
||||||
|
</fileset> |
||||||
|
|
||||||
|
<fileset dir="../docs/guide" id="docs-guide"> |
||||||
|
<exclude name="**/.gitignore"/> |
||||||
|
<exclude name="**/*.bak"/> |
||||||
|
<exclude name="**/*~"/> |
||||||
|
<include name="**/*"/> |
||||||
|
</fileset> |
||||||
|
|
||||||
|
<fileset dir="../docs/blog" id="docs-blog"> |
||||||
|
<exclude name="**/.gitignore"/> |
||||||
|
<exclude name="**/*.bak"/> |
||||||
|
<exclude name="**/*~"/> |
||||||
|
<include name="**/*"/> |
||||||
|
</fileset> |
||||||
|
|
||||||
|
<fileset dir="." id="writables"> |
||||||
|
<include name="${build.src.dir}/**/runtime" /> |
||||||
|
<include name="${build.src.dir}/**/assets" /> |
||||||
|
<include name="${build.src.dir}/demos/**/data" /> |
||||||
|
</fileset> |
||||||
|
|
||||||
|
<fileset dir="." id="executables"> |
||||||
|
<include name="${build.src.dir}/**/yiic" /> |
||||||
|
</fileset> |
||||||
|
|
||||||
|
<target name="src" depends="sync"> |
||||||
|
<echo>Building package ${pkgname}...</echo> |
||||||
|
<echo>Copying files to build directory...</echo> |
||||||
|
<copy todir="${build.src.dir}"> |
||||||
|
<fileset refid="framework"/> |
||||||
|
</copy> |
||||||
|
|
||||||
|
<echo>Changing file permissions...</echo> |
||||||
|
<chmod mode="0777"> |
||||||
|
<fileset refid="writables" /> |
||||||
|
</chmod> |
||||||
|
<chmod mode="0755"> |
||||||
|
<fileset refid="executables" /> |
||||||
|
</chmod> |
||||||
|
|
||||||
|
<echo>Generating source release file...</echo> |
||||||
|
<mkdir dir="${build.dist.dir}" /> |
||||||
|
<if> |
||||||
|
<equals arg1="${yii.winbuild}" arg2="true"/> |
||||||
|
<then> |
||||||
|
<tar destfile="${build.dist.dir}/${pkgname}.tar.gz" compression="gzip"> |
||||||
|
<fileset dir="${build.base.dir}"> |
||||||
|
<include name="${pkgname}/**/*"/> |
||||||
|
</fileset> |
||||||
|
</tar> |
||||||
|
</then> |
||||||
|
<else> |
||||||
|
<exec command="tar czpf ${pkgname}.tar.gz ${pkgname}" dir="${build.base.dir}"/> |
||||||
|
<move file="${build.base.dir}/${pkgname}.tar.gz" todir="${build.dist.dir}" /> |
||||||
|
</else> |
||||||
|
</if> |
||||||
|
<zip destfile="${build.dist.dir}/${pkgname}.zip"> |
||||||
|
<fileset dir="${build.base.dir}"> |
||||||
|
<include name="${pkgname}/**/*"/> |
||||||
|
</fileset> |
||||||
|
</zip> |
||||||
|
</target> |
||||||
|
|
||||||
|
<target name="doc" depends="sync"> |
||||||
|
<echo>Building documentation...</echo> |
||||||
|
|
||||||
|
<echo>Building Guide PDF...</echo> |
||||||
|
<exec command="${build} guideLatex" dir="." passthru="true" /> |
||||||
|
<exec command="${pdflatex} guide.tex -interaction=nonstopmode -max-print-line=120" dir="commands/guide" passthru="true"/> |
||||||
|
<exec command="${pdflatex} guide.tex -interaction=nonstopmode -max-print-line=120" dir="commands/guide" passthru="true"/> |
||||||
|
<exec command="${pdflatex} guide.tex -interaction=nonstopmode -max-print-line=120" dir="commands/guide" passthru="true"/> |
||||||
|
<move file="commands/guide/guide.pdf" tofile="${build.doc.dir}/yii-guide-${yii.version}.pdf" /> |
||||||
|
|
||||||
|
<echo>Building Blog PDF...</echo> |
||||||
|
<exec command="${build} blogLatex" dir="." passthru="true" /> |
||||||
|
<exec command="${pdflatex} blog.tex -interaction=nonstopmode -max-print-line=120" dir="commands/blog" passthru="true"/> |
||||||
|
<exec command="${pdflatex} blog.tex -interaction=nonstopmode -max-print-line=120" dir="commands/blog" passthru="true"/> |
||||||
|
<exec command="${pdflatex} blog.tex -interaction=nonstopmode -max-print-line=120" dir="commands/blog" passthru="true"/> |
||||||
|
<move file="commands/blog/blog.pdf" tofile="${build.doc.dir}/yii-blog-${yii.version}.pdf" /> |
||||||
|
|
||||||
|
<echo>Building API...</echo> |
||||||
|
<exec command="${build} api ${build.doc.dir}" dir="." passthru="true" /> |
||||||
|
|
||||||
|
<!-- |
||||||
|
<echo>Building API CHM...</echo> |
||||||
|
<exec command="${hhc} ${build.doc.dir}/api/manual.hhp" /> |
||||||
|
<move file="${build.doc.dir}/api/manual.chm" tofile="${build.doc.dir}/yii-api-${yii.version}.chm" /> |
||||||
|
<delete> |
||||||
|
<fileset dir="${build.doc.dir}/api"> |
||||||
|
<include name="manual.*" /> |
||||||
|
</fileset> |
||||||
|
</delete> |
||||||
|
--> |
||||||
|
|
||||||
|
<echo>Generating doc release file...</echo> |
||||||
|
<mkdir dir="${build.dist.dir}" /> |
||||||
|
<tar destfile="${build.dist.dir}/${docname}.tar.gz" compression="gzip"> |
||||||
|
<fileset dir="${build.base.dir}"> |
||||||
|
<include name="${docname}/**/*"/> |
||||||
|
</fileset> |
||||||
|
</tar> |
||||||
|
<zip destfile="${build.dist.dir}/${docname}.zip"> |
||||||
|
<fileset dir="${build.base.dir}"> |
||||||
|
<include name="${docname}/**/*"/> |
||||||
|
</fileset> |
||||||
|
</zip> |
||||||
|
</target> |
||||||
|
|
||||||
|
<target name="web" depends="sync"> |
||||||
|
|
||||||
|
<echo>Building online API...</echo> |
||||||
|
<mkdir dir="${build.web.dir}/common/data/${yii.version}" /> |
||||||
|
<exec command="${build} api ${build.web.dir}/common/data/${yii.version} online" dir="." passthru="true" /> |
||||||
|
|
||||||
|
<echo>Copying tutorials...</echo> |
||||||
|
<copy todir="${build.web.dir}/common/data/${yii.version}/tutorials/guide"> |
||||||
|
<fileset refid="docs-guide"/> |
||||||
|
</copy> |
||||||
|
<copy todir="${build.web.dir}/common/data/${yii.version}/tutorials/blog"> |
||||||
|
<fileset refid="docs-blog"/> |
||||||
|
</copy> |
||||||
|
|
||||||
|
<echo>Copying release text files...</echo> |
||||||
|
<mkdir dir="${build.web.dir}/frontend/www/files" /> |
||||||
|
<copy file="../CHANGELOG" tofile="${build.web.dir}/frontend/www/files/CHANGELOG-${yii.version}.txt" /> |
||||||
|
<copy file="../UPGRADE" tofile="${build.web.dir}/frontend/www/files/UPGRADE-${yii.version}.txt" /> |
||||||
|
|
||||||
|
<echo> |
||||||
|
|
||||||
|
Finished building Web files. |
||||||
|
Please update yiisite/common/data/versions.php file with the following code: |
||||||
|
|
||||||
|
'1.1'=>array( |
||||||
|
'version'=>'${yii.version}', |
||||||
|
'revision'=>'${yii.revision}', |
||||||
|
'date'=>'${yii.date}', |
||||||
|
'latest'=>true, |
||||||
|
), |
||||||
|
|
||||||
|
</echo> |
||||||
|
</target> |
||||||
|
|
||||||
|
<target name="sync"> |
||||||
|
<echo>Synchronizing code changes for ${pkgname}...</echo> |
||||||
|
|
||||||
|
<echo>Building autoload map...</echo> |
||||||
|
<exec command="${build} autoload" dir="." passthru="true"/> |
||||||
|
|
||||||
|
<echo>Building yiilite.php...</echo> |
||||||
|
<exec command="${build} lite" dir="." passthru="true"/> |
||||||
|
</target> |
||||||
|
|
||||||
|
<target name="message"> |
||||||
|
<echo>Extracting i18n messages...</echo> |
||||||
|
<exec command="${build} message ../framework/messages/config.php" dir="." passthru="true"/> |
||||||
|
</target> |
||||||
|
|
||||||
|
<!-- |
||||||
|
<target name="pear" depends="clean,build"> |
||||||
|
<echo>Generating pear package for ${phing.project.name}-${yii.release}</echo> |
||||||
|
<mkdir dir="${build.dist.dir}" /> |
||||||
|
<yii-pear pkgdir="${build.pear.src.dir}" |
||||||
|
channel="pear.php.net" |
||||||
|
version="${yii.release}" |
||||||
|
state="stable" |
||||||
|
category="framework" |
||||||
|
package="${phing.project.name}" |
||||||
|
summary="Yii PHP Framework" |
||||||
|
pkgdescription="Yii PHP Framework: Best for Web 2.0 Development" |
||||||
|
notes="http://www.yiiframework.com/files/CHANGELOG-${yii.release}.txt" |
||||||
|
license="BSD" |
||||||
|
/> |
||||||
|
<exec command="pear package" dir="${build.pear.src.dir}" passthru="true" /> |
||||||
|
<move file="${build.pear.src.dir}/${pearname}" tofile="${build.dist.dir}/${pearname}" /> |
||||||
|
</target> |
||||||
|
--> |
||||||
|
|
||||||
|
<target name="clean"> |
||||||
|
<echo>Cleaning up the build...</echo> |
||||||
|
<delete dir="${build.base.dir}"/> |
||||||
|
</target> |
||||||
|
|
||||||
|
<target name="help"> |
||||||
|
<echo> |
||||||
|
|
||||||
|
Welcome to use Yii build script! |
||||||
|
-------------------------------- |
||||||
|
You may use the following command format to build a target: |
||||||
|
|
||||||
|
phing <target name> |
||||||
|
|
||||||
|
where <target name> can be one of the following: |
||||||
|
|
||||||
|
- sync : synchronize yiilite.php and YiiBase.php |
||||||
|
- message : extract i18n messages of the framework |
||||||
|
- src : build source release |
||||||
|
- doc : build documentation release (Windows only) |
||||||
|
- clean : clean up the build |
||||||
|
|
||||||
|
</echo> |
||||||
|
</target> |
||||||
|
</project> |
@ -0,0 +1,112 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
use yii\console\Controller; |
||||||
|
use yii\helpers\FileHelper; |
||||||
|
|
||||||
|
/** |
||||||
|
* http://www.unicode.org/cldr/charts/supplemental/language_plural_rules.html |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class LocaleController extends Controller |
||||||
|
{ |
||||||
|
public $defaultAction = 'plural'; |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates the plural rules data. |
||||||
|
* |
||||||
|
* This command will parse the plural rule XML file from CLDR and convert them |
||||||
|
* into appropriate PHP representation to support Yii message translation feature. |
||||||
|
* @param string $xmlFile the original plural rule XML file (from CLDR). This file may be found in |
||||||
|
* http://www.unicode.org/Public/cldr/latest/core.zip |
||||||
|
* Extract the zip file and locate the file "common/supplemental/plurals.xml". |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
public function actionPlural($xmlFile) |
||||||
|
{ |
||||||
|
if (!is_file($xmlFile)) { |
||||||
|
throw new Exception("The source plural rule file does not exist: $xmlFile"); |
||||||
|
} |
||||||
|
|
||||||
|
$xml = simplexml_load_file($xmlFile); |
||||||
|
|
||||||
|
$allRules = array(); |
||||||
|
|
||||||
|
$patterns = array( |
||||||
|
'/n in 0..1/' => '(n==0||n==1)', |
||||||
|
'/\s+is\s+not\s+/i' => '!=', //is not |
||||||
|
'/\s+is\s+/i' => '==', //is |
||||||
|
'/n\s+mod\s+(\d+)/i' => 'fmod(n,$1)', //mod (CLDR's "mod" is "fmod()", not "%") |
||||||
|
'/^(.*?)\s+not\s+in\s+(\d+)\.\.(\d+)/i' => '!in_array($1,range($2,$3))', //not in |
||||||
|
'/^(.*?)\s+in\s+(\d+)\.\.(\d+)/i' => 'in_array($1,range($2,$3))', //in |
||||||
|
'/^(.*?)\s+not\s+within\s+(\d+)\.\.(\d+)/i' => '($1<$2||$1>$3)', //not within |
||||||
|
'/^(.*?)\s+within\s+(\d+)\.\.(\d+)/i' => '($1>=$2&&$1<=$3)', //within |
||||||
|
); |
||||||
|
foreach ($xml->plurals->pluralRules as $node) { |
||||||
|
$attributes = $node->attributes(); |
||||||
|
$locales = explode(' ', $attributes['locales']); |
||||||
|
$rules = array(); |
||||||
|
|
||||||
|
if (!empty($node->pluralRule)) { |
||||||
|
foreach ($node->pluralRule as $rule) { |
||||||
|
$expr_or = preg_split('/\s+or\s+/i', $rule); |
||||||
|
foreach ($expr_or as $key_or => $val_or) { |
||||||
|
$expr_and = preg_split('/\s+and\s+/i', $val_or); |
||||||
|
$expr_and = preg_replace(array_keys($patterns), array_values($patterns), $expr_and); |
||||||
|
$expr_or[$key_or] = implode('&&', $expr_and); |
||||||
|
} |
||||||
|
$expr = preg_replace('/\\bn\\b/', '$n', implode('||', $expr_or)); |
||||||
|
$rules[] = preg_replace_callback('/range\((\d+),(\d+)\)/', function ($matches) { |
||||||
|
if ($matches[2] - $matches[1] <= 5) { |
||||||
|
return 'array(' . implode(',', range($matches[1], $matches[2])) . ')'; |
||||||
|
} else { |
||||||
|
return $matches[0]; |
||||||
|
} |
||||||
|
}, $expr); |
||||||
|
|
||||||
|
} |
||||||
|
foreach ($locales as $locale) { |
||||||
|
$allRules[$locale] = $rules; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
// hard fix for "br": the rule is too complex |
||||||
|
$allRules['br'] = array( |
||||||
|
0 => 'fmod($n,10)==1&&!in_array(fmod($n,100),array(11,71,91))', |
||||||
|
1 => 'fmod($n,10)==2&&!in_array(fmod($n,100),array(12,72,92))', |
||||||
|
2 => 'in_array(fmod($n,10),array(3,4,9))&&!in_array(fmod($n,100),array_merge(range(10,19),range(70,79),range(90,99)))', |
||||||
|
3 => 'fmod($n,1000000)==0&&$n!=0', |
||||||
|
); |
||||||
|
if (preg_match('/\d+/', $xml->version['number'], $matches)) { |
||||||
|
$revision = $matches[0]; |
||||||
|
} else { |
||||||
|
$revision = -1; |
||||||
|
} |
||||||
|
|
||||||
|
echo "<?php\n";
|
||||||
|
echo <<<EOD |
||||||
|
/** |
||||||
|
* Plural rules. |
||||||
|
* |
||||||
|
* This file is automatically generated by the "yiic locale/plural" command under the "build" folder. |
||||||
|
* Do not modify it directly. |
||||||
|
* |
||||||
|
* The original plural rule data used for generating this file has the following copyright terms: |
||||||
|
* |
||||||
|
* Copyright © 1991-2007 Unicode, Inc. All rights reserved. |
||||||
|
* Distributed under the Terms of Use in http://www.unicode.org/copyright.html. |
||||||
|
* |
||||||
|
* @revision $revision (of the original plural file) |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
EOD; |
||||||
|
echo "\nreturn " . var_export($allRules, true) . ';'; |
||||||
|
} |
||||||
|
} |
@ -1,328 +0,0 @@ |
|||||||
Yii2 code standard |
|
||||||
================== |
|
||||||
|
|
||||||
This code standard is used for all the Yii2 core classes and can be applied to |
|
||||||
your application in order to achieve consistency among your team. Also it will |
|
||||||
help in case you want to opensource code. |
|
||||||
|
|
||||||
PHP file formatting |
|
||||||
------------------- |
|
||||||
|
|
||||||
### General |
|
||||||
|
|
||||||
- Do not end file with `?>` if it contains PHP code only. |
|
||||||
- Do not use `<?`. Use `<?php` instead. |
|
||||||
- Files should be encoded in UTF-8. |
|
||||||
- Any file that contains PHP code should end with the extension `.php`. |
|
||||||
- Do not add trailing spaces to the end of the lines. |
|
||||||
|
|
||||||
#### Indentation |
|
||||||
|
|
||||||
All code must be indented with tabs. That includes both PHP and JavaScript code. |
|
||||||
|
|
||||||
#### Maximum Line Length |
|
||||||
|
|
||||||
We're not strictly limiting maximum line length but sticking to 80 characters |
|
||||||
where possible. |
|
||||||
|
|
||||||
### PHP types |
|
||||||
|
|
||||||
All PHP types and values should be used lowercase. That includes `true`, `false`, |
|
||||||
`null` and `array`. |
|
||||||
|
|
||||||
### Strings |
|
||||||
|
|
||||||
- If string doesn't contain variables or single quotes, use single quotes. |
|
||||||
|
|
||||||
~~~ |
|
||||||
$str = 'Like this.'; |
|
||||||
~~~ |
|
||||||
|
|
||||||
- If string contains single quotes you can use double quotes to avoid extra escaping. |
|
||||||
- You can use the following forms of variable substitution: |
|
||||||
|
|
||||||
~~~ |
|
||||||
$str1 = "Hello $username!"; |
|
||||||
$str2 = "Hello {$username}!"; |
|
||||||
~~~ |
|
||||||
|
|
||||||
The following is not permitted: |
|
||||||
|
|
||||||
~~~ |
|
||||||
$str3 = "Hello ${username}!"; |
|
||||||
~~~ |
|
||||||
|
|
||||||
### String concatenation |
|
||||||
|
|
||||||
Add spaces around dot when concatenating strings: |
|
||||||
|
|
||||||
~~~ |
|
||||||
$name = 'Yii' . ' Framework'; |
|
||||||
~~~ |
|
||||||
|
|
||||||
When string is long format is the following: |
|
||||||
|
|
||||||
~~~ |
|
||||||
$sql = "SELECT *" |
|
||||||
. "FROM `post` " |
|
||||||
. "WHERE `id` = 121 "; |
|
||||||
~~~ |
|
||||||
|
|
||||||
|
|
||||||
### Numerically indexed arrays |
|
||||||
|
|
||||||
- Do not use negative numbers as array indexes. |
|
||||||
|
|
||||||
Use the following formatting when declaring array: |
|
||||||
|
|
||||||
~~~ |
|
||||||
$arr = array(3, 14, 15, 'Yii', 'Framework'); |
|
||||||
~~~ |
|
||||||
|
|
||||||
If there are too many elements for a single line: |
|
||||||
|
|
||||||
~~~ |
|
||||||
$arr = array( |
|
||||||
3, 14, 15, |
|
||||||
92, 6, $test, |
|
||||||
'Yii', 'Framework', |
|
||||||
); |
|
||||||
~~~ |
|
||||||
|
|
||||||
### Associative arrays |
|
||||||
|
|
||||||
Use the following format for associative arrays: |
|
||||||
|
|
||||||
~~~ |
|
||||||
$config = array( |
|
||||||
'name' => 'Yii', |
|
||||||
'options' => array( |
|
||||||
'usePHP' => true, |
|
||||||
), |
|
||||||
); |
|
||||||
~~~ |
|
||||||
|
|
||||||
### Classes |
|
||||||
|
|
||||||
- Classes should be named using `CamelCase`. |
|
||||||
- The brace should always be written on the line underneath the class name. |
|
||||||
- Every class must have a documentation block that conforms to the PHPDoc. |
|
||||||
- All code in a class must be indented with a single tab. |
|
||||||
- There should be only one class in a single PHP file. |
|
||||||
- All classes should be namespaced. |
|
||||||
- Class name should match file name. Class namespace should match directory structure. |
|
||||||
|
|
||||||
~~~ |
|
||||||
/** |
|
||||||
* Documentation |
|
||||||
*/ |
|
||||||
class MyClass extends \yii\Object implements MyInterface |
|
||||||
{ |
|
||||||
// code |
|
||||||
} |
|
||||||
~~~ |
|
||||||
|
|
||||||
|
|
||||||
### Class members and variables |
|
||||||
|
|
||||||
- When declaring public class members specify `public` keyword explicitly. |
|
||||||
- Variables should be declared at the top of the class before any method declarations. |
|
||||||
- Private and protected variables should be named like `$_varName`. |
|
||||||
- Public class members and standalone variables should be named using `$camelCase` |
|
||||||
with first letter lowercase. |
|
||||||
- Use descriptive names. Variables such as `$i` and `$j` are better not to be used. |
|
||||||
|
|
||||||
### Constants |
|
||||||
|
|
||||||
Both class level constants and global constants should be named in uppercase. Words |
|
||||||
are separated by underscore. |
|
||||||
|
|
||||||
~~~ |
|
||||||
class User { |
|
||||||
const STATUS_ACTIVE = 1; |
|
||||||
const STATUS_BANNED = 2; |
|
||||||
} |
|
||||||
~~~ |
|
||||||
|
|
||||||
It's preferable to define class level constants rather than global ones. |
|
||||||
|
|
||||||
### Functions and methods |
|
||||||
|
|
||||||
- Functions and methods should be named using `camelCase` with first letter lowercase. |
|
||||||
- Name should be descriptive by itself indicating the purpose of the function. |
|
||||||
- Class methods should always declare visibility using `private`, `protected` and |
|
||||||
`public` modifiers. `var` is not allowed. |
|
||||||
- Opening brace of a function should be on the line after the function declaration. |
|
||||||
|
|
||||||
~~~ |
|
||||||
/** |
|
||||||
* Documentation |
|
||||||
*/ |
|
||||||
class Foo |
|
||||||
{ |
|
||||||
/** |
|
||||||
* Documentation |
|
||||||
*/ |
|
||||||
public function bar() |
|
||||||
{ |
|
||||||
// code |
|
||||||
return $value; |
|
||||||
} |
|
||||||
} |
|
||||||
~~~ |
|
||||||
|
|
||||||
Use type hinting where possible: |
|
||||||
|
|
||||||
~~~ |
|
||||||
public function __construct(CDbConnection $connection) |
|
||||||
{ |
|
||||||
$this->connection = $connection; |
|
||||||
} |
|
||||||
~~~ |
|
||||||
|
|
||||||
### Function and method calls |
|
||||||
|
|
||||||
~~~ |
|
||||||
doIt(2, 3); |
|
||||||
|
|
||||||
doIt(array( |
|
||||||
'a' => 'b', |
|
||||||
)); |
|
||||||
|
|
||||||
doIt('a', array( |
|
||||||
'a' => 'b', |
|
||||||
)); |
|
||||||
~~~ |
|
||||||
|
|
||||||
### Control statements |
|
||||||
|
|
||||||
- Control statement condition must have single space before and after parenthesis. |
|
||||||
- Operators inside of parenthesis should be separated by spaces. |
|
||||||
- Opening brace is on the same line. |
|
||||||
- Closing brace is on a new line. |
|
||||||
- Always use braces for single line statements. |
|
||||||
|
|
||||||
~~~ |
|
||||||
if ($event === null) { |
|
||||||
return new Event(); |
|
||||||
} elseif ($event instanceof CoolEvent) { |
|
||||||
return $event->instance(); |
|
||||||
} else { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
// the following is NOT allowed: |
|
||||||
if(!$model) |
|
||||||
throw new Exception('test'); |
|
||||||
~~~ |
|
||||||
|
|
||||||
|
|
||||||
### Switch |
|
||||||
|
|
||||||
Use the following formatting for switch: |
|
||||||
|
|
||||||
~~~ |
|
||||||
switch ($this->phpType) { |
|
||||||
case 'string': |
|
||||||
$a = (string)$value; |
|
||||||
break; |
|
||||||
case 'integer': |
|
||||||
case 'int': |
|
||||||
$a = (integer)$value; |
|
||||||
break; |
|
||||||
case 'boolean': |
|
||||||
$a = (boolean)$value; |
|
||||||
break; |
|
||||||
default: |
|
||||||
$a = null; |
|
||||||
} |
|
||||||
~~~ |
|
||||||
|
|
||||||
### Code documentation |
|
||||||
|
|
||||||
- Refer ot [phpDoc](http://phpdoc.org/) for documentation syntax. |
|
||||||
- Code without documentation is not allowed. |
|
||||||
- All class files must contain a "file-level" docblock at the top of each file |
|
||||||
and a "class-level" docblock immediately above each class. |
|
||||||
- There is no need to use `@return` if method does return nothing. |
|
||||||
|
|
||||||
#### File |
|
||||||
|
|
||||||
~~~ |
|
||||||
<?php |
|
||||||
/** |
|
||||||
* @link http://www.yiiframework.com/ |
|
||||||
* @copyright Copyright (c) 2008 Yii Software LLC |
|
||||||
* @license http://www.yiiframework.com/license/ |
|
||||||
*/ |
|
||||||
~~~ |
|
||||||
|
|
||||||
#### Class |
|
||||||
|
|
||||||
~~~ |
|
||||||
/** |
|
||||||
* Component is the base class that provides the *property*, *event* and *behavior* features. |
|
||||||
* |
|
||||||
* @include @yii/docs/base-Component.md |
|
||||||
* |
|
||||||
* @author Qiang Xue <qiang.xue@gmail.com> |
|
||||||
* @since 2.0 |
|
||||||
*/ |
|
||||||
class Component extends \yii\base\Object |
|
||||||
~~~ |
|
||||||
|
|
||||||
|
|
||||||
#### Function / method |
|
||||||
|
|
||||||
~~~ |
|
||||||
/** |
|
||||||
* Returns the list of attached event handlers for an event. |
|
||||||
* You may manipulate the returned [[Vector]] object by adding or removing handlers. |
|
||||||
* For example, |
|
||||||
* |
|
||||||
* ~~~ |
|
||||||
* $component->getEventHandlers($eventName)->insertAt(0, $eventHandler); |
|
||||||
* ~~~ |
|
||||||
* |
|
||||||
* @param string $name the event name |
|
||||||
* @return Vector list of attached event handlers for the event |
|
||||||
* @throws Exception if the event is not defined |
|
||||||
*/ |
|
||||||
public function getEventHandlers($name) |
|
||||||
{ |
|
||||||
if (!isset($this->_e[$name])) { |
|
||||||
$this->_e[$name] = new Vector; |
|
||||||
} |
|
||||||
$this->ensureBehaviors(); |
|
||||||
return $this->_e[$name]; |
|
||||||
} |
|
||||||
~~~ |
|
||||||
|
|
||||||
#### Comments |
|
||||||
|
|
||||||
- One-line comments should be started with `//` and not `#`. |
|
||||||
- One-line comment should be on its own line. |
|
||||||
|
|
||||||
Yii application naming conventions |
|
||||||
---------------------------------- |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Other library and framework standards |
|
||||||
------------------------------------- |
|
||||||
|
|
||||||
It's good to be consistent with other frameworks and libraries whose components |
|
||||||
will be possibly used with Yii2. That's why when there are no objective reasons |
|
||||||
to use different style we should use one that's common among most of the popular |
|
||||||
libraries and frameworks. |
|
||||||
|
|
||||||
That's not only about PHP but about JavaScript as well. Since we're using jQuery |
|
||||||
a lot it's better to be consistent with its style as well. |
|
||||||
|
|
||||||
Application style consistency is much more important than consistency with other frameworks and libraries. |
|
||||||
|
|
||||||
- [Symfony 2](http://symfony.com/doc/current/contributing/code/standards.html) |
|
||||||
- [Zend Framework 1](http://framework.zend.com/manual/en/coding-standard.coding-style.html) |
|
||||||
- [Zend Framework 2](http://framework.zend.com/wiki/display/ZFDEV2/Coding+Standards) |
|
||||||
- [Pear](http://pear.php.net/manual/en/standards.php) |
|
||||||
- [jQuery](http://docs.jquery.com/JQuery_Core_Style_Guidelines) |
|
Before Width: | Height: | Size: 298 KiB |
Before Width: | Height: | Size: 16 KiB |
@ -1,192 +0,0 @@ |
|||||||
Alex's Code Review, 2011.11.12 |
|
||||||
============================== |
|
||||||
|
|
||||||
Overall hierarchy |
|
||||||
------------------ |
|
||||||
|
|
||||||
Generally is OK. Like that `Object` and `Component` are now separated. |
|
||||||
I've generated 2 diagrams under `docs/` to see it better as a whole. |
|
||||||
|
|
||||||
> The purpose of separating `Object` from `Component` is to make `Object` |
|
||||||
> a super-light base class that supports properties defined by getter/setters. |
|
||||||
> Note that `Component` is a bit of heavy because it uses two extra member |
|
||||||
> variables to support events and behaviors. |
|
||||||
|
|
||||||
|
|
||||||
Object |
|
||||||
------ |
|
||||||
|
|
||||||
### property feature |
|
||||||
|
|
||||||
Is it OK that `canGetProperty` and `canSetProperty` will return `false` for real |
|
||||||
class members? |
|
||||||
|
|
||||||
> Added $checkVar parameter |
|
||||||
|
|
||||||
### callbacks and expressions |
|
||||||
|
|
||||||
We're using 5.3. What's the reason to support `eval()` in `evaluateExpression` if |
|
||||||
we have anonymous functions? Is that for storing code as string inside of DB (RBAC)? |
|
||||||
|
|
||||||
If we're going to get rid of `eval()`, cosider remaning method to something about callback. |
|
||||||
If not then we definitely need to use anonymous functions in API docs and the guide |
|
||||||
where possible. |
|
||||||
|
|
||||||
> The purpose of evaluateExpression() is to provide a way of evaluating a PHP expression |
|
||||||
> in the context of an object. Will remove it before release if we find no use of it. |
|
||||||
|
|
||||||
>> mdomba: |
|
||||||
>> As eval() is controversial, and anonymous functions can replace all Yii 1 usage of eval() |
|
||||||
>> how about removing it from the beginning and add it only if we find it necessary. |
|
||||||
>> This way we would not be tempted to stick with eval() and will be forced to first try to find alternatives |
|
||||||
|
|
||||||
### Object::create() |
|
||||||
|
|
||||||
#### `__construct` issue |
|
||||||
|
|
||||||
Often a class doesn't have `__construct` implementation and `stdClass` doesn't have |
|
||||||
default one either but Object::create() always expects constructor to be |
|
||||||
defined. See `ObjectTest`. Either `method_exists` call or `Object::__construct` needed. |
|
||||||
|
|
||||||
> Added Object::__construct. |
|
||||||
|
|
||||||
#### How to support object factory like we do with CWidgetFactory? |
|
||||||
|
|
||||||
~~~ |
|
||||||
class ObjectConfig |
|
||||||
{ |
|
||||||
public function configure($class) |
|
||||||
{ |
|
||||||
$config = $this->load($class); |
|
||||||
// apply config to $class |
|
||||||
} |
|
||||||
|
|
||||||
private function load($class) |
|
||||||
{ |
|
||||||
// get class properties from a config file |
|
||||||
// in this method we need to walk all the |
|
||||||
// inheritance hierarchy down to Object itself |
|
||||||
return array( |
|
||||||
'property' => 'value', |
|
||||||
// … |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
~~~ |
|
||||||
|
|
||||||
Then we need to add `__construct` to `Object` (or implement `Initalbe`): |
|
||||||
|
|
||||||
~~~ |
|
||||||
class Object |
|
||||||
{ |
|
||||||
public function __construct() |
|
||||||
{ |
|
||||||
$conf = new ObjectConfig(); |
|
||||||
$conf->configure($this); |
|
||||||
} |
|
||||||
} |
|
||||||
~~~ |
|
||||||
|
|
||||||
This way we'll be able to set defaults for any object. |
|
||||||
|
|
||||||
> The key issue here is about how to process the config file. Clearly, we cannot |
|
||||||
> do this for every type of component because it would mean an extra file access |
|
||||||
> for every component type |
|
||||||
|
|
||||||
#### Do we need to support lazy class injection? |
|
||||||
|
|
||||||
Currently there's no way to lazy-inject class into another class property via |
|
||||||
config. Do we need it? If yes then we can probably extend component config to support |
|
||||||
the following: |
|
||||||
|
|
||||||
~~~ |
|
||||||
class Foo extends Object |
|
||||||
{ |
|
||||||
public $prop; |
|
||||||
} |
|
||||||
|
|
||||||
class Bar extends Object |
|
||||||
{ |
|
||||||
public $prop; |
|
||||||
} |
|
||||||
|
|
||||||
$config = array( |
|
||||||
'prop' => array( |
|
||||||
'class' => 'Bar', |
|
||||||
'prop' => 'Hello!', |
|
||||||
), |
|
||||||
); |
|
||||||
|
|
||||||
$foo = Foo::create($config); |
|
||||||
echo $foo->bar->prop; |
|
||||||
// will output Hello! |
|
||||||
~~~ |
|
||||||
|
|
||||||
Should it support infinite nesting level? |
|
||||||
|
|
||||||
> I don't think we need this. Foo::$prop cannot be an object unless it needs it to be. |
|
||||||
> In that case, it can be defined with a setter in which it can handle the object creation |
|
||||||
> based on a configuration array. This is a bit inconvenient, but I think such usage is |
|
||||||
> not very common. |
|
||||||
|
|
||||||
### Why `Event` is `Object`? |
|
||||||
|
|
||||||
There's no need to extend from `Object`. Is there a plan to use `Object` features |
|
||||||
later? |
|
||||||
|
|
||||||
> To use properties defined via getter/setter. |
|
||||||
|
|
||||||
|
|
||||||
Behaviors |
|
||||||
--------- |
|
||||||
|
|
||||||
Overall I wasn't able to use behaviors. See `BehaviorTest`. |
|
||||||
|
|
||||||
### Should behaviors be able to define events for owner components? |
|
||||||
|
|
||||||
Why not? Should be a very good feature in order to make behaviors customizable. |
|
||||||
|
|
||||||
> It's a bit hard to implement it efficiently. I tend not to support it for now |
|
||||||
> unless enough people are requesting for it. |
|
||||||
|
|
||||||
### Multiple behaviors can be attached to the same component |
|
||||||
|
|
||||||
What if we'll have multiple methods / properties / events with the same name? |
|
||||||
|
|
||||||
> The first one takes precedence. This is the same as we do in 1.1. |
|
||||||
|
|
||||||
### How to use Behavior::attach? |
|
||||||
|
|
||||||
Looks like it is used by `Component::attachBehavior` but can't be used without it. |
|
||||||
Why it's public then? Can we move it to `Component?` |
|
||||||
|
|
||||||
> It's public because it is called by Component. It is in Behavior such that |
|
||||||
> it can be overridden by behavior classes to customize the attach process. |
|
||||||
|
|
||||||
Events |
|
||||||
------ |
|
||||||
|
|
||||||
Class itself looks OK. Component part is OK as well but I've not tested |
|
||||||
it carefully. Overall it seems concept is the same as in Yii1. |
|
||||||
|
|
||||||
### Event declaration: the on-method is mostly repetitive for every event. Should we choose a different way of declaring events? |
|
||||||
|
|
||||||
Maybe. People complained previously about too many code for event declaration. |
|
||||||
|
|
||||||
### Should we implement some additional event mechanism, such as global events? |
|
||||||
|
|
||||||
Why use two different implementations in a single application? |
|
||||||
|
|
||||||
Exceptions |
|
||||||
---------- |
|
||||||
|
|
||||||
- Should we convert all errors, warnings and notices to exceptions? |
|
||||||
|
|
||||||
> I think not. We used to do this in early versions of 1.0. We found sometimes |
|
||||||
> very mysterious things would happen which makes error fixing harder rather than |
|
||||||
> easier. |
|
||||||
|
|
||||||
Coding style |
|
||||||
------------ |
|
||||||
|
|
||||||
See `docs/code_style.md`. |
|
@ -0,0 +1,5 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
return array( |
||||||
|
'basePath' => __DIR__ . '/web/assets', |
||||||
|
); |
@ -0,0 +1,26 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\base; |
||||||
|
|
||||||
|
/** |
||||||
|
* UnknownClassException represents an exception caused by accessing an unknown class. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class UnknownClassException extends Exception |
||||||
|
{ |
||||||
|
/** |
||||||
|
* @return string the user-friendly name of this exception |
||||||
|
*/ |
||||||
|
public function getName() |
||||||
|
{ |
||||||
|
return \Yii::t('yii|Unknown Class'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,44 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\base; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class ViewEvent extends Event |
||||||
|
{ |
||||||
|
/** |
||||||
|
* @var string the rendering result of [[View::renderFile()]]. |
||||||
|
* Event handlers may modify this property and the modified output will be |
||||||
|
* returned by [[View::renderFile()]]. This property is only used |
||||||
|
* by [[View::EVENT_AFTER_RENDER]] event. |
||||||
|
*/ |
||||||
|
public $output; |
||||||
|
/** |
||||||
|
* @var string the view file path that is being rendered by [[View::renderFile()]]. |
||||||
|
*/ |
||||||
|
public $viewFile; |
||||||
|
/** |
||||||
|
* @var boolean whether to continue rendering the view file. Event handlers of |
||||||
|
* [[View::EVENT_BEFORE_RENDER]] may set this property to decide whether |
||||||
|
* to continue rendering the current view file. |
||||||
|
*/ |
||||||
|
public $isValid = true; |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructor. |
||||||
|
* @param string $viewFile the view file path that is being rendered by [[View::renderFile()]]. |
||||||
|
* @param array $config name-value pairs that will be used to initialize the object properties |
||||||
|
*/ |
||||||
|
public function __construct($viewFile, $config = array()) |
||||||
|
{ |
||||||
|
$this->viewFile = $viewFile; |
||||||
|
parent::__construct($config); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,353 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\console\controllers; |
||||||
|
|
||||||
|
use Yii; |
||||||
|
use yii\console\Exception; |
||||||
|
use yii\console\Controller; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class AssetController extends Controller |
||||||
|
{ |
||||||
|
public $defaultAction = 'compress'; |
||||||
|
|
||||||
|
public $bundles = array(); |
||||||
|
public $extensions = array(); |
||||||
|
/** |
||||||
|
* @var array |
||||||
|
* ~~~ |
||||||
|
* 'all' => array( |
||||||
|
* 'css' => 'all.css', |
||||||
|
* 'js' => 'js.css', |
||||||
|
* 'depends' => array( ... ), |
||||||
|
* ) |
||||||
|
* ~~~ |
||||||
|
*/ |
||||||
|
public $targets = array(); |
||||||
|
public $assetManager = array(); |
||||||
|
public $jsCompressor = 'java -jar compiler.jar --js {from} --js_output_file {to}'; |
||||||
|
public $cssCompressor = 'java -jar yuicompressor.jar {from} -o {to}'; |
||||||
|
|
||||||
|
public function actionCompress($configFile, $bundleFile) |
||||||
|
{ |
||||||
|
$this->loadConfiguration($configFile); |
||||||
|
$bundles = $this->loadBundles($this->bundles, $this->extensions); |
||||||
|
$targets = $this->loadTargets($this->targets, $bundles); |
||||||
|
$this->publishBundles($bundles, $this->publishOptions); |
||||||
|
$timestamp = time(); |
||||||
|
foreach ($targets as $target) { |
||||||
|
if (!empty($target->js)) { |
||||||
|
$this->buildTarget($target, 'js', $bundles, $timestamp); |
||||||
|
} |
||||||
|
if (!empty($target->css)) { |
||||||
|
$this->buildTarget($target, 'css', $bundles, $timestamp); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
$targets = $this->adjustDependency($targets, $bundles); |
||||||
|
$this->saveTargets($targets, $bundleFile); |
||||||
|
} |
||||||
|
|
||||||
|
protected function loadConfiguration($configFile) |
||||||
|
{ |
||||||
|
foreach (require($configFile) as $name => $value) { |
||||||
|
if (property_exists($this, $name)) { |
||||||
|
$this->$name = $value; |
||||||
|
} else { |
||||||
|
throw new Exception("Unknown configuration option: $name"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!isset($this->assetManager['basePath'])) { |
||||||
|
throw new Exception("Please specify 'basePath' for the 'assetManager' option."); |
||||||
|
} |
||||||
|
if (!isset($this->assetManager['baseUrl'])) { |
||||||
|
throw new Exception("Please specify 'baseUrl' for the 'assetManager' option."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected function loadBundles($bundles, $extensions) |
||||||
|
{ |
||||||
|
$result = array(); |
||||||
|
foreach ($bundles as $name => $bundle) { |
||||||
|
$bundle['class'] = 'yii\\web\\AssetBundle'; |
||||||
|
$result[$name] = Yii::createObject($bundle); |
||||||
|
} |
||||||
|
foreach ($extensions as $path) { |
||||||
|
$manifest = $path . '/assets.php'; |
||||||
|
if (!is_file($manifest)) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
foreach (require($manifest) as $name => $bundle) { |
||||||
|
if (!isset($result[$name])) { |
||||||
|
$bundle['class'] = 'yii\\web\\AssetBundle'; |
||||||
|
$result[$name] = Yii::createObject($bundle); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return $result; |
||||||
|
} |
||||||
|
|
||||||
|
protected function loadTargets($targets, $bundles) |
||||||
|
{ |
||||||
|
// build the dependency order of bundles |
||||||
|
$registered = array(); |
||||||
|
foreach ($bundles as $name => $bundle) { |
||||||
|
$this->registerBundle($bundles, $name, $registered); |
||||||
|
} |
||||||
|
$bundleOrders = array_combine(array_keys($registered), range(0, count($bundles) - 1)); |
||||||
|
|
||||||
|
// fill up the target which has empty 'depends'. |
||||||
|
$referenced = array(); |
||||||
|
foreach ($targets as $name => $target) { |
||||||
|
if (empty($target['depends'])) { |
||||||
|
if (!isset($all)) { |
||||||
|
$all = $name; |
||||||
|
} else { |
||||||
|
throw new Exception("Only one target can have empty 'depends' option. Found two now: $all, $name"); |
||||||
|
} |
||||||
|
} else { |
||||||
|
foreach ($target['depends'] as $bundle) { |
||||||
|
if (!isset($referenced[$bundle])) { |
||||||
|
$referenced[$bundle] = $name; |
||||||
|
} else { |
||||||
|
throw new Exception("Target '{$referenced[$bundle]}' and '$name' cannot contain the bundle '$bundle' at the same time."); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (isset($all)) { |
||||||
|
$targets[$all]['depends'] = array_diff(array_keys($registered), array_keys($referenced)); |
||||||
|
} |
||||||
|
|
||||||
|
// adjust the 'depends' order for each target according to the dependency order of bundles |
||||||
|
// create an AssetBundle object for each target |
||||||
|
foreach ($targets as $name => $target) { |
||||||
|
if (!isset($target['basePath'])) { |
||||||
|
throw new Exception("Please specify 'basePath' for the '$name' target."); |
||||||
|
} |
||||||
|
if (!isset($target['baseUrl'])) { |
||||||
|
throw new Exception("Please specify 'baseUrl' for the '$name' target."); |
||||||
|
} |
||||||
|
usort($target['depends'], function ($a, $b) use ($bundleOrders) { |
||||||
|
if ($bundleOrders[$a] == $bundleOrders[$b]) { |
||||||
|
return 0; |
||||||
|
} else { |
||||||
|
return $bundleOrders[$a] > $bundleOrders[$b] ? 1 : -1; |
||||||
|
} |
||||||
|
}); |
||||||
|
$target['class'] = 'yii\\web\\AssetBundle'; |
||||||
|
$targets[$name] = Yii::createObject($target); |
||||||
|
} |
||||||
|
return $targets; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param \yii\web\AssetBundle[] $bundles |
||||||
|
* @param array $options |
||||||
|
*/ |
||||||
|
protected function publishBundles($bundles, $options) |
||||||
|
{ |
||||||
|
if (!isset($options['class'])) { |
||||||
|
$options['class'] = 'yii\\web\\AssetManager'; |
||||||
|
} |
||||||
|
$am = Yii::createObject($options); |
||||||
|
foreach ($bundles as $bundle) { |
||||||
|
$bundle->publish($am); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param \yii\web\AssetBundle $target |
||||||
|
* @param string $type either "js" or "css" |
||||||
|
* @param \yii\web\AssetBundle[] $bundles |
||||||
|
* @param integer $timestamp |
||||||
|
* @throws Exception |
||||||
|
*/ |
||||||
|
protected function buildTarget($target, $type, $bundles, $timestamp) |
||||||
|
{ |
||||||
|
$outputFile = strtr($target->$type, array( |
||||||
|
'{ts}' => $timestamp, |
||||||
|
)); |
||||||
|
$inputFiles = array(); |
||||||
|
|
||||||
|
foreach ($target->depends as $name) { |
||||||
|
if (isset($bundles[$name])) { |
||||||
|
foreach ($bundles[$name]->$type as $file) { |
||||||
|
$inputFiles[] = $bundles[$name]->basePath . '/' . $file; |
||||||
|
} |
||||||
|
} else { |
||||||
|
throw new Exception("Unknown bundle: $name"); |
||||||
|
} |
||||||
|
} |
||||||
|
if ($type === 'js') { |
||||||
|
$this->compressJsFiles($inputFiles, $target->basePath . '/' . $outputFile); |
||||||
|
} else { |
||||||
|
$this->compressCssFiles($inputFiles, $target->basePath . '/' . $outputFile); |
||||||
|
} |
||||||
|
$target->$type = array($outputFile); |
||||||
|
} |
||||||
|
|
||||||
|
protected function adjustDependency($targets, $bundles) |
||||||
|
{ |
||||||
|
$map = array(); |
||||||
|
foreach ($targets as $name => $target) { |
||||||
|
foreach ($target->depends as $bundle) { |
||||||
|
$map[$bundle] = $name; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
foreach ($targets as $name => $target) { |
||||||
|
$depends = array(); |
||||||
|
foreach ($target->depends as $bn) { |
||||||
|
foreach ($bundles[$bn]->depends as $bundle) { |
||||||
|
$depends[$map[$bundle]] = true; |
||||||
|
} |
||||||
|
} |
||||||
|
unset($depends[$name]); |
||||||
|
$target->depends = array_keys($depends); |
||||||
|
} |
||||||
|
|
||||||
|
// detect possible circular dependencies |
||||||
|
foreach ($targets as $name => $target) { |
||||||
|
$registered = array(); |
||||||
|
$this->registerBundle($targets, $name, $registered); |
||||||
|
} |
||||||
|
|
||||||
|
foreach ($map as $bundle => $target) { |
||||||
|
$targets[$bundle] = Yii::createObject(array( |
||||||
|
'class' => 'yii\\web\\AssetBundle', |
||||||
|
'depends' => array($target), |
||||||
|
)); |
||||||
|
} |
||||||
|
return $targets; |
||||||
|
} |
||||||
|
|
||||||
|
protected function registerBundle($bundles, $name, &$registered) |
||||||
|
{ |
||||||
|
if (!isset($registered[$name])) { |
||||||
|
$registered[$name] = false; |
||||||
|
$bundle = $bundles[$name]; |
||||||
|
foreach ($bundle->depends as $depend) { |
||||||
|
$this->registerBundle($bundles, $depend, $registered); |
||||||
|
} |
||||||
|
unset($registered[$name]); |
||||||
|
$registered[$name] = true; |
||||||
|
} elseif ($registered[$name] === false) { |
||||||
|
throw new Exception("A circular dependency is detected for target '$name'."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected function saveTargets($targets, $bundleFile) |
||||||
|
{ |
||||||
|
$array = array(); |
||||||
|
foreach ($targets as $name => $target) { |
||||||
|
foreach (array('js', 'css', 'depends', 'basePath', 'baseUrl') as $prop) { |
||||||
|
if (!empty($target->$prop)) { |
||||||
|
$array[$name][$prop] = $target->$prop; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
$array = var_export($array, true); |
||||||
|
$version = date('Y-m-d H:i:s', time()); |
||||||
|
file_put_contents($bundleFile, <<<EOD |
||||||
|
<?php |
||||||
|
/** |
||||||
|
* This file is generated by the "yiic script" command. |
||||||
|
* DO NOT MODIFY THIS FILE DIRECTLY. |
||||||
|
* @version $version |
||||||
|
*/ |
||||||
|
return $array; |
||||||
|
EOD |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
protected function compressJsFiles($inputFiles, $outputFile) |
||||||
|
{ |
||||||
|
if (is_string($this->jsCompressor)) { |
||||||
|
$tmpFile = $outputFile . '.tmp'; |
||||||
|
$this->combineJsFiles($inputFiles, $tmpFile); |
||||||
|
$log = shell_exec(strtr($this->jsCompressor, array( |
||||||
|
'{from}' => $tmpFile, |
||||||
|
'{to}' => $outputFile, |
||||||
|
))); |
||||||
|
@unlink($tmpFile); |
||||||
|
} else { |
||||||
|
$log = call_user_func($this->jsCompressor, $this, $inputFiles, $outputFile); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected function compressCssFiles($inputFiles, $outputFile) |
||||||
|
{ |
||||||
|
if (is_string($this->cssCompressor)) { |
||||||
|
$tmpFile = $outputFile . '.tmp'; |
||||||
|
$this->combineCssFiles($inputFiles, $tmpFile); |
||||||
|
$log = shell_exec(strtr($this->cssCompressor, array( |
||||||
|
'{from}' => $inputFiles, |
||||||
|
'{to}' => $outputFile, |
||||||
|
))); |
||||||
|
} else { |
||||||
|
$log = call_user_func($this->cssCompressor, $this, $inputFiles, $outputFile); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public function combineJsFiles($files, $tmpFile) |
||||||
|
{ |
||||||
|
$content = ''; |
||||||
|
foreach ($files as $file) { |
||||||
|
$content .= "/*** BEGIN FILE: $file ***/\n" |
||||||
|
. file_get_contents($file) |
||||||
|
. "/*** END FILE: $file ***/\n"; |
||||||
|
} |
||||||
|
file_put_contents($tmpFile, $content); |
||||||
|
} |
||||||
|
|
||||||
|
public function combineCssFiles($files, $tmpFile) |
||||||
|
{ |
||||||
|
// todo: adjust url() references in CSS files |
||||||
|
$content = ''; |
||||||
|
foreach ($files as $file) { |
||||||
|
$content .= "/*** BEGIN FILE: $file ***/\n" |
||||||
|
. file_get_contents($file) |
||||||
|
. "/*** END FILE: $file ***/\n"; |
||||||
|
} |
||||||
|
file_put_contents($tmpFile, $content); |
||||||
|
} |
||||||
|
|
||||||
|
public function actionTemplate($configFile) |
||||||
|
{ |
||||||
|
$template = <<<EOD |
||||||
|
<?php |
||||||
|
|
||||||
|
return array( |
||||||
|
// |
||||||
|
'bundles' => require('path/to/bundles.php'), |
||||||
|
// |
||||||
|
'extensions' => require('path/to/namespaces.php'), |
||||||
|
// |
||||||
|
'targets' => array( |
||||||
|
'all' => array( |
||||||
|
'basePath' => __DIR__, |
||||||
|
'baseUrl' => '/test', |
||||||
|
'js' => 'all-{ts}.js', |
||||||
|
'css' => 'all-{ts}.css', |
||||||
|
), |
||||||
|
), |
||||||
|
|
||||||
|
'assetManager' => array( |
||||||
|
'basePath' => __DIR__, |
||||||
|
'baseUrl' => '/test', |
||||||
|
), |
||||||
|
); |
||||||
|
EOD; |
||||||
|
file_put_contents($configFile, $template); |
||||||
|
} |
||||||
|
} |
@ -1,10 +1,10 @@ |
|||||||
<?php |
<?php |
||||||
define('YII_DEBUG', true); |
define('YII_DEBUG', true); |
||||||
|
|
||||||
$yii = __DIR__.'/../framework/yii.php'; |
require __DIR__.'/../framework/yii.php'; |
||||||
require $yii; |
|
||||||
$config = require dirname(__DIR__).'/protected/config/main.php'; |
$config = require dirname(__DIR__).'/protected/config/main.php'; |
||||||
|
$config['basePath'] = dirname(__DIR__).'/protected'; |
||||||
|
|
||||||
$basePath = dirname(__DIR__).'/protected'; |
$app = new \yii\web\Application($config); |
||||||
$app = new \yii\web\Application('webapp', $basePath, $config); |
|
||||||
$app->run(); |
$app->run(); |
@ -0,0 +1,23 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\db; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class StaleObjectException extends Exception |
||||||
|
{ |
||||||
|
/** |
||||||
|
* @return string the user-friendly name of this exception |
||||||
|
*/ |
||||||
|
public function getName() |
||||||
|
{ |
||||||
|
return \Yii::t('yii|Stale Object Exception'); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,340 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\helpers\base; |
||||||
|
|
||||||
|
use Yii; |
||||||
|
use yii\base\InvalidParamException; |
||||||
|
|
||||||
|
/** |
||||||
|
* ArrayHelper provides additional array functionality you can use in your |
||||||
|
* application. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class ArrayHelper |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Merges two or more arrays into one recursively. |
||||||
|
* If each array has an element with the same string key value, the latter |
||||||
|
* will overwrite the former (different from array_merge_recursive). |
||||||
|
* Recursive merging will be conducted if both arrays have an element of array |
||||||
|
* type and are having the same key. |
||||||
|
* For integer-keyed elements, the elements from the latter array will |
||||||
|
* be appended to the former array. |
||||||
|
* @param array $a array to be merged to |
||||||
|
* @param array $b array to be merged from. You can specify additional |
||||||
|
* arrays via third argument, fourth argument etc. |
||||||
|
* @return array the merged array (the original arrays are not changed.) |
||||||
|
*/ |
||||||
|
public static function merge($a, $b) |
||||||
|
{ |
||||||
|
$args = func_get_args(); |
||||||
|
$res = array_shift($args); |
||||||
|
while ($args !== array()) { |
||||||
|
$next = array_shift($args); |
||||||
|
foreach ($next as $k => $v) { |
||||||
|
if (is_integer($k)) { |
||||||
|
isset($res[$k]) ? $res[] = $v : $res[$k] = $v; |
||||||
|
} elseif (is_array($v) && isset($res[$k]) && is_array($res[$k])) { |
||||||
|
$res[$k] = self::merge($res[$k], $v); |
||||||
|
} else { |
||||||
|
$res[$k] = $v; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return $res; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Retrieves the value of an array element or object property with the given key or property name. |
||||||
|
* If the key does not exist in the array, the default value will be returned instead. |
||||||
|
* |
||||||
|
* Below are some usage examples, |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* // working with array |
||||||
|
* $username = \yii\helpers\ArrayHelper::getValue($_POST, 'username'); |
||||||
|
* // working with object |
||||||
|
* $username = \yii\helpers\ArrayHelper::getValue($user, 'username'); |
||||||
|
* // working with anonymous function |
||||||
|
* $fullName = \yii\helpers\ArrayHelper::getValue($user, function($user, $defaultValue) { |
||||||
|
* return $user->firstName . ' ' . $user->lastName; |
||||||
|
* }); |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* @param array|object $array array or object to extract value from |
||||||
|
* @param string|\Closure $key key name of the array element, or property name of the object, |
||||||
|
* or an anonymous function returning the value. The anonymous function signature should be: |
||||||
|
* `function($array, $defaultValue)`. |
||||||
|
* @param mixed $default the default value to be returned if the specified key does not exist |
||||||
|
* @return mixed the value of the |
||||||
|
*/ |
||||||
|
public static function getValue($array, $key, $default = null) |
||||||
|
{ |
||||||
|
if ($key instanceof \Closure) { |
||||||
|
return $key($array, $default); |
||||||
|
} elseif (is_array($array)) { |
||||||
|
return isset($array[$key]) || array_key_exists($key, $array) ? $array[$key] : $default; |
||||||
|
} else { |
||||||
|
return $array->$key; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Indexes an array according to a specified key. |
||||||
|
* The input array should be multidimensional or an array of objects. |
||||||
|
* |
||||||
|
* The key can be a key name of the sub-array, a property name of object, or an anonymous |
||||||
|
* function which returns the key value given an array element. |
||||||
|
* |
||||||
|
* If a key value is null, the corresponding array element will be discarded and not put in the result. |
||||||
|
* |
||||||
|
* For example, |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* $array = array( |
||||||
|
* array('id' => '123', 'data' => 'abc'), |
||||||
|
* array('id' => '345', 'data' => 'def'), |
||||||
|
* ); |
||||||
|
* $result = ArrayHelper::index($array, 'id'); |
||||||
|
* // the result is: |
||||||
|
* // array( |
||||||
|
* // '123' => array('id' => '123', 'data' => 'abc'), |
||||||
|
* // '345' => array('id' => '345', 'data' => 'def'), |
||||||
|
* // ) |
||||||
|
* |
||||||
|
* // using anonymous function |
||||||
|
* $result = ArrayHelper::index($array, function(element) { |
||||||
|
* return $element['id']; |
||||||
|
* }); |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* @param array $array the array that needs to be indexed |
||||||
|
* @param string|\Closure $key the column name or anonymous function whose result will be used to index the array |
||||||
|
* @return array the indexed array |
||||||
|
*/ |
||||||
|
public static function index($array, $key) |
||||||
|
{ |
||||||
|
$result = array(); |
||||||
|
foreach ($array as $element) { |
||||||
|
$value = static::getValue($element, $key); |
||||||
|
$result[$value] = $element; |
||||||
|
} |
||||||
|
return $result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the values of a specified column in an array. |
||||||
|
* The input array should be multidimensional or an array of objects. |
||||||
|
* |
||||||
|
* For example, |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* $array = array( |
||||||
|
* array('id' => '123', 'data' => 'abc'), |
||||||
|
* array('id' => '345', 'data' => 'def'), |
||||||
|
* ); |
||||||
|
* $result = ArrayHelper::getColumn($array, 'id'); |
||||||
|
* // the result is: array( '123', '345') |
||||||
|
* |
||||||
|
* // using anonymous function |
||||||
|
* $result = ArrayHelper::getColumn($array, function(element) { |
||||||
|
* return $element['id']; |
||||||
|
* }); |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* @param array $array |
||||||
|
* @param string|\Closure $name |
||||||
|
* @param boolean $keepKeys whether to maintain the array keys. If false, the resulting array |
||||||
|
* will be re-indexed with integers. |
||||||
|
* @return array the list of column values |
||||||
|
*/ |
||||||
|
public static function getColumn($array, $name, $keepKeys = true) |
||||||
|
{ |
||||||
|
$result = array(); |
||||||
|
if ($keepKeys) { |
||||||
|
foreach ($array as $k => $element) { |
||||||
|
$result[$k] = static::getValue($element, $name); |
||||||
|
} |
||||||
|
} else { |
||||||
|
foreach ($array as $element) { |
||||||
|
$result[] = static::getValue($element, $name); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return $result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Builds a map (key-value pairs) from a multidimensional array or an array of objects. |
||||||
|
* The `$from` and `$to` parameters specify the key names or property names to set up the map. |
||||||
|
* Optionally, one can further group the map according to a grouping field `$group`. |
||||||
|
* |
||||||
|
* For example, |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* $array = array( |
||||||
|
* array('id' => '123', 'name' => 'aaa', 'class' => 'x'), |
||||||
|
* array('id' => '124', 'name' => 'bbb', 'class' => 'x'), |
||||||
|
* array('id' => '345', 'name' => 'ccc', 'class' => 'y'), |
||||||
|
* ); |
||||||
|
* |
||||||
|
* $result = ArrayHelper::map($array, 'id', 'name'); |
||||||
|
* // the result is: |
||||||
|
* // array( |
||||||
|
* // '123' => 'aaa', |
||||||
|
* // '124' => 'bbb', |
||||||
|
* // '345' => 'ccc', |
||||||
|
* // ) |
||||||
|
* |
||||||
|
* $result = ArrayHelper::map($array, 'id', 'name', 'class'); |
||||||
|
* // the result is: |
||||||
|
* // array( |
||||||
|
* // 'x' => array( |
||||||
|
* // '123' => 'aaa', |
||||||
|
* // '124' => 'bbb', |
||||||
|
* // ), |
||||||
|
* // 'y' => array( |
||||||
|
* // '345' => 'ccc', |
||||||
|
* // ), |
||||||
|
* // ) |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* @param array $array |
||||||
|
* @param string|\Closure $from |
||||||
|
* @param string|\Closure $to |
||||||
|
* @param string|\Closure $group |
||||||
|
* @return array |
||||||
|
*/ |
||||||
|
public static function map($array, $from, $to, $group = null) |
||||||
|
{ |
||||||
|
$result = array(); |
||||||
|
foreach ($array as $element) { |
||||||
|
$key = static::getValue($element, $from); |
||||||
|
$value = static::getValue($element, $to); |
||||||
|
if ($group !== null) { |
||||||
|
$result[static::getValue($element, $group)][$key] = $value; |
||||||
|
} else { |
||||||
|
$result[$key] = $value; |
||||||
|
} |
||||||
|
} |
||||||
|
return $result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sorts an array of objects or arrays (with the same structure) by one or several keys. |
||||||
|
* @param array $array the array to be sorted. The array will be modified after calling this method. |
||||||
|
* @param string|\Closure|array $key the key(s) to be sorted by. This refers to a key name of the sub-array |
||||||
|
* elements, a property name of the objects, or an anonymous function returning the values for comparison |
||||||
|
* purpose. The anonymous function signature should be: `function($item)`. |
||||||
|
* To sort by multiple keys, provide an array of keys here. |
||||||
|
* @param boolean|array $ascending whether to sort in ascending or descending order. When |
||||||
|
* sorting by multiple keys with different ascending orders, use an array of ascending flags. |
||||||
|
* @param integer|array $sortFlag the PHP sort flag. Valid values include: |
||||||
|
* `SORT_REGULAR`, `SORT_NUMERIC`, `SORT_STRING`, and `SORT_STRING | SORT_FLAG_CASE`. The last |
||||||
|
* value is for sorting strings in case-insensitive manner. Please refer to |
||||||
|
* See [PHP manual](http://php.net/manual/en/function.sort.php) for more details. |
||||||
|
* When sorting by multiple keys with different sort flags, use an array of sort flags. |
||||||
|
* @throws InvalidParamException if the $ascending or $sortFlag parameters do not have |
||||||
|
* correct number of elements as that of $key. |
||||||
|
*/ |
||||||
|
public static function multisort(&$array, $key, $ascending = true, $sortFlag = SORT_REGULAR) |
||||||
|
{ |
||||||
|
$keys = is_array($key) ? $key : array($key); |
||||||
|
if (empty($keys) || empty($array)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
$n = count($keys); |
||||||
|
if (is_scalar($ascending)) { |
||||||
|
$ascending = array_fill(0, $n, $ascending); |
||||||
|
} elseif (count($ascending) !== $n) { |
||||||
|
throw new InvalidParamException('The length of $ascending parameter must be the same as that of $keys.'); |
||||||
|
} |
||||||
|
if (is_scalar($sortFlag)) { |
||||||
|
$sortFlag = array_fill(0, $n, $sortFlag); |
||||||
|
} elseif (count($sortFlag) !== $n) { |
||||||
|
throw new InvalidParamException('The length of $ascending parameter must be the same as that of $keys.'); |
||||||
|
} |
||||||
|
$args = array(); |
||||||
|
foreach ($keys as $i => $key) { |
||||||
|
$flag = $sortFlag[$i]; |
||||||
|
if ($flag == (SORT_STRING | SORT_FLAG_CASE)) { |
||||||
|
$flag = SORT_STRING; |
||||||
|
$column = array(); |
||||||
|
foreach (static::getColumn($array, $key) as $k => $value) { |
||||||
|
$column[$k] = strtolower($value); |
||||||
|
} |
||||||
|
$args[] = $column; |
||||||
|
} else { |
||||||
|
$args[] = static::getColumn($array, $key); |
||||||
|
} |
||||||
|
$args[] = $ascending[$i] ? SORT_ASC : SORT_DESC; |
||||||
|
$args[] = $flag; |
||||||
|
} |
||||||
|
$args[] = &$array; |
||||||
|
call_user_func_array('array_multisort', $args); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Encodes special characters in an array of strings into HTML entities. |
||||||
|
* Both the array keys and values will be encoded. |
||||||
|
* If a value is an array, this method will also encode it recursively. |
||||||
|
* @param array $data data to be encoded |
||||||
|
* @param boolean $valuesOnly whether to encode array values only. If false, |
||||||
|
* both the array keys and array values will be encoded. |
||||||
|
* @param string $charset the charset that the data is using. If not set, |
||||||
|
* [[\yii\base\Application::charset]] will be used. |
||||||
|
* @return array the encoded data |
||||||
|
* @see http://www.php.net/manual/en/function.htmlspecialchars.php |
||||||
|
*/ |
||||||
|
public static function htmlEncode($data, $valuesOnly = true, $charset = null) |
||||||
|
{ |
||||||
|
if ($charset === null) { |
||||||
|
$charset = Yii::$app->charset; |
||||||
|
} |
||||||
|
$d = array(); |
||||||
|
foreach ($data as $key => $value) { |
||||||
|
if (!$valuesOnly && is_string($key)) { |
||||||
|
$key = htmlspecialchars($key, ENT_QUOTES, $charset); |
||||||
|
} |
||||||
|
if (is_string($value)) { |
||||||
|
$d[$key] = htmlspecialchars($value, ENT_QUOTES, $charset); |
||||||
|
} elseif (is_array($value)) { |
||||||
|
$d[$key] = static::htmlEncode($value, $charset); |
||||||
|
} |
||||||
|
} |
||||||
|
return $d; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Decodes HTML entities into the corresponding characters in an array of strings. |
||||||
|
* Both the array keys and values will be decoded. |
||||||
|
* If a value is an array, this method will also decode it recursively. |
||||||
|
* @param array $data data to be decoded |
||||||
|
* @param boolean $valuesOnly whether to decode array values only. If false, |
||||||
|
* both the array keys and array values will be decoded. |
||||||
|
* @return array the decoded data |
||||||
|
* @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php |
||||||
|
*/ |
||||||
|
public static function htmlDecode($data, $valuesOnly = true) |
||||||
|
{ |
||||||
|
$d = array(); |
||||||
|
foreach ($data as $key => $value) { |
||||||
|
if (!$valuesOnly && is_string($key)) { |
||||||
|
$key = htmlspecialchars_decode($key, ENT_QUOTES); |
||||||
|
} |
||||||
|
if (is_string($value)) { |
||||||
|
$d[$key] = htmlspecialchars_decode($value, ENT_QUOTES); |
||||||
|
} elseif (is_array($value)) { |
||||||
|
$d[$key] = static::htmlDecode($value); |
||||||
|
} |
||||||
|
} |
||||||
|
return $d; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,470 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\helpers\base; |
||||||
|
|
||||||
|
// todo test this on all kinds of terminals, especially windows (check out lib ncurses) |
||||||
|
|
||||||
|
/** |
||||||
|
* Console View is the base class for console view components |
||||||
|
* |
||||||
|
* A console view provides functionality to create rich console application by allowing to format output |
||||||
|
* by adding color and font style to it. |
||||||
|
* |
||||||
|
* @author Carsten Brandt <mail@cebe.cc> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class ConsoleColor |
||||||
|
{ |
||||||
|
const FG_BLACK = 30; |
||||||
|
const FG_RED = 31; |
||||||
|
const FG_GREEN = 32; |
||||||
|
const FG_YELLOW = 33; |
||||||
|
const FG_BLUE = 34; |
||||||
|
const FG_PURPLE = 35; |
||||||
|
const FG_CYAN = 36; |
||||||
|
const FG_GREY = 37; |
||||||
|
|
||||||
|
const BG_BLACK = 40; |
||||||
|
const BG_RED = 41; |
||||||
|
const BG_GREEN = 42; |
||||||
|
const BG_YELLOW = 43; |
||||||
|
const BG_BLUE = 44; |
||||||
|
const BG_PURPLE = 45; |
||||||
|
const BG_CYAN = 46; |
||||||
|
const BG_GREY = 47; |
||||||
|
|
||||||
|
const BOLD = 1; |
||||||
|
const ITALIC = 3; |
||||||
|
const UNDERLINE = 4; |
||||||
|
const BLINK = 5; |
||||||
|
const NEGATIVE = 7; |
||||||
|
const CONCEALED = 8; |
||||||
|
const CROSSED_OUT = 9; |
||||||
|
const FRAMED = 51; |
||||||
|
const ENCIRCLED = 52; |
||||||
|
const OVERLINED = 53; |
||||||
|
|
||||||
|
/** |
||||||
|
* Moves the terminal cursor up by sending ANSI control code CUU to the terminal. |
||||||
|
* If the cursor is already at the edge of the screen, this has no effect. |
||||||
|
* @param integer $rows number of rows the cursor should be moved up |
||||||
|
*/ |
||||||
|
public static function moveCursorUp($rows=1) |
||||||
|
{ |
||||||
|
echo "\033[" . (int) $rows . 'A'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Moves the terminal cursor down by sending ANSI control code CUD to the terminal. |
||||||
|
* If the cursor is already at the edge of the screen, this has no effect. |
||||||
|
* @param integer $rows number of rows the cursor should be moved down |
||||||
|
*/ |
||||||
|
public static function moveCursorDown($rows=1) |
||||||
|
{ |
||||||
|
echo "\033[" . (int) $rows . 'B'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Moves the terminal cursor forward by sending ANSI control code CUF to the terminal. |
||||||
|
* If the cursor is already at the edge of the screen, this has no effect. |
||||||
|
* @param integer $steps number of steps the cursor should be moved forward |
||||||
|
*/ |
||||||
|
public static function moveCursorForward($steps=1) |
||||||
|
{ |
||||||
|
echo "\033[" . (int) $steps . 'C'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Moves the terminal cursor backward by sending ANSI control code CUB to the terminal. |
||||||
|
* If the cursor is already at the edge of the screen, this has no effect. |
||||||
|
* @param integer $steps number of steps the cursor should be moved backward |
||||||
|
*/ |
||||||
|
public static function moveCursorBackward($steps=1) |
||||||
|
{ |
||||||
|
echo "\033[" . (int) $steps . 'D'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Moves the terminal cursor to the beginning of the next line by sending ANSI control code CNL to the terminal. |
||||||
|
* @param integer $lines number of lines the cursor should be moved down |
||||||
|
*/ |
||||||
|
public static function moveCursorNextLine($lines=1) |
||||||
|
{ |
||||||
|
echo "\033[" . (int) $lines . 'E'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Moves the terminal cursor to the beginning of the previous line by sending ANSI control code CPL to the terminal. |
||||||
|
* @param integer $lines number of lines the cursor should be moved up |
||||||
|
*/ |
||||||
|
public static function moveCursorPrevLine($lines=1) |
||||||
|
{ |
||||||
|
echo "\033[" . (int) $lines . 'F'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Moves the cursor to an absolute position given as column and row by sending ANSI control code CUP or CHA to the terminal. |
||||||
|
* @param integer $column 1-based column number, 1 is the left edge of the screen. |
||||||
|
* @param integer|null $row 1-based row number, 1 is the top edge of the screen. if not set, will move cursor only in current line. |
||||||
|
*/ |
||||||
|
public static function moveCursorTo($column, $row=null) |
||||||
|
{ |
||||||
|
if ($row === null) { |
||||||
|
echo "\033[" . (int) $column . 'G'; |
||||||
|
} else { |
||||||
|
echo "\033[" . (int) $row . ';' . (int) $column . 'H'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Scrolls whole page up by sending ANSI control code SU to the terminal. |
||||||
|
* New lines are added at the bottom. This is not supported by ANSI.SYS used in windows. |
||||||
|
* @param int $lines number of lines to scroll up |
||||||
|
*/ |
||||||
|
public static function scrollUp($lines=1) |
||||||
|
{ |
||||||
|
echo "\033[".(int)$lines."S"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Scrolls whole page down by sending ANSI control code SD to the terminal. |
||||||
|
* New lines are added at the top. This is not supported by ANSI.SYS used in windows. |
||||||
|
* @param int $lines number of lines to scroll down |
||||||
|
*/ |
||||||
|
public static function scrollDown($lines=1) |
||||||
|
{ |
||||||
|
echo "\033[".(int)$lines."T"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Saves the current cursor position by sending ANSI control code SCP to the terminal. |
||||||
|
* Position can then be restored with {@link restoreCursorPosition}. |
||||||
|
*/ |
||||||
|
public static function saveCursorPosition() |
||||||
|
{ |
||||||
|
echo "\033[s"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Restores the cursor position saved with {@link saveCursorPosition} by sending ANSI control code RCP to the terminal. |
||||||
|
*/ |
||||||
|
public static function restoreCursorPosition() |
||||||
|
{ |
||||||
|
echo "\033[u"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Hides the cursor by sending ANSI DECTCEM code ?25l to the terminal. |
||||||
|
* Use {@link showCursor} to bring it back. |
||||||
|
* Do not forget to show cursor when your application exits. Cursor might stay hidden in terminal after exit. |
||||||
|
*/ |
||||||
|
public static function hideCursor() |
||||||
|
{ |
||||||
|
echo "\033[?25l"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Will show a cursor again when it has been hidden by {@link hideCursor} by sending ANSI DECTCEM code ?25h to the terminal. |
||||||
|
*/ |
||||||
|
public static function showCursor() |
||||||
|
{ |
||||||
|
echo "\033[?25h"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Clears entire screen content by sending ANSI control code ED with argument 2 to the terminal. |
||||||
|
* Cursor position will not be changed. |
||||||
|
* **Note:** ANSI.SYS implementation used in windows will reset cursor position to upper left corner of the screen. |
||||||
|
*/ |
||||||
|
public static function clearScreen() |
||||||
|
{ |
||||||
|
echo "\033[2J"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Clears text from cursor to the beginning of the screen by sending ANSI control code ED with argument 1 to the terminal. |
||||||
|
* Cursor position will not be changed. |
||||||
|
*/ |
||||||
|
public static function clearScreenBeforeCursor() |
||||||
|
{ |
||||||
|
echo "\033[1J"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Clears text from cursor to the end of the screen by sending ANSI control code ED with argument 0 to the terminal. |
||||||
|
* Cursor position will not be changed. |
||||||
|
*/ |
||||||
|
public static function clearScreenAfterCursor() |
||||||
|
{ |
||||||
|
echo "\033[0J"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Clears the line, the cursor is currently on by sending ANSI control code EL with argument 2 to the terminal. |
||||||
|
* Cursor position will not be changed. |
||||||
|
*/ |
||||||
|
public static function clearLine() |
||||||
|
{ |
||||||
|
echo "\033[2K"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Clears text from cursor position to the beginning of the line by sending ANSI control code EL with argument 1 to the terminal. |
||||||
|
* Cursor position will not be changed. |
||||||
|
*/ |
||||||
|
public static function clearLineBeforeCursor() |
||||||
|
{ |
||||||
|
echo "\033[1K"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Clears text from cursor position to the end of the line by sending ANSI control code EL with argument 0 to the terminal. |
||||||
|
* Cursor position will not be changed. |
||||||
|
*/ |
||||||
|
public static function clearLineAfterCursor() |
||||||
|
{ |
||||||
|
echo "\033[0K"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Will send ANSI format for following output |
||||||
|
* |
||||||
|
* You can pass any of the FG_*, BG_* and TEXT_* constants and also xterm256ColorBg |
||||||
|
* TODO: documentation |
||||||
|
*/ |
||||||
|
public static function ansiStyle() |
||||||
|
{ |
||||||
|
echo "\033[" . implode(';', func_get_args()) . 'm'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Will return a string formatted with the given ANSI style |
||||||
|
* |
||||||
|
* See {@link ansiStyle} for possible arguments. |
||||||
|
* @param string $string the string to be formatted |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public static function ansiStyleString($string) |
||||||
|
{ |
||||||
|
$args = func_get_args(); |
||||||
|
array_shift($args); |
||||||
|
$code = implode(';', $args); |
||||||
|
return "\033[0m" . ($code !== '' ? "\033[" . $code . "m" : '') . $string."\033[0m"; |
||||||
|
} |
||||||
|
|
||||||
|
//const COLOR_XTERM256 = 38;// http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors |
||||||
|
public static function xterm256ColorFg($i) // TODO naming! |
||||||
|
{ |
||||||
|
return '38;5;'.$i; |
||||||
|
} |
||||||
|
|
||||||
|
public static function xterm256ColorBg($i) // TODO naming! |
||||||
|
{ |
||||||
|
return '48;5;'.$i; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Usage: list($w, $h) = ConsoleHelper::getScreenSize(); |
||||||
|
* |
||||||
|
* @return array |
||||||
|
*/ |
||||||
|
public static function getScreenSize() |
||||||
|
{ |
||||||
|
// TODO implement |
||||||
|
return array(150,50); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* resets any ansi style set by previous method {@link ansiStyle} |
||||||
|
* Any output after this is will have default text style. |
||||||
|
*/ |
||||||
|
public static function reset() |
||||||
|
{ |
||||||
|
echo "\033[0m"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Strips ANSI control codes from a string |
||||||
|
* |
||||||
|
* @param string $string String to strip |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public static function strip($string) |
||||||
|
{ |
||||||
|
return preg_replace('/\033\[[\d;]+m/', '', $string); // TODO currently only strips color |
||||||
|
} |
||||||
|
|
||||||
|
// TODO refactor and review |
||||||
|
public static function ansiToHtml($string) |
||||||
|
{ |
||||||
|
$tags = 0; |
||||||
|
return preg_replace_callback('/\033\[[\d;]+m/', function($ansi) use (&$tags) { |
||||||
|
$styleA = array(); |
||||||
|
foreach(explode(';', $ansi) as $controlCode) |
||||||
|
{ |
||||||
|
switch($controlCode) |
||||||
|
{ |
||||||
|
case static::FG_BLACK: $style = array('color' => '#000000'); break; |
||||||
|
case static::FG_BLUE: $style = array('color' => '#000078'); break; |
||||||
|
case static::FG_CYAN: $style = array('color' => '#007878'); break; |
||||||
|
case static::FG_GREEN: $style = array('color' => '#007800'); break; |
||||||
|
case static::FG_GREY: $style = array('color' => '#787878'); break; |
||||||
|
case static::FG_PURPLE: $style = array('color' => '#780078'); break; |
||||||
|
case static::FG_RED: $style = array('color' => '#780000'); break; |
||||||
|
case static::FG_YELLOW: $style = array('color' => '#787800'); break; |
||||||
|
case static::BG_BLACK: $style = array('background-color' => '#000000'); break; |
||||||
|
case static::BG_BLUE: $style = array('background-color' => '#000078'); break; |
||||||
|
case static::BG_CYAN: $style = array('background-color' => '#007878'); break; |
||||||
|
case static::BG_GREEN: $style = array('background-color' => '#007800'); break; |
||||||
|
case static::BG_GREY: $style = array('background-color' => '#787878'); break; |
||||||
|
case static::BG_PURPLE: $style = array('background-color' => '#780078'); break; |
||||||
|
case static::BG_RED: $style = array('background-color' => '#780000'); break; |
||||||
|
case static::BG_YELLOW: $style = array('background-color' => '#787800'); break; |
||||||
|
case static::BOLD: $style = array('font-weight' => 'bold'); break; |
||||||
|
case static::ITALIC: $style = array('font-style' => 'italic'); break; |
||||||
|
case static::UNDERLINE: $style = array('text-decoration' => array('underline')); break; |
||||||
|
case static::OVERLINED: $style = array('text-decoration' => array('overline')); break; |
||||||
|
case static::CROSSED_OUT:$style = array('text-decoration' => array('line-through')); break; |
||||||
|
case static::BLINK: $style = array('text-decoration' => array('blink')); break; |
||||||
|
case static::NEGATIVE: // ??? |
||||||
|
case static::CONCEALED: |
||||||
|
case static::ENCIRCLED: |
||||||
|
case static::FRAMED: |
||||||
|
// TODO allow resetting codes |
||||||
|
break; |
||||||
|
case 0: // ansi reset |
||||||
|
$return = ''; |
||||||
|
for($n=$tags; $tags>0; $tags--) { |
||||||
|
$return .= '</span>'; |
||||||
|
} |
||||||
|
return $return; |
||||||
|
} |
||||||
|
|
||||||
|
$styleA = ArrayHelper::merge($styleA, $style); |
||||||
|
} |
||||||
|
$styleString[] = array(); |
||||||
|
foreach($styleA as $name => $content) { |
||||||
|
if ($name === 'text-decoration') { |
||||||
|
$content = implode(' ', $content); |
||||||
|
} |
||||||
|
$styleString[] = $name.':'.$content; |
||||||
|
} |
||||||
|
$tags++; |
||||||
|
return '<span' . (!empty($styleString) ? 'style="' . implode(';', $styleString) : '') . '>'; |
||||||
|
}, $string); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* TODO syntax copied from https://github.com/pear/Console_Color2/blob/master/Console/Color2.php |
||||||
|
* |
||||||
|
* Converts colorcodes in the format %y (for yellow) into ansi-control |
||||||
|
* codes. The conversion table is: ('bold' meaning 'light' on some |
||||||
|
* terminals). It's almost the same conversion table irssi uses. |
||||||
|
* <pre> |
||||||
|
* text text background |
||||||
|
* ------------------------------------------------ |
||||||
|
* %k %K %0 black dark grey black |
||||||
|
* %r %R %1 red bold red red |
||||||
|
* %g %G %2 green bold green green |
||||||
|
* %y %Y %3 yellow bold yellow yellow |
||||||
|
* %b %B %4 blue bold blue blue |
||||||
|
* %m %M %5 magenta bold magenta magenta |
||||||
|
* %p %P magenta (think: purple) |
||||||
|
* %c %C %6 cyan bold cyan cyan |
||||||
|
* %w %W %7 white bold white white |
||||||
|
* |
||||||
|
* %F Blinking, Flashing |
||||||
|
* %U Underline |
||||||
|
* %8 Reverse |
||||||
|
* %_,%9 Bold |
||||||
|
* |
||||||
|
* %n Resets the color |
||||||
|
* %% A single % |
||||||
|
* </pre> |
||||||
|
* First param is the string to convert, second is an optional flag if |
||||||
|
* colors should be used. It defaults to true, if set to false, the |
||||||
|
* colorcodes will just be removed (And %% will be transformed into %) |
||||||
|
* |
||||||
|
* @param string $string String to convert |
||||||
|
* @param bool $colored Should the string be colored? |
||||||
|
* |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public static function renderColoredString($string) |
||||||
|
{ |
||||||
|
$colored = true; |
||||||
|
|
||||||
|
|
||||||
|
static $conversions = array ( // static so the array doesn't get built |
||||||
|
// everytime |
||||||
|
// %y - yellow, and so on... {{{ |
||||||
|
'%y' => array('color' => 'yellow'), |
||||||
|
'%g' => array('color' => 'green' ), |
||||||
|
'%b' => array('color' => 'blue' ), |
||||||
|
'%r' => array('color' => 'red' ), |
||||||
|
'%p' => array('color' => 'purple'), |
||||||
|
'%m' => array('color' => 'purple'), |
||||||
|
'%c' => array('color' => 'cyan' ), |
||||||
|
'%w' => array('color' => 'grey' ), |
||||||
|
'%k' => array('color' => 'black' ), |
||||||
|
'%n' => array('color' => 'reset' ), |
||||||
|
'%Y' => array('color' => 'yellow', 'style' => 'light'), |
||||||
|
'%G' => array('color' => 'green', 'style' => 'light'), |
||||||
|
'%B' => array('color' => 'blue', 'style' => 'light'), |
||||||
|
'%R' => array('color' => 'red', 'style' => 'light'), |
||||||
|
'%P' => array('color' => 'purple', 'style' => 'light'), |
||||||
|
'%M' => array('color' => 'purple', 'style' => 'light'), |
||||||
|
'%C' => array('color' => 'cyan', 'style' => 'light'), |
||||||
|
'%W' => array('color' => 'grey', 'style' => 'light'), |
||||||
|
'%K' => array('color' => 'black', 'style' => 'light'), |
||||||
|
'%N' => array('color' => 'reset', 'style' => 'light'), |
||||||
|
'%3' => array('background' => 'yellow'), |
||||||
|
'%2' => array('background' => 'green' ), |
||||||
|
'%4' => array('background' => 'blue' ), |
||||||
|
'%1' => array('background' => 'red' ), |
||||||
|
'%5' => array('background' => 'purple'), |
||||||
|
'%6' => array('background' => 'cyan' ), |
||||||
|
'%7' => array('background' => 'grey' ), |
||||||
|
'%0' => array('background' => 'black' ), |
||||||
|
// Don't use this, I can't stand flashing text |
||||||
|
'%F' => array('style' => 'blink'), |
||||||
|
'%U' => array('style' => 'underline'), |
||||||
|
'%8' => array('style' => 'inverse'), |
||||||
|
'%9' => array('style' => 'bold'), |
||||||
|
'%_' => array('style' => 'bold') |
||||||
|
// }}} |
||||||
|
); |
||||||
|
|
||||||
|
if ($colored) { |
||||||
|
$string = str_replace('%%', '% ', $string); |
||||||
|
foreach ($conversions as $key => $value) { |
||||||
|
$string = str_replace($key, Console_Color::color($value), |
||||||
|
$string); |
||||||
|
} |
||||||
|
$string = str_replace('% ', '%', $string); |
||||||
|
|
||||||
|
} else { |
||||||
|
$string = preg_replace('/%((%)|.)/', '$2', $string); |
||||||
|
} |
||||||
|
|
||||||
|
return $string; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Escapes % so they don't get interpreted as color codes |
||||||
|
* |
||||||
|
* @param string $string String to escape |
||||||
|
* |
||||||
|
* @access public |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public static function escape($string) |
||||||
|
{ |
||||||
|
return str_replace('%', '%%', $string); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,172 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* Filesystem helper class file. |
||||||
|
* |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\helpers\base; |
||||||
|
|
||||||
|
use Yii; |
||||||
|
|
||||||
|
/** |
||||||
|
* Filesystem helper |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @author Alex Makarov <sam@rmcreative.ru> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class FileHelper |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Normalizes a file/directory path. |
||||||
|
* After normalization, the directory separators in the path will be `DIRECTORY_SEPARATOR`, |
||||||
|
* and any trailing directory separators will be removed. For example, '/home\demo/' on Linux |
||||||
|
* will be normalized as '/home/demo'. |
||||||
|
* @param string $path the file/directory path to be normalized |
||||||
|
* @param string $ds the directory separator to be used in the normalized result. Defaults to `DIRECTORY_SEPARATOR`. |
||||||
|
* @return string the normalized file/directory path |
||||||
|
*/ |
||||||
|
public static function normalizePath($path, $ds = DIRECTORY_SEPARATOR) |
||||||
|
{ |
||||||
|
return rtrim(strtr($path, array('/' => $ds, '\\' => $ds)), $ds); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the localized version of a specified file. |
||||||
|
* |
||||||
|
* The searching is based on the specified language code. In particular, |
||||||
|
* a file with the same name will be looked for under the subdirectory |
||||||
|
* whose name is the same as the language code. For example, given the file "path/to/view.php" |
||||||
|
* and language code "zh_CN", the localized file will be looked for as |
||||||
|
* "path/to/zh_CN/view.php". If the file is not found, the original file |
||||||
|
* will be returned. |
||||||
|
* |
||||||
|
* If the target and the source language codes are the same, |
||||||
|
* the original file will be returned. |
||||||
|
* |
||||||
|
* @param string $file the original file |
||||||
|
* @param string $language the target language that the file should be localized to. |
||||||
|
* If not set, the value of [[\yii\base\Application::language]] will be used. |
||||||
|
* @param string $sourceLanguage the language that the original file is in. |
||||||
|
* If not set, the value of [[\yii\base\Application::sourceLanguage]] will be used. |
||||||
|
* @return string the matching localized file, or the original file if the localized version is not found. |
||||||
|
* If the target and the source language codes are the same, the original file will be returned. |
||||||
|
*/ |
||||||
|
public static function localize($file, $language = null, $sourceLanguage = null) |
||||||
|
{ |
||||||
|
if ($language === null) { |
||||||
|
$language = Yii::$app->language; |
||||||
|
} |
||||||
|
if ($sourceLanguage === null) { |
||||||
|
$sourceLanguage = Yii::$app->sourceLanguage; |
||||||
|
} |
||||||
|
if ($language === $sourceLanguage) { |
||||||
|
return $file; |
||||||
|
} |
||||||
|
$desiredFile = dirname($file) . DIRECTORY_SEPARATOR . $sourceLanguage . DIRECTORY_SEPARATOR . basename($file); |
||||||
|
return is_file($desiredFile) ? $desiredFile : $file; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Determines the MIME type of the specified file. |
||||||
|
* This method will first try to determine the MIME type based on |
||||||
|
* [finfo_open](http://php.net/manual/en/function.finfo-open.php). If this doesn't work, it will |
||||||
|
* fall back to [[getMimeTypeByExtension()]]. |
||||||
|
* @param string $file the file name. |
||||||
|
* @param string $magicFile name of the optional magic database file, usually something like `/path/to/magic.mime`. |
||||||
|
* This will be passed as the second parameter to [finfo_open](http://php.net/manual/en/function.finfo-open.php). |
||||||
|
* @param boolean $checkExtension whether to use the file extension to determine the MIME type in case |
||||||
|
* `finfo_open()` cannot determine it. |
||||||
|
* @return string the MIME type (e.g. `text/plain`). Null is returned if the MIME type cannot be determined. |
||||||
|
*/ |
||||||
|
public static function getMimeType($file, $magicFile = null, $checkExtension = true) |
||||||
|
{ |
||||||
|
if (function_exists('finfo_open')) { |
||||||
|
$info = finfo_open(FILEINFO_MIME_TYPE, $magicFile); |
||||||
|
if ($info) { |
||||||
|
$result = finfo_file($info, $file); |
||||||
|
finfo_close($info); |
||||||
|
if ($result !== false) { |
||||||
|
return $result; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return $checkExtension ? self::getMimeTypeByExtension($file) : null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Determines the MIME type based on the extension name of the specified file. |
||||||
|
* This method will use a local map between extension names and MIME types. |
||||||
|
* @param string $file the file name. |
||||||
|
* @param string $magicFile the path of the file that contains all available MIME type information. |
||||||
|
* If this is not set, the default file aliased by `@yii/util/mimeTypes.php` will be used. |
||||||
|
* @return string the MIME type. Null is returned if the MIME type cannot be determined. |
||||||
|
*/ |
||||||
|
public static function getMimeTypeByExtension($file, $magicFile = null) |
||||||
|
{ |
||||||
|
static $mimeTypes = array(); |
||||||
|
if ($magicFile === null) { |
||||||
|
$magicFile = __DIR__ . '/mimeTypes.php'; |
||||||
|
} |
||||||
|
if (!isset($mimeTypes[$magicFile])) { |
||||||
|
$mimeTypes[$magicFile] = require($magicFile); |
||||||
|
} |
||||||
|
if (($ext = pathinfo($file, PATHINFO_EXTENSION)) !== '') { |
||||||
|
$ext = strtolower($ext); |
||||||
|
if (isset($mimeTypes[$magicFile][$ext])) { |
||||||
|
return $mimeTypes[$magicFile][$ext]; |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Copies a whole directory as another one. |
||||||
|
* The files and sub-directories will also be copied over. |
||||||
|
* @param string $src the source directory |
||||||
|
* @param string $dst the destination directory |
||||||
|
* @param array $options options for directory copy. Valid options are: |
||||||
|
* |
||||||
|
* - dirMode: integer, the permission to be set for newly copied directories. Defaults to 0777. |
||||||
|
* - fileMode: integer, the permission to be set for newly copied files. Defaults to the current environment setting. |
||||||
|
* - beforeCopy: callback, a PHP callback that is called before copying each sub-directory or file. |
||||||
|
* If the callback returns false, the copy operation for the sub-directory or file will be cancelled. |
||||||
|
* The signature of the callback should be: `function ($from, $to)`, where `$from` is the sub-directory or |
||||||
|
* file to be copied from, while `$to` is the copy target. |
||||||
|
* - afterCopy: callback, a PHP callback that is called after a sub-directory or file is successfully copied. |
||||||
|
* The signature of the callback is similar to that of `beforeCopy`. |
||||||
|
*/ |
||||||
|
public static function copyDirectory($src, $dst, $options = array()) |
||||||
|
{ |
||||||
|
if (!is_dir($dst)) { |
||||||
|
mkdir($dst, isset($options['dirMode']) ? $options['dirMode'] : 0777, true); |
||||||
|
} |
||||||
|
|
||||||
|
$handle = opendir($src); |
||||||
|
while (($file = readdir($handle)) !== false) { |
||||||
|
if ($file === '.' || $file === '..') { |
||||||
|
continue; |
||||||
|
} |
||||||
|
$from = $src . DIRECTORY_SEPARATOR . $file; |
||||||
|
$to = $dst . DIRECTORY_SEPARATOR . $file; |
||||||
|
if (!isset($options['beforeCopy']) || call_user_func($options['beforeCopy'], $from, $to)) { |
||||||
|
if (is_file($from)) { |
||||||
|
copy($from, $to); |
||||||
|
if (isset($options['fileMode'])) { |
||||||
|
@chmod($to, $options['fileMode']); |
||||||
|
} |
||||||
|
} else { |
||||||
|
static::copyDirectory($from, $to, $options); |
||||||
|
} |
||||||
|
if (isset($options['afterCopy'])) { |
||||||
|
call_user_func($options['afterCopy'], $from, $to); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
closedir($handle); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,981 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\helpers\base; |
||||||
|
|
||||||
|
use Yii; |
||||||
|
use yii\base\InvalidParamException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Html provides a set of static methods for generating commonly used HTML tags. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class Html |
||||||
|
{ |
||||||
|
/** |
||||||
|
* @var boolean whether to close void (empty) elements. Defaults to true. |
||||||
|
* @see voidElements |
||||||
|
*/ |
||||||
|
public static $closeVoidElements = true; |
||||||
|
/** |
||||||
|
* @var array list of void elements (element name => 1) |
||||||
|
* @see closeVoidElements |
||||||
|
* @see http://www.w3.org/TR/html-markup/syntax.html#void-element |
||||||
|
*/ |
||||||
|
public static $voidElements = array( |
||||||
|
'area' => 1, |
||||||
|
'base' => 1, |
||||||
|
'br' => 1, |
||||||
|
'col' => 1, |
||||||
|
'command' => 1, |
||||||
|
'embed' => 1, |
||||||
|
'hr' => 1, |
||||||
|
'img' => 1, |
||||||
|
'input' => 1, |
||||||
|
'keygen' => 1, |
||||||
|
'link' => 1, |
||||||
|
'meta' => 1, |
||||||
|
'param' => 1, |
||||||
|
'source' => 1, |
||||||
|
'track' => 1, |
||||||
|
'wbr' => 1, |
||||||
|
); |
||||||
|
/** |
||||||
|
* @var boolean whether to show the values of boolean attributes in element tags. |
||||||
|
* If false, only the attribute names will be generated. |
||||||
|
* @see booleanAttributes |
||||||
|
*/ |
||||||
|
public static $showBooleanAttributeValues = true; |
||||||
|
/** |
||||||
|
* @var array list of boolean attributes. The presence of a boolean attribute on |
||||||
|
* an element represents the true value, and the absence of the attribute represents the false value. |
||||||
|
* @see showBooleanAttributeValues |
||||||
|
* @see http://www.w3.org/TR/html5/infrastructure.html#boolean-attributes |
||||||
|
*/ |
||||||
|
public static $booleanAttributes = array( |
||||||
|
'async' => 1, |
||||||
|
'autofocus' => 1, |
||||||
|
'autoplay' => 1, |
||||||
|
'checked' => 1, |
||||||
|
'controls' => 1, |
||||||
|
'declare' => 1, |
||||||
|
'default' => 1, |
||||||
|
'defer' => 1, |
||||||
|
'disabled' => 1, |
||||||
|
'formnovalidate' => 1, |
||||||
|
'hidden' => 1, |
||||||
|
'ismap' => 1, |
||||||
|
'loop' => 1, |
||||||
|
'multiple' => 1, |
||||||
|
'muted' => 1, |
||||||
|
'nohref' => 1, |
||||||
|
'noresize' => 1, |
||||||
|
'novalidate' => 1, |
||||||
|
'open' => 1, |
||||||
|
'readonly' => 1, |
||||||
|
'required' => 1, |
||||||
|
'reversed' => 1, |
||||||
|
'scoped' => 1, |
||||||
|
'seamless' => 1, |
||||||
|
'selected' => 1, |
||||||
|
'typemustmatch' => 1, |
||||||
|
); |
||||||
|
/** |
||||||
|
* @var array the preferred order of attributes in a tag. This mainly affects the order of the attributes |
||||||
|
* that are rendered by [[renderAttributes()]]. |
||||||
|
*/ |
||||||
|
public static $attributeOrder = array( |
||||||
|
'type', |
||||||
|
'id', |
||||||
|
'class', |
||||||
|
'name', |
||||||
|
'value', |
||||||
|
|
||||||
|
'href', |
||||||
|
'src', |
||||||
|
'action', |
||||||
|
'method', |
||||||
|
|
||||||
|
'selected', |
||||||
|
'checked', |
||||||
|
'readonly', |
||||||
|
'disabled', |
||||||
|
'multiple', |
||||||
|
|
||||||
|
'size', |
||||||
|
'maxlength', |
||||||
|
'width', |
||||||
|
'height', |
||||||
|
'rows', |
||||||
|
'cols', |
||||||
|
|
||||||
|
'alt', |
||||||
|
'title', |
||||||
|
'rel', |
||||||
|
'media', |
||||||
|
); |
||||||
|
|
||||||
|
/** |
||||||
|
* Encodes special characters into HTML entities. |
||||||
|
* The [[yii\base\Application::charset|application charset]] will be used for encoding. |
||||||
|
* @param string $content the content to be encoded |
||||||
|
* @return string the encoded content |
||||||
|
* @see decode |
||||||
|
* @see http://www.php.net/manual/en/function.htmlspecialchars.php |
||||||
|
*/ |
||||||
|
public static function encode($content) |
||||||
|
{ |
||||||
|
return htmlspecialchars($content, ENT_QUOTES, Yii::$app->charset); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Decodes special HTML entities back to the corresponding characters. |
||||||
|
* This is the opposite of [[encode()]]. |
||||||
|
* @param string $content the content to be decoded |
||||||
|
* @return string the decoded content |
||||||
|
* @see encode |
||||||
|
* @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php |
||||||
|
*/ |
||||||
|
public static function decode($content) |
||||||
|
{ |
||||||
|
return htmlspecialchars_decode($content, ENT_QUOTES); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a complete HTML tag. |
||||||
|
* @param string $name the tag name |
||||||
|
* @param string $content the content to be enclosed between the start and end tags. It will not be HTML-encoded. |
||||||
|
* If this is coming from end users, you should consider [[encode()]] it to prevent XSS attacks. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated HTML tag |
||||||
|
* @see beginTag |
||||||
|
* @see endTag |
||||||
|
*/ |
||||||
|
public static function tag($name, $content = '', $options = array()) |
||||||
|
{ |
||||||
|
$html = '<' . $name . static::renderTagAttributes($options); |
||||||
|
if (isset(static::$voidElements[strtolower($name)])) { |
||||||
|
return $html . (static::$closeVoidElements ? ' />' : '>'); |
||||||
|
} else { |
||||||
|
return $html . ">$content</$name>"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a start tag. |
||||||
|
* @param string $name the tag name |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated start tag |
||||||
|
* @see endTag |
||||||
|
* @see tag |
||||||
|
*/ |
||||||
|
public static function beginTag($name, $options = array()) |
||||||
|
{ |
||||||
|
return '<' . $name . static::renderTagAttributes($options) . '>'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates an end tag. |
||||||
|
* @param string $name the tag name |
||||||
|
* @return string the generated end tag |
||||||
|
* @see beginTag |
||||||
|
* @see tag |
||||||
|
*/ |
||||||
|
public static function endTag($name) |
||||||
|
{ |
||||||
|
return "</$name>"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Encloses the given content within a CDATA tag. |
||||||
|
* @param string $content the content to be enclosed within the CDATA tag |
||||||
|
* @return string the CDATA tag with the enclosed content. |
||||||
|
*/ |
||||||
|
public static function cdata($content) |
||||||
|
{ |
||||||
|
return '<![CDATA[' . $content . ']]>'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a style tag. |
||||||
|
* @param string $content the style content |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* If the options does not contain "type", a "type" attribute with value "text/css" will be used. |
||||||
|
* @return string the generated style tag |
||||||
|
*/ |
||||||
|
public static function style($content, $options = array()) |
||||||
|
{ |
||||||
|
if (!isset($options['type'])) { |
||||||
|
$options['type'] = 'text/css'; |
||||||
|
} |
||||||
|
return static::tag('style', "/*<![CDATA[*/\n{$content}\n/*]]>*/", $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a script tag. |
||||||
|
* @param string $content the script content |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* If the options does not contain "type", a "type" attribute with value "text/javascript" will be rendered. |
||||||
|
* @return string the generated script tag |
||||||
|
*/ |
||||||
|
public static function script($content, $options = array()) |
||||||
|
{ |
||||||
|
if (!isset($options['type'])) { |
||||||
|
$options['type'] = 'text/javascript'; |
||||||
|
} |
||||||
|
return static::tag('script', "/*<![CDATA[*/\n{$content}\n/*]]>*/", $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a link tag that refers to an external CSS file. |
||||||
|
* @param array|string $url the URL of the external CSS file. This parameter will be processed by [[url()]]. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated link tag |
||||||
|
* @see url |
||||||
|
*/ |
||||||
|
public static function cssFile($url, $options = array()) |
||||||
|
{ |
||||||
|
$options['rel'] = 'stylesheet'; |
||||||
|
$options['type'] = 'text/css'; |
||||||
|
$options['href'] = static::url($url); |
||||||
|
return static::tag('link', '', $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a script tag that refers to an external JavaScript file. |
||||||
|
* @param string $url the URL of the external JavaScript file. This parameter will be processed by [[url()]]. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated script tag |
||||||
|
* @see url |
||||||
|
*/ |
||||||
|
public static function jsFile($url, $options = array()) |
||||||
|
{ |
||||||
|
$options['type'] = 'text/javascript'; |
||||||
|
$options['src'] = static::url($url); |
||||||
|
return static::tag('script', '', $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a form start tag. |
||||||
|
* @param array|string $action the form action URL. This parameter will be processed by [[url()]]. |
||||||
|
* @param string $method the form submission method, either "post" or "get" (case-insensitive) |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated form start tag. |
||||||
|
* @see endForm |
||||||
|
*/ |
||||||
|
public static function beginForm($action = '', $method = 'post', $options = array()) |
||||||
|
{ |
||||||
|
$action = static::url($action); |
||||||
|
|
||||||
|
// query parameters in the action are ignored for GET method |
||||||
|
// we use hidden fields to add them back |
||||||
|
$hiddens = array(); |
||||||
|
if (!strcasecmp($method, 'get') && ($pos = strpos($action, '?')) !== false) { |
||||||
|
foreach (explode('&', substr($action, $pos + 1)) as $pair) { |
||||||
|
if (($pos1 = strpos($pair, '=')) !== false) { |
||||||
|
$hiddens[] = static::hiddenInput(urldecode(substr($pair, 0, $pos1)), urldecode(substr($pair, $pos1 + 1))); |
||||||
|
} else { |
||||||
|
$hiddens[] = static::hiddenInput(urldecode($pair), ''); |
||||||
|
} |
||||||
|
} |
||||||
|
$action = substr($action, 0, $pos); |
||||||
|
} |
||||||
|
|
||||||
|
$options['action'] = $action; |
||||||
|
$options['method'] = $method; |
||||||
|
$form = static::beginTag('form', $options); |
||||||
|
if ($hiddens !== array()) { |
||||||
|
$form .= "\n" . implode("\n", $hiddens); |
||||||
|
} |
||||||
|
|
||||||
|
return $form; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a form end tag. |
||||||
|
* @return string the generated tag |
||||||
|
* @see beginForm |
||||||
|
*/ |
||||||
|
public static function endForm() |
||||||
|
{ |
||||||
|
return '</form>'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a hyperlink tag. |
||||||
|
* @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code |
||||||
|
* such as an image tag. If this is is coming from end users, you should consider [[encode()]] |
||||||
|
* it to prevent XSS attacks. |
||||||
|
* @param array|string|null $url the URL for the hyperlink tag. This parameter will be processed by [[url()]] |
||||||
|
* and will be used for the "href" attribute of the tag. If this parameter is null, the "href" attribute |
||||||
|
* will not be generated. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated hyperlink |
||||||
|
* @see url |
||||||
|
*/ |
||||||
|
public static function a($text, $url = null, $options = array()) |
||||||
|
{ |
||||||
|
if ($url !== null) { |
||||||
|
$options['href'] = static::url($url); |
||||||
|
} |
||||||
|
return static::tag('a', $text, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a mailto hyperlink. |
||||||
|
* @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code |
||||||
|
* such as an image tag. If this is is coming from end users, you should consider [[encode()]] |
||||||
|
* it to prevent XSS attacks. |
||||||
|
* @param string $email email address. If this is null, the first parameter (link body) will be treated |
||||||
|
* as the email address and used. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated mailto link |
||||||
|
*/ |
||||||
|
public static function mailto($text, $email = null, $options = array()) |
||||||
|
{ |
||||||
|
return static::a($text, 'mailto:' . ($email === null ? $text : $email), $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates an image tag. |
||||||
|
* @param string $src the image URL. This parameter will be processed by [[url()]]. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated image tag |
||||||
|
*/ |
||||||
|
public static function img($src, $options = array()) |
||||||
|
{ |
||||||
|
$options['src'] = static::url($src); |
||||||
|
if (!isset($options['alt'])) { |
||||||
|
$options['alt'] = ''; |
||||||
|
} |
||||||
|
return static::tag('img', null, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a label tag. |
||||||
|
* @param string $content label text. It will NOT be HTML-encoded. Therefore you can pass in HTML code |
||||||
|
* such as an image tag. If this is is coming from end users, you should consider [[encode()]] |
||||||
|
* it to prevent XSS attacks. |
||||||
|
* @param string $for the ID of the HTML element that this label is associated with. |
||||||
|
* If this is null, the "for" attribute will not be generated. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated label tag |
||||||
|
*/ |
||||||
|
public static function label($content, $for = null, $options = array()) |
||||||
|
{ |
||||||
|
$options['for'] = $for; |
||||||
|
return static::tag('label', $content, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a button tag. |
||||||
|
* @param string $name the name attribute. If it is null, the name attribute will not be generated. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||||
|
* @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded. |
||||||
|
* Therefore you can pass in HTML code such as an image tag. If this is is coming from end users, |
||||||
|
* you should consider [[encode()]] it to prevent XSS attacks. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* If the options does not contain "type", a "type" attribute with value "button" will be rendered. |
||||||
|
* @return string the generated button tag |
||||||
|
*/ |
||||||
|
public static function button($name = null, $value = null, $content = 'Button', $options = array()) |
||||||
|
{ |
||||||
|
$options['name'] = $name; |
||||||
|
$options['value'] = $value; |
||||||
|
if (!isset($options['type'])) { |
||||||
|
$options['type'] = 'button'; |
||||||
|
} |
||||||
|
return static::tag('button', $content, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a submit button tag. |
||||||
|
* @param string $name the name attribute. If it is null, the name attribute will not be generated. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||||
|
* @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded. |
||||||
|
* Therefore you can pass in HTML code such as an image tag. If this is is coming from end users, |
||||||
|
* you should consider [[encode()]] it to prevent XSS attacks. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated submit button tag |
||||||
|
*/ |
||||||
|
public static function submitButton($name = null, $value = null, $content = 'Submit', $options = array()) |
||||||
|
{ |
||||||
|
$options['type'] = 'submit'; |
||||||
|
return static::button($name, $value, $content, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a reset button tag. |
||||||
|
* @param string $name the name attribute. If it is null, the name attribute will not be generated. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||||
|
* @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded. |
||||||
|
* Therefore you can pass in HTML code such as an image tag. If this is is coming from end users, |
||||||
|
* you should consider [[encode()]] it to prevent XSS attacks. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated reset button tag |
||||||
|
*/ |
||||||
|
public static function resetButton($name = null, $value = null, $content = 'Reset', $options = array()) |
||||||
|
{ |
||||||
|
$options['type'] = 'reset'; |
||||||
|
return static::button($name, $value, $content, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates an input type of the given type. |
||||||
|
* @param string $type the type attribute. |
||||||
|
* @param string $name the name attribute. If it is null, the name attribute will not be generated. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated input tag |
||||||
|
*/ |
||||||
|
public static function input($type, $name = null, $value = null, $options = array()) |
||||||
|
{ |
||||||
|
$options['type'] = $type; |
||||||
|
$options['name'] = $name; |
||||||
|
$options['value'] = $value; |
||||||
|
return static::tag('input', null, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates an input button. |
||||||
|
* @param string $name the name attribute. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated button tag |
||||||
|
*/ |
||||||
|
public static function buttonInput($name, $value = 'Button', $options = array()) |
||||||
|
{ |
||||||
|
return static::input('button', $name, $value, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a submit input button. |
||||||
|
* @param string $name the name attribute. If it is null, the name attribute will not be generated. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated button tag |
||||||
|
*/ |
||||||
|
public static function submitInput($name = null, $value = 'Submit', $options = array()) |
||||||
|
{ |
||||||
|
return static::input('submit', $name, $value, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a reset input button. |
||||||
|
* @param string $name the name attribute. If it is null, the name attribute will not be generated. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||||
|
* @param array $options the attributes of the button tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* Attributes whose value is null will be ignored and not put in the tag returned. |
||||||
|
* @return string the generated button tag |
||||||
|
*/ |
||||||
|
public static function resetInput($name = null, $value = 'Reset', $options = array()) |
||||||
|
{ |
||||||
|
return static::input('reset', $name, $value, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a text input field. |
||||||
|
* @param string $name the name attribute. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated button tag |
||||||
|
*/ |
||||||
|
public static function textInput($name, $value = null, $options = array()) |
||||||
|
{ |
||||||
|
return static::input('text', $name, $value, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a hidden input field. |
||||||
|
* @param string $name the name attribute. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated button tag |
||||||
|
*/ |
||||||
|
public static function hiddenInput($name, $value = null, $options = array()) |
||||||
|
{ |
||||||
|
return static::input('hidden', $name, $value, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a password input field. |
||||||
|
* @param string $name the name attribute. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated button tag |
||||||
|
*/ |
||||||
|
public static function passwordInput($name, $value = null, $options = array()) |
||||||
|
{ |
||||||
|
return static::input('password', $name, $value, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a file input field. |
||||||
|
* To use a file input field, you should set the enclosing form's "enctype" attribute to |
||||||
|
* be "multipart/form-data". After the form is submitted, the uploaded file information |
||||||
|
* can be obtained via $_FILES[$name] (see PHP documentation). |
||||||
|
* @param string $name the name attribute. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be generated. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated button tag |
||||||
|
*/ |
||||||
|
public static function fileInput($name, $value = null, $options = array()) |
||||||
|
{ |
||||||
|
return static::input('file', $name, $value, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a text area input. |
||||||
|
* @param string $name the input name |
||||||
|
* @param string $value the input value. Note that it will be encoded using [[encode()]]. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. These will be rendered as |
||||||
|
* the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. |
||||||
|
* If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* @return string the generated text area tag |
||||||
|
*/ |
||||||
|
public static function textarea($name, $value = '', $options = array()) |
||||||
|
{ |
||||||
|
$options['name'] = $name; |
||||||
|
return static::tag('textarea', static::encode($value), $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a radio button input. |
||||||
|
* @param string $name the name attribute. |
||||||
|
* @param boolean $checked whether the radio button should be checked. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be rendered. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. The following options are supported: |
||||||
|
* |
||||||
|
* - uncheck: string, the value associated with the uncheck state of the radio button. When this attribute |
||||||
|
* is present, a hidden input will be generated so that if the radio button is not checked and is submitted, |
||||||
|
* the value of this attribute will still be submitted to the server via the hidden input. |
||||||
|
* |
||||||
|
* The rest of the options will be rendered as the attributes of the resulting tag. The values will |
||||||
|
* be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* |
||||||
|
* @return string the generated radio button tag |
||||||
|
*/ |
||||||
|
public static function radio($name, $checked = false, $value = '1', $options = array()) |
||||||
|
{ |
||||||
|
$options['checked'] = $checked; |
||||||
|
$options['value'] = $value; |
||||||
|
if (isset($options['uncheck'])) { |
||||||
|
// add a hidden field so that if the radio button is not selected, it still submits a value |
||||||
|
$hidden = static::hiddenInput($name, $options['uncheck']); |
||||||
|
unset($options['uncheck']); |
||||||
|
} else { |
||||||
|
$hidden = ''; |
||||||
|
} |
||||||
|
return $hidden . static::input('radio', $name, $value, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a checkbox input. |
||||||
|
* @param string $name the name attribute. |
||||||
|
* @param boolean $checked whether the checkbox should be checked. |
||||||
|
* @param string $value the value attribute. If it is null, the value attribute will not be rendered. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. The following options are supported: |
||||||
|
* |
||||||
|
* - uncheck: string, the value associated with the uncheck state of the checkbox. When this attribute |
||||||
|
* is present, a hidden input will be generated so that if the checkbox is not checked and is submitted, |
||||||
|
* the value of this attribute will still be submitted to the server via the hidden input. |
||||||
|
* |
||||||
|
* The rest of the options will be rendered as the attributes of the resulting tag. The values will |
||||||
|
* be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* |
||||||
|
* @return string the generated checkbox tag |
||||||
|
*/ |
||||||
|
public static function checkbox($name, $checked = false, $value = '1', $options = array()) |
||||||
|
{ |
||||||
|
$options['checked'] = $checked; |
||||||
|
$options['value'] = $value; |
||||||
|
if (isset($options['uncheck'])) { |
||||||
|
// add a hidden field so that if the checkbox is not selected, it still submits a value |
||||||
|
$hidden = static::hiddenInput($name, $options['uncheck']); |
||||||
|
unset($options['uncheck']); |
||||||
|
} else { |
||||||
|
$hidden = ''; |
||||||
|
} |
||||||
|
return $hidden . static::input('checkbox', $name, $value, $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a drop-down list. |
||||||
|
* @param string $name the input name |
||||||
|
* @param string $selection the selected value |
||||||
|
* @param array $items the option data items. The array keys are option values, and the array values |
||||||
|
* are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too). |
||||||
|
* For each sub-array, an option group will be generated whose label is the key associated with the sub-array. |
||||||
|
* If you have a list of data models, you may convert them into the format described above using |
||||||
|
* [[\yii\helpers\ArrayHelper::map()]]. |
||||||
|
* |
||||||
|
* Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in |
||||||
|
* the labels will also be HTML-encoded. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. The following options are supported: |
||||||
|
* |
||||||
|
* - prompt: string, a prompt text to be displayed as the first option; |
||||||
|
* - options: array, the attributes for the select option tags. The array keys must be valid option values, |
||||||
|
* and the array values are the extra attributes for the corresponding option tags. For example, |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* array( |
||||||
|
* 'value1' => array('disabled' => true), |
||||||
|
* 'value2' => array('label' => 'value 2'), |
||||||
|
* ); |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options', |
||||||
|
* except that the array keys represent the optgroup labels specified in $items. |
||||||
|
* |
||||||
|
* The rest of the options will be rendered as the attributes of the resulting tag. The values will |
||||||
|
* be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* |
||||||
|
* @return string the generated drop-down list tag |
||||||
|
*/ |
||||||
|
public static function dropDownList($name, $selection = null, $items = array(), $options = array()) |
||||||
|
{ |
||||||
|
$options['name'] = $name; |
||||||
|
$selectOptions = static::renderSelectOptions($selection, $items, $options); |
||||||
|
return static::tag('select', "\n" . $selectOptions . "\n", $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a list box. |
||||||
|
* @param string $name the input name |
||||||
|
* @param string|array $selection the selected value(s) |
||||||
|
* @param array $items the option data items. The array keys are option values, and the array values |
||||||
|
* are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too). |
||||||
|
* For each sub-array, an option group will be generated whose label is the key associated with the sub-array. |
||||||
|
* If you have a list of data models, you may convert them into the format described above using |
||||||
|
* [[\yii\helpers\ArrayHelper::map()]]. |
||||||
|
* |
||||||
|
* Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in |
||||||
|
* the labels will also be HTML-encoded. |
||||||
|
* @param array $options the tag options in terms of name-value pairs. The following options are supported: |
||||||
|
* |
||||||
|
* - prompt: string, a prompt text to be displayed as the first option; |
||||||
|
* - options: array, the attributes for the select option tags. The array keys must be valid option values, |
||||||
|
* and the array values are the extra attributes for the corresponding option tags. For example, |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* array( |
||||||
|
* 'value1' => array('disabled' => true), |
||||||
|
* 'value2' => array('label' => 'value 2'), |
||||||
|
* ); |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options', |
||||||
|
* except that the array keys represent the optgroup labels specified in $items. |
||||||
|
* - unselect: string, the value that will be submitted when no option is selected. |
||||||
|
* When this attribute is set, a hidden field will be generated so that if no option is selected in multiple |
||||||
|
* mode, we can still obtain the posted unselect value. |
||||||
|
* |
||||||
|
* The rest of the options will be rendered as the attributes of the resulting tag. The values will |
||||||
|
* be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. |
||||||
|
* |
||||||
|
* @return string the generated list box tag |
||||||
|
*/ |
||||||
|
public static function listBox($name, $selection = null, $items = array(), $options = array()) |
||||||
|
{ |
||||||
|
if (!isset($options['size'])) { |
||||||
|
$options['size'] = 4; |
||||||
|
} |
||||||
|
if (!empty($options['multiple']) && substr($name, -2) !== '[]') { |
||||||
|
$name .= '[]'; |
||||||
|
} |
||||||
|
$options['name'] = $name; |
||||||
|
if (isset($options['unselect'])) { |
||||||
|
// add a hidden field so that if the list box has no option being selected, it still submits a value |
||||||
|
if (substr($name, -2) === '[]') { |
||||||
|
$name = substr($name, 0, -2); |
||||||
|
} |
||||||
|
$hidden = static::hiddenInput($name, $options['unselect']); |
||||||
|
unset($options['unselect']); |
||||||
|
} else { |
||||||
|
$hidden = ''; |
||||||
|
} |
||||||
|
$selectOptions = static::renderSelectOptions($selection, $items, $options); |
||||||
|
return $hidden . static::tag('select', "\n" . $selectOptions . "\n", $options); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a list of checkboxes. |
||||||
|
* A checkbox list allows multiple selection, like [[listBox()]]. |
||||||
|
* As a result, the corresponding submitted value is an array. |
||||||
|
* @param string $name the name attribute of each checkbox. |
||||||
|
* @param string|array $selection the selected value(s). |
||||||
|
* @param array $items the data item used to generate the checkboxes. |
||||||
|
* The array keys are the labels, while the array values are the corresponding checkbox values. |
||||||
|
* Note that the labels will NOT be HTML-encoded, while the values will. |
||||||
|
* @param array $options options (name => config) for the checkbox list. The following options are supported: |
||||||
|
* |
||||||
|
* - unselect: string, the value that should be submitted when none of the checkboxes is selected. |
||||||
|
* By setting this option, a hidden input will be generated. |
||||||
|
* - separator: string, the HTML code that separates items. |
||||||
|
* - item: callable, a callback that can be used to customize the generation of the HTML code |
||||||
|
* corresponding to a single item in $items. The signature of this callback must be: |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* function ($index, $label, $name, $checked, $value) |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* where $index is the zero-based index of the checkbox in the whole list; $label |
||||||
|
* is the label for the checkbox; and $name, $value and $checked represent the name, |
||||||
|
* value and the checked status of the checkbox input. |
||||||
|
* @return string the generated checkbox list |
||||||
|
*/ |
||||||
|
public static function checkboxList($name, $selection = null, $items = array(), $options = array()) |
||||||
|
{ |
||||||
|
if (substr($name, -2) !== '[]') { |
||||||
|
$name .= '[]'; |
||||||
|
} |
||||||
|
|
||||||
|
$formatter = isset($options['item']) ? $options['item'] : null; |
||||||
|
$lines = array(); |
||||||
|
$index = 0; |
||||||
|
foreach ($items as $value => $label) { |
||||||
|
$checked = $selection !== null && |
||||||
|
(!is_array($selection) && !strcmp($value, $selection) |
||||||
|
|| is_array($selection) && in_array($value, $selection)); |
||||||
|
if ($formatter !== null) { |
||||||
|
$lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value); |
||||||
|
} else { |
||||||
|
$lines[] = static::label(static::checkbox($name, $checked, $value) . ' ' . $label); |
||||||
|
} |
||||||
|
$index++; |
||||||
|
} |
||||||
|
|
||||||
|
if (isset($options['unselect'])) { |
||||||
|
// add a hidden field so that if the list box has no option being selected, it still submits a value |
||||||
|
$name2 = substr($name, -2) === '[]' ? substr($name, 0, -2) : $name; |
||||||
|
$hidden = static::hiddenInput($name2, $options['unselect']); |
||||||
|
} else { |
||||||
|
$hidden = ''; |
||||||
|
} |
||||||
|
$separator = isset($options['separator']) ? $options['separator'] : "\n"; |
||||||
|
|
||||||
|
return $hidden . implode($separator, $lines); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a list of radio buttons. |
||||||
|
* A radio button list is like a checkbox list, except that it only allows single selection. |
||||||
|
* @param string $name the name attribute of each radio button. |
||||||
|
* @param string|array $selection the selected value(s). |
||||||
|
* @param array $items the data item used to generate the radio buttons. |
||||||
|
* The array keys are the labels, while the array values are the corresponding radio button values. |
||||||
|
* Note that the labels will NOT be HTML-encoded, while the values will. |
||||||
|
* @param array $options options (name => config) for the radio button list. The following options are supported: |
||||||
|
* |
||||||
|
* - unselect: string, the value that should be submitted when none of the radio buttons is selected. |
||||||
|
* By setting this option, a hidden input will be generated. |
||||||
|
* - separator: string, the HTML code that separates items. |
||||||
|
* - item: callable, a callback that can be used to customize the generation of the HTML code |
||||||
|
* corresponding to a single item in $items. The signature of this callback must be: |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* function ($index, $label, $name, $checked, $value) |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* where $index is the zero-based index of the radio button in the whole list; $label |
||||||
|
* is the label for the radio button; and $name, $value and $checked represent the name, |
||||||
|
* value and the checked status of the radio button input. |
||||||
|
* @return string the generated radio button list |
||||||
|
*/ |
||||||
|
public static function radioList($name, $selection = null, $items = array(), $options = array()) |
||||||
|
{ |
||||||
|
$formatter = isset($options['item']) ? $options['item'] : null; |
||||||
|
$lines = array(); |
||||||
|
$index = 0; |
||||||
|
foreach ($items as $value => $label) { |
||||||
|
$checked = $selection !== null && |
||||||
|
(!is_array($selection) && !strcmp($value, $selection) |
||||||
|
|| is_array($selection) && in_array($value, $selection)); |
||||||
|
if ($formatter !== null) { |
||||||
|
$lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value); |
||||||
|
} else { |
||||||
|
$lines[] = static::label(static::radio($name, $checked, $value) . ' ' . $label); |
||||||
|
} |
||||||
|
$index++; |
||||||
|
} |
||||||
|
|
||||||
|
$separator = isset($options['separator']) ? $options['separator'] : "\n"; |
||||||
|
if (isset($options['unselect'])) { |
||||||
|
// add a hidden field so that if the list box has no option being selected, it still submits a value |
||||||
|
$hidden = static::hiddenInput($name, $options['unselect']); |
||||||
|
} else { |
||||||
|
$hidden = ''; |
||||||
|
} |
||||||
|
|
||||||
|
return $hidden . implode($separator, $lines); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Renders the option tags that can be used by [[dropDownList()]] and [[listBox()]]. |
||||||
|
* @param string|array $selection the selected value(s). This can be either a string for single selection |
||||||
|
* or an array for multiple selections. |
||||||
|
* @param array $items the option data items. The array keys are option values, and the array values |
||||||
|
* are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too). |
||||||
|
* For each sub-array, an option group will be generated whose label is the key associated with the sub-array. |
||||||
|
* If you have a list of data models, you may convert them into the format described above using |
||||||
|
* [[\yii\helpers\ArrayHelper::map()]]. |
||||||
|
* |
||||||
|
* Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in |
||||||
|
* the labels will also be HTML-encoded. |
||||||
|
* @param array $tagOptions the $options parameter that is passed to the [[dropDownList()]] or [[listBox()]] call. |
||||||
|
* This method will take out these elements, if any: "prompt", "options" and "groups". See more details |
||||||
|
* in [[dropDownList()]] for the explanation of these elements. |
||||||
|
* |
||||||
|
* @return string the generated list options |
||||||
|
*/ |
||||||
|
public static function renderSelectOptions($selection, $items, &$tagOptions = array()) |
||||||
|
{ |
||||||
|
$lines = array(); |
||||||
|
if (isset($tagOptions['prompt'])) { |
||||||
|
$prompt = str_replace(' ', ' ', static::encode($tagOptions['prompt'])); |
||||||
|
$lines[] = static::tag('option', $prompt, array('value' => '')); |
||||||
|
} |
||||||
|
|
||||||
|
$options = isset($tagOptions['options']) ? $tagOptions['options'] : array(); |
||||||
|
$groups = isset($tagOptions['groups']) ? $tagOptions['groups'] : array(); |
||||||
|
unset($tagOptions['prompt'], $tagOptions['options'], $tagOptions['groups']); |
||||||
|
|
||||||
|
foreach ($items as $key => $value) { |
||||||
|
if (is_array($value)) { |
||||||
|
$groupAttrs = isset($groups[$key]) ? $groups[$key] : array(); |
||||||
|
$groupAttrs['label'] = $key; |
||||||
|
$attrs = array('options' => $options, 'groups' => $groups); |
||||||
|
$content = static::renderSelectOptions($selection, $value, $attrs); |
||||||
|
$lines[] = static::tag('optgroup', "\n" . $content . "\n", $groupAttrs); |
||||||
|
} else { |
||||||
|
$attrs = isset($options[$key]) ? $options[$key] : array(); |
||||||
|
$attrs['value'] = $key; |
||||||
|
$attrs['selected'] = $selection !== null && |
||||||
|
(!is_array($selection) && !strcmp($key, $selection) |
||||||
|
|| is_array($selection) && in_array($key, $selection)); |
||||||
|
$lines[] = static::tag('option', str_replace(' ', ' ', static::encode($value)), $attrs); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return implode("\n", $lines); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Renders the HTML tag attributes. |
||||||
|
* Boolean attributes such as s 'checked', 'disabled', 'readonly', will be handled specially |
||||||
|
* according to [[booleanAttributes]] and [[showBooleanAttributeValues]]. |
||||||
|
* @param array $attributes attributes to be rendered. The attribute values will be HTML-encoded using [[encode()]]. |
||||||
|
* Attributes whose value is null will be ignored and not put in the rendering result. |
||||||
|
* @return string the rendering result. If the attributes are not empty, they will be rendered |
||||||
|
* into a string with a leading white space (such that it can be directly appended to the tag name |
||||||
|
* in a tag. If there is no attribute, an empty string will be returned. |
||||||
|
*/ |
||||||
|
public static function renderTagAttributes($attributes) |
||||||
|
{ |
||||||
|
if (count($attributes) > 1) { |
||||||
|
$sorted = array(); |
||||||
|
foreach (static::$attributeOrder as $name) { |
||||||
|
if (isset($attributes[$name])) { |
||||||
|
$sorted[$name] = $attributes[$name]; |
||||||
|
} |
||||||
|
} |
||||||
|
$attributes = array_merge($sorted, $attributes); |
||||||
|
} |
||||||
|
|
||||||
|
$html = ''; |
||||||
|
foreach ($attributes as $name => $value) { |
||||||
|
if (isset(static::$booleanAttributes[strtolower($name)])) { |
||||||
|
if ($value || strcasecmp($name, $value) === 0) { |
||||||
|
$html .= static::$showBooleanAttributeValues ? " $name=\"$name\"" : " $name"; |
||||||
|
} |
||||||
|
} elseif ($value !== null) { |
||||||
|
$html .= " $name=\"" . static::encode($value) . '"'; |
||||||
|
} |
||||||
|
} |
||||||
|
return $html; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Normalizes the input parameter to be a valid URL. |
||||||
|
* |
||||||
|
* If the input parameter |
||||||
|
* |
||||||
|
* - is an empty string: the currently requested URL will be returned; |
||||||
|
* - is a non-empty string: it will be processed by [[Yii::getAlias()]] and returned; |
||||||
|
* - is an array: the first array element is considered a route, while the rest of the name-value |
||||||
|
* pairs are treated as the parameters to be used for URL creation using [[\yii\web\Controller::createUrl()]]. |
||||||
|
* For example: `array('post/index', 'page' => 2)`, `array('index')`. |
||||||
|
* |
||||||
|
* @param array|string $url the parameter to be used to generate a valid URL |
||||||
|
* @return string the normalized URL |
||||||
|
* @throws InvalidParamException if the parameter is invalid. |
||||||
|
*/ |
||||||
|
public static function url($url) |
||||||
|
{ |
||||||
|
if (is_array($url)) { |
||||||
|
if (isset($url[0])) { |
||||||
|
$route = $url[0]; |
||||||
|
$params = array_splice($url, 1); |
||||||
|
if (Yii::$app->controller !== null) { |
||||||
|
return Yii::$app->controller->createUrl($route, $params); |
||||||
|
} else { |
||||||
|
return Yii::$app->getUrlManager()->createUrl($route, $params); |
||||||
|
} |
||||||
|
} else { |
||||||
|
throw new InvalidParamException('The array specifying a URL must contain at least one element.'); |
||||||
|
} |
||||||
|
} elseif ($url === '') { |
||||||
|
return Yii::$app->getRequest()->getUrl(); |
||||||
|
} else { |
||||||
|
return Yii::getAlias($url); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,272 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\helpers\base; |
||||||
|
|
||||||
|
use Yii; |
||||||
|
use yii\base\Exception; |
||||||
|
use yii\base\InvalidConfigException; |
||||||
|
use yii\base\InvalidParamException; |
||||||
|
|
||||||
|
/** |
||||||
|
* SecurityHelper provides a set of methods to handle common security-related tasks. |
||||||
|
* |
||||||
|
* In particular, SecurityHelper supports the following features: |
||||||
|
* |
||||||
|
* - Encryption/decryption: [[encrypt()]] and [[decrypt()]] |
||||||
|
* - Data tampering prevention: [[hashData()]] and [[validateData()]] |
||||||
|
* - Password validation: [[generatePasswordHash()]] and [[validatePassword()]] |
||||||
|
* |
||||||
|
* Additionally, SecurityHelper provides [[getSecretKey()]] to support generating |
||||||
|
* named secret keys. These secret keys, once generated, will be stored in a file |
||||||
|
* and made available in future requests. |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @author Tom Worster <fsb@thefsb.org> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class SecurityHelper |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Encrypts data. |
||||||
|
* @param string $data data to be encrypted. |
||||||
|
* @param string $key the encryption secret key |
||||||
|
* @return string the encrypted data |
||||||
|
* @throws Exception if PHP Mcrypt extension is not loaded or failed to be initialized |
||||||
|
* @see decrypt() |
||||||
|
*/ |
||||||
|
public static function encrypt($data, $key) |
||||||
|
{ |
||||||
|
$module = static::openCryptModule(); |
||||||
|
$key = StringHelper::substr($key, 0, mcrypt_enc_get_key_size($module)); |
||||||
|
srand(); |
||||||
|
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($module), MCRYPT_RAND); |
||||||
|
mcrypt_generic_init($module, $key, $iv); |
||||||
|
$encrypted = $iv . mcrypt_generic($module, $data); |
||||||
|
mcrypt_generic_deinit($module); |
||||||
|
mcrypt_module_close($module); |
||||||
|
return $encrypted; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Decrypts data |
||||||
|
* @param string $data data to be decrypted. |
||||||
|
* @param string $key the decryption secret key |
||||||
|
* @return string the decrypted data |
||||||
|
* @throws Exception if PHP Mcrypt extension is not loaded or failed to be initialized |
||||||
|
* @see encrypt() |
||||||
|
*/ |
||||||
|
public static function decrypt($data, $key) |
||||||
|
{ |
||||||
|
$module = static::openCryptModule(); |
||||||
|
$key = StringHelper::substr($key, 0, mcrypt_enc_get_key_size($module)); |
||||||
|
$ivSize = mcrypt_enc_get_iv_size($module); |
||||||
|
$iv = StringHelper::substr($data, 0, $ivSize); |
||||||
|
mcrypt_generic_init($module, $key, $iv); |
||||||
|
$decrypted = mdecrypt_generic($module, StringHelper::substr($data, $ivSize, StringHelper::strlen($data))); |
||||||
|
mcrypt_generic_deinit($module); |
||||||
|
mcrypt_module_close($module); |
||||||
|
return rtrim($decrypted, "\0"); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Prefixes data with a keyed hash value so that it can later be detected if it is tampered. |
||||||
|
* @param string $data the data to be protected |
||||||
|
* @param string $key the secret key to be used for generating hash |
||||||
|
* @param string $algorithm the hashing algorithm (e.g. "md5", "sha1", "sha256", etc.). Call PHP "hash_algos()" |
||||||
|
* function to see the supported hashing algorithms on your system. |
||||||
|
* @return string the data prefixed with the keyed hash |
||||||
|
* @see validateData() |
||||||
|
* @see getSecretKey() |
||||||
|
*/ |
||||||
|
public static function hashData($data, $key, $algorithm = 'sha256') |
||||||
|
{ |
||||||
|
return hash_hmac($algorithm, $data, $key) . $data; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Validates if the given data is tampered. |
||||||
|
* @param string $data the data to be validated. The data must be previously |
||||||
|
* generated by [[hashData()]]. |
||||||
|
* @param string $key the secret key that was previously used to generate the hash for the data in [[hashData()]]. |
||||||
|
* @param string $algorithm the hashing algorithm (e.g. "md5", "sha1", "sha256", etc.). Call PHP "hash_algos()" |
||||||
|
* function to see the supported hashing algorithms on your system. This must be the same |
||||||
|
* as the value passed to [[hashData()]] when generating the hash for the data. |
||||||
|
* @return string the real data with the hash stripped off. False if the data is tampered. |
||||||
|
* @see hashData() |
||||||
|
*/ |
||||||
|
public static function validateData($data, $key, $algorithm = 'sha256') |
||||||
|
{ |
||||||
|
$hashSize = StringHelper::strlen(hash_hmac($algorithm, 'test', $key)); |
||||||
|
$n = StringHelper::strlen($data); |
||||||
|
if ($n >= $hashSize) { |
||||||
|
$hash = StringHelper::substr($data, 0, $hashSize); |
||||||
|
$data2 = StringHelper::substr($data, $hashSize, $n - $hashSize); |
||||||
|
return $hash === hash_hmac($algorithm, $data2, $key) ? $data2 : false; |
||||||
|
} else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a secret key associated with the specified name. |
||||||
|
* If the secret key does not exist, a random key will be generated |
||||||
|
* and saved in the file "keys.php" under the application's runtime directory |
||||||
|
* so that the same secret key can be returned in future requests. |
||||||
|
* @param string $name the name that is associated with the secret key |
||||||
|
* @param integer $length the length of the key that should be generated if not exists |
||||||
|
* @return string the secret key associated with the specified name |
||||||
|
*/ |
||||||
|
public static function getSecretKey($name, $length = 32) |
||||||
|
{ |
||||||
|
static $keys; |
||||||
|
$keyFile = Yii::$app->getRuntimePath() . '/keys.php'; |
||||||
|
if ($keys === null) { |
||||||
|
$keys = is_file($keyFile) ? require($keyFile) : array(); |
||||||
|
} |
||||||
|
if (!isset($keys[$name])) { |
||||||
|
// generate a 32-char random key |
||||||
|
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; |
||||||
|
$keys[$name] = substr(str_shuffle(str_repeat($chars, 5)), 0, $length); |
||||||
|
file_put_contents($keyFile, "<?php\nreturn " . var_export($keys, true) . ";\n");
|
||||||
|
} |
||||||
|
return $keys[$name]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Opens the mcrypt module. |
||||||
|
* @return resource the mcrypt module handle. |
||||||
|
* @throws InvalidConfigException if mcrypt extension is not installed |
||||||
|
* @throws Exception if mcrypt initialization fails |
||||||
|
*/ |
||||||
|
protected static function openCryptModule() |
||||||
|
{ |
||||||
|
if (!extension_loaded('mcrypt')) { |
||||||
|
throw new InvalidConfigException('The mcrypt PHP extension is not installed.'); |
||||||
|
} |
||||||
|
$module = @mcrypt_module_open('rijndael-256', '', MCRYPT_MODE_CBC, ''); |
||||||
|
if ($module === false) { |
||||||
|
throw new Exception('Failed to initialize the mcrypt module.'); |
||||||
|
} |
||||||
|
return $module; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a secure hash from a password and a random salt. |
||||||
|
* |
||||||
|
* The generated hash can be stored in database (e.g. `CHAR(64) CHARACTER SET latin1` on MySQL). |
||||||
|
* Later when a password needs to be validated, the hash can be fetched and passed |
||||||
|
* to [[validatePassword()]]. For example, |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* // generates the hash (usually done during user registration or when the password is changed) |
||||||
|
* $hash = SecurityHelper::hashPassword($password); |
||||||
|
* // ...save $hash in database... |
||||||
|
* |
||||||
|
* // during login, validate if the password entered is correct using $hash fetched from database |
||||||
|
* if (PasswordHelper::verifyPassword($password, $hash) { |
||||||
|
* // password is good |
||||||
|
* } else { |
||||||
|
* // password is bad |
||||||
|
* } |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* @param string $password The password to be hashed. |
||||||
|
* @param integer $cost Cost parameter used by the Blowfish hash algorithm. |
||||||
|
* The higher the value of cost, |
||||||
|
* the longer it takes to generate the hash and to verify a password against it. Higher cost |
||||||
|
* therefore slows down a brute-force attack. For best protection against brute for attacks, |
||||||
|
* set it to the highest value that is tolerable on production servers. The time taken to |
||||||
|
* compute the hash doubles for every increment by one of $cost. So, for example, if the |
||||||
|
* hash takes 1 second to compute when $cost is 14 then then the compute time varies as |
||||||
|
* 2^($cost - 14) seconds. |
||||||
|
* @throws Exception on bad password parameter or cost parameter |
||||||
|
* @return string The password hash string, ASCII and not longer than 64 characters. |
||||||
|
* @see validatePassword() |
||||||
|
*/ |
||||||
|
public static function generatePasswordHash($password, $cost = 13) |
||||||
|
{ |
||||||
|
$salt = static::generateSalt($cost); |
||||||
|
$hash = crypt($password, $salt); |
||||||
|
|
||||||
|
if (!is_string($hash) || strlen($hash) < 32) { |
||||||
|
throw new Exception('Unknown error occurred while generating hash.'); |
||||||
|
} |
||||||
|
|
||||||
|
return $hash; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Verifies a password against a hash. |
||||||
|
* @param string $password The password to verify. |
||||||
|
* @param string $hash The hash to verify the password against. |
||||||
|
* @return boolean whether the password is correct. |
||||||
|
* @throws InvalidParamException on bad password or hash parameters or if crypt() with Blowfish hash is not available. |
||||||
|
* @see generatePasswordHash() |
||||||
|
*/ |
||||||
|
public static function validatePassword($password, $hash) |
||||||
|
{ |
||||||
|
if (!is_string($password) || $password === '') { |
||||||
|
throw new InvalidParamException('Password must be a string and cannot be empty.'); |
||||||
|
} |
||||||
|
|
||||||
|
if (!preg_match('/^\$2[axy]\$(\d\d)\$[\./0-9A-Za-z]{22}/', $hash, $matches) || $matches[1] < 4 || $matches[1] > 30) { |
||||||
|
throw new InvalidParamException('Hash is invalid.'); |
||||||
|
} |
||||||
|
|
||||||
|
$test = crypt($password, $hash); |
||||||
|
$n = strlen($test); |
||||||
|
if (strlen($test) < 32 || $n !== strlen($hash)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// Use a for-loop to compare two strings to prevent timing attacks. See: |
||||||
|
// http://codereview.stackexchange.com/questions/13512 |
||||||
|
$check = 0; |
||||||
|
for ($i = 0; $i < $n; ++$i) { |
||||||
|
$check |= (ord($test[$i]) ^ ord($hash[$i])); |
||||||
|
} |
||||||
|
|
||||||
|
return $check === 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a salt that can be used to generate a password hash. |
||||||
|
* |
||||||
|
* The PHP [crypt()](http://php.net/manual/en/function.crypt.php) built-in function |
||||||
|
* requires, for the Blowfish hash algorithm, a salt string in a specific format: |
||||||
|
* "$2a$", "$2x$" or "$2y$", a two digit cost parameter, "$", and 22 characters |
||||||
|
* from the alphabet "./0-9A-Za-z". |
||||||
|
* |
||||||
|
* @param integer $cost the cost parameter |
||||||
|
* @return string the random salt value. |
||||||
|
* @throws InvalidParamException if the cost parameter is not between 4 and 30 |
||||||
|
*/ |
||||||
|
protected static function generateSalt($cost = 13) |
||||||
|
{ |
||||||
|
$cost = (int)$cost; |
||||||
|
if ($cost < 4 || $cost > 30) { |
||||||
|
throw new InvalidParamException('Cost must be between 4 and 31.'); |
||||||
|
} |
||||||
|
|
||||||
|
// Get 20 * 8bits of pseudo-random entropy from mt_rand(). |
||||||
|
$rand = ''; |
||||||
|
for ($i = 0; $i < 20; ++$i) { |
||||||
|
$rand .= chr(mt_rand(0, 255)); |
||||||
|
} |
||||||
|
|
||||||
|
// Add the microtime for a little more entropy. |
||||||
|
$rand .= microtime(); |
||||||
|
// Mix the bits cryptographically into a 20-byte binary string. |
||||||
|
$rand = sha1($rand, true); |
||||||
|
// Form the prefix that specifies Blowfish algorithm and cost parameter. |
||||||
|
$salt = sprintf("$2y$%02d$", $cost); |
||||||
|
// Append the random salt data in the required base64 format. |
||||||
|
$salt .= str_replace('+', '.', substr(base64_encode($rand), 0, 22)); |
||||||
|
return $salt; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,125 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\helpers\base; |
||||||
|
|
||||||
|
/** |
||||||
|
* StringHelper |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @author Alex Makarov <sam@rmcreative.ru> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class StringHelper |
||||||
|
{ |
||||||
|
/** |
||||||
|
* Returns the number of bytes in the given string. |
||||||
|
* This method ensures the string is treated as a byte array. |
||||||
|
* It will use `mb_strlen()` if it is available. |
||||||
|
* @param string $string the string being measured for length |
||||||
|
* @return integer the number of bytes in the given string. |
||||||
|
*/ |
||||||
|
public static function strlen($string) |
||||||
|
{ |
||||||
|
return function_exists('mb_strlen') ? mb_strlen($string, '8bit') : strlen($string); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the portion of string specified by the start and length parameters. |
||||||
|
* This method ensures the string is treated as a byte array. |
||||||
|
* It will use `mb_substr()` if it is available. |
||||||
|
* @param string $string the input string. Must be one character or longer. |
||||||
|
* @param integer $start the starting position |
||||||
|
* @param integer $length the desired portion length |
||||||
|
* @return string the extracted part of string, or FALSE on failure or an empty string. |
||||||
|
* @see http://www.php.net/manual/en/function.substr.php |
||||||
|
*/ |
||||||
|
public static function substr($string, $start, $length) |
||||||
|
{ |
||||||
|
return function_exists('mb_substr') ? mb_substr($string, $start, $length, '8bit') : substr($string, $start, $length); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Converts a word to its plural form. |
||||||
|
* Note that this is for English only! |
||||||
|
* For example, 'apple' will become 'apples', and 'child' will become 'children'. |
||||||
|
* @param string $name the word to be pluralized |
||||||
|
* @return string the pluralized word |
||||||
|
*/ |
||||||
|
public static function pluralize($name) |
||||||
|
{ |
||||||
|
static $rules = array( |
||||||
|
'/(m)ove$/i' => '\1oves', |
||||||
|
'/(f)oot$/i' => '\1eet', |
||||||
|
'/(c)hild$/i' => '\1hildren', |
||||||
|
'/(h)uman$/i' => '\1umans', |
||||||
|
'/(m)an$/i' => '\1en', |
||||||
|
'/(s)taff$/i' => '\1taff', |
||||||
|
'/(t)ooth$/i' => '\1eeth', |
||||||
|
'/(p)erson$/i' => '\1eople', |
||||||
|
'/([m|l])ouse$/i' => '\1ice', |
||||||
|
'/(x|ch|ss|sh|us|as|is|os)$/i' => '\1es', |
||||||
|
'/([^aeiouy]|qu)y$/i' => '\1ies', |
||||||
|
'/(?:([^f])fe|([lr])f)$/i' => '\1\2ves', |
||||||
|
'/(shea|lea|loa|thie)f$/i' => '\1ves', |
||||||
|
'/([ti])um$/i' => '\1a', |
||||||
|
'/(tomat|potat|ech|her|vet)o$/i' => '\1oes', |
||||||
|
'/(bu)s$/i' => '\1ses', |
||||||
|
'/(ax|test)is$/i' => '\1es', |
||||||
|
'/s$/' => 's', |
||||||
|
); |
||||||
|
foreach ($rules as $rule => $replacement) { |
||||||
|
if (preg_match($rule, $name)) { |
||||||
|
return preg_replace($rule, $replacement, $name); |
||||||
|
} |
||||||
|
} |
||||||
|
return $name . 's'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Converts a CamelCase name into space-separated words. |
||||||
|
* For example, 'PostTag' will be converted to 'Post Tag'. |
||||||
|
* @param string $name the string to be converted |
||||||
|
* @param boolean $ucwords whether to capitalize the first letter in each word |
||||||
|
* @return string the resulting words |
||||||
|
*/ |
||||||
|
public static function camel2words($name, $ucwords = true) |
||||||
|
{ |
||||||
|
$label = trim(strtolower(str_replace(array('-', '_', '.'), ' ', preg_replace('/(?<![A-Z])[A-Z]/', ' \0', $name)))); |
||||||
|
return $ucwords ? ucwords($label) : $label; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Converts a CamelCase name into an ID in lowercase. |
||||||
|
* Words in the ID may be concatenated using the specified character (defaults to '-'). |
||||||
|
* For example, 'PostTag' will be converted to 'post-tag'. |
||||||
|
* @param string $name the string to be converted |
||||||
|
* @param string $separator the character used to concatenate the words in the ID |
||||||
|
* @return string the resulting ID |
||||||
|
*/ |
||||||
|
public static function camel2id($name, $separator = '-') |
||||||
|
{ |
||||||
|
if ($separator === '_') { |
||||||
|
return trim(strtolower(preg_replace('/(?<![A-Z])[A-Z]/', '_\0', $name)), '_'); |
||||||
|
} else { |
||||||
|
return trim(strtolower(str_replace('_', $separator, preg_replace('/(?<![A-Z])[A-Z]/', $separator . '\0', $name))), $separator); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Converts an ID into a CamelCase name. |
||||||
|
* Words in the ID separated by `$separator` (defaults to '-') will be concatenated into a CamelCase name. |
||||||
|
* For example, 'post-tag' is converted to 'PostTag'. |
||||||
|
* @param string $id the ID to be converted |
||||||
|
* @param string $separator the character used to separate the words in the ID |
||||||
|
* @return string the resulting CamelCase name |
||||||
|
*/ |
||||||
|
public static function id2camel($id, $separator = '-') |
||||||
|
{ |
||||||
|
return str_replace(' ', '', ucwords(implode(' ', explode($separator, $id)))); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,134 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright © 2008-2011 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace yii\helpers\base; |
||||||
|
|
||||||
|
/** |
||||||
|
* VarDumper is intended to replace the buggy PHP function var_dump and print_r. |
||||||
|
* It can correctly identify the recursively referenced objects in a complex |
||||||
|
* object structure. It also has a recursive depth control to avoid indefinite |
||||||
|
* recursive display of some peculiar variables. |
||||||
|
* |
||||||
|
* VarDumper can be used as follows, |
||||||
|
* |
||||||
|
* ~~~ |
||||||
|
* VarDumper::dump($var); |
||||||
|
* ~~~ |
||||||
|
* |
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com> |
||||||
|
* @since 2.0 |
||||||
|
*/ |
||||||
|
class VarDumper |
||||||
|
{ |
||||||
|
private static $_objects; |
||||||
|
private static $_output; |
||||||
|
private static $_depth; |
||||||
|
|
||||||
|
/** |
||||||
|
* Displays a variable. |
||||||
|
* This method achieves the similar functionality as var_dump and print_r |
||||||
|
* but is more robust when handling complex objects such as Yii controllers. |
||||||
|
* @param mixed $var variable to be dumped |
||||||
|
* @param integer $depth maximum depth that the dumper should go into the variable. Defaults to 10. |
||||||
|
* @param boolean $highlight whether the result should be syntax-highlighted |
||||||
|
*/ |
||||||
|
public static function dump($var, $depth = 10, $highlight = false) |
||||||
|
{ |
||||||
|
echo self::dumpAsString($var, $depth, $highlight); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Dumps a variable in terms of a string. |
||||||
|
* This method achieves the similar functionality as var_dump and print_r |
||||||
|
* but is more robust when handling complex objects such as Yii controllers. |
||||||
|
* @param mixed $var variable to be dumped |
||||||
|
* @param integer $depth maximum depth that the dumper should go into the variable. Defaults to 10. |
||||||
|
* @param boolean $highlight whether the result should be syntax-highlighted |
||||||
|
* @return string the string representation of the variable |
||||||
|
*/ |
||||||
|
public static function dumpAsString($var, $depth = 10, $highlight = false) |
||||||
|
{ |
||||||
|
self::$_output = ''; |
||||||
|
self::$_objects = array(); |
||||||
|
self::$_depth = $depth; |
||||||
|
self::dumpInternal($var, 0); |
||||||
|
if ($highlight) { |
||||||
|
$result = highlight_string("<?php\n" . self::$_output, true);
|
||||||
|
self::$_output = preg_replace('/<\\?php<br \\/>/', '', $result, 1); |
||||||
|
} |
||||||
|
return self::$_output; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param mixed $var variable to be dumped |
||||||
|
* @param integer $level depth level |
||||||
|
*/ |
||||||
|
private static function dumpInternal($var, $level) |
||||||
|
{ |
||||||
|
switch (gettype($var)) { |
||||||
|
case 'boolean': |
||||||
|
self::$_output .= $var ? 'true' : 'false'; |
||||||
|
break; |
||||||
|
case 'integer': |
||||||
|
self::$_output .= "$var"; |
||||||
|
break; |
||||||
|
case 'double': |
||||||
|
self::$_output .= "$var"; |
||||||
|
break; |
||||||
|
case 'string': |
||||||
|
self::$_output .= "'" . addslashes($var) . "'"; |
||||||
|
break; |
||||||
|
case 'resource': |
||||||
|
self::$_output .= '{resource}'; |
||||||
|
break; |
||||||
|
case 'NULL': |
||||||
|
self::$_output .= "null"; |
||||||
|
break; |
||||||
|
case 'unknown type': |
||||||
|
self::$_output .= '{unknown}'; |
||||||
|
break; |
||||||
|
case 'array': |
||||||
|
if (self::$_depth <= $level) { |
||||||
|
self::$_output .= 'array(...)'; |
||||||
|
} elseif (empty($var)) { |
||||||
|
self::$_output .= 'array()'; |
||||||
|
} else { |
||||||
|
$keys = array_keys($var); |
||||||
|
$spaces = str_repeat(' ', $level * 4); |
||||||
|
self::$_output .= "array\n" . $spaces . '('; |
||||||
|
foreach ($keys as $key) { |
||||||
|
self::$_output .= "\n" . $spaces . ' '; |
||||||
|
self::dumpInternal($key, 0); |
||||||
|
self::$_output .= ' => '; |
||||||
|
self::dumpInternal($var[$key], $level + 1); |
||||||
|
} |
||||||
|
self::$_output .= "\n" . $spaces . ')'; |
||||||
|
} |
||||||
|
break; |
||||||
|
case 'object': |
||||||
|
if (($id = array_search($var, self::$_objects, true)) !== false) { |
||||||
|
self::$_output .= get_class($var) . '#' . ($id + 1) . '(...)'; |
||||||
|
} elseif (self::$_depth <= $level) { |
||||||
|
self::$_output .= get_class($var) . '(...)'; |
||||||
|
} else { |
||||||
|
$id = self::$_objects[] = $var; |
||||||
|
$className = get_class($var); |
||||||
|
$members = (array)$var; |
||||||
|
$spaces = str_repeat(' ', $level * 4); |
||||||
|
self::$_output .= "$className#$id\n" . $spaces . '('; |
||||||
|
foreach ($members as $key => $value) { |
||||||
|
$keyDisplay = strtr(trim($key), array("\0" => ':')); |
||||||
|
self::$_output .= "\n" . $spaces . " [$keyDisplay] => "; |
||||||
|
self::dumpInternal($value, $level + 1); |
||||||
|
} |
||||||
|
self::$_output .= "\n" . $spaces . ')'; |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,627 @@ |
|||||||
|
<?php |
||||||
|
/** |
||||||
|
* Plural rules. |
||||||
|
* |
||||||
|
* This file is automatically generated by the "yiic locale/plural" command under the "build" folder. |
||||||
|
* Do not modify it directly. |
||||||
|
* |
||||||
|
* The original plural rule data used for generating this file has the following copyright terms: |
||||||
|
* |
||||||
|
* Copyright © 1991-2007 Unicode, Inc. All rights reserved. |
||||||
|
* Distributed under the Terms of Use in http://www.unicode.org/copyright.html. |
||||||
|
* |
||||||
|
* @revision 6008 (of the original plural file) |
||||||
|
* @link http://www.yiiframework.com/ |
||||||
|
* @copyright Copyright (c) 2008 Yii Software LLC |
||||||
|
* @license http://www.yiiframework.com/license/ |
||||||
|
*/ |
||||||
|
return array ( |
||||||
|
'ar' => |
||||||
|
array ( |
||||||
|
0 => '$n==0', |
||||||
|
1 => '$n==1', |
||||||
|
2 => '$n==2', |
||||||
|
3 => 'in_array(fmod($n,100),range(3,10))', |
||||||
|
4 => 'in_array(fmod($n,100),range(11,99))', |
||||||
|
), |
||||||
|
'asa' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'af' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'bem' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'bez' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'bg' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'bn' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'brx' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'ca' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'cgg' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'chr' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'da' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'de' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'dv' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'ee' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'el' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'en' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'eo' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'es' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'et' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'eu' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'fi' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'fo' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'fur' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'fy' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'gl' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'gsw' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'gu' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'ha' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'haw' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'he' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'is' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'it' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'jmc' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'kaj' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'kcg' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'kk' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'kl' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'ksb' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'ku' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'lb' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'lg' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'mas' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'ml' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'mn' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'mr' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'nah' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'nb' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'nd' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'ne' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'nl' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'nn' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'no' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'nr' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'ny' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'nyn' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'om' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'or' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'pa' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'pap' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'ps' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'pt' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'rof' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'rm' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'rwk' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'saq' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'seh' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'sn' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'so' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'sq' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'ss' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'ssy' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'st' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'sv' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'sw' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'syr' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'ta' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'te' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'teo' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'tig' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'tk' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'tn' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'ts' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'ur' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'wae' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
've' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'vun' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'xh' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'xog' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'zu' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
), |
||||||
|
'ak' => |
||||||
|
array ( |
||||||
|
0 => '($n==0||$n==1)', |
||||||
|
), |
||||||
|
'am' => |
||||||
|
array ( |
||||||
|
0 => '($n==0||$n==1)', |
||||||
|
), |
||||||
|
'bh' => |
||||||
|
array ( |
||||||
|
0 => '($n==0||$n==1)', |
||||||
|
), |
||||||
|
'fil' => |
||||||
|
array ( |
||||||
|
0 => '($n==0||$n==1)', |
||||||
|
), |
||||||
|
'tl' => |
||||||
|
array ( |
||||||
|
0 => '($n==0||$n==1)', |
||||||
|
), |
||||||
|
'guw' => |
||||||
|
array ( |
||||||
|
0 => '($n==0||$n==1)', |
||||||
|
), |
||||||
|
'hi' => |
||||||
|
array ( |
||||||
|
0 => '($n==0||$n==1)', |
||||||
|
), |
||||||
|
'ln' => |
||||||
|
array ( |
||||||
|
0 => '($n==0||$n==1)', |
||||||
|
), |
||||||
|
'mg' => |
||||||
|
array ( |
||||||
|
0 => '($n==0||$n==1)', |
||||||
|
), |
||||||
|
'nso' => |
||||||
|
array ( |
||||||
|
0 => '($n==0||$n==1)', |
||||||
|
), |
||||||
|
'ti' => |
||||||
|
array ( |
||||||
|
0 => '($n==0||$n==1)', |
||||||
|
), |
||||||
|
'wa' => |
||||||
|
array ( |
||||||
|
0 => '($n==0||$n==1)', |
||||||
|
), |
||||||
|
'ff' => |
||||||
|
array ( |
||||||
|
0 => '($n>=0&&$n<=2)&&$n!=2', |
||||||
|
), |
||||||
|
'fr' => |
||||||
|
array ( |
||||||
|
0 => '($n>=0&&$n<=2)&&$n!=2', |
||||||
|
), |
||||||
|
'kab' => |
||||||
|
array ( |
||||||
|
0 => '($n>=0&&$n<=2)&&$n!=2', |
||||||
|
), |
||||||
|
'lv' => |
||||||
|
array ( |
||||||
|
0 => '$n==0', |
||||||
|
1 => 'fmod($n,10)==1&&fmod($n,100)!=11', |
||||||
|
), |
||||||
|
'iu' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
1 => '$n==2', |
||||||
|
), |
||||||
|
'kw' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
1 => '$n==2', |
||||||
|
), |
||||||
|
'naq' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
1 => '$n==2', |
||||||
|
), |
||||||
|
'se' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
1 => '$n==2', |
||||||
|
), |
||||||
|
'sma' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
1 => '$n==2', |
||||||
|
), |
||||||
|
'smi' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
1 => '$n==2', |
||||||
|
), |
||||||
|
'smj' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
1 => '$n==2', |
||||||
|
), |
||||||
|
'smn' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
1 => '$n==2', |
||||||
|
), |
||||||
|
'sms' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
1 => '$n==2', |
||||||
|
), |
||||||
|
'ga' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
1 => '$n==2', |
||||||
|
2 => 'in_array($n,array(3,4,5,6))', |
||||||
|
3 => 'in_array($n,array(7,8,9,10))', |
||||||
|
), |
||||||
|
'ro' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
1 => '$n==0||$n!=1&&in_array(fmod($n,100),range(1,19))', |
||||||
|
), |
||||||
|
'mo' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
1 => '$n==0||$n!=1&&in_array(fmod($n,100),range(1,19))', |
||||||
|
), |
||||||
|
'lt' => |
||||||
|
array ( |
||||||
|
0 => 'fmod($n,10)==1&&!in_array(fmod($n,100),range(11,19))', |
||||||
|
1 => 'in_array(fmod($n,10),range(2,9))&&!in_array(fmod($n,100),range(11,19))', |
||||||
|
), |
||||||
|
'be' => |
||||||
|
array ( |
||||||
|
0 => 'fmod($n,10)==1&&fmod($n,100)!=11', |
||||||
|
1 => 'in_array(fmod($n,10),array(2,3,4))&&!in_array(fmod($n,100),array(12,13,14))', |
||||||
|
2 => 'fmod($n,10)==0||in_array(fmod($n,10),array(5,6,7,8,9))||in_array(fmod($n,100),array(11,12,13,14))', |
||||||
|
), |
||||||
|
'bs' => |
||||||
|
array ( |
||||||
|
0 => 'fmod($n,10)==1&&fmod($n,100)!=11', |
||||||
|
1 => 'in_array(fmod($n,10),array(2,3,4))&&!in_array(fmod($n,100),array(12,13,14))', |
||||||
|
2 => 'fmod($n,10)==0||in_array(fmod($n,10),array(5,6,7,8,9))||in_array(fmod($n,100),array(11,12,13,14))', |
||||||
|
), |
||||||
|
'hr' => |
||||||
|
array ( |
||||||
|
0 => 'fmod($n,10)==1&&fmod($n,100)!=11', |
||||||
|
1 => 'in_array(fmod($n,10),array(2,3,4))&&!in_array(fmod($n,100),array(12,13,14))', |
||||||
|
2 => 'fmod($n,10)==0||in_array(fmod($n,10),array(5,6,7,8,9))||in_array(fmod($n,100),array(11,12,13,14))', |
||||||
|
), |
||||||
|
'ru' => |
||||||
|
array ( |
||||||
|
0 => 'fmod($n,10)==1&&fmod($n,100)!=11', |
||||||
|
1 => 'in_array(fmod($n,10),array(2,3,4))&&!in_array(fmod($n,100),array(12,13,14))', |
||||||
|
2 => 'fmod($n,10)==0||in_array(fmod($n,10),array(5,6,7,8,9))||in_array(fmod($n,100),array(11,12,13,14))', |
||||||
|
), |
||||||
|
'sh' => |
||||||
|
array ( |
||||||
|
0 => 'fmod($n,10)==1&&fmod($n,100)!=11', |
||||||
|
1 => 'in_array(fmod($n,10),array(2,3,4))&&!in_array(fmod($n,100),array(12,13,14))', |
||||||
|
2 => 'fmod($n,10)==0||in_array(fmod($n,10),array(5,6,7,8,9))||in_array(fmod($n,100),array(11,12,13,14))', |
||||||
|
), |
||||||
|
'sr' => |
||||||
|
array ( |
||||||
|
0 => 'fmod($n,10)==1&&fmod($n,100)!=11', |
||||||
|
1 => 'in_array(fmod($n,10),array(2,3,4))&&!in_array(fmod($n,100),array(12,13,14))', |
||||||
|
2 => 'fmod($n,10)==0||in_array(fmod($n,10),array(5,6,7,8,9))||in_array(fmod($n,100),array(11,12,13,14))', |
||||||
|
), |
||||||
|
'uk' => |
||||||
|
array ( |
||||||
|
0 => 'fmod($n,10)==1&&fmod($n,100)!=11', |
||||||
|
1 => 'in_array(fmod($n,10),array(2,3,4))&&!in_array(fmod($n,100),array(12,13,14))', |
||||||
|
2 => 'fmod($n,10)==0||in_array(fmod($n,10),array(5,6,7,8,9))||in_array(fmod($n,100),array(11,12,13,14))', |
||||||
|
), |
||||||
|
'cs' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
1 => 'in_array($n,array(2,3,4))', |
||||||
|
), |
||||||
|
'sk' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
1 => 'in_array($n,array(2,3,4))', |
||||||
|
), |
||||||
|
'pl' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
1 => 'in_array(fmod($n,10),array(2,3,4))&&!in_array(fmod($n,100),array(12,13,14))', |
||||||
|
2 => '$n!=1&&in_array(fmod($n,10),array(0,1))||in_array(fmod($n,10),array(5,6,7,8,9))||in_array(fmod($n,100),array(12,13,14))', |
||||||
|
), |
||||||
|
'sl' => |
||||||
|
array ( |
||||||
|
0 => 'fmod($n,100)==1', |
||||||
|
1 => 'fmod($n,100)==2', |
||||||
|
2 => 'in_array(fmod($n,100),array(3,4))', |
||||||
|
), |
||||||
|
'mt' => |
||||||
|
array ( |
||||||
|
0 => '$n==1', |
||||||
|
1 => '$n==0||in_array(fmod($n,100),range(2,10))', |
||||||
|
2 => 'in_array(fmod($n,100),range(11,19))', |
||||||
|
), |
||||||
|
'mk' => |
||||||
|
array ( |
||||||
|
0 => 'fmod($n,10)==1&&$n!=11', |
||||||
|
), |
||||||
|
'cy' => |
||||||
|
array ( |
||||||
|
0 => '$n==0', |
||||||
|
1 => '$n==1', |
||||||
|
2 => '$n==2', |
||||||
|
3 => '$n==3', |
||||||
|
4 => '$n==6', |
||||||
|
), |
||||||
|
'lag' => |
||||||
|
array ( |
||||||
|
0 => '$n==0', |
||||||
|
1 => '($n>=0&&$n<=2)&&$n!=0&&$n!=2', |
||||||
|
), |
||||||
|
'shi' => |
||||||
|
array ( |
||||||
|
0 => '($n>=0&&$n<=1)', |
||||||
|
1 => 'in_array($n,range(2,10))', |
||||||
|
), |
||||||
|
'br' => |
||||||
|
array ( |
||||||
|
0 => 'fmod($n,10)==1&&!in_array(fmod($n,100),array(11,71,91))', |
||||||
|
1 => 'fmod($n,10)==2&&!in_array(fmod($n,100),array(12,72,92))', |
||||||
|
2 => 'in_array(fmod($n,10),array(3,4,9))&&!in_array(fmod($n,100),array_merge(range(10,19),range(70,79),range(90,99)))', |
||||||
|
3 => 'fmod($n,1000000)==0&&$n!=0', |
||||||
|
), |
||||||
|
'ksh' => |
||||||
|
array ( |
||||||
|
0 => '$n==0', |
||||||
|
1 => '$n==1', |
||||||
|
), |
||||||
|
'tzm' => |
||||||
|
array ( |
||||||
|
0 => '($n==0||$n==1)||in_array($n,range(11,99))', |
||||||
|
), |
||||||
|
'gv' => |
||||||
|
array ( |
||||||
|
0 => 'in_array(fmod($n,10),array(1,2))||fmod($n,20)==0', |
||||||
|
), |
||||||
|
); |
@ -0,0 +1,109 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||||
|
<!DOCTYPE supplementalData SYSTEM "../../common/dtd/ldmlSupplemental.dtd"> |
||||||
|
<supplementalData> |
||||||
|
<version number="$Revision: 6008 $"/> |
||||||
|
<generation date="$Date: 2011-07-12 13:18:01 -0500 (Tue, 12 Jul 2011) $"/> |
||||||
|
<plurals> |
||||||
|
<!-- if locale is known to have no plurals, there are no rules --> |
||||||
|
<pluralRules locales="az bm bo dz fa id ig ii hu ja jv ka kde kea km kn ko lo ms my sah ses sg th to tr vi wo yo zh"/> |
||||||
|
<pluralRules locales="ar"> |
||||||
|
<pluralRule count="zero">n is 0</pluralRule> |
||||||
|
<pluralRule count="one">n is 1</pluralRule> |
||||||
|
<pluralRule count="two">n is 2</pluralRule> |
||||||
|
<pluralRule count="few">n mod 100 in 3..10</pluralRule> |
||||||
|
<pluralRule count="many">n mod 100 in 11..99</pluralRule> |
||||||
|
</pluralRules> |
||||||
|
<pluralRules locales="asa af bem bez bg bn brx ca cgg chr da de dv ee el en eo es et eu fi fo fur fy gl gsw gu ha haw he is it jmc kaj kcg kk kl ksb ku lb lg mas ml mn mr nah nb nd ne nl nn no nr ny nyn om or pa pap ps pt rof rm rwk saq seh sn so sq ss ssy st sv sw syr ta te teo tig tk tn ts ur wae ve vun xh xog zu"> |
||||||
|
<pluralRule count="one">n is 1</pluralRule> |
||||||
|
</pluralRules> |
||||||
|
<pluralRules locales="ak am bh fil tl guw hi ln mg nso ti wa"> |
||||||
|
<pluralRule count="one">n in 0..1</pluralRule> |
||||||
|
</pluralRules> |
||||||
|
<pluralRules locales="ff fr kab"> |
||||||
|
<pluralRule count="one">n within 0..2 and n is not 2</pluralRule> |
||||||
|
</pluralRules> |
||||||
|
<pluralRules locales="lv"> |
||||||
|
<pluralRule count="zero">n is 0</pluralRule> |
||||||
|
<pluralRule count="one">n mod 10 is 1 and n mod 100 is not 11</pluralRule> |
||||||
|
</pluralRules> |
||||||
|
<pluralRules locales="iu kw naq se sma smi smj smn sms"> |
||||||
|
<pluralRule count="one">n is 1</pluralRule> |
||||||
|
<pluralRule count="two">n is 2</pluralRule> |
||||||
|
</pluralRules> |
||||||
|
<pluralRules locales="ga"> <!-- http://unicode.org/cldr/trac/ticket/3915 --> |
||||||
|
<pluralRule count="one">n is 1</pluralRule> |
||||||
|
<pluralRule count="two">n is 2</pluralRule> |
||||||
|
<pluralRule count="few">n in 3..6</pluralRule> |
||||||
|
<pluralRule count="many">n in 7..10</pluralRule> |
||||||
|
</pluralRules> |
||||||
|
<pluralRules locales="ro mo"> |
||||||
|
<pluralRule count="one">n is 1</pluralRule> |
||||||
|
<pluralRule count="few">n is 0 OR n is not 1 AND n mod 100 in 1..19</pluralRule> |
||||||
|
</pluralRules> |
||||||
|
<pluralRules locales="lt"> |
||||||
|
<pluralRule count="one">n mod 10 is 1 and n mod 100 not in 11..19</pluralRule> |
||||||
|
<pluralRule count="few">n mod 10 in 2..9 and n mod 100 not in 11..19</pluralRule> |
||||||
|
</pluralRules> |
||||||
|
<pluralRules locales="be bs hr ru sh sr uk"> |
||||||
|
<pluralRule count="one">n mod 10 is 1 and n mod 100 is not 11</pluralRule> |
||||||
|
<pluralRule count="few">n mod 10 in 2..4 and n mod 100 not in 12..14</pluralRule> |
||||||
|
<pluralRule count="many">n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14</pluralRule> |
||||||
|
<!-- others are fractions --> |
||||||
|
</pluralRules> |
||||||
|
<pluralRules locales="cs sk"> |
||||||
|
<pluralRule count="one">n is 1</pluralRule> |
||||||
|
<pluralRule count="few">n in 2..4</pluralRule> |
||||||
|
</pluralRules> |
||||||
|
<pluralRules locales="pl"> |
||||||
|
<pluralRule count="one">n is 1</pluralRule> |
||||||
|
<pluralRule count="few">n mod 10 in 2..4 and n mod 100 not in 12..14</pluralRule> |
||||||
|
<pluralRule count="many">n is not 1 and n mod 10 in 0..1 or n mod 10 in 5..9 or n mod 100 in 12..14</pluralRule> |
||||||
|
<!-- others are fractions --> |
||||||
|
<!-- and n mod 100 not in 22..24 from Tamplin --> |
||||||
|
</pluralRules> |
||||||
|
<pluralRules locales="sl"> |
||||||
|
<pluralRule count="one">n mod 100 is 1</pluralRule> |
||||||
|
<pluralRule count="two">n mod 100 is 2</pluralRule> |
||||||
|
<pluralRule count="few">n mod 100 in 3..4</pluralRule> |
||||||
|
</pluralRules> |
||||||
|
<pluralRules locales="mt"> <!-- from Tamplin's data --> |
||||||
|
<pluralRule count="one">n is 1</pluralRule> |
||||||
|
<pluralRule count="few">n is 0 or n mod 100 in 2..10</pluralRule> |
||||||
|
<pluralRule count="many">n mod 100 in 11..19</pluralRule> |
||||||
|
</pluralRules> |
||||||
|
<pluralRules locales="mk"> <!-- from Tamplin's data --> |
||||||
|
<pluralRule count="one">n mod 10 is 1 and n is not 11</pluralRule> |
||||||
|
</pluralRules> |
||||||
|
<pluralRules locales="cy"> <!-- from http://www.saltcymru.org/wordpress/?p=99&lang=en --> |
||||||
|
<pluralRule count="zero">n is 0</pluralRule> |
||||||
|
<pluralRule count="one">n is 1</pluralRule> |
||||||
|
<pluralRule count="two">n is 2</pluralRule> |
||||||
|
<pluralRule count="few">n is 3</pluralRule> |
||||||
|
<pluralRule count="many">n is 6</pluralRule> |
||||||
|
</pluralRules> |
||||||
|
<pluralRules locales="lag"> |
||||||
|
<pluralRule count="zero">n is 0</pluralRule> |
||||||
|
<pluralRule count="one">n within 0..2 and n is not 0 and n is not 2</pluralRule> |
||||||
|
</pluralRules> |
||||||
|
<pluralRules locales="shi"> |
||||||
|
<pluralRule count="one">n within 0..1</pluralRule> |
||||||
|
<pluralRule count="few">n in 2..10</pluralRule> |
||||||
|
</pluralRules> |
||||||
|
<pluralRules locales="br"> <!-- from http://unicode.org/cldr/trac/ticket/2886 --> |
||||||
|
<pluralRule count="one">n mod 10 is 1 and n mod 100 not in 11,71,91</pluralRule> |
||||||
|
<pluralRule count="two">n mod 10 is 2 and n mod 100 not in 12,72,92</pluralRule> |
||||||
|
<pluralRule count="few">n mod 10 in 3..4,9 and n mod 100 not in 10..19,70..79,90..99</pluralRule> |
||||||
|
<pluralRule count="many">n mod 1000000 is 0 and n is not 0</pluralRule> |
||||||
|
</pluralRules> |
||||||
|
<pluralRules locales="ksh"> |
||||||
|
<pluralRule count="zero">n is 0</pluralRule> |
||||||
|
<pluralRule count="one">n is 1</pluralRule> |
||||||
|
</pluralRules> |
||||||
|
<pluralRules locales="tzm"> |
||||||
|
<pluralRule count="one">n in 0..1 or n in 11..99</pluralRule> |
||||||
|
</pluralRules> |
||||||
|
<pluralRules locales="gv"> |
||||||
|
<pluralRule count="one">n mod 10 in 1..2 or n mod 20 is 0</pluralRule> |
||||||
|
</pluralRules> |
||||||
|
</plurals> |
||||||
|
</supplementalData> |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue