-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathComponent.php
164 lines (141 loc) · 4.4 KB
/
Component.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
<?php
/**
* Class description
*
* @author Safouane Ahmed Salah
* @license MIT
*/
namespace React;
abstract class Component extends React{
/**
* Associative array that holds the state of components
*
* @var array
*/
protected $state = [];
/**
* the queue that holds all previous component
* used for registering the previous states
*
* @var array
*/
private static $queue = []; //queue of components
/**
* the post object when state is changes
*
* @var object
*/
private static $isSetState = false;
/**
* Setup the javascript of handling state
*
*/
static function setup(): void {
//script tag to setup setState function
self::import('phpreact.js', '1.3');
}
/**
* Check if the render is for updating a state
*
* @return bool
*/
static function isSetState(): bool{
return self::$isSetState;
}
static function renderState(){
$post = json_decode(@$_POST['phpreact']);
if(!$post) return;
self::$isSetState = true;
self::$queue = $post->components;
$component = self::decode($post->current);
$oldState = $component->state;
$component->state = (object)array_merge((array)$oldState, (array)$post->state);
$component->componentDidUpdate($oldState, $component->state);
return $component->handleRender();
}
private static function decode(string $encode){
$arr = json_decode(base64_decode($encode));
include_once $arr->file;
return unserialize($arr->component);
}
private function encode(){
$ref = $this instanceof Func ? new \ReflectionFunction(get_class($this)) : new \ReflectionClass($this);
$file = $ref->getFileName();
return base64_encode(json_encode(['file'=> $file, 'component'=> serialize($this)]));
}
/**
* Retrieve the component from the queue
*
* @return Component
*/
private function getQueueComponent(){
$encode = array_shift(self::$queue);
return $encode ? self::decode($encode) : null;
}
/**
* Render the update state
*
* @return string
*/
private function stateManager(): string {
$component = $this->getQueueComponent();
if(!$component) $component = $this;
return $component->handleRender();
}
/**
* Convert a Component to html string
*
* @return string
*/
private function handleRender(): string {
$this->beforeRender();
$components = $this->render();
if((array)$this->state && $components instanceof Tag){
// $componentProps = ['component'=> base64_encode(serialize($this)), 'component-state'=> $this->state];
$componentProps = ['component'=> $this->encode(), 'component-state'=> $this->state];
$components->props = (object)array_merge((array)$components->props,$componentProps);
}
if(!is_array($components)) $components = [$components]; //must be list of components
$components = array_map(function($v){ return $v instanceof React ? $v : htmlspecialchars((string)$v); }, $components);
return implode('', $components);
}
/**
* Convert a component to html string
*
* @return string
*/
function __toString(): string {
return self::isSetState() ? $this->stateManager() : $this->handleRender();
}
/**
* Construct the tag with list of child component and props
*
* 2 possible usage: Component($children, $props) or Component($props)
*
* @param string|array|Component $children
* 1- string|[string] allowed only if html tag
* 2- associative array then it will be considered props
* 3- Component|[Component]
* @param array $props associative array of key=> value
*
*/
function __construct($args = [], $children = []){
parent::__construct($args, $children);
$this->state = (object)$this->state;
}
/**
* Hook when state is changed
*
* @param object $oldState the previous state
* @param object $currentState the current state
*
* @return void
*/
function componentDidUpdate($oldState, $currentState){}
/**
* Hook before rendering the component
*
* @return void
*/
function beforeRender(){}
}