Skip to content

Commit b7f88d2

Browse files
Update React.php
1 parent 17a7d7c commit b7f88d2

File tree

1 file changed

+32
-15
lines changed

1 file changed

+32
-15
lines changed

React.php

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,14 @@ abstract class Component{
66
private static $isTagsSet = false; //flag to track if all html tag classes created
77

88
//all html tags that are allowed
9-
private static $htmlTags = ['div','p','img','a','ul','li', 'h1','h2','h3','h4','h5','h6','iframe','article', 'form','input','textarea','select','option', 'link', 'script', 'button', 'nav', 'title', 'meta', 'code', 'pre', 'abbr', 'svg', 'g', 'path', 'image', 'circle'];
9+
private static $htmlTags = ['div','p','img','a','ul','li', 'h1','h2','h3','h4','h5','h6','iframe','article', 'form','input','textarea','select','option', 'link', 'script', 'button', 'nav', 'title', 'meta', 'code', 'pre'];
1010

1111
private static $hasNoChild = ['img', 'link', 'input', 'meta']; //tags that have no children
1212
private const tagNameSpace= 'React\Tag'; //name space for the tags
1313

14-
private static $counter = 1; // couter for generating sequencial id
14+
private static $counter = 1; // counter for generating sequencial id
1515
protected $id = ''; //the current id of the component
1616
protected $state = []; //the current state
17-
private static $states = []; //used to save all states of every component in the page
1817

1918
/*
2019
run the first time when first component called
@@ -30,7 +29,7 @@ private function setTags(){
3029
self::$isTagsSet = true;
3130

3231
//script tag to setup setState function
33-
echo new \React\Tag\script('const phpReact={setState:function(t,e,n){var a=document.getElementById(t);if(a){var r=this.getState(t);"function"==typeof e&&(e=e(r));var o=new XMLHttpRequest;o.onreadystatechange=function(){4==this.readyState&&200==this.status&&(a.outerHTML=this.responseText,"function"==typeof n&&n())},o.open("POST",location.href,!0),o.setRequestHeader("Content-type","application/x-www-form-urlencoded"),o.send("phpreact="+JSON.stringify({id:t,state:e,prevState:r}))}},getState:function(t){try{var e=document.getElementById(t);return JSON.parse(e.getAttribute("prevstate"))}catch(t){return{}}}};');
32+
echo new \React\Tag\script('!function(t,e){var n=function(){e.querySelectorAll("[component-id] *:not([component-id])").forEach(function(t){t.setState||(t.getState=i,t.setState=o)})},o=function(t,o){var i=this.closest("[component-id]");if(i){var a=i.getAttribute("component-id"),r=this.getAttribute("key"),c=this.value,s=this.getState();"function"==typeof t&&(t=t(s));var u=new XMLHttpRequest,p={id:a,state:t,prevState:s};u.onreadystatechange=function(){if(4==this.readyState&&200==this.status){if(i.outerHTML=this.responseText,r){var t=e.querySelector("[component-id='"+a+"'] [key='"+r+"']");t&&(t.focus(),c&&(t.value="",t.value=c))}"function"==typeof o&&o(),n()}},u.open("POST",location.href,!0),u.setRequestHeader("Content-type","application/x-www-form-urlencoded"),u.send("phpreact="+JSON.stringify(p))}},i=function(){try{var t=this.closest("[component-id]");return JSON.parse(t.getAttribute("component-state"))}catch(t){return{}}};t.addEventListener("load",n)}(window,document);');
3433
}
3534
3635
/*
@@ -60,7 +59,7 @@ protected function hasNoChild(){
6059
@param: $hasNoChild: bool if the tags accept no children
6160
*/
6261
static function registerTag($tags, $hasNoChild = false){
63-
self::$htmlTags= array_unique(array_merge(self::$htmlTags, (array)$tags));
62+
self::$htmlTags= array_unique(array_merge(self::$htmlTags, self::parseTags($tags)));
6463
if($hasNoChild) $this->setHasNoChild($tags);
6564
}
6665

@@ -69,18 +68,31 @@ static function registerTag($tags, $hasNoChild = false){
6968
@param: $tag: string|array[list of string] html tags
7069
*/
7170
static function setHasNoChild($tags){
72-
self::$hasNoChild= array_unique(array_merge(self::$hasNoChild, (array)$tags));
71+
self::$hasNoChild= array_unique(array_merge(self::$hasNoChild, self::parseTags($tags)));
72+
}
73+
74+
/*
75+
@param: $tags: string|array -- array string to be parse
76+
@return: parsed array string
77+
*/
78+
private static function parseTags($tags){
79+
return array_map(function($tag){ return self::parseAttribute($tag); }, (array)$tags);
80+
}
81+
82+
/*
83+
allow only [words or dash] for attribute or tag
84+
@param: $attr: string -- the string to be parse
85+
@return: parsed string
86+
*/
87+
private static function parseAttribute($attr){
88+
return preg_replace('/[^\w-]/','', $attr); //allow only [words or dash]
7389
}
7490

7591
/*
7692
render the html tag only
7793
*/
7894
function render(){
7995
if(!$this->isHtmlTage()) return '';
80-
81-
//save states in dom attribute [prevstate]
82-
if($this->props->id && self::$states[$this->props->id])
83-
$this->props->prevstate = json_encode(self::$states[$this->props->id]);
8496

8597
$tag = $this->getTagName();
8698
$innerHtml = '';
@@ -89,11 +101,12 @@ function render(){
89101
if($k == 'dangerouslyInnerHTML'){ //if has dangerouslyInnerHTML attribute
90102
$innerHtml = $v; continue;
91103
}
92-
$att = preg_replace('/[^\w-]/','', $k); //allow only [words or dash]
93-
$val = htmlspecialchars($v); //escape html
104+
$att = self::parseAttribute($k); //allow only [words or dash]
105+
$val = htmlspecialchars( is_object($v) || is_array($v) ? json_encode($v) : $v); //escape html
94106

95-
$attr[] = "$att='$v'";
107+
$attr[] = "$att='$val'";
96108
}
109+
97110
$attributes = implode(' ',$attr);
98111

99112
//if theres innerHtml then ignore children else escape any string passed as html
@@ -110,6 +123,12 @@ function render(){
110123
*/
111124
function __toString(){
112125
$components = $this->render();
126+
127+
//save state of custom component in top html wrapper
128+
if(!$this->isHtmlTage() && $components instanceof Component && $components->isHtmlTage()){
129+
$components->props = (object)array_merge((array)$components->props, ['component-id'=> $this->id, 'component-state'=> $this->state]);
130+
}
131+
113132
if(!is_array($components)) $components = [$components]; //must be list of components
114133

115134
//if custom component the render should return component or list of components
@@ -155,7 +174,6 @@ function componentDidUpdate($oldState, $currentState){}
155174
private function setId(){
156175
if($this->isHtmlTage()) return;
157176
$this->id = md5(self::$counter); //generate id
158-
self::$states[$this->id] = $this->state; //save all states by id
159177
self::$counter++;
160178
}
161179

@@ -169,7 +187,6 @@ private function setStateListener(){
169187
if(!$post || $post->id != $this->id) return;
170188
$oldState = $post->prevState;
171189
$this->state = (object)array_merge((array)$oldState, (array)$post->state);
172-
self::$states[$this->id] = $this->state;
173190
$this->componentDidUpdate($oldState, $this->state);
174191
@ob_end_clean();
175192
die($this);

0 commit comments

Comments
 (0)