mirror of
https://github.com/yiisoft/yii2.git
synced 2025-12-01 23:51:12 +08:00
Replaced Jsonable with Arrayable.
Added support for different response formats. Support for error response in different formats.
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
namespace yii;
|
||||
|
||||
use yii\base\Arrayable;
|
||||
use yii\base\Exception;
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\base\InvalidParamException;
|
||||
@@ -636,6 +637,31 @@ class YiiBase
|
||||
{
|
||||
return get_object_vars($object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the object into an array.
|
||||
* @param object|array $object the object to be converted into an array
|
||||
* @param boolean $recursive whether to recursively converts properties which are objects into arrays.
|
||||
* @return array the array representation of the object
|
||||
*/
|
||||
public static function toArray($object, $recursive = true)
|
||||
{
|
||||
if ($object instanceof Arrayable) {
|
||||
$object = $object->toArray();
|
||||
if (!$recursive) {
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
$result = array();
|
||||
foreach ($object as $key => $value) {
|
||||
if ($recursive && (is_array($value) || is_object($value))) {
|
||||
$result[$key] = static::toArray($value, true);
|
||||
} else {
|
||||
$result[$key] = $value;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
YiiBase::$aliases = array(
|
||||
|
||||
@@ -18,10 +18,6 @@ use yii\web\HttpException;
|
||||
*/
|
||||
abstract class Application extends Module
|
||||
{
|
||||
/**
|
||||
* @event ResponseEvent an event that is triggered after [[handle()]] returns a response.
|
||||
*/
|
||||
const EVENT_RESPONSE = 'response';
|
||||
/**
|
||||
* @var string the application name.
|
||||
*/
|
||||
@@ -144,11 +140,7 @@ abstract class Application extends Module
|
||||
public function run()
|
||||
{
|
||||
$response = $this->handle($this->getRequest());
|
||||
|
||||
$event = new ResponseEvent($response);
|
||||
$this->trigger(self::EVENT_RESPONSE, $event);
|
||||
$event->response->send();
|
||||
|
||||
$response->send();
|
||||
return $response->exitStatus;
|
||||
}
|
||||
|
||||
@@ -422,18 +414,19 @@ abstract class Application extends Module
|
||||
*/
|
||||
public function handleFatalError()
|
||||
{
|
||||
// load ErrorException manually here because autoloading them will not work
|
||||
// when error occurs while autoloading a class
|
||||
if (!class_exists('\\yii\\base\\Exception', false)) {
|
||||
require_once(__DIR__ . '/Exception.php');
|
||||
}
|
||||
if (!class_exists('\\yii\\base\\ErrorException', false)) {
|
||||
require_once(__DIR__ . '/ErrorException.php');
|
||||
}
|
||||
|
||||
$error = error_get_last();
|
||||
|
||||
if (ErrorException::isFatalError($error)) {
|
||||
unset($this->_memoryReserve);
|
||||
// load ErrorException manually here because autoloading them will not work
|
||||
// when error occurs while autoloading a class
|
||||
if (!class_exists('\\yii\\base\\Exception', false)) {
|
||||
require_once(__DIR__ . '/Exception.php');
|
||||
}
|
||||
if (!class_exists('\\yii\\base\\ErrorException', false)) {
|
||||
require_once(__DIR__ . '/ErrorException.php');
|
||||
}
|
||||
$exception = new ErrorException($error['message'], $error['type'], $error['type'], $error['file'], $error['line']);
|
||||
// use error_log because it's too late to use Yii log
|
||||
error_log($exception);
|
||||
|
||||
@@ -8,15 +8,16 @@
|
||||
namespace yii\base;
|
||||
|
||||
/**
|
||||
* Jsonable should be implemented by classes that need to be represented in JSON format.
|
||||
* Arrayable should be implemented by classes that need to be represented in array format.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
interface Jsonable
|
||||
interface Arrayable
|
||||
{
|
||||
/**
|
||||
* @return string the JSON representation of this object
|
||||
* Converts the object into an array.
|
||||
* @return array the array representation of this object
|
||||
*/
|
||||
public function toJson();
|
||||
public function toArray();
|
||||
}
|
||||
@@ -9,7 +9,6 @@ namespace yii\base;
|
||||
|
||||
use Yii;
|
||||
use yii\web\HttpException;
|
||||
use yii\web\Response;
|
||||
|
||||
/**
|
||||
* ErrorHandler handles uncaught PHP errors and exceptions.
|
||||
@@ -90,31 +89,41 @@ class ErrorHandler extends Component
|
||||
|
||||
$useErrorView = !YII_DEBUG || $exception instanceof UserException;
|
||||
|
||||
$response = Yii::$app->getResponse();
|
||||
if ($useErrorView && $this->errorAction !== null) {
|
||||
$result = Yii::$app->runAction($this->errorAction);
|
||||
if ($result instanceof Response) {
|
||||
$response = $result;
|
||||
} else {
|
||||
$response = new Response;
|
||||
$response->content = $result;
|
||||
$response->setContent($result);
|
||||
}
|
||||
} else {
|
||||
$response = new Response;
|
||||
} elseif ($response->format === \yii\web\Response::FORMAT_HTML) {
|
||||
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') {
|
||||
// AJAX request
|
||||
$response->content = Yii::$app->renderException($exception);
|
||||
$response->setContent(Yii::$app->renderException($exception));
|
||||
} else {
|
||||
// if there is an error during error rendering it's useful to
|
||||
// display PHP error in debug mode instead of a blank screen
|
||||
if (YII_DEBUG) {
|
||||
ini_set('display_errors', 1);
|
||||
}
|
||||
|
||||
$file = $useErrorView ? $this->errorView : $this->exceptionView;
|
||||
$response->content = $this->renderFile($file, array(
|
||||
$response->setContent($this->renderFile($file, array(
|
||||
'exception' => $exception,
|
||||
));
|
||||
)));
|
||||
}
|
||||
} else {
|
||||
if ($exception instanceof Exception) {
|
||||
$content = $exception->toArray();
|
||||
} else {
|
||||
$content = array(
|
||||
'type' => get_class($exception),
|
||||
'name' => 'Exception',
|
||||
'message' => $exception->getMessage(),
|
||||
'code' => $exception->getCode(),
|
||||
);
|
||||
}
|
||||
$response->setContent($content);
|
||||
}
|
||||
|
||||
if ($exception instanceof HttpException) {
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace yii\base;
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Exception extends \Exception
|
||||
class Exception extends \Exception implements Arrayable
|
||||
{
|
||||
/**
|
||||
* @return string the user-friendly name of this exception
|
||||
@@ -22,4 +22,18 @@ class Exception extends \Exception
|
||||
{
|
||||
return \Yii::t('yii', 'Exception');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array representation of this object.
|
||||
* @return array the array representation of this object.
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return array(
|
||||
'type' => get_class($this),
|
||||
'name' => $this->getName(),
|
||||
'message' => $this->getMessage(),
|
||||
'code' => $this->getCode(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
|
||||
namespace yii\base;
|
||||
|
||||
use Yii;
|
||||
use ArrayObject;
|
||||
use ArrayIterator;
|
||||
use yii\helpers\Inflector;
|
||||
use yii\helpers\Json;
|
||||
use yii\validators\RequiredValidator;
|
||||
use yii\validators\Validator;
|
||||
|
||||
@@ -42,7 +42,7 @@ use yii\validators\Validator;
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsonable
|
||||
class Model extends Component implements \IteratorAggregate, \ArrayAccess
|
||||
{
|
||||
/**
|
||||
* @event ModelEvent an event raised at the beginning of [[validate()]]. You may set
|
||||
@@ -639,13 +639,13 @@ class Model extends Component implements \IteratorAggregate, \ArrayAccess, Jsona
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the JSON representation of this object.
|
||||
* Converts the object into an array.
|
||||
* The default implementation will return [[attributes]].
|
||||
* @return string the JSON representation of this object.
|
||||
* @return array the array representation of the object
|
||||
*/
|
||||
public function toJson()
|
||||
public function toArray()
|
||||
{
|
||||
return Json::encode($this->getAttributes());
|
||||
return $this->getAttributes();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,14 +8,13 @@
|
||||
namespace yii\base;
|
||||
|
||||
use Yii;
|
||||
use yii\helpers\Json;
|
||||
|
||||
/**
|
||||
* @include @yii/base/Object.md
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class Object implements Jsonable
|
||||
class Object implements Arrayable
|
||||
{
|
||||
/**
|
||||
* @return string the fully qualified name of this class.
|
||||
@@ -221,12 +220,13 @@ class Object implements Jsonable
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the JSON representation of this object.
|
||||
* The default implementation will return all public member variables.
|
||||
* @return string the JSON representation of this object.
|
||||
* Converts the object into an array.
|
||||
* The default implementation will return all public property values as an array.
|
||||
* However, if the object is traversable, it will return the data obtained by the data iteration.
|
||||
* @return array the array representation of the object
|
||||
*/
|
||||
public function toJson()
|
||||
public function toArray()
|
||||
{
|
||||
return Json::encode(Yii::getObjectVars($this));
|
||||
return Yii::toArray($this, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,13 +13,25 @@ namespace yii\base;
|
||||
*/
|
||||
class Response extends Component
|
||||
{
|
||||
/**
|
||||
* @event ResponseEvent an event that is triggered by [[send()]] before it sends the response to client.
|
||||
* You may respond to this event to modify the response before it is sent out.
|
||||
*/
|
||||
const EVENT_SEND = 'send';
|
||||
|
||||
/**
|
||||
* @var integer the exit status. Exit statuses should be in the range 0 to 254.
|
||||
* The status 0 means the program terminates successfully.
|
||||
*/
|
||||
public $exitStatus = 0;
|
||||
|
||||
/**
|
||||
* Sends the response to client.
|
||||
* This method will trigger the [[EVENT_SEND]] event. Please make sure you call
|
||||
* the parent implementation first if you override this method.
|
||||
*/
|
||||
public function send()
|
||||
{
|
||||
$this->trigger(self::EVENT_SEND, new ResponseEvent($this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
namespace yii\helpers\base;
|
||||
|
||||
use yii\base\InvalidParamException;
|
||||
use yii\base\Jsonable;
|
||||
use yii\base\Arrayable;
|
||||
use yii\web\JsExpression;
|
||||
|
||||
/**
|
||||
@@ -91,9 +91,8 @@ class Json
|
||||
$token = '!{[' . count($expressions) . ']}!';
|
||||
$expressions['"' . $token . '"'] = $data->expression;
|
||||
return $token;
|
||||
} elseif ($data instanceof Jsonable) {
|
||||
return $data->toJson();
|
||||
} else {
|
||||
$data = $data instanceof Arrayable ? $data->toArray() : get_object_vars($data);
|
||||
$result = array();
|
||||
foreach ($data as $key => $value) {
|
||||
if (is_array($value) || is_object($value)) {
|
||||
|
||||
@@ -71,7 +71,9 @@ class Application extends \yii\base\Application
|
||||
return $result;
|
||||
} else {
|
||||
$response = $this->getResponse();
|
||||
$response->content = $result;
|
||||
if ($result !== null) {
|
||||
$response->setContent($result);
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
} catch (InvalidRouteException $e) {
|
||||
|
||||
@@ -51,4 +51,15 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
namespace yii\web;
|
||||
|
||||
use Yii;
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\web\HttpException;
|
||||
use yii\base\InvalidParamException;
|
||||
use yii\helpers\FileHelper;
|
||||
@@ -23,6 +24,21 @@ use yii\helpers\StringHelper;
|
||||
*/
|
||||
class Response extends \yii\base\Response
|
||||
{
|
||||
const FORMAT_RAW = 'raw';
|
||||
const FORMAT_HTML = 'html';
|
||||
const FORMAT_JSON = 'json';
|
||||
const FORMAT_JSONP = 'jsonp';
|
||||
const FORMAT_XML = 'xml';
|
||||
|
||||
/**
|
||||
* @var string the response format.
|
||||
*/
|
||||
public $format = self::FORMAT_HTML;
|
||||
/**
|
||||
* @var string the charset of the text response. If not set, it will use
|
||||
* the value of [[Application::charset]].
|
||||
*/
|
||||
public $charset;
|
||||
/**
|
||||
* @var integer the HTTP status code that should be used when redirecting in AJAX mode.
|
||||
* This is used by [[redirect()]]. A 2xx code should normally be used for this purpose
|
||||
@@ -30,10 +46,6 @@ class Response extends \yii\base\Response
|
||||
* @see redirect
|
||||
*/
|
||||
public $ajaxRedirectCode = 278;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $content;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
@@ -133,6 +145,9 @@ class Response extends \yii\base\Response
|
||||
$this->version = '1.1';
|
||||
}
|
||||
}
|
||||
if ($this->charset === null) {
|
||||
$this->charset = Yii::$app->charset;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,7 +187,7 @@ class Response extends \yii\base\Response
|
||||
public function renderJson($data)
|
||||
{
|
||||
$this->getHeaders()->set('Content-Type', 'application/json');
|
||||
$this->content = Json::encode($data);
|
||||
$this->setContent(Json::encode($data));
|
||||
$this->send();
|
||||
}
|
||||
|
||||
@@ -180,16 +195,16 @@ class Response extends \yii\base\Response
|
||||
{
|
||||
$this->getHeaders()->set('Content-Type', 'text/javascript');
|
||||
$data = Json::encode($data);
|
||||
$this->content = "$callbackName($data);";
|
||||
$this->setContent("$callbackName($data);");
|
||||
$this->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the response to the client.
|
||||
* @return boolean true if the response was sent
|
||||
*/
|
||||
public function send()
|
||||
{
|
||||
parent::send();
|
||||
$this->sendHeaders();
|
||||
$this->sendContent();
|
||||
$this->clear();
|
||||
@@ -199,8 +214,8 @@ class Response extends \yii\base\Response
|
||||
{
|
||||
$this->_headers = null;
|
||||
$this->_statusCode = null;
|
||||
$this->_content = null;
|
||||
$this->statusText = null;
|
||||
$this->content = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -253,7 +268,7 @@ class Response extends \yii\base\Response
|
||||
*/
|
||||
protected function sendContent()
|
||||
{
|
||||
echo $this->content;
|
||||
echo $this->getContent();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -304,10 +319,10 @@ class Response extends \yii\base\Response
|
||||
if ($begin !=0 || $end != $contentLength - 1) {
|
||||
$this->setStatusCode(206);
|
||||
$headers->set('Content-Range', "bytes $begin-$end/$contentLength");
|
||||
$this->content = StringHelper::substr($content, $begin, $end - $begin + 1);
|
||||
$this->setContent(StringHelper::substr($content, $begin, $end - $begin + 1), self::FORMAT_RAW);
|
||||
} else {
|
||||
$this->setStatusCode(200);
|
||||
$this->content = $content;
|
||||
$this->setContent($content, self::FORMAT_RAW);
|
||||
}
|
||||
|
||||
$this->send();
|
||||
@@ -628,4 +643,44 @@ class Response extends \yii\base\Response
|
||||
{
|
||||
return in_array($this->getStatusCode(), array(201, 204, 304));
|
||||
}
|
||||
|
||||
private $_content;
|
||||
|
||||
public function getContent()
|
||||
{
|
||||
return $this->_content;
|
||||
}
|
||||
|
||||
public function setContent($value, $format = null)
|
||||
{
|
||||
if ($format !== null) {
|
||||
$this->format = $format;
|
||||
}
|
||||
$this->_content = $this->formatContent($value, $format);
|
||||
}
|
||||
|
||||
protected function formatContent($data, $format)
|
||||
{
|
||||
switch ($this->format) {
|
||||
case self::FORMAT_RAW:
|
||||
return $data;
|
||||
case self::FORMAT_HTML:
|
||||
$this->getHeaders()->setDefault('Content-Type', 'text/html; charset=' . $this->charset);
|
||||
return $data;
|
||||
case self::FORMAT_JSON:
|
||||
$this->getHeaders()->set('Content-Type', 'application/json');
|
||||
return Json::encode($data);
|
||||
case self::FORMAT_JSONP:
|
||||
$this->getHeaders()->set('Content-Type', 'text/javascript');
|
||||
if (is_array($data) && isset($data['data'], $data['callback'])) {
|
||||
return sprintf('%s(%s);', $data['callback'], Json::encode($data['data']));
|
||||
} else {
|
||||
throw new InvalidParamException("The 'jsonp' response requires that the data be an array consisting of both 'data' and 'callback' elements.");
|
||||
}
|
||||
case self::FORMAT_XML:
|
||||
// todo
|
||||
default:
|
||||
throw new InvalidConfigException("Unsupported response format: $format");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user