44
55abstract class Component{
66 private static $ isTagsSet = false ;
7- private static $ htmlTags = ['div ' ,'p ' ,'img ' ,'h1 ' ,'h2 ' ,'h3 ' ,'h4 ' ,'h5 ' ,'h6 ' ,'iframe ' ,'article ' , 'form ' ,'input ' ,'textarea ' ,'select ' ,'option ' , 'link ' , 'script ' , 'button ' ];
8- private static $ hasNoChild = ['img ' , 'link ' , 'input ' ];
7+ private static $ htmlTags = ['div ' ,'p ' ,'img ' ,'h1 ' ,'h2 ' ,'h3 ' ,'h4 ' ,'h5 ' ,'h6 ' ,'iframe ' ,'article ' , 'form ' ,'input ' ,'textarea ' ,'select ' ,'option ' , 'link ' , 'script ' , 'button ' , ' nav ' , ' title ' , ' meta ' ];
8+ private static $ hasNoChild = ['img ' , 'link ' , 'input ' , ' meta ' ];
99 private const tagNameSpace = 'React\Tag ' ;
1010 private static $ counter = 1 ;
1111 protected $ id = '' ;
1212 protected $ state = [];
13+ private static $ states = [];
1314
1415 private function setTags (){
16+ @ob_start ();
1517 foreach (self ::$ htmlTags as $ el ){
1618 eval ("namespace " . self ::tagNameSpace ."; class $ el extends \React\Component{} " );
1719 }
1820 self ::$ isTagsSet = true ;
1921
2022 //script tag to setup setState function
21- echo new \React \Tag \script ('const phpReact={setState:function(t,e){var n =document.getElementById(t);if(n ){var a= new XMLHttpRequest;a .onreadystatechange=function(){4==this.readyState&&200==this.status&&(n .outerHTML=this.responseText)},a .open("POST",location.href,!0),a .setRequestHeader("Content-type","application/x-www-form-urlencoded"),a .send("phpreact="+JSON.stringify({id:t,state:e}))}}}; ' );
23+ 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{} }}}; ' );
2224 }
2325
2426 private function getTagName (){
@@ -33,7 +35,7 @@ private function hasNoChild(){
3335
3436 static function registerTag ($ tags , $ hasNoChild = false ){
3537 self ::$ htmlTags = array_unique (array_merge (self ::$ htmlTags , (array )$ tags ));
36- if ($ hasNoChild ) self :: setHasNoChild ($ tags );
38+ if ($ hasNoChild ) $ this -> setHasNoChild ($ tags );
3739 }
3840
3941 static function setHasNoChild ($ tags ){
@@ -43,6 +45,10 @@ static function setHasNoChild($tags){
4345 function render (){
4446 if (!$ this ->isHtmlTage ()) return '' ;
4547
48+ //save states in dom attribute [prevState]
49+ if ($ this ->props ->id && self ::$ states [$ this ->props ->id ])
50+ $ this ->props ->prevState = json_encode (self ::$ states [$ this ->props ->id ]);
51+
4652 $ tag = $ this ->getTagName ();
4753 $ attr = [];
4854 foreach ($ this ->props as $ k => $ v ){ $ attr [] = $ k .'=" ' .htmlspecialchars ($ v ).'" ' ; }
@@ -62,30 +68,36 @@ function __construct($children = [], $props = []){
6268 if (!self ::$ isTagsSet ) $ this ->setTags ();
6369 $ hasNoChild = $ this ->hasNoChild ();
6470 $ this ->setId ();
65- $ this ->setStateListener ();
71+
72+ if (!is_array ($ children )) $ children = [$ children ];
6673
6774 //set properties
6875 $ this ->props = (object )($ hasNoChild ? $ children : $ props );
69- $ this ->children = (array )($ hasNoChild ? [] : $ children );
76+ $ this ->children = $ hasNoChild ? [] : $ children ;
77+ $ this ->state = (object )$ this ->state ;
78+
79+ //listen to state change
80+ $ this ->setStateListener ();
7081 }
7182
7283 function componentDidUpdate ($ oldState , $ currentState ){}
7384
7485 private function setId (){
7586 if ($ this ->isHtmlTage ()) return ;
76- $ this ->id = md5 (self ::$ counter );
87+ $ this ->id = md5 (self ::$ counter ); //generate id
88+ self ::$ states [$ this ->id ] = $ this ->state ; //save all states by id
7789 self ::$ counter ++;
7890 }
7991
8092 private function setStateListener (){
8193 if (empty ($ _POST ['phpreact ' ])) return ;
8294 $ post = json_decode ($ _POST ['phpreact ' ]);
83- if ($ post ->id != $ this ->id ) return ;
84- @ob_start ();
85- @ob_end_clean ();
86- $ oldState = $ this ->state ;
87- $ this ->state = array_merge ($ oldState , (array )$ post ->state );
95+ if (!$ post || $ post ->id != $ this ->id ) return ;
96+ $ oldState = $ post ->prevState ;
97+ $ this ->state = (object )array_merge ((array )$ oldState , (array )$ post ->state );
98+ self ::$ states [$ this ->id ] = $ this ->state ;
8899 $ this ->componentDidUpdate ($ oldState , $ this ->state );
100+ @ob_end_clean ();
89101 die ($ this );
90102 }
91103}
0 commit comments