diff --git a/apps/advanced/README.md b/apps/advanced/README.md
index a2bcdd4..c443c90 100644
--- a/apps/advanced/README.md
+++ b/apps/advanced/README.md
@@ -67,32 +67,32 @@ If you do not have [Composer](http://getcomposer.org/), you may download it from
curl -s http://getcomposer.org/installer | php
~~~
-You can then install the Bootstrap Application using the following command:
+You can then install the application using the following command:
~~~
php composer.phar create-project --stability=dev yiisoft/yii2-app-advanced yii-advanced
~~~
-Now you should be able to access:
-
-- the frontend using the URL `http://localhost/yii-advanced/frontend/www/`
-- the backstage using the URL `http://localhost/yii-advanced/backstage/www/`
-
-assuming `yii-advanced` is directly under the document root of your Web server.
-
### Install from an Archive File
This is not currently available. We will provide it when Yii 2 is formally released.
+
GETTING STARTED
---------------
-After template application and its dependencies are downloaded you need to initialize it and set some config values to
-match your application requirements.
+After you install the application, you have to conduct the following steps to initialize
+the installed application. You only need to do these once for all.
-1. Execute `install` command selecting `dev` as environment.
-2. Set `id` value in `console/config/main.php`, `frontend/config/main.php`, `backstage/config/main.php`.
-3. Create new database. It is assumed that MySQL InnoDB is used. If not, adjust `console/migrations/m130524_201442_init.php`.
-4. In `common/config/params.php` set your database details in `components.db` values.
+1. Execute the `init` command and select `dev` as environment.
+2. Create a new database. It is assumed that MySQL InnoDB is used. If not, adjust `console/migrations/m130524_201442_init.php`.
+3. In `common/config/params.php` set your database details in `components.db` values.
+
+Now you should be able to access:
+
+- the frontend using the URL `http://localhost/yii-advanced/frontend/www/`
+- the backstage using the URL `http://localhost/yii-advanced/backstage/www/`
+
+assuming `yii-advanced` is directly under the document root of your Web server.
diff --git a/apps/advanced/backstage/config/main.php b/apps/advanced/backstage/config/main.php
index 4898bfd..6e55c47 100644
--- a/apps/advanced/backstage/config/main.php
+++ b/apps/advanced/backstage/config/main.php
@@ -9,7 +9,7 @@ $params = array_merge(
);
return array(
- 'id' => 'change-me',
+ 'id' => 'app-backend',
'basePath' => dirname(__DIR__),
'vendorPath' => dirname(dirname(__DIR__)) . '/vendor',
'preload' => array('log'),
diff --git a/apps/advanced/composer.json b/apps/advanced/composer.json
index db97efd..95b3b13 100644
--- a/apps/advanced/composer.json
+++ b/apps/advanced/composer.json
@@ -36,9 +36,6 @@
"frontend/runtime",
"frontend/www/assets"
- ],
- "yii-install-executable": [
- "yii"
]
}
}
diff --git a/apps/advanced/console/config/main.php b/apps/advanced/console/config/main.php
index cceb311..37db1d2 100644
--- a/apps/advanced/console/config/main.php
+++ b/apps/advanced/console/config/main.php
@@ -9,7 +9,7 @@ $params = array_merge(
);
return array(
- 'id' => 'change-me',
+ 'id' => 'app-console',
'basePath' => dirname(__DIR__),
'vendorPath' => dirname(dirname(__DIR__)) . '/vendor',
'preload' => array('log'),
diff --git a/apps/advanced/console/migrations/m130524_201442_init.php b/apps/advanced/console/migrations/m130524_201442_init.php
index 24a74c3..a5e9d30 100644
--- a/apps/advanced/console/migrations/m130524_201442_init.php
+++ b/apps/advanced/console/migrations/m130524_201442_init.php
@@ -1,5 +1,7 @@
'change-me',
+ 'id' => 'app-frontend',
'basePath' => dirname(__DIR__),
'vendorPath' => dirname(dirname(__DIR__)) . '/vendor',
'preload' => array('log'),
diff --git a/apps/advanced/install b/apps/advanced/init
similarity index 87%
rename from apps/advanced/install
rename to apps/advanced/init
index 6864440..17ed854 100755
--- a/apps/advanced/install
+++ b/apps/advanced/init
@@ -4,27 +4,27 @@ $root = str_replace('\\', '/', __DIR__);
$envs = require("$root/environments/index.php");
$envNames = array_keys($envs);
-echo "Yii Application Installation Tool v1.0\n\n";
-echo "Which environment do you want to install the application to?\n\n";
+echo "Yii Application Init Tool v1.0\n\n";
+echo "Which environment do you want the application to be initialized in?\n\n";
foreach ($envNames as $i => $name) {
echo " [$i] $name\n";
}
echo "\n Your choice [0-" . (count($envs) - 1) . ', or "q" to quit] ';
$answer = trim(fgets(STDIN));
if (!ctype_digit($answer) || !isset($envNames[$answer])) {
- echo "\n Quit installation.\n";
+ echo "\n Quit initialization.\n";
return;
}
$env = $envs[$envNames[$answer]];
-echo "\n Install the application under '{$envNames[$answer]}' environment? [yes|no] ";
+echo "\n Initialize the application under '{$envNames[$answer]}' environment? [yes|no] ";
$answer = trim(fgets(STDIN));
if (strncasecmp($answer, 'y', 1)) {
- echo "\n Quit installation.\n";
+ echo "\n Quit initialization.\n";
return;
}
-echo "\n Start installation ...\n\n";
+echo "\n Start initialization ...\n\n";
$files = getFileList("$root/environments/{$env['path']}");
$all = false;
foreach ($files as $file) {
@@ -47,7 +47,7 @@ if (isset($env['executable'])) {
}
}
-echo "\n ... installation completed.\n\n";
+echo "\n ... initialization completed.\n\n";
function getFileList($root, $basePath = '')
{
diff --git a/apps/advanced/install.bat b/apps/advanced/init.bat
similarity index 100%
rename from apps/advanced/install.bat
rename to apps/advanced/init.bat
diff --git a/docs/guide/upgrade-from-v1.md b/docs/guide/upgrade-from-v1.md
index b3d4411..ebfe94b 100644
--- a/docs/guide/upgrade-from-v1.md
+++ b/docs/guide/upgrade-from-v1.md
@@ -13,7 +13,7 @@ The most obvious change in Yii 2.0 is the use of namespaces. Almost every core c
is namespaced, e.g., `yii\web\Request`. The "C" prefix is no longer used in class names.
The naming of the namespaces follows the directory structure. For example, `yii\web\Request`
indicates the corresponding class file is `web/Request.php` under the Yii framework folder.
-You can use any core class without explicitly include that class file, thanks to the Yii
+You can use any core class without explicitly including that class file, thanks to the Yii
class loader.
@@ -117,17 +117,17 @@ supported in most places in the Yii core code. For example, `FileCache::cachePat
both a path alias and a normal directory path.
Path alias is also closely related with class namespaces. It is recommended that a path
-alias defined for each root namespace so that you can use Yii class autoloader without
+alias be defined for each root namespace so that you can use Yii the class autoloader without
any further configuration. For example, because `@yii` refers to the Yii installation directory,
a class like `yii\web\Request` can be autoloaded by Yii. If you use a third party library
-such as Zend Framework, you may define a path alias `@Zend` which refers to its installation directory.
-And Yii will be able to autoload any class in this library.
+such as Zend Framework, you may define a path alias `@Zend` which refers to its installation
+directory and Yii will be able to autoload any class in this library.
View
----
-Yii 2.0 introduces a `View` class to represent the view part in the MVC pattern.
+Yii 2.0 introduces a `View` class to represent the view part of the MVC pattern.
It can be configured globally through the "view" application component. It is also
accessible in any view file via `$this`. This is one of the biggest changes compared to 1.1:
**`$this` in a view file no longer refers to the controller or widget object.**
@@ -159,7 +159,7 @@ extension for your Smarty views, or `twig` for Twig views. You may also configur
Models
------
-A model is now associated with a form name returned its `formName()` method. This is
+A model is now associated with a form name returned by its `formName()` method. This is
mainly used when using HTML forms to collect user inputs for a model. Previously in 1.1,
this is usually hardcoded as the class name of the model.
@@ -235,7 +235,7 @@ Previously in 1.1, you would have to enter the widget class names as strings via
Themes
------
-Theme works completely different in 2.0. It is now based on a path map to "translate" a source
+Themes work completely different in 2.0. They are now based on a path map to "translate" a source
view into a themed view. For example, if the path map for a theme is
`array('/www/views' => '/www/themes/basic')`, then the themed version for a view file
`/www/views/site/index.php` will be `/www/themes/basic/site/index.php`.
@@ -250,7 +250,7 @@ application component.
Console Applications
--------------------
-Console applications are now composed by controllers, too, like Web applications. In fact,
+Console applications are now composed by controllers, like Web applications. In fact,
console controllers and Web controllers share the same base controller class.
Each console controller is like `CConsoleCommand` in 1.1. It consists of one or several
@@ -300,7 +300,7 @@ public function behaviors()
Assets
------
-Yii 2.0 introduces a new concept called *asset bundle*. It is a bit similar to script
+Yii 2.0 introduces a new concept called *asset bundle*. It is similar to script
packages (managed by `CClientScript`) in 1.1, but with better support.
An asset bundle is a collection of asset files (e.g. JavaScript files, CSS files, image files, etc.)
@@ -315,7 +315,7 @@ Static Helpers
Yii 2.0 introduces many commonly used static helper classes, such as `Html`, `ArrayHelper`,
`StringHelper`. These classes are designed to be easily extended. Note that static classes
-are usually hard to be extended because of the fixed class name references. But Yii 2.0
+are usually hard to extend because of the fixed class name references. But Yii 2.0
introduces the class map (via `Yii::$classMap`) to overcome this difficulty.
@@ -343,7 +343,7 @@ Query Builder
In 1.1, query building is scattered among several classes, including `CDbCommand`,
`CDbCriteria`, and `CDbCommandBuilder`. Yii 2.0 uses `Query` to represent a DB query
-and `QueryBuilder` to generate SQL statements from query objects. For example,
+and `QueryBuilder` to generate SQL statements from query objects. For example:
```php
$query = new \yii\db\Query;
@@ -365,7 +365,7 @@ ActiveRecord
------------
ActiveRecord has undergone significant changes in Yii 2.0. The most important one
-is about relational ActiveRecord query. In 1.1, you have to declare the relations
+is the relational ActiveRecord query. In 1.1, you have to declare the relations
in the `relations()` method. In 2.0, this is done via getter methods that return
an `ActiveQuery` object. For example, the following method declares an "orders" relation:
@@ -392,7 +392,7 @@ by filtering with the primary keys of the primary records.
Yii 2.0 no longer uses the `model()` method when performing queries. Instead, you
-use the `find()` method like the following:
+use the `find()` method:
```php
// to retrieve all *active* customers and order them by their ID:
@@ -410,14 +410,14 @@ Therefore, you can use all query methods of `Query`.
Instead of returning ActiveRecord objects, you may call `ActiveQuery::asArray()` to
return results in terms of arrays. This is more efficient and is especially useful
-when you need to return large number of records. For example,
+when you need to return a large number of records:
```php
$customers = Customer::find()->asArray()->all();
```
By default, ActiveRecord now only saves dirty attributes. In 1.1, all attributes
-would be saved to database when you call `save()`, regardless they are changed or not,
+are saved to database when you call `save()`, regardless of having changed or not,
unless you explicitly list the attributes to save.
@@ -427,7 +427,7 @@ Auto-quoting Table and Column Names
Yii 2.0 supports automatic quoting of database table and column names. A name enclosed
within double curly brackets is treated as a table name, and a name enclosed within
double square brackets is treated as a column name. They will be quoted according to
-the database driver being used. For example,
+the database driver being used:
```php
$command = $connection->createCommand('SELECT [[id]] FROM {{posts}}');
diff --git a/framework/yii/assets.php b/framework/yii/assets.php
index 79fbeb5..63f7560 100644
--- a/framework/yii/assets.php
+++ b/framework/yii/assets.php
@@ -48,4 +48,11 @@ return array(
YII_DEBUG ? 'punycode/punycode.js' : 'punycode/punycode.min.js',
),
),
+ 'yii/maskedinput' => array(
+ 'sourcePath' => __DIR__ . '/assets',
+ 'js' => array(
+ 'jquery.maskedinput.js',
+ ),
+ 'depends' => array('yii/jquery'),
+ ),
);
diff --git a/framework/yii/assets/jquery.maskedinput.js b/framework/yii/assets/jquery.maskedinput.js
new file mode 100644
index 0000000..49a5a72
--- /dev/null
+++ b/framework/yii/assets/jquery.maskedinput.js
@@ -0,0 +1,338 @@
+/*
+ Masked Input plugin for jQuery
+ Copyright (c) 2007-2013 Josh Bush (digitalbush.com)
+ Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license)
+ Version: 1.3.1
+*/
+(function($) {
+ function getPasteEvent() {
+ var el = document.createElement('input'),
+ name = 'onpaste';
+ el.setAttribute(name, '');
+ return (typeof el[name] === 'function')?'paste':'input';
+}
+
+var pasteEventName = getPasteEvent() + ".mask",
+ ua = navigator.userAgent,
+ iPhone = /iphone/i.test(ua),
+ android=/android/i.test(ua),
+ caretTimeoutId;
+
+$.mask = {
+ //Predefined character definitions
+ definitions: {
+ '9': "[0-9]",
+ 'a': "[A-Za-z]",
+ '*': "[A-Za-z0-9]"
+ },
+ dataName: "rawMaskFn",
+ placeholder: '_',
+};
+
+$.fn.extend({
+ //Helper Function for Caret positioning
+ caret: function(begin, end) {
+ var range;
+
+ if (this.length === 0 || this.is(":hidden")) {
+ return;
+ }
+
+ if (typeof begin == 'number') {
+ end = (typeof end === 'number') ? end : begin;
+ return this.each(function() {
+ if (this.setSelectionRange) {
+ this.setSelectionRange(begin, end);
+ } else if (this.createTextRange) {
+ range = this.createTextRange();
+ range.collapse(true);
+ range.moveEnd('character', end);
+ range.moveStart('character', begin);
+ range.select();
+ }
+ });
+ } else {
+ if (this[0].setSelectionRange) {
+ begin = this[0].selectionStart;
+ end = this[0].selectionEnd;
+ } else if (document.selection && document.selection.createRange) {
+ range = document.selection.createRange();
+ begin = 0 - range.duplicate().moveStart('character', -100000);
+ end = begin + range.text.length;
+ }
+ return { begin: begin, end: end };
+ }
+ },
+ unmask: function() {
+ return this.trigger("unmask");
+ },
+ mask: function(mask, settings) {
+ var input,
+ defs,
+ tests,
+ partialPosition,
+ firstNonMaskPos,
+ len;
+
+ if (!mask && this.length > 0) {
+ input = $(this[0]);
+ return input.data($.mask.dataName)();
+ }
+ settings = $.extend({
+ placeholder: $.mask.placeholder, // Load default placeholder
+ completed: null
+ }, settings);
+
+
+ defs = $.mask.definitions;
+ tests = [];
+ partialPosition = len = mask.length;
+ firstNonMaskPos = null;
+
+ $.each(mask.split(""), function(i, c) {
+ if (c == '?') {
+ len--;
+ partialPosition = i;
+ } else if (defs[c]) {
+ tests.push(new RegExp(defs[c]));
+ if (firstNonMaskPos === null) {
+ firstNonMaskPos = tests.length - 1;
+ }
+ } else {
+ tests.push(null);
+ }
+ });
+
+ return this.trigger("unmask").each(function() {
+ var input = $(this),
+ buffer = $.map(
+ mask.split(""),
+ function(c, i) {
+ if (c != '?') {
+ return defs[c] ? settings.placeholder : c;
+ }
+ }),
+ focusText = input.val();
+
+ function seekNext(pos) {
+ while (++pos < len && !tests[pos]);
+ return pos;
+ }
+
+ function seekPrev(pos) {
+ while (--pos >= 0 && !tests[pos]);
+ return pos;
+ }
+
+ function shiftL(begin,end) {
+ var i,
+ j;
+
+ if (begin<0) {
+ return;
+ }
+
+ for (i = begin, j = seekNext(end); i < len; i++) {
+ if (tests[i]) {
+ if (j < len && tests[i].test(buffer[j])) {
+ buffer[i] = buffer[j];
+ buffer[j] = settings.placeholder;
+ } else {
+ break;
+ }
+
+ j = seekNext(j);
+ }
+ }
+ writeBuffer();
+ input.caret(Math.max(firstNonMaskPos, begin));
+ }
+
+ function shiftR(pos) {
+ var i,
+ c,
+ j,
+ t;
+
+ for (i = pos, c = settings.placeholder; i < len; i++) {
+ if (tests[i]) {
+ j = seekNext(i);
+ t = buffer[i];
+ buffer[i] = c;
+ if (j < len && tests[j].test(t)) {
+ c = t;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ function keydownEvent(e) {
+ var k = e.which,
+ pos,
+ begin,
+ end;
+
+ //backspace, delete, and escape get special treatment
+ if (k === 8 || k === 46 || (iPhone && k === 127)) {
+ pos = input.caret();
+ begin = pos.begin;
+ end = pos.end;
+
+ if (end - begin === 0) {
+ begin=k!==46?seekPrev(begin):(end=seekNext(begin-1));
+ end=k===46?seekNext(end):end;
+ }
+ clearBuffer(begin, end);
+ shiftL(begin, end - 1);
+
+ e.preventDefault();
+ } else if (k == 27) {//escape
+ input.val(focusText);
+ input.caret(0, checkVal());
+ e.preventDefault();
+ }
+ }
+
+ function keypressEvent(e) {
+ var k = e.which,
+ pos = input.caret(),
+ p,
+ c,
+ next;
+
+ if (e.ctrlKey || e.altKey || e.metaKey || k < 32) {//Ignore
+ return;
+ } else if (k) {
+ if (pos.end - pos.begin !== 0){
+ clearBuffer(pos.begin, pos.end);
+ shiftL(pos.begin, pos.end-1);
+ }
+
+ p = seekNext(pos.begin - 1);
+ if (p < len) {
+ c = String.fromCharCode(k);
+ if (tests[p].test(c)) {
+ shiftR(p);
+
+ buffer[p] = c;
+ writeBuffer();
+ next = seekNext(p);
+
+ if(android){
+ setTimeout($.proxy($.fn.caret,input,next),0);
+ }else{
+ input.caret(next);
+ }
+
+ if (settings.completed && next >= len) {
+ settings.completed.call(input);
+ }
+ }
+ }
+ e.preventDefault();
+ }
+ }
+
+ function clearBuffer(start, end) {
+ var i;
+ for (i = start; i < end && i < len; i++) {
+ if (tests[i]) {
+ buffer[i] = settings.placeholder;
+ }
+ }
+ }
+
+ function writeBuffer() { input.val(buffer.join('')); }
+
+ function checkVal(allow) {
+ //try to place characters where they belong
+ var test = input.val(),
+ lastMatch = -1,
+ i,
+ c;
+
+ for (i = 0, pos = 0; i < len; i++) {
+ if (tests[i]) {
+ buffer[i] = settings.placeholder;
+ while (pos++ < test.length) {
+ c = test.charAt(pos - 1);
+ if (tests[i].test(c)) {
+ buffer[i] = c;
+ lastMatch = i;
+ break;
+ }
+ }
+ if (pos > test.length) {
+ break;
+ }
+ } else if (buffer[i] === test.charAt(pos) && i !== partialPosition) {
+ pos++;
+ lastMatch = i;
+ }
+ }
+ if (allow) {
+ writeBuffer();
+ } else if (lastMatch + 1 < partialPosition) {
+ input.val("");
+ clearBuffer(0, len);
+ } else {
+ writeBuffer();
+ input.val(input.val().substring(0, lastMatch + 1));
+ }
+ return (partialPosition ? i : firstNonMaskPos);
+ }
+
+ input.data($.mask.dataName,function(){
+ return $.map(buffer, function(c, i) {
+ return tests[i]&&c!=settings.placeholder ? c : null;
+ }).join('');
+ });
+
+ if (!input.attr("readonly"))
+ input
+ .one("unmask", function() {
+ input
+ .unbind(".mask")
+ .removeData($.mask.dataName);
+ })
+ .bind("focus.mask", function() {
+ clearTimeout(caretTimeoutId);
+ var pos,
+ moveCaret;
+
+ focusText = input.val();
+ pos = checkVal();
+
+ caretTimeoutId = setTimeout(function(){
+ writeBuffer();
+ if (pos == mask.length) {
+ input.caret(0, pos);
+ } else {
+ input.caret(pos);
+ }
+ }, 10);
+ })
+ .bind("blur.mask", function() {
+ checkVal();
+ if (input.val() != focusText)
+ input.change();
+ })
+ .bind("keydown.mask", keydownEvent)
+ .bind("keypress.mask", keypressEvent)
+ .bind(pasteEventName, function() {
+ setTimeout(function() {
+ var pos=checkVal(true);
+ input.caret(pos);
+ if (settings.completed && pos == input.val().length)
+ settings.completed.call(input);
+ }, 0);
+ });
+ checkVal(); //Perform initial check for existing values
+ });
+ }
+});
+
+
+})(jQuery);
\ No newline at end of file
diff --git a/framework/yii/base/ActionFilter.php b/framework/yii/base/ActionFilter.php
index d69c0fe..20ff142 100644
--- a/framework/yii/base/ActionFilter.php
+++ b/framework/yii/base/ActionFilter.php
@@ -30,8 +30,8 @@ class ActionFilter extends Behavior
public function events()
{
return array(
- 'beforeAction' => 'beforeFilter',
- 'afterAction' => 'afterFilter',
+ Controller::EVENT_BEFORE_ACTION => 'beforeFilter',
+ Controller::EVENT_AFTER_ACTION => 'afterFilter',
);
}
diff --git a/framework/yii/base/Application.php b/framework/yii/base/Application.php
index eb0a0d3..d38f6a9 100644
--- a/framework/yii/base/Application.php
+++ b/framework/yii/base/Application.php
@@ -279,6 +279,15 @@ class Application extends Module
}
/**
+ * Returns the formatter component.
+ * @return \yii\base\Formatter the formatter application component.
+ */
+ public function getFormatter()
+ {
+ return $this->getComponent('formatter');
+ }
+
+ /**
* Returns the request component.
* @return \yii\web\Request|\yii\console\Request the request component
*/
@@ -333,6 +342,9 @@ class Application extends Module
'errorHandler' => array(
'class' => 'yii\base\ErrorHandler',
),
+ 'formatter' => array(
+ 'class' => 'yii\base\Formatter',
+ ),
'i18n' => array(
'class' => 'yii\i18n\I18N',
),
diff --git a/framework/yii/base/Controller.php b/framework/yii/base/Controller.php
index 543605f..af33b63 100644
--- a/framework/yii/base/Controller.php
+++ b/framework/yii/base/Controller.php
@@ -151,43 +151,13 @@ class Controller extends Component
/**
* Binds the parameters to the action.
* This method is invoked by [[Action]] when it begins to run with the given parameters.
- * This method will check the parameter names that the action requires and return
- * the provided parameters according to the requirement. If there is any missing parameter,
- * an exception will be thrown.
* @param Action $action the action to be bound with parameters
* @param array $params the parameters to be bound to the action
* @return array the valid parameters that the action can run with.
- * @throws InvalidRequestException if there are missing parameters.
*/
public function bindActionParams($action, $params)
{
- if ($action instanceof InlineAction) {
- $method = new \ReflectionMethod($this, $action->actionMethod);
- } else {
- $method = new \ReflectionMethod($action, 'run');
- }
-
- $args = array();
- $missing = array();
- foreach ($method->getParameters() as $param) {
- $name = $param->getName();
- if (array_key_exists($name, $params)) {
- $args[] = $params[$name];
- unset($params[$name]);
- } elseif ($param->isDefaultValueAvailable()) {
- $args[] = $param->getDefaultValue();
- } else {
- $missing[] = $name;
- }
- }
-
- if (!empty($missing)) {
- throw new InvalidRequestException(Yii::t('yii', 'Missing required parameters: {params}', array(
- '{params}' => implode(', ', $missing),
- )));
- }
-
- return $args;
+ return array();
}
/**
@@ -272,18 +242,6 @@ class Controller extends Component
}
/**
- * Validates the parameter being bound to actions.
- * This method is invoked when parameters are being bound to the currently requested action.
- * Child classes may override this method to throw exceptions when there are missing and/or unknown parameters.
- * @param Action $action the currently requested action
- * @param array $missingParams the names of the missing parameters
- * @param array $unknownParams the unknown parameters (name => value)
- */
- public function validateActionParams($action, $missingParams, $unknownParams)
- {
- }
-
- /**
* @return string the controller ID that is prefixed with the module ID (if any).
*/
public function getUniqueId()
diff --git a/framework/yii/base/Formatter.php b/framework/yii/base/Formatter.php
new file mode 100644
index 0000000..d15e5f2
--- /dev/null
+++ b/framework/yii/base/Formatter.php
@@ -0,0 +1,292 @@
+
+ * @since 2.0
+ */
+class Formatter extends Component
+{
+ /**
+ * @var string the default format string to be used to format a date using PHP date() function.
+ */
+ public $dateFormat = 'Y/m/d';
+ /**
+ * @var string the default format string to be used to format a time using PHP date() function.
+ */
+ public $timeFormat = 'h:i:s A';
+ /**
+ * @var string the default format string to be used to format a date and time using PHP date() function.
+ */
+ public $datetimeFormat = 'Y/m/d h:i:s A';
+ /**
+ * @var array the text to be displayed when formatting a boolean value. The first element corresponds
+ * to the text display for false, the second element for true. Defaults to array('No', 'Yes')
.
+ */
+ public $booleanFormat;
+ /**
+ * @var string the character displayed as the decimal point when formatting a number.
+ */
+ public $decimalSeparator = '.';
+ /**
+ * @var string the character displayed as the thousands separator character when formatting a number.
+ */
+ public $thousandSeparator = ',';
+
+
+ /**
+ * Initializes the component.
+ */
+ public function init()
+ {
+ if (empty($this->booleanFormat)) {
+ $this->booleanFormat = array(Yii::t('yii', 'No'), Yii::t('yii', 'Yes'));
+ }
+ }
+
+ /**
+ * Formats the value as is without any formatting.
+ * This method simply returns back the parameter without any format.
+ * @param mixed $value the value to be formatted
+ * @return string the formatted result
+ */
+ public function asRaw($value)
+ {
+ return $value;
+ }
+
+ /**
+ * Formats the value as an HTML-encoded plain text.
+ * @param mixed $value the value to be formatted
+ * @return string the formatted result
+ */
+ public function asText($value)
+ {
+ return Html::encode($value);
+ }
+
+ /**
+ * Formats the value as an HTML-encoded plain text with newlines converted into breaks.
+ * @param mixed $value the value to be formatted
+ * @return string the formatted result
+ */
+ public function asNtext($value)
+ {
+ return nl2br(Html::encode($value));
+ }
+
+ /**
+ * Formats the value as HTML-encoded text paragraphs.
+ * Each text paragraph is enclosed within a `
` tag. + * One or multiple consecutive empty lines divide two paragraphs. + * @param mixed $value the value to be formatted + * @return string the formatted result + */ + public function asParagraphs($value) + { + return str_replace('
', '', + '' . preg_replace('/[\r\n]{2,}/', "
\n", Html::encode($value)) . '
' + ); + } + + /** + * Formats the value as HTML text. + * The value will be purified using [[HtmlPurifier]] to avoid XSS attacks. + * Use [[asRaw()]] if you do not want any purification of the value. + * @param mixed $value the value to be formatted + * @param array|null $config the configuration for the HTMLPurifier class. + * @return string the formatted result + */ + public function asHtml($value, $config = null) + { + return HtmlPurifier::process($value, $config); + } + + /** + * Formats the value as a mailto link. + * @param mixed $value the value to be formatted + * @return string the formatted result + */ + public function asEmail($value) + { + return Html::mailto($value); + } + + /** + * Formats the value as an image tag. + * @param mixed $value the value to be formatted + * @return string the formatted result + */ + public function asImage($value) + { + return Html::img($value); + } + + /** + * Formats the value as a hyperlink. + * @param mixed $value the value to be formatted + * @return string the formatted result + */ + public function asUrl($value) + { + $url = $value; + if (strpos($url, 'http://') !== 0 && strpos($url, 'https://') !== 0) { + $url = 'http://' . $url; + } + return Html::a(Html::encode($value), $url); + } + + /** + * Formats the value as a boolean. + * @param mixed $value the value to be formatted + * @return string the formatted result + * @see booleanFormat + */ + public function asBoolean($value) + { + return $value ? $this->booleanFormat[1] : $this->booleanFormat[0]; + } + + /** + * Formats the value as a date. + * @param integer|string|DateTime $value the value to be formatted. The following + * types of value are supported: + * + * - an integer representing a UNIX timestamp + * - a string that can be parsed into a UNIX timestamp via `strtotime()` + * - a PHP DateTime object + * + * @param string $format the format used to convert the value into a date string. + * If null, [[dateFormat]] will be used. The format string should be one + * that can be recognized by the PHP `date()` function. + * @return string the formatted result + * @see dateFormat + */ + public function asDate($value, $format = null) + { + $value = $this->normalizeDatetimeValue($value); + return date($format === null ? $this->dateFormat : $format, $value); + } + + /** + * Formats the value as a time. + * @param integer|string|DateTime $value the value to be formatted. The following + * types of value are supported: + * + * - an integer representing a UNIX timestamp + * - a string that can be parsed into a UNIX timestamp via `strtotime()` + * - a PHP DateTime object + * + * @param string $format the format used to convert the value into a date string. + * If null, [[timeFormat]] will be used. The format string should be one + * that can be recognized by the PHP `date()` function. + * @return string the formatted result + * @see timeFormat + */ + public function asTime($value, $format = null) + { + $value = $this->normalizeDatetimeValue($value); + return date($format === null ? $this->timeFormat : $format, $value); + } + + /** + * Formats the value as a datetime. + * @param integer|string|DateTime $value the value to be formatted. The following + * types of value are supported: + * + * - an integer representing a UNIX timestamp + * - a string that can be parsed into a UNIX timestamp via `strtotime()` + * - a PHP DateTime object + * + * @param string $format the format used to convert the value into a date string. + * If null, [[datetimeFormat]] will be used. The format string should be one + * that can be recognized by the PHP `date()` function. + * @return string the formatted result + * @see datetimeFormat + */ + public function asDatetime($value, $format = null) + { + $value = $this->normalizeDatetimeValue($value); + return date($format === null ? $this->datetimeFormat : $format, $value); + } + + /** + * Normalizes the given datetime value as one that can be taken by various date/time formatting methods. + * @param mixed $value the datetime value to be normalized. + * @return mixed the normalized datetime value + */ + protected function normalizeDatetimeValue($value) + { + if (is_string($value)) { + if (ctype_digit($value) || $value[0] === '-' && ctype_digit(substr($value, 1))) { + return (int)$value; + } else { + return strtotime($value); + } + } elseif ($value instanceof DateTime) { + return $value->getTimestamp(); + } else { + return (int)$value; + } + } + + /** + * Formats the value as an integer. + * @param mixed $value the value to be formatted + * @return string the formatting result. + */ + public function asInteger($value) + { + if (is_string($value) && preg_match('/^(-?\d+)/', $value, $matches)) { + return $matches[1]; + } else { + $value = (int)$value; + return "$value"; + } + } + + /** + * Formats the value as a double number. + * Property [[decimalSeparator]] will be used to represent the decimal point. + * @param mixed $value the value to be formatted + * @param integer $decimals the number of digits after the decimal point + * @return string the formatting result. + * @see decimalSeparator + */ + public function asDouble($value, $decimals = 2) + { + return str_replace('.', $this->decimalSeparator, sprintf("%.{$decimals}f", $value)); + } + + /** + * Formats the value as a number with decimal and thousand separators. + * This method calls the PHP number_format() function to do the formatting. + * @param mixed $value the value to be formatted + * @param integer $decimals the number of digits after the decimal point + * @return string the formatted result + * @see decimalSeparator + * @see thousandSeparator + */ + public function asNumber($value, $decimals = 0) + { + return number_format($value, $decimals, $this->decimalSeparator, $this->thousandSeparator); + } +} diff --git a/framework/yii/base/InvalidRequestException.php b/framework/yii/base/InvalidRequestException.php deleted file mode 100644 index f4806ce..0000000 --- a/framework/yii/base/InvalidRequestException.php +++ /dev/null @@ -1,26 +0,0 @@ - - * @since 2.0 - */ -class InvalidRequestException extends UserException -{ - /** - * @return string the user-friendly name of this exception - */ - public function getName() - { - return \Yii::t('yii', 'Invalid Request'); - } -} - diff --git a/framework/yii/helpers/base/Html.php b/framework/yii/helpers/base/Html.php index 90396ec..9a0001c 100644 --- a/framework/yii/helpers/base/Html.php +++ b/framework/yii/helpers/base/Html.php @@ -344,7 +344,7 @@ class Html /** * 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()]] + * such as an image tag. If this 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 @@ -366,7 +366,7 @@ class Html /** * 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()]] + * such as an image tag. If this 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. diff --git a/framework/yii/i18n/Formatter.php b/framework/yii/i18n/Formatter.php new file mode 100644 index 0000000..d688a15 --- /dev/null +++ b/framework/yii/i18n/Formatter.php @@ -0,0 +1,232 @@ + + * @since 2.0 + */ +class Formatter extends \yii\base\Formatter +{ + /** + * @var string the locale ID that is used to localize the date and number formatting. + * If not set, [[\yii\base\Application::language]] will be used. + */ + public $locale; + /** + * @var string the default format string to be used to format a date using PHP date() function. + */ + public $dateFormat = 'short'; + /** + * @var string the default format string to be used to format a time using PHP date() function. + */ + public $timeFormat = 'short'; + /** + * @var string the default format string to be used to format a date and time using PHP date() function. + */ + public $datetimeFormat = 'short'; + /** + * @var array the options to be set for the NumberFormatter objects. Please refer to + */ + public $numberFormatOptions = array(); + + + /** + * Initializes the component. + * This method will check if the "intl" PHP extension is installed and set the + * default value of [[locale]]. + * @throws InvalidConfigException if the "intl" PHP extension is not installed. + */ + public function init() + { + if (!extension_loaded('intl')) { + throw new InvalidConfigException('The "intl" PHP extension is not install. It is required to format data values in localized formats.'); + } + if ($this->locale === null) { + $this->locale = Yii::$app->language; + } + parent::init(); + } + + private $_dateFormats = array( + 'short' => IntlDateFormatter::SHORT, + 'medium' => IntlDateFormatter::MEDIUM, + 'long' => IntlDateFormatter::LONG, + 'full' => IntlDateFormatter::FULL, + ); + + /** + * Formats the value as a date. + * @param integer|string|DateTime $value the value to be formatted. The following + * types of value are supported: + * + * - an integer representing a UNIX timestamp + * - a string that can be parsed into a UNIX timestamp via `strtotime()` + * - a PHP DateTime object + * + * @param string $format the format used to convert the value into a date string. + * If null, [[dateFormat]] will be used. The format string should be the one + * that can be recognized by the PHP `date()` function. + * @return string the formatted result + * @see dateFormat + */ + public function asDate($value, $format = null) + { + $value = $this->normalizeDatetimeValue($value); + if ($format === null) { + $format = $this->dateFormat; + } + if (isset($this->_dateFormats[$format])) { + $formatter = new IntlDateFormatter($this->locale, $this->_dateFormats[$format], IntlDateFormatter::NONE); + } else { + $formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, IntlDateFormatter::NONE); + $formatter->setPattern($format); + } + return $formatter->format($value); + } + + /** + * Formats the value as a time. + * @param integer|string|DateTime $value the value to be formatted. The following + * types of value are supported: + * + * - an integer representing a UNIX timestamp + * - a string that can be parsed into a UNIX timestamp via `strtotime()` + * - a PHP DateTime object + * + * @param string $format the format used to convert the value into a date string. + * If null, [[dateFormat]] will be used. The format string should be the one + * that can be recognized by the PHP `date()` function. + * @return string the formatted result + * @see timeFormat + */ + public function asTime($value, $format = null) + { + $value = $this->normalizeDatetimeValue($value); + if ($format === null) { + $format = $this->timeFormat; + } + if (isset($this->_dateFormats[$format])) { + $formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, $this->_dateFormats[$format]); + } else { + $formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, IntlDateFormatter::NONE); + $formatter->setPattern($format); + } + return $formatter->format($value); + } + + /** + * Formats the value as a datetime. + * @param integer|string|DateTime $value the value to be formatted. The following + * types of value are supported: + * + * - an integer representing a UNIX timestamp + * - a string that can be parsed into a UNIX timestamp via `strtotime()` + * - a PHP DateTime object + * + * @param string $format the format used to convert the value into a date string. + * If null, [[dateFormat]] will be used. The format string should be the one + * that can be recognized by the PHP `date()` function. + * @return string the formatted result + * @see datetimeFormat + */ + public function asDatetime($value, $format = null) + { + $value = $this->normalizeDatetimeValue($value); + if ($format === null) { + $format = $this->datetimeFormat; + } + if (isset($this->_dateFormats[$format])) { + $formatter = new IntlDateFormatter($this->locale, $this->_dateFormats[$format], $this->_dateFormats[$format]); + } else { + $formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, IntlDateFormatter::NONE); + $formatter->setPattern($format); + } + return $formatter->format($value); + } + + /** + * Formats the value as a decimal number. + * @param mixed $value the value to be formatted + * @param string $format the format to be used. Please refer to [ICU manual](http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details) + * for details on how to specify a format. + * @return string the formatted result. + */ + public function asDecimal($value, $format = null) + { + return $this->createNumberFormatter(NumberFormatter::DECIMAL, $format)->format($value); + } + + /** + * Formats the value as a currency number. + * @param mixed $value the value to be formatted + * @param string $currency the 3-letter ISO 4217 currency code indicating the currency to use. + * @param string $format the format to be used. Please refer to [ICU manual](http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details) + * for details on how to specify a format. + * @return string the formatted result. + */ + public function asCurrency($value, $currency = 'USD', $format = null) + { + return $this->createNumberFormatter(NumberFormatter::CURRENCY, $format)->formatCurrency($value, $currency); + } + + /** + * Formats the value as a percent number. + * @param mixed $value the value to be formatted + * @param string $format the format to be used. Please refer to [ICU manual](http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details) + * for details on how to specify a format. + * @return string the formatted result. + */ + public function asPercent($value, $format = null) + { + return $this->createNumberFormatter(NumberFormatter::PERCENT, $format)->format($value); + } + + /** + * Formats the value as a scientific number. + * @param mixed $value the value to be formatted + * @param string $format the format to be used. Please refer to [ICU manual](http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details) + * for details on how to specify a format. + * @return string the formatted result. + */ + public function asScientific($value, $format = null) + { + return $this->createNumberFormatter(NumberFormatter::SCIENTIFIC, $format)->format($value); + } + + /** + * Creates a number formatter based on the given type and format. + * @param integer $type the type of the number formatter + * @param string $format the format to be used + * @return NumberFormatter the created formatter instance + */ + protected function createNumberFormatter($type, $format) + { + $formatter = new NumberFormatter($this->locale, $type); + if ($format !== null) { + $formatter->setPattern($format); + } + if (!empty($this->numberFormatOptions)) { + foreach ($this->numberFormatOptions as $name => $attribute) { + $formatter->setAttribute($name, $attribute); + } + } + return $formatter; + } +} diff --git a/framework/yii/requirements/requirements.php b/framework/yii/requirements/requirements.php index 0dbc1fc..63aa70d 100644 --- a/framework/yii/requirements/requirements.php +++ b/framework/yii/requirements/requirements.php @@ -43,6 +43,6 @@ return array( 'mandatory' => false, 'condition' => $this->checkPhpExtensionVersion('intl', '1.0.2'), 'by' => 'Internationalization support', - 'memo' => 'PHP Intl extension 1.0.2 or higher is required when you want to use IDN-feature of EmailValidator or UrlValidator.' + 'memo' => 'PHP Intl extension 1.0.2 or higher is required when you want to use IDN-feature of EmailValidator or UrlValidator or theyii\i18n\Formatter
class.'
),
);
\ No newline at end of file
diff --git a/framework/yii/web/Controller.php b/framework/yii/web/Controller.php
index 517f4b4..026c078 100644
--- a/framework/yii/web/Controller.php
+++ b/framework/yii/web/Controller.php
@@ -8,6 +8,8 @@
namespace yii\web;
use Yii;
+use yii\base\HttpException;
+use yii\base\InlineAction;
/**
* Controller is the base class of Web controllers.
@@ -19,6 +21,48 @@ use Yii;
class Controller extends \yii\base\Controller
{
/**
+ * Binds the parameters to the action.
+ * This method is invoked by [[Action]] when it begins to run with the given parameters.
+ * This method will check the parameter names that the action requires and return
+ * the provided parameters according to the requirement. If there is any missing parameter,
+ * an exception will be thrown.
+ * @param \yii\base\Action $action the action to be bound with parameters
+ * @param array $params the parameters to be bound to the action
+ * @return array the valid parameters that the action can run with.
+ * @throws HttpException if there are missing parameters.
+ */
+ public function bindActionParams($action, $params)
+ {
+ if ($action instanceof InlineAction) {
+ $method = new \ReflectionMethod($this, $action->actionMethod);
+ } else {
+ $method = new \ReflectionMethod($action, 'run');
+ }
+
+ $args = array();
+ $missing = array();
+ foreach ($method->getParameters() as $param) {
+ $name = $param->getName();
+ if (array_key_exists($name, $params)) {
+ $args[] = $params[$name];
+ unset($params[$name]);
+ } elseif ($param->isDefaultValueAvailable()) {
+ $args[] = $param->getDefaultValue();
+ } else {
+ $missing[] = $name;
+ }
+ }
+
+ if (!empty($missing)) {
+ throw new HttpException(400, Yii::t('yii', 'Missing required parameters: {params}', array(
+ '{params}' => implode(', ', $missing),
+ )));
+ }
+
+ return $args;
+ }
+
+ /**
* Creates a URL using the given route and parameters.
*
* This method enhances [[UrlManager::createUrl()]] by supporting relative routes.
diff --git a/framework/yii/web/VerbFilter.php b/framework/yii/web/VerbFilter.php
new file mode 100644
index 0000000..9b475e3
--- /dev/null
+++ b/framework/yii/web/VerbFilter.php
@@ -0,0 +1,90 @@
+ array(
+ * 'class' => \yii\web\VerbFilter::className(),
+ * 'actions' => array(
+ * 'index' => array('get'),
+ * 'view' => array('get'),
+ * 'create' => array('get', 'post'),
+ * 'update' => array('get', 'put', 'post'),
+ * 'delete' => array('post', 'delete'),
+ * ),
+ * ),
+ * );
+ * }
+ * ~~~
+ *
+ * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.7
+ * @author Carsten Brandt $value
", $this->formatter->asParagraphs($value)); + $value = 123; + $this->assertSame("$value
", $this->formatter->asParagraphs($value)); + $value = '<>'; + $this->assertSame('<>
', $this->formatter->asParagraphs($value)); + $value = "123\n456"; + $this->assertSame("123\n456
", $this->formatter->asParagraphs($value)); + $value = "123\n\n456"; + $this->assertSame("123
\n456
", $this->formatter->asParagraphs($value)); + $value = "123\n\n\n456"; + $this->assertSame("123
\n456
", $this->formatter->asParagraphs($value)); + } + + public function testAsHtml() + { + // todo: dependency on HtmlPurifier + } + + public function testAsEmail() + { + $value = 'test@sample.com'; + $this->assertSame("$value", $this->formatter->asEmail($value)); + } + + public function testAsImage() + { + $value = 'http://sample.com/img.jpg'; + $this->assertSame("", $this->formatter->asImage($value)); + } + + public function testAsBoolean() + { + $value = true; + $this->assertSame('Yes', $this->formatter->asBoolean($value)); + $value = false; + $this->assertSame('No', $this->formatter->asBoolean($value)); + $value = "111"; + $this->assertSame('Yes', $this->formatter->asBoolean($value)); + $value = ""; + $this->assertSame('No', $this->formatter->asBoolean($value)); + } + + public function testAsDate() + { + $value = time(); + $this->assertSame(date('Y/m/d', $value), $this->formatter->asDate($value)); + $this->assertSame(date('Y-m-d', $value), $this->formatter->asDate($value, 'Y-m-d')); + } + + public function testAsTime() + { + $value = time(); + $this->assertSame(date('h:i:s A', $value), $this->formatter->asTime($value)); + $this->assertSame(date('h:i:s', $value), $this->formatter->asTime($value, 'h:i:s')); + } + + public function testAsDatetime() + { + $value = time(); + $this->assertSame(date('Y/m/d h:i:s A', $value), $this->formatter->asDatetime($value)); + $this->assertSame(date('Y-m-d h:i:s', $value), $this->formatter->asDatetime($value, 'Y-m-d h:i:s')); + } + + public function testAsInteger() + { + $value = 123; + $this->assertSame("$value", $this->formatter->asInteger($value)); + $value = 123.23; + $this->assertSame("123", $this->formatter->asInteger($value)); + $value = 'a'; + $this->assertSame("0", $this->formatter->asInteger($value)); + $value = -123.23; + $this->assertSame("-123", $this->formatter->asInteger($value)); + $value = "-123abc"; + $this->assertSame("-123", $this->formatter->asInteger($value)); + } + + public function testAsDouble() + { + $value = 123.12; + $this->assertSame("123.12", $this->formatter->asDouble($value)); + $this->assertSame("123.1", $this->formatter->asDouble($value, 1)); + $this->assertSame("123", $this->formatter->asDouble($value, 0)); + $value = 123; + $this->assertSame("123.00", $this->formatter->asDouble($value)); + $this->formatter->decimalSeparator = ','; + $value = 123.12; + $this->assertSame("123,12", $this->formatter->asDouble($value)); + $this->assertSame("123,1", $this->formatter->asDouble($value, 1)); + $this->assertSame("123", $this->formatter->asDouble($value, 0)); + $value = 123123.123; + $this->assertSame("123123,12", $this->formatter->asDouble($value)); + } + + public function testAsNumber() + { + $value = 123123.123; + $this->assertSame("123,123", $this->formatter->asNumber($value)); + $this->assertSame("123,123.12", $this->formatter->asNumber($value, 2)); + $this->formatter->decimalSeparator = ','; + $this->formatter->thousandSeparator = ' '; + $this->assertSame("123 123", $this->formatter->asNumber($value)); + $this->assertSame("123 123,12", $this->formatter->asNumber($value, 2)); + $this->formatter->thousandSeparator = ''; + $this->assertSame("123123", $this->formatter->asNumber($value)); + $this->assertSame("123123,12", $this->formatter->asNumber($value, 2)); + } +} diff --git a/tests/unit/framework/i18n/FormatterTest.php b/tests/unit/framework/i18n/FormatterTest.php new file mode 100644 index 0000000..f742f22 --- /dev/null +++ b/tests/unit/framework/i18n/FormatterTest.php @@ -0,0 +1,88 @@ + + * @since 2.0 + */ +class FormatterTest extends TestCase +{ + /** + * @var Formatter + */ + protected $formatter; + + protected function setUp() + { + parent::setUp(); + if (!extension_loaded('intl')) { + $this->markTestSkipped('intl extension is required.'); + } + $this->mockApplication(); + $this->formatter = new Formatter(array( + 'locale' => 'en_US', + )); + } + + protected function tearDown() + { + parent::tearDown(); + $this->formatter = null; + } + + public function testAsDecimal() + { + $value = '123'; + $this->assertSame($value, $this->formatter->asDecimal($value)); + $value = '123456'; + $this->assertSame("123,456", $this->formatter->asDecimal($value)); + $value = '-123456.123'; + $this->assertSame("-123,456.123", $this->formatter->asDecimal($value)); + } + + public function testAsPercent() + { + $value = '123'; + $this->assertSame('12,300%', $this->formatter->asPercent($value)); + $value = '0.1234'; + $this->assertSame("12%", $this->formatter->asPercent($value)); + $value = '-0.009343'; + $this->assertSame("-1%", $this->formatter->asPercent($value)); + } + + public function testAsScientific() + { + $value = '123'; + $this->assertSame('1.23E2', $this->formatter->asScientific($value)); + $value = '123456'; + $this->assertSame("1.23456E5", $this->formatter->asScientific($value)); + $value = '-123456.123'; + $this->assertSame("-1.23456123E5", $this->formatter->asScientific($value)); + } + + public function testAsCurrency() + { + $value = '123'; + $this->assertSame('$123.00', $this->formatter->asCurrency($value)); + $value = '123.456'; + $this->assertSame("$123.46", $this->formatter->asCurrency($value)); + $value = '-123456.123'; + $this->assertSame("($123,456.12)", $this->formatter->asCurrency($value)); + } + + public function testDate() + { + $time = time(); + $this->assertSame(date('n/j/y', $time), $this->formatter->asDate($time)); + $this->assertSame(date('M j, Y', $time), $this->formatter->asDate($time, 'long')); + } +}