Skip to content

Commit

Permalink
New behavior for Plugs in 2.3: they are rendered in their local context.
Browse files Browse the repository at this point in the history
Possibility to configure the View for legacy plug behavior.
  • Loading branch information
zzgab committed Jan 1, 2016
1 parent 493ba45 commit 0194504
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 25 deletions.
68 changes: 53 additions & 15 deletions src/figdice/View.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?php
/**
* @author Gabriel Zerbib <gabriel@figdice.org>
* @copyright 2004-2015, Gabriel Zerbib.
* @version 2.2
* @copyright 2004-2016, Gabriel Zerbib.
* @version 2.3
* @package FigDice
*
* This file is part of FigDice.
Expand All @@ -25,6 +25,7 @@

use figdice\classes\AutoloadFeedFactory;
use figdice\classes\NativeFunctionFactory;
use figdice\classes\Plug;
use figdice\classes\TagFigAttr;
use figdice\classes\File;
use figdice\classes\ViewElement;
Expand All @@ -47,6 +48,9 @@
* Then you would {@see loadFile} an XML source file, and finally {@see render} it to obtain its final result (which you would typically output to the browser).
*/
class View {

const GLOBAL_PLUGS = 'GLOBAL_PLUGS';

/**
* Textual source of the transformation.
*
Expand Down Expand Up @@ -157,7 +161,7 @@ class View {
/**
* Array of named elements that are used as content providers
* to fill in slots by the same name.
* @var ViewElementTag[]
* @var Plug[]
*/
private $plugs;

Expand All @@ -166,7 +170,7 @@ class View {
* A Macro is a ViewElementTag that is rendered
* only upon calling from within another element.
*
* @var array ( ViewElementTag )
* @var ViewElementTag[]
*/
public $macros;

Expand Down Expand Up @@ -225,7 +229,15 @@ class View {

private $doctype = null;

public function __construct() {
private $options = [];

/**
* View constructor.
* @param array $options Optional indexed array of options, for specific behavior of the library.
* Introduced in 2.3 for the remodeling of the plug execution context.
*/
public function __construct(array $options = []) {
$this->options = $options;
$this->source = '';
$this->result = '';
$this->rootNode = null;
Expand All @@ -238,6 +250,16 @@ public function __construct() {
$this->language = null;
}

/**
* Checks whether the View has the specified option configured at construction.
* @param $option
* @return bool
*/
public function hasOption($option)
{
return in_array($option, $this->options);
}

/**
*
* Specifies the target language code in which you wish to translate
Expand Down Expand Up @@ -642,27 +664,43 @@ private function plugIntoSlots($input) {
$slotPos = strpos($result, $slot->getAnchorString());

if( isset($this->plugs[$slotName]) ) {
/** @var ViewElementTag[] $plugsForSlot */
/** @var Plug[] $plugsForSlot */
$plugsForSlot = $this->plugs[$slotName];

foreach ($plugsForSlot as $plugElement) {
$plugElement->clearAttribute($this->figNamespace . 'plug');
foreach ($plugsForSlot as $plug) {

$plugRender = $plugElement->render();
if ($this->hasOption(self::GLOBAL_PLUGS)) {
$plugElement = $plug->getTag();
$plugElement->clearAttribute($this->figNamespace . 'plug');

if (($plugElement->hasAttribute($this->figNamespace . 'append')) &&
($plugElement->evaluate($plugElement->getAttribute($this->figNamespace . 'append'))) ) {
$plugRender = $plugElement->render();

if ($plugElement->evalFigAttribute('append')) {
$plugOutput .= $plugRender;
}
else {
$plugOutput = $plugRender;
}

$plugElement->setAttribute($this->figNamespace . 'plug', $slotName);
}

else {
$plugOutput = $plugRender;
$plugRender = $plug->getRenderedString();
if ($plug->isAppend()) {
$plugOutput .= $plugRender;
}
else {
$plugOutput = $plugRender;
}
}

$plugElement->setAttribute($this->figNamespace . 'plug', $slotName);

}
$result = substr_replace( $result, $plugOutput, $slotPos, strlen($slot->getAnchorString()) + $slot->getLength() );
}


else {
// If a slot did not receive any plugged content, we use its
// hardcoded template content as default. But we still need
Expand Down Expand Up @@ -794,7 +832,7 @@ public function assignSlot($slotName, Slot & $slot) {
$this->slots[$slotName] = & $slot;
}

public function addPlug($slotName, ViewElementTag & $element) {
$this->plugs[$slotName] [] = & $element;
public function addPlug($slotName, ViewElementTag $element, $renderedString = null, $isAppend = false) {
$this->plugs[$slotName] [] = new Plug($element, $renderedString, $isAppend);
}
}
50 changes: 50 additions & 0 deletions src/figdice/classes/Plug.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php
/**
* @author Gabriel Zerbib <gabriel@figdice.org>
* @copyright 2004-2016, Gabriel Zerbib.
* @version 2.3
* @package FigDice
*/
namespace figdice\classes;

class Plug {
/**
* @var ViewElementTag
*/
private $tag;
/**
* @var string
*/
private $renderedString;
/**
* @var bool
*/
private $append;

public function __construct(ViewElementTag $plugTag, $renderedString = null, $isAppend = false) {
$this->tag = $plugTag;
$this->renderedString = $renderedString;
$this->append = $isAppend;
}

/**
* @return ViewElementTag
*/
public function getTag()
{
return $this->tag;
}

/**
* @return null|string
*/
public function getRenderedString()
{
return $this->renderedString;
}

public function isAppend()
{
return $this->append;
}
}
68 changes: 58 additions & 10 deletions src/figdice/classes/ViewElementTag.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?php
/**
* @author Gabriel Zerbib <gabriel@figdice.org>
* @copyright 2004-2015, Gabriel Zerbib.
* @version 2.2
* @copyright 2004-2016, Gabriel Zerbib.
* @version 2.3
* @package FigDice
*
* This file is part of FigDice.
Expand All @@ -23,6 +23,7 @@

namespace figdice\classes;

use figdice\Filter;
use Psr\Log\LoggerIntergace;
use figdice\View;
use figdice\LoggerFactory;
Expand All @@ -33,6 +34,8 @@
use figdice\exceptions\FileNotFoundException;

class ViewElementTag extends ViewElement {


/**
* Tag name.
* @var string
Expand Down Expand Up @@ -88,6 +91,12 @@ class ViewElementTag extends ViewElement {
*/
private $voidtag;

/**
* @var array of flags that indicate things to be bypassed during one specific rendering,
* such as macros, plugs, cases etc.
*/
private $transientFlags = [];

/**
*
* @param View $view
Expand Down Expand Up @@ -333,7 +342,11 @@ public function render($bypassWalk = false) {
return $this->fig_macro();
}

return $this->renderNoMacro($bypassWalk);
$result = $this->renderNoMacro($bypassWalk);

// Clear transient flags
$this->transientFlags = [];
return $result;
}
private function renderNoMacro($bypassWalk = false) {

Expand Down Expand Up @@ -498,9 +511,7 @@ private function renderNoMacro($bypassWalk = false) {
//Then, render the slot element, as a default content in case
//nothing is plugged into it.
//In case of plug for this slot, the complete slot tag (outer) is replaced.
if(isset($this->attributes[$this->view->figNamespace . 'slot'])) {
//Extract name of slot
$slotName = $this->attributes[$this->view->figNamespace . 'slot'];
if($slotName = $this->getFigAttribute('slot')) {
//Store a reference to current node, into the View's map of slots

$anchorString = '/==SLOT==' . $slotName . '==/';
Expand All @@ -526,16 +537,23 @@ private function renderNoMacro($bypassWalk = false) {
//its content is appended to whatever was already filled into the slot.
//The entire plug+append tag (outer) is appended to the slot placeholder
//(the slot's outer tag being removed).
if(isset($this->attributes[$this->view->figNamespace . 'plug'])) {
$slotName = $this->attributes[$this->view->figNamespace . 'plug'];
if(($slotName = $this->getFigAttribute('plug')) && ! $this->isTransient(self::TRANSIENT_PLUG_RENDERING)) {

//Keep track of the callback node (the fig:plug node).
//The callbacks are maintained as a chain. Several callbacks
//can enchain one after the other,
//in the order they were parsed.
$this->view->addPlug($slotName, $this);
if ($this->view->hasOption(View::GLOBAL_PLUGS)) {
//The plugs are rendered at the end of the rendering of the View.
$this->view->addPlug($slotName, $this);
}
else {
// The plugs are rendered in their local context
// (but still, remain stuffed at the end of the template rendering)
$this->transient(self::TRANSIENT_PLUG_RENDERING);
$this->view->addPlug($slotName, $this, $this->render(), $this->evalFigAttribute('append'));
}

//The slots are rendered at the end of the rendering of the View.
//A fig:plug tag does not produce any in-place output.
return '';
}
Expand Down Expand Up @@ -748,6 +766,15 @@ private function evalAttribute($name) {
return false;
}

/**
* @param string $name the fig attribute to evaluate
* @return mixed or false if attribute not found.
*/
public function evalFigAttribute($name)
{
return $this->evalAttribute($this->view->figNamespace . $name);
}

public function getAttribute($name, $default = null) {
if(isset($this->attributes[$name])) {
return $this->attributes[$name];
Expand Down Expand Up @@ -1354,4 +1381,25 @@ private function getLogger() {
}
return $this->logger;
}


/**
* Checks whether the current object has a specified rendering flag.
* Transient flags are automatically cleared at the end of the tag's rendering loop.
* @param $flag
* @return bool
*/
private function isTransient($flag)
{
return isset($this->transientFlags[$flag]);
}
private function transient($flag)
{
$this->transientFlags[$flag] = true;
}

/**
* This const is used internally to indicate that the object is currently being rendered as a local plug.
*/
const TRANSIENT_PLUG_RENDERING = 'TRANSIENT_PLUG_RENDERING';
}
50 changes: 50 additions & 0 deletions test/SlotPlugTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php
/**
* @author Gabriel Zerbib <gabriel@figdice.org>
* @copyright 2004-2016, Gabriel Zerbib.
* @version 2.3
* @package FigDice
*/

namespace figdice\test;

use figdice\View;
use PHPUnit_Framework_TestCase;

class SlotPlugTest extends PHPUnit_Framework_TestCase
{
public function testPlugExecutesInLocalContext()
{
$template = <<<ENDTEMPLATE
<fig:template>
<!-- define a slot -->
<slot fig:slot="myslot">Default Slot Content</slot>
<!-- create some contextual data -->
<fig:mount target="someData" value="1" />
<!-- execute the plug in the current context -->
<title fig:mute="true" fig:plug="myslot" fig:text="/someData"/>
<!-- modify the data that the plug refers to.
Prior to version 2.3, the plug contents would render using the ending global context,
but since 2.3 the plug is executed in its local context. -->
<fig:mount target="someData" value="2" />
</fig:template>
ENDTEMPLATE;

$view = new View([View::GLOBAL_PLUGS]);
$view->loadString(trim($template));

$rendered = $view->render();
$this->assertEquals(2, trim($rendered));

$view = new View();
$view->loadString(trim($template));

$rendered = $view->render();
$this->assertEquals(1, trim($rendered));
}
}

0 comments on commit 0194504

Please sign in to comment.