-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathphpnuts.class.php
More file actions
478 lines (427 loc) · 17.2 KB
/
Copy pathphpnuts.class.php
File metadata and controls
478 lines (427 loc) · 17.2 KB
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
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
<?php
/*
* PHP-Nuts. A PHP package loader system.
* Copyright (C) 2005-2022 Víctor Román Archidona <victor@victorroman.es>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
class PHPNuts {
private static $classpath = array (); /**< Our classpath */
private static $packages = array (); /**< Loaded packages */
/*
* \fn getLoadedPackages()
* \brief Returns the currently loaded packages
*
* This functions returns (in an array) the current list of loaded
* packages.
*/
public static function getLoadedPackages() {
return self::$packages;
}
/*
* \fn setClassPath($path)
* \brief Adds a path to PHP-Nuts search classpath.
*
* \param $path Path to be added
*
* We must set where PHP-Nuts must search for any package. This function
* adds the specified path to do this mission.
*/
public static function setClassPath($path) {
$current_path = self::getClassPath();
$new_path = (array) $path;
self::$classpath = array_merge($current_path, $new_path);
}
/*
* \fn getClassPath($as_array = true)
* \brief Retuns the current classpath
* \param $package Package to be loaded
* \param $as Optionally variable which will contain the new instance (by reference).
* \return An array with every path if first param is true
* \return A line broken by ';' if the first param is false
*
* This functions allows the programmer to get the current classpath
* used by the class where it search the packages.
*/
public static function getClassPath($as_array = true) {
/*
* Is possible to return our classpath in two different
* flavors:
*
* By default: Returns an array
* In a line.: In the same line returns the classpath, broken
* by ';'.
*/
if ($as_array == true) {
return (array) self::$classpath;
} else {
$retval = implode(";", self::$classpath);
return $retval;
}
}
/*
* \fn PackageLoad($package, &$as = NULL)
* \brief Loads a package searching for it in the classpath
*
* \param $package Package to be loaded
* \param $as Optionally variable which will contain the new instance (by reference).
*
* \return An instancie to a new object in the second parameter is specified
* \return An instancie to a new object
*
* This function loads the package and returns an instance to it if the
* function was called with one parameter, or stores it into the second
* parameter.
*
* If is a superpackage (tld_domain.* or tld_domain.package.*), the function
* returns an array with the instances as show:
* $array['tld.domain'] = Main package instance
* $array['tld.domain.package'] AND $array['package'] = Subpackage instance
*/
public static function Load($package, & $as = null) {
if (self::isPackageLoaded($package)) {
trigger_error("Package $package was previously loaded.", E_USER_WARNING);
return null;
}
if ($package[strlen($package) - 1] != '*')
return self::packageLoad($package, $as);
else
return self::packageLoadRecursive($package, $as);
}
/*
* \fn isPackageLoaded($package)
* \brief Checks if a package was previously loaded
*
* \param $package Package to be checked
*
* \return 0 If the package is not loaded
* \return 1 If the package was loaded before
*
* With isPackageLoaded we can check if the specified package was
* loaded before with a 'Load' method call.
*/
public static function isPackageLoaded($package) {
$package = strtolower($package);
return in_array($package, self::$packages);
}
/*
* \fn packageAdd($package)
* \brief Adds a package to internal package array list
*
* \param $package Package to be added to our internal array list
*
* After load a package, it MUST be added to the internal packages
* list to skip problems loading the same package two or more times.
*/
private static function packageAdd($package) {
if (!in_array($package, self::$packages))
self::$packages[] = strtolower($package);
}
/*
* \fn PackageLoad($package, &$as = NULL)
* \brief Loads a package searching for it in the classpath
* \param $package Package to be loaded
* \param $as Optionally variable which will contain the new instance (by reference).
* \return An instancie to a new object in the second parameter is specified
* \return An instancie to a new object
*
* This function loads the package using one of the two available kinds
* to do it:
* - The first is putting the class into tld/domain with the class name
* and adding .class.php (IE: tld/domain/ClassName/ClassName.class.php). The class
* will be caled "ClassName":
* class ClassName
* {
* [code]
* }
*
* - The second is very similar. Puts the class into tld/domain and call
* it "ClassName.class.php" (tld/domain/ClassName/ClassName.class.php). BUT in
* his definition, it MUST me called tld_domain_classname. With this kind
* of call, avoid redefining classes is much more easiest.
* class tld_domain_ClassName
* {
* [code]
* }
*
* Is transparent which kind had you used, this functions try to determine
* it.
*/
private static function packageLoad($package, & $as = null) {
/* The following variable is false until the class file is found */
$found = false;
/*
* Extracts the file name. With this filename also builds the
* $classfile adding ".class.php";
*/
$file = substr($package, strrpos($package, '.') + 1);
$classfile = $file.".class.php";
/*
* Now the directory to search. It is build in as shown below:
*
* With the package name: tld.domain.Package, first gets off
* the "Package". This Package is the final filename with
* .class.php added ("Package.class.php").
*
* The directory "tld/domain" is build replacing the '.' (dots)
* with a '/' (slash) using str_replace.
*/
$directory = substr($package, 0, -strlen($file) - 1);
$directory = str_replace('.', '/', $directory);
/*
* Now we iterate over $classpath to search where the
* directory will be. When the directory is found, try
* to search for the class file, and sets $found to true
* if the file is found.
*/
foreach (self::getClassPath() as $cpath) {
$cpath = $cpath.'/'.$directory.'/'.$file;
if (is_dir($cpath)) {
$classfile = $cpath.'/'.$classfile;
if (is_file($classfile)) {
$found = true;
break;
}
}
}
/*
* If the file was not found, advertise the user to correct
* his classpath.
*/
if ($found !== true) {
trigger_error("Package $package not found on CLASSPATH", E_USER_ERROR);
}
/* Includes the file (only once) */
include_once "$classfile";
$classname = $file;
/*
* Checks if the class exists (based on the previous step). If it
* not exists, warns the user.
*/
if (!class_exists($classname)) {
$new_classname = str_replace('/', '_', $directory).'_'.$file;
/*
* If "class ClassName" does not exists search for the other
* possible construction "class tld_domain_classname".
*/
if (!class_exists($new_classname)) {
trigger_error("Neither \"$classname\" nor \"$new_classname\" class exists on package $package", E_USER_ERROR);
} else {
/* Sets the fixed name into $classname */
$classname = $new_classname;
}
}
/* Adds the loaded package to internal array packages list */
self::packageAdd($package);
/*
* This code determines how many args was the function called with. Is
* necessary to determine what kind of operation do it, and will be of
* two types:
*
* Only one argument: Object is returned with return an assigned to
* calling variable.
* Two arguments: The variable in the second parameter is used to put
* (by reference) an instance to the class loaded.
*/
$numargs = func_num_args();
if ($numargs == 1)
return new $classname;
else
(object) $as = new $classname;
}
/*
* \fn PackageLoad($package, &$as = NULL)
* \brief Loads a package searching for it in the classpath
*
* \param $package Package to be loaded
* \param $as Optionally variable which will contain the new instance (by reference).
*
* \return An instancie to a new object in the second parameter is specified
* \return An instancie to a new object
*
* This function loads entire package and his subpackages into an array if
* it was specified as second parameter, or returns them if only wass called
* with one parameter.
*
* The kind of resultant array is:
*
* $array['tld.domain.package'] AND $array['package'] = Subpackage instance
*/
private static function packageLoadRecursive($package, & $as) {
/* Drops ".*" from the package name */
$pkg = substr($package, 0, -2);
/* Builds the "virtual" package path */
$pkg_path = str_replace('.', '/', $pkg);
foreach (self::$getClassPath() as $cpath) {
/* Real path is "SEARCH_PATH/PACKAGE_PATH" */
$real_path = $cpath.'/'.$pkg_path;
/*
* If the search path not exists, continues the iteration with
* the following classpath entry.
*
* FIXME: If $real_path does not exists, we MUST NOT continue
* without try to search the alternative path. This alternative
* path is build taking the current path, and search for the
* package here.
*/
if (!is_dir($real_path))
continue;
/* Si el directorio existe, obtiene el listado de ficheros */
$files = self::searchFilesToInclude($real_path);
/* Try to load every packet one by one */
foreach ($files as $file) {
/* Drops the current search path */
$to_load = substr($file, strlen($cpath) + 1);
/* Drops ClassName.class.php */
$to_load = substr($to_load, 0, strrpos($to_load, '/'));
/* Replaces directories '/' with packages '.' */
$to_load = str_replace('/', '.', $to_load);
/* Convert to lower case */
$to_load = strtolower($to_load);
/* Now loads it */
$pkg_name = substr($to_load, strlen($pkg) + 1);
/*
* And finally builds the array. In first place we check
* if $pkg_name after drops exists. If it exists is a
* subpackage, and assings it to the array as show:
*
* $array['tld.domain.subpackage']; AND
* $array['subpackage']
*
* If $pkg_name does not exists is the base package, and
* assigns to the array as shows:
*
* $array['tld.domain']
*/
if ($pkg_name) {
self::packageLoad($to_load, $as[$pkg_name]);
$as[$pkg.'.'.$pkg_name] = $as[$pkg_name];
} else {
self::packageLoad($to_load, $as[$pkg]);
}
} /* Foreach files */
} /* Foreach classpath */
if ($as && (func_num_args() == 1))
return $as;
}
/*
* \fn getAvailablePackages()
* \brief Gets the available packages searching them in the classpath.
*
* \return An array with the available packages
* \return NULL if there is not any package.
*
* With getAvailablePackages we can get an array list with all available
* packages. This function searchs for them in the classpath, and returns
* the result or NULL if none is found.
*/
public static function getAvailablePackages() {
$retval = array ();
foreach (self::getClassPath() as $cpath) {
$cpath_len = strlen($cpath);
foreach (self::searchFilesToInclude($cpath) as $file) {
$file = substr($file, $cpath_len +1);
$file = substr($file, 0, strrpos($file, '/'));
$file = str_replace('/', '.', $file);
$retval[] = $file;
}
}
return count($retval) ? $retval : NULL;
}
/*
* \fn Unload($package, &$as = NULL)
* \brief Unloads a package cleaning his content
* \param $as Object where the package was loaded
*
* Destroys the variable fixing her value to NULL and doing an unset
* after it.
*/
public static function Unload(& $as) {
$as = null;
unset ($as);
}
/*
* \fn searchFilesToInclude($parent, $autocall = false)
* \brief Searchs files recursively to be loaded after.
*
* \param $parent Main directory to search ("root directory")
* \param $autocall Set it to true if you call this function from inside it.
*
* \return An array with the files
* \return NULL if he can found any file
*
* This functions searchs on the specified parent and in all his subdirs
* for "Package/Package.class.php". The param $autocall is a hack to
* destroy the $ar_files array inside it, because if the function is
* called two times it returns the result of the first execution PLUS
* results of second execution.
*
* In the very near future this function should be rewritten or entirely
* drop, due is a problem origin.
*/
private static function searchFilesToInclude($parent, $autocall = false) {
/* Internal file array */
static $ar_files = array ();
/*
* If the function is not called from it, we drop the previous
* content stored in $ar_files. Read the function documentation
* to know more about this.
*/
if (!$autocall)
$ar_files = array ();
/*
* If the specified directory is not a directory (yups), we
* return NULL. With this isn't necessary check if the directory
* handler is valid after. Also drops a possible error if the
* directory not exists (without hide the warning with @).
*/
if (!is_dir($parent))
return NULL;
/* Opens the directory to be readed */
$dh = opendir($parent);
/* Extracts the base directory which class resides */
$class_directory = substr($parent, strrpos($parent, '/') + 1);
/* Full path to class file */
$file = $parent.'/'.$class_directory.".class.php";
/* Reads the current directory (with his subdirectories) */
while (($current = readdir($dh)) !== false) {
/*
* Skips the current (.) and previous (..) directory to
* avoid an infinite loop.
*/
if ($current == '.' || $current == "..")
continue;
/*
* If the file exists and was not previously stored into our
* internal files array, we store it.
*/
if (is_file($file) && !in_array($file, $ar_files)) {
$ar_files[] = $file;
continue;
}
/*
* Makes the recursive search. If the current data in $current is
* a directory, we read it calling this funcion, and passing 'true'
* as second parameter DUE THIS IS AN INTERNAL AUTOCALL.
*/
if (is_dir($parent.'/'.$current))
self::searchFilesToInclude($parent.'/'.$current, true);
}
/* Close the directory handler */
closedir($dh);
/* Returns an array with the full path to the files, or NULL */
return count($ar_files) ? $ar_files : NULL;
}
}
?>