mirror of
https://github.com/torrentpier/torrentpier-lts.git
synced 2025-03-01 15:21:02 +03:00
244 lines
7.0 KiB
PHP
244 lines
7.0 KiB
PHP
|
<?php
|
||
|
/**
|
||
|
* Zend Framework (http://framework.zend.com/)
|
||
|
*
|
||
|
* @link http://github.com/zendframework/zf2 for the canonical source repository
|
||
|
* @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
|
||
|
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||
|
*/
|
||
|
|
||
|
namespace Zend\View\Renderer;
|
||
|
|
||
|
use JsonSerializable;
|
||
|
use Traversable;
|
||
|
use Zend\Json\Json;
|
||
|
use Zend\Stdlib\ArrayUtils;
|
||
|
use Zend\View\Exception;
|
||
|
use Zend\View\Model\JsonModel;
|
||
|
use Zend\View\Model\ModelInterface as Model;
|
||
|
use Zend\View\Renderer\RendererInterface as Renderer;
|
||
|
use Zend\View\Resolver\ResolverInterface as Resolver;
|
||
|
|
||
|
/**
|
||
|
* JSON renderer
|
||
|
*/
|
||
|
class JsonRenderer implements Renderer, TreeRendererInterface
|
||
|
{
|
||
|
/**
|
||
|
* Whether or not to merge child models with no capture-to value set
|
||
|
* @var bool
|
||
|
*/
|
||
|
protected $mergeUnnamedChildren = false;
|
||
|
|
||
|
/**
|
||
|
* @var Resolver
|
||
|
*/
|
||
|
protected $resolver;
|
||
|
|
||
|
/**
|
||
|
* JSONP callback (if set, wraps the return in a function call)
|
||
|
*
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $jsonpCallback = null;
|
||
|
|
||
|
/**
|
||
|
* Return the template engine object, if any
|
||
|
*
|
||
|
* If using a third-party template engine, such as Smarty, patTemplate,
|
||
|
* phplib, etc, return the template engine object. Useful for calling
|
||
|
* methods on these objects, such as for setting filters, modifiers, etc.
|
||
|
*
|
||
|
* @return mixed
|
||
|
*/
|
||
|
public function getEngine()
|
||
|
{
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the resolver used to map a template name to a resource the renderer may consume.
|
||
|
*
|
||
|
* @todo Determine use case for resolvers when rendering JSON
|
||
|
* @param Resolver $resolver
|
||
|
* @return Renderer
|
||
|
*/
|
||
|
public function setResolver(Resolver $resolver)
|
||
|
{
|
||
|
$this->resolver = $resolver;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set flag indicating whether or not to merge unnamed children
|
||
|
*
|
||
|
* @param bool $mergeUnnamedChildren
|
||
|
* @return JsonRenderer
|
||
|
*/
|
||
|
public function setMergeUnnamedChildren($mergeUnnamedChildren)
|
||
|
{
|
||
|
$this->mergeUnnamedChildren = (bool) $mergeUnnamedChildren;
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the JSONP callback function name
|
||
|
*
|
||
|
* @param string $callback
|
||
|
* @return JsonRenderer
|
||
|
*/
|
||
|
public function setJsonpCallback($callback)
|
||
|
{
|
||
|
$callback = (string) $callback;
|
||
|
if (!empty($callback)) {
|
||
|
$this->jsonpCallback = $callback;
|
||
|
}
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns whether or not the jsonpCallback has been set
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
public function hasJsonpCallback()
|
||
|
{
|
||
|
return (null !== $this->jsonpCallback);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Should we merge unnamed children?
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
public function mergeUnnamedChildren()
|
||
|
{
|
||
|
return $this->mergeUnnamedChildren;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Renders values as JSON
|
||
|
*
|
||
|
* @todo Determine what use case exists for accepting both $nameOrModel and $values
|
||
|
* @param string|Model $nameOrModel The script/resource process, or a view model
|
||
|
* @param null|array|\ArrayAccess $values Values to use during rendering
|
||
|
* @throws Exception\DomainException
|
||
|
* @return string The script output.
|
||
|
*/
|
||
|
public function render($nameOrModel, $values = null)
|
||
|
{
|
||
|
// use case 1: View Models
|
||
|
// Serialize variables in view model
|
||
|
if ($nameOrModel instanceof Model) {
|
||
|
if ($nameOrModel instanceof JsonModel) {
|
||
|
$children = $this->recurseModel($nameOrModel, false);
|
||
|
$this->injectChildren($nameOrModel, $children);
|
||
|
$values = $nameOrModel->serialize();
|
||
|
} else {
|
||
|
$values = $this->recurseModel($nameOrModel);
|
||
|
$values = Json::encode($values);
|
||
|
}
|
||
|
|
||
|
if ($this->hasJsonpCallback()) {
|
||
|
$values = $this->jsonpCallback . '(' . $values . ');';
|
||
|
}
|
||
|
return $values;
|
||
|
}
|
||
|
|
||
|
// use case 2: $nameOrModel is populated, $values is not
|
||
|
// Serialize $nameOrModel
|
||
|
if (null === $values) {
|
||
|
if (!is_object($nameOrModel) || $nameOrModel instanceof JsonSerializable) {
|
||
|
$return = Json::encode($nameOrModel);
|
||
|
} elseif ($nameOrModel instanceof Traversable) {
|
||
|
$nameOrModel = ArrayUtils::iteratorToArray($nameOrModel);
|
||
|
$return = Json::encode($nameOrModel);
|
||
|
} else {
|
||
|
$return = Json::encode(get_object_vars($nameOrModel));
|
||
|
}
|
||
|
|
||
|
if ($this->hasJsonpCallback()) {
|
||
|
$return = $this->jsonpCallback . '(' . $return . ');';
|
||
|
}
|
||
|
return $return;
|
||
|
}
|
||
|
|
||
|
// use case 3: Both $nameOrModel and $values are populated
|
||
|
throw new Exception\DomainException(sprintf(
|
||
|
'%s: Do not know how to handle operation when both $nameOrModel and $values are populated',
|
||
|
__METHOD__
|
||
|
));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Can this renderer render trees of view models?
|
||
|
*
|
||
|
* Yes.
|
||
|
*
|
||
|
* @return true
|
||
|
*/
|
||
|
public function canRenderTrees()
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve values from a model and recurse its children to build a data structure
|
||
|
*
|
||
|
* @param Model $model
|
||
|
* @param bool $mergeWithVariables Whether or not to merge children with
|
||
|
* the variables of the $model
|
||
|
* @return array
|
||
|
*/
|
||
|
protected function recurseModel(Model $model, $mergeWithVariables = true)
|
||
|
{
|
||
|
$values = array();
|
||
|
if ($mergeWithVariables) {
|
||
|
$values = $model->getVariables();
|
||
|
}
|
||
|
|
||
|
if ($values instanceof Traversable) {
|
||
|
$values = ArrayUtils::iteratorToArray($values);
|
||
|
}
|
||
|
|
||
|
if (!$model->hasChildren()) {
|
||
|
return $values;
|
||
|
}
|
||
|
|
||
|
$mergeChildren = $this->mergeUnnamedChildren();
|
||
|
foreach ($model as $child) {
|
||
|
$captureTo = $child->captureTo();
|
||
|
if (!$captureTo && !$mergeChildren) {
|
||
|
// We don't want to do anything with this child
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$childValues = $this->recurseModel($child);
|
||
|
if ($captureTo) {
|
||
|
// Capturing to a specific key
|
||
|
// TODO please complete if append is true. must change old
|
||
|
// value to array and append to array?
|
||
|
$values[$captureTo] = $childValues;
|
||
|
} elseif ($mergeChildren) {
|
||
|
// Merging values with parent
|
||
|
$values = array_replace_recursive($values, $childValues);
|
||
|
}
|
||
|
}
|
||
|
return $values;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Inject discovered child model values into parent model
|
||
|
*
|
||
|
* @todo detect collisions and decide whether to append and/or aggregate?
|
||
|
* @param Model $model
|
||
|
* @param array $children
|
||
|
*/
|
||
|
protected function injectChildren(Model $model, array $children)
|
||
|
{
|
||
|
foreach ($children as $child => $value) {
|
||
|
// TODO detect collisions and decide whether to append and/or aggregate?
|
||
|
$model->setVariable($child, $value);
|
||
|
}
|
||
|
}
|
||
|
}
|