Commit fad63544 by Qiang Xue

Fixes #2655: Arrayable and ArrayableTrait are incompatible for some PHP versions.

parent 6d97ded6
......@@ -8,7 +8,14 @@
namespace yii\base;
/**
* Arrayable should be implemented by classes that need to be represented in array format.
* Arrayable is the interface that should be implemented by classes who want to support customizable representation of their instances.
*
* For example, if a class implements Arrayable, by calling [[toArray()]], an instance of this class
* can be turned into an array (including all its embedded objects) which can then be further transformed easily
* into other formats, such as JSON, XML.
*
* The methods [[fields()]] and [[extraFields()]] allow the implementing classes to customize how and which of their data
* should be formatted and put into the result of [[toArray()]].
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
......@@ -16,8 +23,68 @@ namespace yii\base;
interface Arrayable
{
/**
* Returns the list of fields that should be returned by default by [[toArray()]] when no specific fields are specified.
*
* A field is a named element in the returned array by [[toArray()]].
*
* This method should return an array of field names or field definitions.
* If the former, the field name will be treated as an object property name whose value will be used
* as the field value. If the latter, the array key should be the field name while the array value should be
* the corresponding field definition which can be either an object property name or a PHP callable
* returning the corresponding field value. The signature of the callable should be:
*
* ```php
* function ($field, $model) {
* // return field value
* }
* ```
*
* For example, the following code declares four fields:
*
* - `email`: the field name is the same as the property name `email`;
* - `firstName` and `lastName`: the field names are `firstName` and `lastName`, and their
* values are obtained from the `first_name` and `last_name` properties;
* - `fullName`: the field name is `fullName`. Its value is obtained by concatenating `first_name`
* and `last_name`.
*
* ```php
* return [
* 'email',
* 'firstName' => 'first_name',
* 'lastName' => 'last_name',
* 'fullName' => function () {
* return $this->first_name . ' ' . $this->last_name;
* },
* ];
* ```
*
* @return array the list of field names or field definitions.
* @see toArray()
*/
public function fields();
/**
* Returns the list of additional fields that can be returned by [[toArray()]] in addition to those listed in [[fields()]].
*
* This method is similar to [[fields()]] except that the list of fields declared
* by this method are not returned by default by [[toArray()]]. Only when a field in the list
* is explicitly requested, will it be included in the result of [[toArray()]].
*
* @return array the list of expandable field names or field definitions. Please refer
* to [[fields()]] on the format of the return value.
* @see toArray()
* @see fields()
*/
public function extraFields();
/**
* Converts the object into an array.
* @return array the array representation of this object
*
* @param array $fields the fields that the output array should contain. Fields not specified
* in [[fields()]] will be ignored. If this parameter is empty, all fields as specified in [[fields()]] will be returned.
* @param array $expand the additional fields that the output array should contain.
* Fields not specified in [[extraFields()]] will be ignored. If this parameter is empty, no extra fields
* will be returned.
* @param boolean $recursive whether to recursively return array representation of embedded objects.
* @return array the array representation of the object
*/
public function toArray();
public function toArray(array $fields = [], array $expand = [], $recursive = true);
}
......@@ -13,6 +13,10 @@ use yii\web\Link;
use yii\web\Linkable;
/**
* ArrayableTrait provides a common implementation of the [[Arrayable]] interface.
*
* ArrayableTrait implements [[toArray()]] by respecting the field definitions as declared
* in [[fields()]] and [[extraFields()]].
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
......
......@@ -15,7 +15,7 @@ use Yii;
* @author Alexander Makarov <sam@rmcreative.ru>
* @since 2.0
*/
class ErrorException extends \ErrorException implements Arrayable
class ErrorException extends \ErrorException
{
/**
* Constructs the exception.
......@@ -93,32 +93,4 @@ class ErrorException extends \ErrorException implements Arrayable
];
return isset($names[$this->getCode()]) ? $names[$this->getCode()] : 'Error';
}
/**
* Returns the array representation of this object.
* @return array the array representation of this object.
*/
public function toArray()
{
return $this->toArrayRecursive($this);
}
/**
* Returns the array representation of the exception and all previous exceptions recursively.
* @param \Exception $exception object
* @return array the array representation of the exception.
*/
protected function toArrayRecursive($exception)
{
$array = [
'type' => get_class($exception),
'name' => $exception instanceof self ? $exception->getName() : 'Exception',
'message' => $exception->getMessage(),
'code' => $exception->getCode(),
];
if (($prev = $exception->getPrevious()) !== null) {
$array['previous'] = $this->toArrayRecursive($prev);
}
return $array;
}
}
......@@ -119,15 +119,8 @@ class ErrorHandler extends Component
'exception' => $exception,
]);
}
} elseif ($exception instanceof Arrayable) {
$response->data = $exception->toArray();
} else {
$response->data = [
'type' => get_class($exception),
'name' => 'Exception',
'message' => $exception->getMessage(),
'code' => $exception->getCode(),
];
$response->data = $this->convertExceptionToArray($exception);
}
if ($exception instanceof HttpException) {
......@@ -140,6 +133,25 @@ class ErrorHandler extends Component
}
/**
* Converts an exception into an array.
* @param \Exception $exception the exception being converted
* @return array the array representation of the exception.
*/
protected function convertExceptionToArray($exception)
{
$array = [
'type' => get_class($exception),
'name' => $exception instanceof \yii\base\Exception || $exception instanceof \yii\base\ErrorException ? $exception->getName() : 'Exception',
'message' => $exception->getMessage(),
'code' => $exception->getCode(),
];
if (($prev = $exception->getPrevious()) !== null) {
$array['previous'] = $this->convertExceptionToArray($prev);
}
return $array;
}
/**
* Converts special characters to HTML entities.
* @param string $text to encode.
* @return string encoded original text.
......
......@@ -13,7 +13,7 @@ namespace yii\base;
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Exception extends \Exception implements Arrayable
class Exception extends \Exception
{
/**
* @return string the user-friendly name of this exception
......@@ -22,32 +22,4 @@ class Exception extends \Exception implements Arrayable
{
return 'Exception';
}
/**
* Returns the array representation of this object.
* @return array the array representation of this object.
*/
public function toArray()
{
return $this->toArrayRecursive($this);
}
/**
* Returns the array representation of the exception and all previous exceptions recursively.
* @param \Exception $exception object
* @return array the array representation of the exception.
*/
protected function toArrayRecursive($exception)
{
$array = [
'type' => get_class($exception),
'name' => $exception instanceof self ? $exception->getName() : 'Exception',
'message' => $exception->getMessage(),
'code' => $exception->getCode(),
];
if (($prev = $exception->getPrevious()) !== null) {
$array['previous'] = $this->toArrayRecursive($prev);
}
return $array;
}
}
......@@ -8,7 +8,6 @@
namespace yii\data;
use Yii;
use yii\base\Arrayable;
use yii\base\Object;
use yii\web\Link;
use yii\web\Linkable;
......@@ -68,7 +67,7 @@ use yii\web\Request;
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Pagination extends Object implements Linkable, Arrayable
class Pagination extends Object implements Linkable
{
const LINK_NEXT = 'next';
const LINK_PREV = 'prev';
......@@ -317,19 +316,6 @@ class Pagination extends Object implements Linkable, Arrayable
}
/**
* @inheritdoc
*/
public function toArray()
{
return [
'totalCount' => $this->totalCount,
'pageCount' => $this->getPageCount(),
'currentPage' => $this->getPage(),
'perPage' => $this->getPageSize(),
];
}
/**
* Returns the value of the specified query parameter.
* This method returns the named parameter value from [[params]]. Null is returned if the value does not exist.
* @param string $name the parameter name
......
......@@ -91,7 +91,11 @@ class BaseJson
} elseif ($data instanceof Arrayable) {
$data = $data->toArray();
} else {
$data = get_object_vars($data);
$result = [];
foreach ($data as $name => $value) {
$result[$name] = $value;
}
$data = $result;
}
if ($data === []) {
......
......@@ -8,7 +8,6 @@
namespace yii\i18n;
use Yii;
use yii\base\Arrayable;
use yii\base\Component;
use yii\base\InvalidConfigException;
......@@ -104,11 +103,7 @@ class I18N extends Component
*/
public function format($message, $params, $language)
{
if ($params instanceof Arrayable) {
$params = $params->toArray();
} else {
$params = (array)$params;
}
$params = (array)$params;
if ($params === []) {
return $message;
}
......
......@@ -169,14 +169,33 @@ class Serializer extends Component
$this->collectionEnvelope => $models,
];
if ($pagination !== false) {
$result['_links'] = Link::serialize($pagination->getLinks());
$result['_meta'] = $pagination->toArray();
return array_merge($result, $this->serializePagination($pagination));
} else {
return $result;
}
return $result;
}
}
/**
* Serializes a pagination into an array.
* @param Pagination $pagination
* @return array the array representation of the pagination
* @see addPaginationHeader()
*/
protected function serializePagination($pagination)
{
return [
'_links' => Link::serialize($pagination->getLinks(true)),
'_meta' => [
'totalCount' => $pagination->totalCount,
'pageCount' => $pagination->getPageCount(),
'currentPage' => $pagination->getPage(),
'perPage' => $pagination->getPageSize(),
],
];
}
/**
* Adds HTTP headers about the pagination to the response.
* @param Pagination $pagination
*/
......
......@@ -9,7 +9,6 @@ namespace yii\web;
use Yii;
use ArrayIterator;
use yii\base\Arrayable;
use yii\base\InvalidCallException;
use yii\base\Object;
......@@ -23,7 +22,7 @@ use yii\base\Object;
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class CookieCollection extends Object implements \IteratorAggregate, \ArrayAccess, \Countable, Arrayable
class CookieCollection extends Object implements \IteratorAggregate, \ArrayAccess, \Countable
{
/**
* @var boolean whether this collection is read only.
......@@ -167,17 +166,6 @@ class CookieCollection extends Object implements \IteratorAggregate, \ArrayAcces
}
/**
* Returns the collection as a PHP array.
* @return array the array representation of the collection.
* The array keys are cookie names, and the array values are the corresponding
* cookie objects.
*/
public function toArray()
{
return $this->_cookies;
}
/**
* Returns whether there is a cookie with the specified name.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `isset($collection[$name])`.
......
......@@ -8,7 +8,6 @@
namespace yii\web;
use Yii;
use yii\base\Arrayable;
use yii\base\Object;
use ArrayIterator;
......@@ -22,7 +21,7 @@ use ArrayIterator;
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class HeaderCollection extends Object implements \IteratorAggregate, \ArrayAccess, \Countable, Arrayable
class HeaderCollection extends Object implements \IteratorAggregate, \ArrayAccess, \Countable
{
/**
* @var array the headers in this collection (indexed by the header names)
......@@ -161,16 +160,6 @@ class HeaderCollection extends Object implements \IteratorAggregate, \ArrayAcces
}
/**
* Returns the collection as a PHP array.
* @return array the array representation of the collection.
* The array keys are header names, and the array values are the corresponding header values.
*/
public function toArray()
{
return $this->_headers;
}
/**
* Returns whether there is a header with the specified name.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `isset($collection[$name])`.
......
......@@ -58,15 +58,4 @@ class HttpException extends UserException
return 'Error';
}
}
/**
* Returns the array representation of this object.
* @return array the array representation of this object.
*/
public function toArray()
{
$array = parent::toArray();
$array['status'] = $this->statusCode;
return $array;
}
}
......@@ -7,7 +7,6 @@
namespace yii\web;
use yii\base\Arrayable;
use yii\base\Object;
/**
......@@ -16,7 +15,7 @@ use yii\base\Object;
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Link extends Object implements Arrayable
class Link extends Object
{
/**
* The self link.
......@@ -53,13 +52,6 @@ class Link extends Object implements Arrayable
*/
public $hreflang;
/**
* @inheritdoc
*/
public function toArray()
{
return array_filter((array)$this);
}
/**
* Serializes a list of links into proper array format.
......@@ -71,7 +63,7 @@ class Link extends Object implements Arrayable
foreach ($links as $rel => $link) {
if (is_array($link)) {
foreach ($link as $i => $l) {
$link[$i] = $l instanceof self ? $l->toArray() : ['href' => $l];
$link[$i] = $l instanceof self ? array_filter((array)$l) : ['href' => $l];
}
$links[$rel] = $link;
} elseif (!$link instanceof self) {
......
......@@ -8,7 +8,6 @@
namespace yii\web;
use Yii;
use yii\base\Arrayable;
use yii\base\Component;
use yii\base\InvalidConfigException;
use yii\base\InvalidParamException;
......@@ -72,7 +71,7 @@ use yii\base\InvalidParamException;
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Countable, Arrayable
class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Countable
{
/**
* @var string the name of the session variable that stores the flash message data.
......@@ -600,15 +599,6 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
}
/**
* @return array the list of all session variables in array
*/
public function toArray()
{
$this->open();
return $_SESSION;
}
/**
* Updates the counters for flash messages and removes outdated flash messages.
* This method should only be called once in [[init()]].
*/
......
<?php
namespace yiiunit\framework\base;
use yiiunit\TestCase;
use yii\base\UserException;
use yii\base\InvalidCallException;
class ExceptionTest extends TestCase
{
public function testToArrayWithPrevious()
{
$e = new InvalidCallException('bar', 0 ,new InvalidCallException('foo'));
$array = $e->toArray();
$this->assertEquals('bar', $array['message']);
$this->assertEquals('foo', $array['previous']['message']);
$e = new InvalidCallException('bar', 0, new UserException('foo'));
$array = $e->toArray();
$this->assertEquals('bar', $array['message']);
$this->assertEquals('foo', $array['previous']['message']);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment