<?php12/**3* @task children Managing Children4*/5abstract class AphrontView extends Phobject6implements PhutilSafeHTMLProducerInterface {78private $viewer;9protected $children = array();101112/* -( Configuration )------------------------------------------------------ */131415/**16* Set the user viewing this element.17*18* @param PhabricatorUser Viewing user.19* @return this20*/21public function setViewer(PhabricatorUser $viewer) {22$this->viewer = $viewer;23return $this;24}252627/**28* Get the user viewing this element.29*30* Throws an exception if no viewer has been set.31*32* @return PhabricatorUser Viewing user.33*/34public function getViewer() {35if (!$this->viewer) {36throw new PhutilInvalidStateException('setViewer');37}3839return $this->viewer;40}414243/**44* Test if a viewer has been set on this element.45*46* @return bool True if a viewer is available.47*/48public function hasViewer() {49return (bool)$this->viewer;50}515253/**54* Deprecated, use @{method:setViewer}.55*56* @task config57* @deprecated58*/59public function setUser(PhabricatorUser $user) {60return $this->setViewer($user);61}626364/**65* Deprecated, use @{method:getViewer}.66*67* @task config68* @deprecated69*/70protected function getUser() {71if (!$this->hasViewer()) {72return null;73}74return $this->getViewer();75}767778/* -( Managing Children )-------------------------------------------------- */798081/**82* Test if this View accepts children.83*84* By default, views accept children, but subclases may override this method85* to prevent children from being appended. Doing so will cause86* @{method:appendChild} to throw exceptions instead of appending children.87*88* @return bool True if the View should accept children.89* @task children90*/91protected function canAppendChild() {92return true;93}949596/**97* Append a child to the list of children.98*99* This method will only work if the view supports children, which is100* determined by @{method:canAppendChild}.101*102* @param wild Something renderable.103* @return this104*/105final public function appendChild($child) {106if (!$this->canAppendChild()) {107$class = get_class($this);108throw new Exception(109pht("View '%s' does not support children.", $class));110}111112$this->children[] = $child;113114return $this;115}116117118/**119* Produce children for rendering.120*121* Historically, this method reduced children to a string representation,122* but it no longer does.123*124* @return wild Renderable children.125* @task126*/127final protected function renderChildren() {128return $this->children;129}130131132/**133* Test if an element has no children.134*135* @return bool True if this element has children.136* @task children137*/138final public function hasChildren() {139if ($this->children) {140$this->children = $this->reduceChildren($this->children);141}142return (bool)$this->children;143}144145146/**147* Reduce effectively-empty lists of children to be actually empty. This148* recursively removes `null`, `''`, and `array()` from the list of children149* so that @{method:hasChildren} can more effectively align with expectations.150*151* NOTE: Because View children are not rendered, a View which renders down152* to nothing will not be reduced by this method.153*154* @param list<wild> Renderable children.155* @return list<wild> Reduced list of children.156* @task children157*/158private function reduceChildren(array $children) {159foreach ($children as $key => $child) {160if ($child === null) {161unset($children[$key]);162} else if ($child === '') {163unset($children[$key]);164} else if (is_array($child)) {165$child = $this->reduceChildren($child);166if ($child) {167$children[$key] = $child;168} else {169unset($children[$key]);170}171}172}173return $children;174}175176public function getDefaultResourceSource() {177return 'phabricator';178}179180public function requireResource($symbol) {181$response = CelerityAPI::getStaticResourceResponse();182$response->requireResource($symbol, $this->getDefaultResourceSource());183return $this;184}185186public function initBehavior($name, $config = array()) {187Javelin::initBehavior(188$name,189$config,190$this->getDefaultResourceSource());191return $this;192}193194195/* -( Rendering )---------------------------------------------------------- */196197198/**199* Inconsistent, unreliable pre-rendering hook.200*201* This hook //may// fire before views render. It is not fired reliably, and202* may fire multiple times.203*204* If it does fire, views might use it to register data for later loads, but205* almost no datasources support this now; this is currently only useful for206* tokenizers. This mechanism might eventually see wider support or might be207* removed.208*/209public function willRender() {210return;211}212213214abstract public function render();215216217/* -( PhutilSafeHTMLProducerInterface )------------------------------------ */218219220public function producePhutilSafeHTML() {221return $this->render();222}223224}225226227