Hoi,
Naar aanleiding van reacties in een vorig topic heb ik mij eens verdiept in programmeer-patterns. Vooral de factory- en singleton-aanpak vond ik reuze interessant, veel dank aan iedereen die me erop gewezen heeft.
Echter, ik kon met geen van de bekende aanpakken lekker werken. Ik heb daarom zelf maar wat in mekaar geknutseld, en bij de verschillende patterns hier en daar wat geleend (vooral factory en singleton). Tot nu toe heeft dat voor mij een prima werkbare aanpak opgeleverd, maar omdat ik het in mijn framework gebruik wil ik een beetje proberen vooruit te kijken of deze aanpak nu echt zo handig is.
Daarom ben ik benieuwd naar jullie mening over mijn (sterk vereenvoudigd weergegeven) aanpak:
De class doet in feite drie dingen:
Toch wegen naar mijn ervaring tot nu toe deze nadelen op tegen de voordelen: Je kunt lekker snel ontwikkelen, alles kan met alles praten, en objecten maak je lekker makkelijk aan met
Als er al een instance bestaat dan krijg je die, anders maakt ie em voor je aan.Natuurlijk is dit geen zuivere meting, maar na invoeren van dit systeem geeft mijn debugger/profiler een snelheidswinst van meer dan 20% aan.
Kortom, ik ben best enthousiast, maar ik kan niet helemaal overzien of dit systeem ook op de lange termijn (bij redelijk grote en complexe systemen) handig is en gaat werken. Daarom graag jullie feedback (liefst inhoudelijk op de aanpak, en niet vallen over de programmeerstijl van de code zelf, daar ga ik zelf nog wel een keer aan schaven!).
Alvast bedankt, sowieso voor het lezen van deze lap tekst
Naar aanleiding van reacties in een vorig topic heb ik mij eens verdiept in programmeer-patterns. Vooral de factory- en singleton-aanpak vond ik reuze interessant, veel dank aan iedereen die me erop gewezen heeft.
Echter, ik kon met geen van de bekende aanpakken lekker werken. Ik heb daarom zelf maar wat in mekaar geknutseld, en bij de verschillende patterns hier en daar wat geleend (vooral factory en singleton). Tot nu toe heeft dat voor mij een prima werkbare aanpak opgeleverd, maar omdat ik het in mijn framework gebruik wil ik een beetje proberen vooruit te kijken of deze aanpak nu echt zo handig is.
Daarom ben ik benieuwd naar jullie mening over mijn (sterk vereenvoudigd weergegeven) aanpak:
- index.php parsed een config.ini, en leidt daaruit oa af welke versie van het framework moet worden geladen.
- het framework definieert een drietal global vars: de $debugger, $paths (met daarin enkele paths die vaak in de code terugkomen) en $incl, een soort backbone, zie hieronder.
- het framework bepaalt vervolgens welk type file er wordt opgevraagd, en roept een docbuilder aan.
- de docbuilder handelt een aantal standaard dingen af zoals bijv logging, voert evt custom code voor het bestand uit, kijkt vervolgens of het bestand in cache staat en zoniet compiled ie het bestand.
- docbuilder output de content
- framework report eventuele errors etc
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
| #-------------------------------------------------------------------------------------------------- # # # Notes: Cascade: # [1] site theme / pagedir # [2] core theme / pagedir # [3] site pagedir # [4] core pagedir # [5] site theme # [6] core theme # [7] web root # [8] core # #-------------------------------------------------------------------------------------------------- class Path { #declarations---------------------------------------------------------------------------------- private $public_objects = array(); private $private_objects = array(); #check if file is readable--------------------------------------------------------------------- public function readable($file, $include_adds=true) { #include adds if needed if ( $include_adds ) { $include_adds = $this->readable(str_replace(strrchr($file, "."), "_add" . strrchr($file, "."), $file), false); } #lookup $cascade = $this->cascade($file); foreach ( $cascade as $vcasc ) { if ( is_readable($vcasc) ) { return true; }} #return false if not found return false; } #check if file is readable--------------------------------------------------------------------- public function incl($file, $include_adds=true, $is_add=false) { #init global $_debug; $found = false; #include adds if needed if ( $include_adds ) { $this->incl(str_replace(strrchr($file, "."), "_add" . strrchr($file, "."), $file), false, true); } #lookup $cascade = $this->cascade($file); foreach ( $cascade as $vcasc ) { if ( is_readable($vcasc) ) { include($vcasc); $_debug->file[$vcasc] = true; $_debug->add_debuglog("Including " . $vcasc); if ( !$is_add ) { return true; } else { $found = true; } } } #return if ( !$found ) { return false; } else { return true; } } #check if file is readable--------------------------------------------------------------------- public function incl_once($file) { #init global $_debug; #lookup foreach ( $this->cascade($file) as $vcasc ) { if ( is_readable($vcasc) ) { include_once($vcasc); $_debug->files[$vcasc] = true; $_debug->add_debuglog("Including " . $vcasc); return true; } } #return false if not found return false; } #lookup first file in cascade------------------------------------------------------------------ public function lookup($filename, $include_adds=false) { #lookup foreach ( $this->cascade($filename) as $vcasc ) { if ( is_readable($vcasc) ) { if ( !$include_adds ) { return $vcasc; } else { $return['self'] = $vcasc; break; } } } #lookup if ( $include_adds ) { foreach ( array_reverse($this->cascade(str_replace(strrchr($filename, "."), "_add" . strrchr($filename, "."), $filename))) as $vcasc ) { if ( is_readable($vcasc) ) { $return['add'][] = $vcasc . $filename; } } } #return false if not found if ( isset($return) ) { return $return; } else { return false; } } #parse ini using the cascade------------------------------------------------------------------- public function parse_ini($file, $include_adds=true, $is_add=false) { #init global $_debug, $_file; $temp = false; $array = array(); #add filehandler if needed $_file = $this->obj("_functions/file_handler/file_handler.php"); #set cascade if ( $is_add ) { $cascade = array_reverse($this->cascade($file)); } else { $cascade = $this->cascade($file); } #lookup foreach ( $cascade as $vcasc ) { if ( is_readable($vcasc) ) { $temp = $_file->read_ini($vcasc); $_debug->files[$vcasc] = true; $_debug->add_debuglog("Including " . $vcasc); if ( !$is_add ) { if ( $include_adds ) { $include_adds = $this->parse_ini(str_replace(strrchr($file, "."), "_add" . strrchr($file, "."), $file), false, true); if ( $include_adds ) { foreach ( $include_adds as $kadd=>$vadd ) { foreach ( $vadd as $kkadd=>$vvadd ) { $temp[$kadd][$kkadd] = $vvadd; }}} } return $temp; } else { foreach ( $temp as $kadd=>$vadd ) { foreach ( $vadd as $kkadd=>$vvadd ) { $finaltemp[$kadd][$kkadd] = $vvadd; }}} } } #return if ( isset($finaltemp) ) { return $finaltemp; } else { return false; } } #load objects---------------------------------------------------------------------------------- public function obj($path, $public=true, $update=false ) { #if only one instance allowed and in cache if ( $public && isset($this->public_objects[$path]) ) { if ( $newsettings ) { foreach ( $newsettings as $kset=>$vset ) { $this->public_objects[$path]->change_setting($kset, $vset); }} return $this->public_objects[$path]; } #if in cache elseif ( isset($this->public_objects[$path]) ) { #make new instance global $_settings; $classname = ucfirst(str_replace(".php", "", basename($path))); if ( !isset($_settings[strtolower($path)]) ) { $temp = new $classname(false); } else { $temp = new $classname($_settings[strtolower($path)]); } #if updates on if ( $update ) { $this->private_objects[$path][] = $temp; } #return the instance return $temp; } #if not in cache but public elseif ( $public ) { #include $this->incl_once($path); #make new instance global $_settings, $_debug; $classname = ucfirst(str_replace(".php", "", basename($path))); $_debug->add_debuglog("Init " . $classname); if ( !isset($_settings[strtolower($path)]) ) { $this->public_objects[$path] = new $classname(false); } else { $this->public_objects[$path] = new $classname($_settings[strtolower($path)]); } #return the instance return $this->public_objects[$path]; } #if not in cache, no public else { #include $this->incl_once($path); #make new instance global $_settings, $_debug; $classname = ucfirst(str_replace(".php", "", basename($path))); $_debug->add_debuglog("Init " . $classname); if ( !isset($_settings[strtolower($path)]) ) { $temp = new $classname(false); } else { $temp = new $classname($_settings[strtolower($path)]); } if ( $newsettings ) { foreach ( $newsettings as $kset=>$vset ) { $temp->change_setting($kset, $vset); }} #if updates on if ( $update ) { $this->private_objects[$path][] = $temp; } #return the instance return $temp; } } #global settings update------------------------------------------------------------------------ public function update_setting($file, $var=false, $value=false) { if ( !$var && is_array($file) ) { foreach ( $file as $kvar=>$vvar ) { $this->update_setting($kvar, $vvar, false); }} elseif ( !$value && is_array($var) ) { foreach ( $var as $kvar=>$vvar ) { $this->update_setting($file, $kvar, $vvar); }} else { if ( isset($this->public_objects[$file]) ) { $this->public_objects[$file]->change_setting($var, $value); } if ( isset($this->private_objects[$file]) ) { foreach ( $this->private_objects[$file] as $vobj ) { $vobj->change_setting($var, $value); }} } } #parse cascade items--------------------------------------------------------------------------- private function cascade($filename) { #init global $_paths; #lookup using cascade if ( isset($_paths['page']) && isset($_paths['theme']) ) { $return[] = $_paths['web_root'] . "_themes/" . $_paths['theme'] . "/_pages/" . $_paths['page'] . "/" . $filename; } if ( isset($_paths['page']) && isset($_paths['theme']) ) { $return[] = $_paths['core'] . "_themes/" . $_paths['theme'] . "/_pages/" . $_paths['page'] . "/" . $filename; } if ( isset($_paths['page']) ) { $return[] = $_paths['web_root'] . "_pages/" . $_paths['page'] . "/" . $filename; } if ( isset($_paths['page']) ) { $return[] = $_paths['core'] . "_pages/" . $_paths['page'] . "/" . $filename; } if ( isset($_paths['theme']) ) { $return[] = $_paths['web_root'] . "_themes/" . $_paths['theme'] . "/" . $filename; } if ( isset($_paths['theme']) ) { $return[] = $_paths['core'] . "_themes/" . $_paths['theme'] . "/" . $filename; } $return[] = $_paths['web_root'] . $filename; $return[] = $_paths['core'] . $filename; #return if ( isset($return) ) { return $return; } else { return false; } } } |
De class doet in feite drie dingen:
- Alle includes verlopen via een cascade, waarbij de prio loopt van specifiek naar algemeen. Dus indien er een file wordt geinclude kijkt ie eerst naar de custom code voor de gekozen pagina in de gekozen template, als die er niet is de custom code voor de gekozen pagina voor de site, enz, tot ie uiteindelijk uitkomt bij de custom code in het framework. Deze cascade kan worden uitgebreid met zogen. "adds", bestanden met dezelfde naam, alleen dan "add" erachter. Deze worden automatisch toegevoegd onderaan de te laden code. Zo kun je je op specifiek niveau beperken tot aanpassingen of aanvullingen van de code op algemeen niveau.
- Objecten worden factorymatig aangeroepen. Omdat de ini file sections heeft met dezelfde naam als het bestand waarvoor de settings gelden worden automatisch de settings die bij het bestand horen meegegeven. Bovendien zorgt de code ervoor dat, tenzij anders aangegeven, er altijd maar 1 instance van een object wordt aangemaakt. Alle bestanden die toegang hebben tot deze class kunnen hun objecten dus delen.
- Het is mogelijk om settings te wijzigen en vervolgens door te geven aan alle objecten tegelijk. Deze class zorgt ervoor dat de juiste updates bij de juiste objecten uitkomt.
Toch wegen naar mijn ervaring tot nu toe deze nadelen op tegen de voordelen: Je kunt lekker snel ontwikkelen, alles kan met alles praten, en objecten maak je lekker makkelijk aan met
PHP:
1
| $newobject = $_incl->obj("path_naar_object"); |
Als er al een instance bestaat dan krijg je die, anders maakt ie em voor je aan.Natuurlijk is dit geen zuivere meting, maar na invoeren van dit systeem geeft mijn debugger/profiler een snelheidswinst van meer dan 20% aan.
Kortom, ik ben best enthousiast, maar ik kan niet helemaal overzien of dit systeem ook op de lange termijn (bij redelijk grote en complexe systemen) handig is en gaat werken. Daarom graag jullie feedback (liefst inhoudelijk op de aanpak, en niet vallen over de programmeerstijl van de code zelf, daar ga ik zelf nog wel een keer aan schaven!).
Alvast bedankt, sowieso voor het lezen van deze lap tekst