summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMagnus Hagander2008-01-06 13:51:52 +0000
committerMagnus Hagander2008-01-06 13:51:52 +0000
commitedb3f52aafe1d50396f634abee24d42ac43aa5cf (patch)
tree7c0827af23f2a7f02f963eb71843419d6d11dcab
parent4a706e69447451f7709d27e5a046844153a9f79e (diff)
Add the required pear modules to our own svn so it's
easier to get all the required dependencies. git-svn-id: file:///Users/dpage/pgweb/svn-repo/trunk@1854 8f5c7a92-453e-0410-a47f-ad33c8a6b003
-rw-r--r--pearlib/.filemap1
-rw-r--r--pearlib/.lock0
-rw-r--r--pearlib/.registry/archive_tar.reg26
-rw-r--r--pearlib/.registry/benchmark.reg26
-rw-r--r--pearlib/.registry/console_getargs.reg18
-rw-r--r--pearlib/.registry/console_getopt.reg12
-rw-r--r--pearlib/.registry/html_common.reg19
-rw-r--r--pearlib/.registry/html_quickform.reg499
-rw-r--r--pearlib/.registry/html_quickform_controller.reg50
-rw-r--r--pearlib/.registry/html_template_sigma.reg76
-rw-r--r--pearlib/.registry/http_client.reg27
-rw-r--r--pearlib/.registry/http_request.reg61
-rw-r--r--pearlib/.registry/log.reg105
-rw-r--r--pearlib/.registry/net_socket.reg10
-rw-r--r--pearlib/.registry/net_url.reg31
-rw-r--r--pearlib/.registry/pear.reg54
-rw-r--r--pearlib/.registry/xml_parser.reg91
-rw-r--r--pearlib/.registry/xml_rpc.reg21
-rw-r--r--pearlib/.registry/xml_serializer.reg69
-rw-r--r--pearlib/.registry/xml_util.reg40
-rw-r--r--pearlib/Archive/Tar.php1660
-rw-r--r--pearlib/Benchmark/Iterate.php155
-rw-r--r--pearlib/Benchmark/Profiler.php371
-rw-r--r--pearlib/Benchmark/Timer.php254
-rw-r--r--pearlib/Console/Getargs.php1019
-rw-r--r--pearlib/Console/Getopt.php251
-rw-r--r--pearlib/HTML/Common.php419
-rw-r--r--pearlib/HTML/QuickForm.php1911
-rw-r--r--pearlib/HTML/QuickForm/Action.php47
-rw-r--r--pearlib/HTML/QuickForm/Action/Back.php54
-rw-r--r--pearlib/HTML/QuickForm/Action/Direct.php52
-rw-r--r--pearlib/HTML/QuickForm/Action/Display.php74
-rw-r--r--pearlib/HTML/QuickForm/Action/Jump.php57
-rw-r--r--pearlib/HTML/QuickForm/Action/Next.php63
-rw-r--r--pearlib/HTML/QuickForm/Action/Submit.php57
-rw-r--r--pearlib/HTML/QuickForm/Controller.php441
-rw-r--r--pearlib/HTML/QuickForm/Page.php180
-rw-r--r--pearlib/HTML/QuickForm/Renderer.php150
-rw-r--r--pearlib/HTML/QuickForm/Renderer/Array.php319
-rw-r--r--pearlib/HTML/QuickForm/Renderer/ArraySmarty.php376
-rw-r--r--pearlib/HTML/QuickForm/Renderer/Default.php471
-rw-r--r--pearlib/HTML/QuickForm/Renderer/ITDynamic.php287
-rw-r--r--pearlib/HTML/QuickForm/Renderer/ITStatic.php490
-rw-r--r--pearlib/HTML/QuickForm/Renderer/Object.php438
-rw-r--r--pearlib/HTML/QuickForm/Renderer/ObjectFlexy.php413
-rw-r--r--pearlib/HTML/QuickForm/Renderer/QuickHtml.php203
-rw-r--r--pearlib/HTML/QuickForm/Rule.php67
-rw-r--r--pearlib/HTML/QuickForm/Rule/Callback.php113
-rw-r--r--pearlib/HTML/QuickForm/Rule/Compare.php95
-rw-r--r--pearlib/HTML/QuickForm/Rule/Email.php61
-rw-r--r--pearlib/HTML/QuickForm/Rule/Range.php64
-rw-r--r--pearlib/HTML/QuickForm/Rule/Regex.php89
-rw-r--r--pearlib/HTML/QuickForm/Rule/Required.php52
-rw-r--r--pearlib/HTML/QuickForm/RuleRegistry.php317
-rw-r--r--pearlib/HTML/QuickForm/advcheckbox.php279
-rw-r--r--pearlib/HTML/QuickForm/autocomplete.php240
-rw-r--r--pearlib/HTML/QuickForm/button.php73
-rw-r--r--pearlib/HTML/QuickForm/checkbox.php268
-rw-r--r--pearlib/HTML/QuickForm/date.php451
-rw-r--r--pearlib/HTML/QuickForm/element.php478
-rw-r--r--pearlib/HTML/QuickForm/file.php346
-rw-r--r--pearlib/HTML/QuickForm/group.php568
-rw-r--r--pearlib/HTML/QuickForm/header.php65
-rw-r--r--pearlib/HTML/QuickForm/hidden.php87
-rw-r--r--pearlib/HTML/QuickForm/hiddenselect.php103
-rw-r--r--pearlib/HTML/QuickForm/hierselect.php444
-rw-r--r--pearlib/HTML/QuickForm/html.php67
-rw-r--r--pearlib/HTML/QuickForm/image.php119
-rw-r--r--pearlib/HTML/QuickForm/input.php202
-rw-r--r--pearlib/HTML/QuickForm/link.php192
-rw-r--r--pearlib/HTML/QuickForm/password.php108
-rw-r--r--pearlib/HTML/QuickForm/radio.php244
-rw-r--r--pearlib/HTML/QuickForm/reset.php72
-rw-r--r--pearlib/HTML/QuickForm/select.php577
-rw-r--r--pearlib/HTML/QuickForm/static.php193
-rw-r--r--pearlib/HTML/QuickForm/submit.php82
-rw-r--r--pearlib/HTML/QuickForm/text.php91
-rw-r--r--pearlib/HTML/QuickForm/textarea.php222
-rw-r--r--pearlib/HTML/QuickForm/xbutton.php145
-rw-r--r--pearlib/HTML/Template/Sigma.php1778
-rw-r--r--pearlib/HTTP/Client.php461
-rw-r--r--pearlib/HTTP/Client/CookieManager.php183
-rw-r--r--pearlib/HTTP/Request.php1132
-rw-r--r--pearlib/HTTP/Request/Listener.php96
-rw-r--r--pearlib/Log.php631
-rw-r--r--pearlib/Log/composite.php196
-rw-r--r--pearlib/Log/console.php190
-rw-r--r--pearlib/Log/display.php108
-rw-r--r--pearlib/Log/error_log.php104
-rw-r--r--pearlib/Log/file.php286
-rw-r--r--pearlib/Log/mail.php222
-rw-r--r--pearlib/Log/mcal.php171
-rw-r--r--pearlib/Log/null.php68
-rw-r--r--pearlib/Log/observer.php126
-rw-r--r--pearlib/Log/sql.php225
-rw-r--r--pearlib/Log/sqlite.php238
-rw-r--r--pearlib/Log/syslog.php160
-rw-r--r--pearlib/Log/win.php256
-rw-r--r--pearlib/Net/Socket.php456
-rw-r--r--pearlib/Net/URL.php410
-rw-r--r--pearlib/OS/Guess.php287
-rw-r--r--pearlib/PEAR.php1056
-rw-r--r--pearlib/PEAR/Autoloader.php208
-rw-r--r--pearlib/PEAR/Builder.php392
-rw-r--r--pearlib/PEAR/Command.php398
-rw-r--r--pearlib/PEAR/Command/Auth.php155
-rw-r--r--pearlib/PEAR/Command/Build.php89
-rw-r--r--pearlib/PEAR/Command/Common.php249
-rw-r--r--pearlib/PEAR/Command/Config.php225
-rw-r--r--pearlib/PEAR/Command/Install.php470
-rw-r--r--pearlib/PEAR/Command/Mirror.php101
-rw-r--r--pearlib/PEAR/Command/Package.php751
-rw-r--r--pearlib/PEAR/Command/Registry.php351
-rw-r--r--pearlib/PEAR/Command/Remote.php435
-rw-r--r--pearlib/PEAR/Common.php2081
-rw-r--r--pearlib/PEAR/Config.php1169
-rw-r--r--pearlib/PEAR/Dependency.php486
-rw-r--r--pearlib/PEAR/Downloader.php680
-rw-r--r--pearlib/PEAR/ErrorStack.php955
-rw-r--r--pearlib/PEAR/Exception.php353
-rw-r--r--pearlib/PEAR/Frontend/CLI.php509
-rw-r--r--pearlib/PEAR/Installer.php1053
-rw-r--r--pearlib/PEAR/Packager.php165
-rw-r--r--pearlib/PEAR/Registry.php538
-rw-r--r--pearlib/PEAR/Remote.php394
-rw-r--r--pearlib/System.php540
-rw-r--r--pearlib/XML/Parser.php670
-rw-r--r--pearlib/XML/Parser/Simple.php279
-rw-r--r--pearlib/XML/RPC.php1189
-rw-r--r--pearlib/XML/RPC/Server.php310
-rw-r--r--pearlib/XML/Serializer.php508
-rw-r--r--pearlib/XML/Unserializer.php491
-rw-r--r--pearlib/XML/Util.php700
-rw-r--r--pearlib/pearcmd.php313
134 files changed, 44069 insertions, 0 deletions
diff --git a/pearlib/.filemap b/pearlib/.filemap
new file mode 100644
index 00000000..995bf3ea
--- /dev/null
+++ b/pearlib/.filemap
@@ -0,0 +1 @@
+a:102:{s:18:"Console/Getopt.php";s:14:"console_getopt";s:15:"Archive/Tar.php";s:11:"archive_tar";s:7:"dirtree";s:8:"xml_util";s:18:"HTML/QuickForm.php";s:14:"html_quickform";s:30:"HTML/QuickForm/advcheckbox.php";s:14:"html_quickform";s:31:"HTML/QuickForm/autocomplete.php";s:14:"html_quickform";s:25:"HTML/QuickForm/button.php";s:14:"html_quickform";s:27:"HTML/QuickForm/checkbox.php";s:14:"html_quickform";s:23:"HTML/QuickForm/date.php";s:14:"html_quickform";s:26:"HTML/QuickForm/element.php";s:14:"html_quickform";s:23:"HTML/QuickForm/file.php";s:14:"html_quickform";s:24:"HTML/QuickForm/group.php";s:14:"html_quickform";s:25:"HTML/QuickForm/header.php";s:14:"html_quickform";s:25:"HTML/QuickForm/hidden.php";s:14:"html_quickform";s:31:"HTML/QuickForm/hiddenselect.php";s:14:"html_quickform";s:29:"HTML/QuickForm/hierselect.php";s:14:"html_quickform";s:23:"HTML/QuickForm/html.php";s:14:"html_quickform";s:24:"HTML/QuickForm/image.php";s:14:"html_quickform";s:24:"HTML/QuickForm/input.php";s:14:"html_quickform";s:23:"HTML/QuickForm/link.php";s:14:"html_quickform";s:27:"HTML/QuickForm/password.php";s:14:"html_quickform";s:24:"HTML/QuickForm/radio.php";s:14:"html_quickform";s:24:"HTML/QuickForm/reset.php";s:14:"html_quickform";s:25:"HTML/QuickForm/select.php";s:14:"html_quickform";s:25:"HTML/QuickForm/static.php";s:14:"html_quickform";s:25:"HTML/QuickForm/submit.php";s:14:"html_quickform";s:23:"HTML/QuickForm/text.php";s:14:"html_quickform";s:27:"HTML/QuickForm/textarea.php";s:14:"html_quickform";s:26:"HTML/QuickForm/xbutton.php";s:14:"html_quickform";s:27:"HTML/QuickForm/Renderer.php";s:14:"html_quickform";s:23:"HTML/QuickForm/Rule.php";s:14:"html_quickform";s:31:"HTML/QuickForm/RuleRegistry.php";s:14:"html_quickform";s:33:"HTML/QuickForm/Renderer/Array.php";s:14:"html_quickform";s:39:"HTML/QuickForm/Renderer/ArraySmarty.php";s:14:"html_quickform";s:35:"HTML/QuickForm/Renderer/Default.php";s:14:"html_quickform";s:37:"HTML/QuickForm/Renderer/ITDynamic.php";s:14:"html_quickform";s:36:"HTML/QuickForm/Renderer/ITStatic.php";s:14:"html_quickform";s:34:"HTML/QuickForm/Renderer/Object.php";s:14:"html_quickform";s:39:"HTML/QuickForm/Renderer/ObjectFlexy.php";s:14:"html_quickform";s:37:"HTML/QuickForm/Renderer/QuickHtml.php";s:14:"html_quickform";s:32:"HTML/QuickForm/Rule/Callback.php";s:14:"html_quickform";s:31:"HTML/QuickForm/Rule/Compare.php";s:14:"html_quickform";s:29:"HTML/QuickForm/Rule/Email.php";s:14:"html_quickform";s:29:"HTML/QuickForm/Rule/Range.php";s:14:"html_quickform";s:29:"HTML/QuickForm/Rule/Regex.php";s:14:"html_quickform";s:32:"HTML/QuickForm/Rule/Required.php";s:14:"html_quickform";s:15:"HTML/Common.php";s:11:"html_common";s:14:"Net/Socket.php";s:10:"net_socket";s:17:"Log/composite.php";s:3:"log";s:15:"Log/console.php";s:3:"log";s:15:"Log/display.php";s:3:"log";s:17:"Log/error_log.php";s:3:"log";s:12:"Log/file.php";s:3:"log";s:12:"Log/mail.php";s:3:"log";s:12:"Log/mcal.php";s:3:"log";s:12:"Log/null.php";s:3:"log";s:16:"Log/observer.php";s:3:"log";s:11:"Log/sql.php";s:3:"log";s:14:"Log/sqlite.php";s:3:"log";s:14:"Log/syslog.php";s:3:"log";s:11:"Log/win.php";s:3:"log";s:7:"Log.php";s:3:"log";s:23:"HTML/Template/Sigma.php";s:19:"html_template_sigma";s:15:"HTTP/Client.php";s:11:"http_client";s:29:"HTTP/Client/CookieManager.php";s:11:"http_client";s:16:"HTTP/Request.php";s:12:"http_request";s:25:"HTTP/Request/Listener.php";s:12:"http_request";s:21:"XML/Parser\Simple.php";s:10:"xml_parser";s:14:"XML/Parser.php";s:10:"xml_parser";s:11:"Net/URL.php";s:7:"net_url";s:12:"OS/Guess.php";s:4:"pear";s:21:"PEAR/Command/Auth.php";s:4:"pear";s:22:"PEAR/Command/Build.php";s:4:"pear";s:23:"PEAR/Command/Common.php";s:4:"pear";s:23:"PEAR/Command/Config.php";s:4:"pear";s:24:"PEAR/Command/Install.php";s:4:"pear";s:24:"PEAR/Command/Package.php";s:4:"pear";s:25:"PEAR/Command/Registry.php";s:4:"pear";s:23:"PEAR/Command/Remote.php";s:4:"pear";s:23:"PEAR/Command/Mirror.php";s:4:"pear";s:21:"PEAR/Frontend/CLI.php";s:4:"pear";s:19:"PEAR/Autoloader.php";s:4:"pear";s:16:"PEAR/Command.php";s:4:"pear";s:15:"PEAR/Common.php";s:4:"pear";s:15:"PEAR/Config.php";s:4:"pear";s:19:"PEAR/Dependency.php";s:4:"pear";s:19:"PEAR/Downloader.php";s:4:"pear";s:18:"PEAR/Exception.php";s:4:"pear";s:19:"PEAR/ErrorStack.php";s:4:"pear";s:16:"PEAR/Builder.php";s:4:"pear";s:18:"PEAR/Installer.php";s:4:"pear";s:17:"PEAR/Packager.php";s:4:"pear";s:17:"PEAR/Registry.php";s:4:"pear";s:15:"PEAR/Remote.php";s:4:"pear";s:19:"scripts/pearcmd.php";s:4:"pear";s:8:"PEAR.php";s:4:"pear";s:10:"System.php";s:4:"pear";s:18:"XML/Serializer.php";s:14:"xml_serializer";s:20:"XML/Unserializer.php";s:14:"xml_serializer";s:11:"XML/RPC.php";s:7:"xml_rpc";s:18:"XML/RPC/Server.php";s:7:"xml_rpc";s:12:"XML/Util.php";s:8:"xml_util";} \ No newline at end of file
diff --git a/pearlib/.lock b/pearlib/.lock
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pearlib/.lock
diff --git a/pearlib/.registry/archive_tar.reg b/pearlib/.registry/archive_tar.reg
new file mode 100644
index 00000000..7f12b1b2
--- /dev/null
+++ b/pearlib/.registry/archive_tar.reg
@@ -0,0 +1,26 @@
+a:13:{s:8:"provides";a:12:{s:17:"class;Archive_Tar";a:3:{s:4:"type";s:5:"class";s:4:"name";s:11:"Archive_Tar";s:8:"explicit";b:1;}s:28:"function;Archive_Tar::create";a:3:{s:4:"type";s:8:"function";s:4:"name";s:19:"Archive_Tar::create";s:8:"explicit";b:1;}s:25:"function;Archive_Tar::add";a:3:{s:4:"type";s:8:"function";s:4:"name";s:16:"Archive_Tar::add";s:8:"explicit";b:1;}s:29:"function;Archive_Tar::extract";a:3:{s:4:"type";s:8:"function";s:4:"name";s:20:"Archive_Tar::extract";s:8:"explicit";b:1;}s:33:"function;Archive_Tar::listContent";a:3:{s:4:"type";s:8:"function";s:4:"name";s:24:"Archive_Tar::listContent";s:8:"explicit";b:1;}s:34:"function;Archive_Tar::createModify";a:3:{s:4:"type";s:8:"function";s:4:"name";s:25:"Archive_Tar::createModify";s:8:"explicit";b:1;}s:31:"function;Archive_Tar::addModify";a:3:{s:4:"type";s:8:"function";s:4:"name";s:22:"Archive_Tar::addModify";s:8:"explicit";b:1;}s:31:"function;Archive_Tar::addString";a:3:{s:4:"type";s:8:"function";s:4:"name";s:22:"Archive_Tar::addString";s:8:"explicit";b:1;}s:35:"function;Archive_Tar::extractModify";a:3:{s:4:"type";s:8:"function";s:4:"name";s:26:"Archive_Tar::extractModify";s:8:"explicit";b:1;}s:37:"function;Archive_Tar::extractInString";a:3:{s:4:"type";s:8:"function";s:4:"name";s:28:"Archive_Tar::extractInString";s:8:"explicit";b:1;}s:33:"function;Archive_Tar::extractList";a:3:{s:4:"type";s:8:"function";s:4:"name";s:24:"Archive_Tar::extractList";s:8:"explicit";b:1;}s:34:"function;Archive_Tar::setAttribute";a:3:{s:4:"type";s:8:"function";s:4:"name";s:25:"Archive_Tar::setAttribute";s:8:"explicit";b:1;}}s:8:"filelist";a:3:{s:15:"Archive/Tar.php";a:4:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:1:"/";s:6:"md5sum";s:32:"4b718a4f5943d3b88defb243359b2866";s:12:"installed_as";s:64:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Archive/Tar.php";}s:20:"docs/Archive_Tar.txt";a:4:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:1:"/";s:6:"md5sum";s:32:"ae640b797078a6542ea0d236f28efffb";s:12:"installed_as";s:82:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Archive_Tar/docs/Archive_Tar.txt";}s:7:"dirtree";a:2:{s:56:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Archive";b:1;s:66:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Archive_Tar/docs";b:1;}}s:7:"package";s:11:"Archive_Tar";s:7:"summary";s:25:"Tar file management class";s:11:"description";s:257:"This class provides handling of tar files in PHP.
+It supports creating, listing, extracting and adding to tar files.
+Gzip support is available if PHP has the zlib extension built-in or
+loaded. Bz2 compression is also supported with the bz2 extension loaded.";s:11:"maintainers";a:2:{i:0;a:4:{s:6:"handle";s:7:"vblavet";s:4:"name";s:14:"Vincent Blavet";s:5:"email";s:22:"[email protected]";s:4:"role";s:4:"lead";}i:1;a:4:{s:6:"handle";s:3:"ssb";s:4:"name";s:18:"Stig S�ther Bakken";s:5:"email";s:12:"[email protected]";s:4:"role";s:6:"helper";}}s:7:"version";s:3:"1.2";s:12:"release_date";s:10:"2004-05-08";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:71:"Add support for other separator than the space char and bug
+ correction";s:9:"changelog";a:7:{i:0;a:4:{s:7:"version";s:3:"1.1";s:12:"release_date";s:10:"2003-05-28";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:143:"* Add support for BZ2 compression
+* Add support for add and extract without using temporary files : methods addString() and extractInString()
+
+";}i:1;a:4:{s:7:"version";s:3:"1.0";s:12:"release_date";s:10:"2003-01-24";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:26:"Change status to stable
+
+
+";}i:2;a:4:{s:7:"version";s:7:"0.10-b1";s:12:"release_date";s:10:"2003-01-08";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:62:"Add support for long filenames (greater than 99 characters)
+
+
+";}i:3;a:4:{s:7:"version";s:3:"0.9";s:12:"release_date";s:10:"2002-05-27";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:28:"Auto-detect gzip'ed files
+
+
+";}i:4;a:4:{s:7:"version";s:3:"0.4";s:12:"release_date";s:10:"2002-05-20";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:54:"Windows bugfix: use forward slashes inside archives
+
+
+";}i:5;a:4:{s:7:"version";s:3:"0.2";s:12:"release_date";s:10:"2002-02-18";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:32:"From initial commit to stable
+
+
+";}i:6;a:4:{s:7:"version";s:3:"0.3";s:12:"release_date";s:10:"2002-04-13";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:50:"Windows bugfix: used wrong directory separators
+
+
+";}}s:13:"_lastmodified";i:1086963219;} \ No newline at end of file
diff --git a/pearlib/.registry/benchmark.reg b/pearlib/.registry/benchmark.reg
new file mode 100644
index 00000000..539c1886
--- /dev/null
+++ b/pearlib/.registry/benchmark.reg
@@ -0,0 +1,26 @@
+a:13:{s:8:"provides";a:19:{s:21:"class;Benchmark_Timer";a:3:{s:4:"type";s:5:"class";s:4:"name";s:15:"Benchmark_Timer";s:8:"explicit";b:1;}s:35:"function;Benchmark_Timer::getOutput";a:3:{s:4:"type";s:8:"function";s:4:"name";s:26:"Benchmark_Timer::getOutput";s:8:"explicit";b:1;}s:33:"function;Benchmark_Timer::display";a:3:{s:4:"type";s:8:"function";s:4:"name";s:24:"Benchmark_Timer::display";s:8:"explicit";b:1;}s:31:"function;Benchmark_Timer::start";a:3:{s:4:"type";s:8:"function";s:4:"name";s:22:"Benchmark_Timer::start";s:8:"explicit";b:1;}s:30:"function;Benchmark_Timer::stop";a:3:{s:4:"type";s:8:"function";s:4:"name";s:21:"Benchmark_Timer::stop";s:8:"explicit";b:1;}s:35:"function;Benchmark_Timer::setMarker";a:3:{s:4:"type";s:8:"function";s:4:"name";s:26:"Benchmark_Timer::setMarker";s:8:"explicit";b:1;}s:37:"function;Benchmark_Timer::timeElapsed";a:3:{s:4:"type";s:8:"function";s:4:"name";s:28:"Benchmark_Timer::timeElapsed";s:8:"explicit";b:1;}s:38:"function;Benchmark_Timer::getProfiling";a:3:{s:4:"type";s:8:"function";s:4:"name";s:29:"Benchmark_Timer::getProfiling";s:8:"explicit";b:1;}s:23:"class;Benchmark_Iterate";a:3:{s:4:"type";s:5:"class";s:4:"name";s:17:"Benchmark_Iterate";s:8:"explicit";b:1;}s:31:"function;Benchmark_Iterate::run";a:3:{s:4:"type";s:8:"function";s:4:"name";s:22:"Benchmark_Iterate::run";s:8:"explicit";b:1;}s:31:"function;Benchmark_Iterate::get";a:3:{s:4:"type";s:8:"function";s:4:"name";s:22:"Benchmark_Iterate::get";s:8:"explicit";b:1;}s:24:"class;Benchmark_Profiler";a:3:{s:4:"type";s:5:"class";s:4:"name";s:18:"Benchmark_Profiler";s:8:"explicit";b:1;}s:51:"function;Benchmark_Profiler::getSectionInformations";a:3:{s:4:"type";s:8:"function";s:4:"name";s:42:"Benchmark_Profiler::getSectionInformations";s:8:"explicit";b:1;}s:55:"function;Benchmark_Profiler::getAllSectionsInformations";a:3:{s:4:"type";s:8:"function";s:4:"name";s:46:"Benchmark_Profiler::getAllSectionsInformations";s:8:"explicit";b:1;}s:36:"function;Benchmark_Profiler::display";a:3:{s:4:"type";s:8:"function";s:4:"name";s:27:"Benchmark_Profiler::display";s:8:"explicit";b:1;}s:34:"function;Benchmark_Profiler::start";a:3:{s:4:"type";s:8:"function";s:4:"name";s:25:"Benchmark_Profiler::start";s:8:"explicit";b:1;}s:33:"function;Benchmark_Profiler::stop";a:3:{s:4:"type";s:8:"function";s:4:"name";s:24:"Benchmark_Profiler::stop";s:8:"explicit";b:1;}s:41:"function;Benchmark_Profiler::enterSection";a:3:{s:4:"type";s:8:"function";s:4:"name";s:32:"Benchmark_Profiler::enterSection";s:8:"explicit";b:1;}s:41:"function;Benchmark_Profiler::leaveSection";a:3:{s:4:"type";s:8:"function";s:4:"name";s:32:"Benchmark_Profiler::leaveSection";s:8:"explicit";b:1;}}s:8:"filelist";a:5:{s:21:"doc/timer_example.php";a:4:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:9:"Benchmark";s:6:"md5sum";s:32:"411bb0c4f48ca5578c2c520242ad70dc";s:12:"installed_as";s:81:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Benchmark/doc/timer_example.php";}s:9:"Timer.php";a:4:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:9:"Benchmark";s:6:"md5sum";s:32:"b4b2f23b25259a19352059e6d40f02bd";s:12:"installed_as";s:68:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Benchmark/Timer.php";}s:11:"Iterate.php";a:4:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:9:"Benchmark";s:6:"md5sum";s:32:"83c56d8513330dc5222196a3b3b2a9f9";s:12:"installed_as";s:70:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Benchmark/Iterate.php";}s:12:"Profiler.php";a:4:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:9:"Benchmark";s:6:"md5sum";s:32:"86ad787bd0dd5ad7146a7a402ad335d7";s:12:"installed_as";s:71:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Benchmark/Profiler.php";}s:7:"dirtree";a:2:{s:63:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Benchmark/doc";b:1;s:58:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Benchmark";b:1;}}s:7:"package";s:9:"Benchmark";s:7:"summary";s:53:"Framework to benchmark PHP scripts or function calls.";s:11:"description";s:53:"Framework to benchmark PHP scripts or function calls.";s:11:"maintainers";a:1:{i:0;a:4:{s:6:"handle";s:9:"sebastian";s:4:"name";s:18:"Sebastian Bergmann";s:5:"email";s:24:"[email protected]";s:4:"role";s:4:"lead";}}s:7:"version";s:5:"1.2.1";s:12:"release_date";s:10:"2003-05-20";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:201:"+ Profiler: Added calculation of netto execution time of sections.
+
++ Profiler: Optional output to an array instead of screen.
+
+ (Contributed by Jean-Marc Fontaine)
+
+* Profiler: Fixed typos.";s:9:"changelog";a:2:{i:0;a:4:{s:7:"version";s:3:"1.2";s:12:"release_date";s:10:"2002-09-25";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:70:"+ Added Profiler class.
+
+ (Contribution by Matthias Englert)
+
+
+";}i:1;a:4:{s:7:"version";s:3:"1.1";s:12:"release_date";s:10:"2002-01-26";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:384:"+ Added getOutput() to print the results in human readable
+ format and display() to show it.
+
+ This functions will automatically detect the environment
+ to select HTML or text output.
+
++ Added a new param $auto to constructor.
+
+ When true is passed, the results will be output
+ automatically at the end of the script execution.
+
+ (Contribution by Ludovico Magnocavallo)
+
+
+";}}s:13:"_lastmodified";i:1099429051;} \ No newline at end of file
diff --git a/pearlib/.registry/console_getargs.reg b/pearlib/.registry/console_getargs.reg
new file mode 100644
index 00000000..be3dc40c
--- /dev/null
+++ b/pearlib/.registry/console_getargs.reg
@@ -0,0 +1,18 @@
+a:14:{s:8:"provides";a:16:{s:21:"class;Console_Getargs";a:3:{s:4:"type";s:5:"class";s:4:"name";s:15:"Console_Getargs";s:8:"explicit";b:1;}s:29:"class;Console_Getargs_Options";a:3:{s:4:"type";s:5:"class";s:4:"name";s:23:"Console_Getargs_Options";s:8:"explicit";b:1;}s:33:"function;Console_Getargs::factory";a:3:{s:4:"type";s:8:"function";s:4:"name";s:24:"Console_Getargs::factory";s:8:"explicit";b:1;}s:33:"function;Console_Getargs::getHelp";a:3:{s:4:"type";s:8:"function";s:4:"name";s:24:"Console_Getargs::getHelp";s:8:"explicit";b:1;}s:45:"function;Console_Getargs::getOptionalRequired";a:3:{s:4:"type";s:8:"function";s:4:"name";s:36:"Console_Getargs::getOptionalRequired";s:8:"explicit";b:1;}s:38:"function;Console_Getargs_Options::init";a:3:{s:4:"type";s:8:"function";s:4:"name";s:29:"Console_Getargs_Options::init";s:8:"explicit";b:1;}s:43:"function;Console_Getargs_Options::buildMaps";a:3:{s:4:"type";s:8:"function";s:4:"name";s:34:"Console_Getargs_Options::buildMaps";s:8:"explicit";b:1;}s:43:"function;Console_Getargs_Options::parseArgs";a:3:{s:4:"type";s:8:"function";s:4:"name";s:34:"Console_Getargs_Options::parseArgs";s:8:"explicit";b:1;}s:42:"function;Console_Getargs_Options::parseArg";a:3:{s:4:"type";s:8:"function";s:4:"name";s:33:"Console_Getargs_Options::parseArg";s:8:"explicit";b:1;}s:42:"function;Console_Getargs_Options::setValue";a:3:{s:4:"type";s:8:"function";s:4:"name";s:33:"Console_Getargs_Options::setValue";s:8:"explicit";b:1;}s:41:"function;Console_Getargs_Options::isValue";a:3:{s:4:"type";s:8:"function";s:4:"name";s:32:"Console_Getargs_Options::isValue";s:8:"explicit";b:1;}s:45:"function;Console_Getargs_Options::updateValue";a:3:{s:4:"type";s:8:"function";s:4:"name";s:36:"Console_Getargs_Options::updateValue";s:8:"explicit";b:1;}s:45:"function;Console_Getargs_Options::setDefaults";a:3:{s:4:"type";s:8:"function";s:4:"name";s:36:"Console_Getargs_Options::setDefaults";s:8:"explicit";b:1;}s:43:"function;Console_Getargs_Options::isDefined";a:3:{s:4:"type";s:8:"function";s:4:"name";s:34:"Console_Getargs_Options::isDefined";s:8:"explicit";b:1;}s:45:"function;Console_Getargs_Options::getLongName";a:3:{s:4:"type";s:8:"function";s:4:"name";s:36:"Console_Getargs_Options::getLongName";s:8:"explicit";b:1;}s:42:"function;Console_Getargs_Options::getValue";a:3:{s:4:"type";s:8:"function";s:4:"name";s:33:"Console_Getargs_Options::getValue";s:8:"explicit";b:1;}}s:8:"filelist";a:4:{s:11:"Getargs.php";a:4:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:7:"Console";s:6:"md5sum";s:32:"a240bb25aa946393871c6b58e63616f7";s:12:"installed_as";s:68:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Console/Getargs.php";}s:20:"examples/example.php";a:4:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:7:"Console";s:6:"md5sum";s:32:"284d4447dfb29c645c83bcc3b812e6e4";s:12:"installed_as";s:86:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Console_Getargs/examples/example.php";}s:21:"examples/example2.php";a:4:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:7:"Console";s:6:"md5sum";s:32:"97ea32031a27fbba19f5628223e2b3b1";s:12:"installed_as";s:87:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Console_Getargs/examples/example2.php";}s:7:"dirtree";a:2:{s:56:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Console";b:1;s:74:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Console_Getargs/examples";b:1;}}s:7:"package";s:15:"Console_Getargs";s:7:"summary";s:31:"A command-line arguments parser";s:11:"description";s:295:"The Console_Getargs package implements a Command Line arguments
+parser that your CLI applications can use to parse arguments found in
+$_SERVER['argv']. It performs some basic arguments validation and is
+capable to return a formatted help text to the user, based on the
+configuration it is given.";s:11:"maintainers";a:3:{i:0;a:4:{s:6:"handle";s:7:"mansion";s:4:"name";s:16:"Bertrand Mansion";s:5:"email";s:20:"[email protected]";s:4:"role";s:4:"lead";}i:1;a:4:{s:6:"handle";s:13:"scottmattocks";s:4:"name";s:14:"Scott Mattocks";s:5:"email";s:21:"[email protected]";s:4:"role";s:4:"lead";}i:2;a:4:{s:6:"handle";s:4:"wenz";s:4:"name";s:14:"Christian Wenz";s:5:"email";s:12:"[email protected]";s:4:"role";s:9:"developer";}}s:7:"version";s:5:"1.2.0";s:12:"release_date";s:10:"2004-10-22";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:268:"Implement feature request #2428: Passing parameters on the command line
+Implement feature request #2445: Allow user defined argument lists
+Implement fix for bug #2486: Notice in parseArgs
+More detailed help message. Shows optional/required arguments.
+New example file.";s:12:"release_deps";a:1:{i:1;a:3:{s:4:"type";s:3:"php";s:3:"rel";s:2:"ge";s:7:"version";s:5:"4.1.0";}}s:9:"changelog";a:3:{i:0;a:4:{s:7:"version";s:5:"1.1.0";s:12:"release_date";s:10:"2004-09-30";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:66:"Implement feature request #2409: Parse args like '-dt'...
+
+
+";}i:1;a:4:{s:7:"version";s:5:"1.0.0";s:12:"release_date";s:10:"2004-09-20";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:97:"First stable release
+Fixed bug #1823 : The getHelp() method ignores user-defined header.
+
+
+";}i:2;a:4:{s:7:"version";s:5:"0.1.0";s:12:"release_date";s:10:"2004-05-16";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:17:"Initial release
+
+";}}s:13:"_lastmodified";i:1099255834;} \ No newline at end of file
diff --git a/pearlib/.registry/console_getopt.reg b/pearlib/.registry/console_getopt.reg
new file mode 100644
index 00000000..20b69f45
--- /dev/null
+++ b/pearlib/.registry/console_getopt.reg
@@ -0,0 +1,12 @@
+a:13:{s:8:"provides";a:5:{s:20:"class;Console_Getopt";a:3:{s:4:"type";s:5:"class";s:4:"name";s:14:"Console_Getopt";s:8:"explicit";b:1;}s:32:"function;Console_Getopt::getopt2";a:3:{s:4:"type";s:8:"function";s:4:"name";s:23:"Console_Getopt::getopt2";s:8:"explicit";b:1;}s:31:"function;Console_Getopt::getopt";a:3:{s:4:"type";s:8:"function";s:4:"name";s:22:"Console_Getopt::getopt";s:8:"explicit";b:1;}s:33:"function;Console_Getopt::doGetopt";a:3:{s:4:"type";s:8:"function";s:4:"name";s:24:"Console_Getopt::doGetopt";s:8:"explicit";b:1;}s:36:"function;Console_Getopt::readPHPArgv";a:3:{s:4:"type";s:8:"function";s:4:"name";s:27:"Console_Getopt::readPHPArgv";s:8:"explicit";b:1;}}s:8:"filelist";a:1:{s:18:"Console/Getopt.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"add0781a1cae0b3daf5e8521b8a954cc";s:12:"installed_as";s:67:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Console/Getopt.php";}}s:7:"package";s:14:"Console_Getopt";s:7:"summary";s:26:"Command-line option parser";s:11:"description";s:80:"This is a PHP implementation of "getopt" supporting both
+short and long options.";s:11:"maintainers";a:2:{i:0;a:4:{s:6:"handle";s:6:"andrei";s:4:"name";s:15:"Andrei Zmievski";s:5:"email";s:14:"[email protected]";s:4:"role";s:4:"lead";}i:1;a:4:{s:6:"handle";s:3:"ssb";s:4:"name";s:11:"Stig Bakken";s:5:"email";s:12:"[email protected]";s:4:"role";s:9:"developer";}}s:7:"version";s:3:"1.2";s:12:"release_date";s:10:"2003-12-11";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:69:"Fix to preserve BC with 1.0 and allow correct behaviour for new users";s:9:"changelog";a:4:{i:0;a:4:{s:7:"version";s:3:"1.0";s:12:"release_date";s:10:"2002-09-13";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:16:"Stable release
+
+";}i:1;a:4:{s:7:"version";s:4:"0.11";s:12:"release_date";s:10:"2002-05-26";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:96:"POSIX getopt compatibility fix: treat first element of args
+ array as command name
+
+
+";}i:2;a:4:{s:7:"version";s:4:"0.10";s:12:"release_date";s:10:"2002-05-12";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:15:"Packaging fix
+
+";}i:3;a:4:{s:7:"version";s:3:"0.9";s:12:"release_date";s:10:"2002-05-12";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:17:"Initial release
+
+";}}s:13:"_lastmodified";i:1078577418;} \ No newline at end of file
diff --git a/pearlib/.registry/html_common.reg b/pearlib/.registry/html_common.reg
new file mode 100644
index 00000000..e8c6f7ea
--- /dev/null
+++ b/pearlib/.registry/html_common.reg
@@ -0,0 +1,19 @@
+a:13:{s:8:"provides";a:15:{s:17:"class;HTML_Common";a:3:{s:4:"type";s:5:"class";s:4:"name";s:11:"HTML_Common";s:8:"explicit";b:1;}s:32:"function;HTML_Common::apiVersion";a:3:{s:4:"type";s:8:"function";s:4:"name";s:23:"HTML_Common::apiVersion";s:8:"explicit";b:1;}s:34:"function;HTML_Common::getAttribute";a:3:{s:4:"type";s:8:"function";s:4:"name";s:25:"HTML_Common::getAttribute";s:8:"explicit";b:1;}s:35:"function;HTML_Common::setAttributes";a:3:{s:4:"type";s:8:"function";s:4:"name";s:26:"HTML_Common::setAttributes";s:8:"explicit";b:1;}s:35:"function;HTML_Common::getAttributes";a:3:{s:4:"type";s:8:"function";s:4:"name";s:26:"HTML_Common::getAttributes";s:8:"explicit";b:1;}s:38:"function;HTML_Common::updateAttributes";a:3:{s:4:"type";s:8:"function";s:4:"name";s:29:"HTML_Common::updateAttributes";s:8:"explicit";b:1;}s:37:"function;HTML_Common::removeAttribute";a:3:{s:4:"type";s:8:"function";s:4:"name";s:28:"HTML_Common::removeAttribute";s:8:"explicit";b:1;}s:32:"function;HTML_Common::setLineEnd";a:3:{s:4:"type";s:8:"function";s:4:"name";s:23:"HTML_Common::setLineEnd";s:8:"explicit";b:1;}s:34:"function;HTML_Common::setTabOffset";a:3:{s:4:"type";s:8:"function";s:4:"name";s:25:"HTML_Common::setTabOffset";s:8:"explicit";b:1;}s:34:"function;HTML_Common::getTabOffset";a:3:{s:4:"type";s:8:"function";s:4:"name";s:25:"HTML_Common::getTabOffset";s:8:"explicit";b:1;}s:28:"function;HTML_Common::setTab";a:3:{s:4:"type";s:8:"function";s:4:"name";s:19:"HTML_Common::setTab";s:8:"explicit";b:1;}s:32:"function;HTML_Common::setComment";a:3:{s:4:"type";s:8:"function";s:4:"name";s:23:"HTML_Common::setComment";s:8:"explicit";b:1;}s:32:"function;HTML_Common::getComment";a:3:{s:4:"type";s:8:"function";s:4:"name";s:23:"HTML_Common::getComment";s:8:"explicit";b:1;}s:28:"function;HTML_Common::toHtml";a:3:{s:4:"type";s:8:"function";s:4:"name";s:19:"HTML_Common::toHtml";s:8:"explicit";b:1;}s:29:"function;HTML_Common::display";a:3:{s:4:"type";s:8:"function";s:4:"name";s:20:"HTML_Common::display";s:8:"explicit";b:1;}}s:8:"filelist";a:1:{s:10:"Common.php";a:4:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:6:"md5sum";s:32:"ba7cc89be8a2bf4d69fec74c09e2b65c";s:12:"installed_as";s:64:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/Common.php";}}s:7:"package";s:11:"HTML_Common";s:7:"summary";s:57:"PEAR::HTML_Common is a base class for other HTML classes.";s:11:"description";s:234:"The PEAR::HTML_Common package provides methods for html code display and attributes handling.
+* Methods to set, remove, update html attributes.
+* Handles comments in HTML code.
+* Handles layout, tabs, line endings for nicer HTML code.";s:11:"maintainers";a:2:{i:0;a:4:{s:6:"handle";s:7:"adaniel";s:4:"name";s:11:"Adam Daniel";s:5:"email";s:22:"[email protected]";s:4:"role";s:4:"lead";}i:1;a:4:{s:6:"handle";s:7:"mansion";s:4:"name";s:16:"Bertrand Mansion";s:5:"email";s:20:"[email protected]";s:4:"role";s:4:"lead";}}s:7:"version";s:5:"1.2.1";s:12:"release_date";s:10:"2003-06-19";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:82:"* Method getAttributes() can now return the attributes in a string (Alexey Borzov)";s:9:"changelog";a:3:{i:0;a:4:{s:7:"version";s:3:"1.2";s:12:"release_date";s:10:"2003-05-20";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:248:"Applied patch by Klaus Guenther:
+* setTab(): Allows the indent string (default: \t) to be set
+* setLineEnd(): Allows the line end string (default: \n) to be set
+* Allows the indent and line ending settings to be propagated to content objects
+
+
+";}i:1;a:4:{s:7:"version";s:3:"1.1";s:12:"release_date";s:10:"2003-04-15";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:336:"This new release optimizes HTML_Common's way to deal with attributes.
+* Fix a bug in _parseAttributes when a string was passed to it (Alexey Borzov)
+* Only key-value associative arrays are now used, XHTML compliant (Alexey Borzov)
+* Fix a bug related to getAttribute case sensitivity detected by Ian Harder #22628 (Alexey Borzov)
+
+
+";}i:2;a:4:{s:7:"version";s:3:"1.0";s:12:"release_date";s:10:"2002-05-28";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:21:"First release.
+
+
+";}}s:13:"_lastmodified";i:1078577490;} \ No newline at end of file
diff --git a/pearlib/.registry/html_quickform.reg b/pearlib/.registry/html_quickform.reg
new file mode 100644
index 00000000..79d840cb
--- /dev/null
+++ b/pearlib/.registry/html_quickform.reg
@@ -0,0 +1,499 @@
+a:14:{s:8:"provides";a:0:{}s:8:"filelist";a:72:{s:13:"QuickForm.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:67:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm.php";}s:25:"QuickForm/advcheckbox.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:79:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/advcheckbox.php";}s:26:"QuickForm/autocomplete.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:80:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/autocomplete.php";}s:20:"QuickForm/button.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:74:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/button.php";}s:22:"QuickForm/checkbox.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:76:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/checkbox.php";}s:18:"QuickForm/date.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:72:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/date.php";}s:21:"QuickForm/element.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:75:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/element.php";}s:18:"QuickForm/file.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:72:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/file.php";}s:19:"QuickForm/group.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:73:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/group.php";}s:20:"QuickForm/header.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:74:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/header.php";}s:20:"QuickForm/hidden.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:74:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/hidden.php";}s:26:"QuickForm/hiddenselect.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:80:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/hiddenselect.php";}s:24:"QuickForm/hierselect.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:78:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/hierselect.php";}s:18:"QuickForm/html.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:72:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/html.php";}s:19:"QuickForm/image.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:73:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/image.php";}s:19:"QuickForm/input.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:73:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/input.php";}s:18:"QuickForm/link.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:72:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/link.php";}s:22:"QuickForm/password.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:76:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/password.php";}s:19:"QuickForm/radio.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:73:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/radio.php";}s:19:"QuickForm/reset.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:73:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/reset.php";}s:20:"QuickForm/select.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:74:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/select.php";}s:20:"QuickForm/static.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:74:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/static.php";}s:20:"QuickForm/submit.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:74:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/submit.php";}s:18:"QuickForm/text.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:72:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/text.php";}s:22:"QuickForm/textarea.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:76:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/textarea.php";}s:21:"QuickForm/xbutton.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:75:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/xbutton.php";}s:22:"QuickForm/Renderer.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:76:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Renderer.php";}s:18:"QuickForm/Rule.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:72:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Rule.php";}s:26:"QuickForm/RuleRegistry.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:80:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/RuleRegistry.php";}s:28:"QuickForm/Renderer/Array.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:82:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Renderer/Array.php";}s:34:"QuickForm/Renderer/ArraySmarty.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:88:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Renderer/ArraySmarty.php";}s:30:"QuickForm/Renderer/Default.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:84:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Renderer/Default.php";}s:32:"QuickForm/Renderer/ITDynamic.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:86:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Renderer/ITDynamic.php";}s:31:"QuickForm/Renderer/ITStatic.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:85:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Renderer/ITStatic.php";}s:29:"QuickForm/Renderer/Object.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:83:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Renderer/Object.php";}s:34:"QuickForm/Renderer/ObjectFlexy.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:88:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Renderer/ObjectFlexy.php";}s:32:"QuickForm/Renderer/QuickHtml.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:86:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Renderer/QuickHtml.php";}s:27:"QuickForm/Rule/Callback.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:81:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Rule/Callback.php";}s:26:"QuickForm/Rule/Compare.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:80:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Rule/Compare.php";}s:24:"QuickForm/Rule/Email.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:78:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Rule/Email.php";}s:24:"QuickForm/Rule/Range.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:78:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Rule/Range.php";}s:24:"QuickForm/Rule/Regex.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:78:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Rule/Regex.php";}s:27:"QuickForm/Rule/Required.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:81:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Rule/Required.php";}s:17:"docs/elements.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:82:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/elements.php";}s:16:"docs/filters.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:81:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/filters.php";}s:17:"docs/formrule.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:82:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/formrule.php";}s:15:"docs/groups.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:80:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/groups.php";}s:22:"docs/rules-builtin.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:87:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/rules-builtin.php";}s:21:"docs/rules-custom.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:86:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/rules-custom.php";}s:39:"docs/renderers/FlexyDynamic_example.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:104:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers/FlexyDynamic_example.php";}s:38:"docs/renderers/FlexyStatic_example.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:103:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers/FlexyStatic_example.php";}s:36:"docs/renderers/ITDynamic_example.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:101:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers/ITDynamic_example.php";}s:37:"docs/renderers/ITDynamic_example2.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:102:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers/ITDynamic_example2.php";}s:35:"docs/renderers/ITStatic_example.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:100:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers/ITStatic_example.php";}s:36:"docs/renderers/QuickHtml_example.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:101:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers/QuickHtml_example.php";}s:40:"docs/renderers/SmartyDynamic_example.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:105:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers/SmartyDynamic_example.php";}s:39:"docs/renderers/SmartyStatic_example.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:104:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers/SmartyStatic_example.php";}s:34:"docs/renderers/multiple-labels.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:99:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers/multiple-labels.php";}s:42:"docs/renderers/templates/it-dynamic-2.html";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:107:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers/templates/it-dynamic-2.html";}s:40:"docs/renderers/templates/it-dynamic.html";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:105:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers/templates/it-dynamic.html";}s:39:"docs/renderers/templates/it-static.html";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:104:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers/templates/it-static.html";}s:54:"docs/renderers/templates/smarty-dynamic-fancygroup.tpl";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:119:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers/templates/smarty-dynamic-fancygroup.tpl";}s:49:"docs/renderers/templates/smarty-dynamic-green.tpl";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:114:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers/templates/smarty-dynamic-green.tpl";}s:43:"docs/renderers/templates/smarty-dynamic.tpl";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:108:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers/templates/smarty-dynamic.tpl";}s:42:"docs/renderers/templates/smarty-static.tpl";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:107:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers/templates/smarty-static.tpl";}s:43:"docs/renderers/templates/flexy-dynamic.html";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:108:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers/templates/flexy-dynamic.html";}s:42:"docs/renderers/templates/flexy-static.html";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:107:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers/templates/flexy-static.html";}s:34:"docs/renderers/templates/html.html";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:99:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers/templates/html.html";}s:35:"docs/renderers/templates/label.html";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:100:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers/templates/label.html";}s:42:"docs/renderers/templates/styles/green.html";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:107:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers/templates/styles/green.html";}s:47:"docs/renderers/templates/styles/fancygroup.html";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTML";s:12:"installed_as";s:112:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers/templates/styles/fancygroup.html";}s:7:"dirtree";a:8:{s:53:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML";b:1;s:63:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm";b:1;s:72:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Renderer";b:1;s:68:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Rule";b:1;s:69:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs";b:1;s:79:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers";b:1;s:89:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers/templates";b:1;s:96:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm/docs/renderers/templates/styles";b:1;}}s:7:"package";s:14:"HTML_QuickForm";s:7:"summary";s:98:"The PEAR::HTML_QuickForm package provides methods for creating, validating, processing HTML forms.";s:11:"description";s:545:"The HTML_QuickForm package provides methods for dynamically create, validate and render HTML forms.
+
+Features:
+* More than 20 ready-to-use form elements.
+* XHTML compliant generated code.
+* Numerous mixable and extendable validation rules.
+* Automatic server-side validation and filtering.
+* On request javascript code generation for client-side validation.
+* File uploads support.
+* Total customization of form rendering.
+* Support for external template engines (ITX, Sigma, Flexy, Smarty).
+* Pluggable elements, rules and renderers extensions.";s:11:"maintainers";a:5:{i:0;a:4:{s:6:"handle";s:7:"mansion";s:4:"name";s:16:"Bertrand Mansion";s:5:"email";s:20:"[email protected]";s:4:"role";s:4:"lead";}i:1;a:4:{s:6:"handle";s:7:"adaniel";s:4:"name";s:11:"Adam Daniel";s:5:"email";s:22:"[email protected]";s:4:"role";s:4:"lead";}i:2;a:4:{s:6:"handle";s:3:"avb";s:4:"name";s:13:"Alexey Borzov";s:5:"email";s:18:"[email protected]";s:4:"role";s:4:"lead";}i:3;a:4:{s:6:"handle";s:5:"jrust";s:4:"name";s:10:"Jason Rust";s:5:"email";s:20:"[email protected]";s:4:"role";s:9:"developer";}i:4;a:4:{s:6:"handle";s:3:"ths";s:4:"name";s:13:"Thomas Schulz";s:5:"email";s:16:"[email protected]";s:4:"role";s:9:"developer";}}s:7:"version";s:8:"3.2.4pl1";s:12:"release_date";s:10:"2004-10-20";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:431:"- Fixed #2576: calling HTML_QuickForm::getSubmitValue() on a group
+ of radio elements caused an infinite loop with PHP eventually
+ dying of memory exhaustion. This was introduced in 3.2.4 in a
+ fix for bug #2517, those upgrading from earlier releases should
+ skip 3.2.4, those running 3.2.4 should upgrade ASAP.
+
+- Fixed #2571: selects after the second were not correctly
+ populated in hierselect with 3 or more elements.";s:12:"release_deps";a:2:{i:1;a:3:{s:4:"type";s:3:"php";s:3:"rel";s:2:"ge";s:7:"version";s:3:"4.2";}i:2;a:4:{s:4:"type";s:3:"pkg";s:3:"rel";s:2:"ge";s:7:"version";s:5:"1.2.1";s:4:"name";s:11:"HTML_Common";}}s:9:"changelog";a:24:{i:0;a:5:{s:7:"version";s:5:"3.2.4";s:12:"release_date";s:10:"2004-10-18";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:1620:"* Additions and changes
+- Implemented HTML_QuickForm::insertElementBefore() method which
+ allows adding elements into arbitrary places and reordering the
+ existing form elements (thanks to Justin Patrin for initial
+ implementation and helpful comments, see request #1587)
+- Implemented HTML_QuickForm_element::unfreeze() method
+- HTML_QuickForm::removeElement() now returns reference to element
+ being removed
+- Array renderer outputs header name into the resultant array
+ (request #2276)
+- Added $staticLabels option to Array renderer's constructor, this
+ makes the renderer "flatten" the array of labels
+- Added Ukrainian translation for 'date' element (Andrey Kornilov)
+- Removed newline after <script> tags in 'autocomplete' and
+ 'hierselect' elements (request #2373)
+
+* Bugfixes
+- HTML_QuickForm::getSubmitValue() now properly returns null for
+ groups if form was not yet submitted (bug #2517)
+- stripslashes() is used only on 'name' element of file upload
+ data (bug #1843)
+- HTML_QuickForm::addGroupRule() now works better with <select
+ multiple> elements (bug #1810)
+- Fixed notice if empty string was passed to date::setValue()
+ (bug #2530)
+- Calling HTML_QuickForm_group::getFrozenHtml() no longer freezes
+ the group (bug #1836)
+- Groups now correctly handle setPersistantFreeze() call (bug #1836)
+- ITDynamic renderer does not replace empty separator with &nbsp;
+ anymore (bug #2354)
+- 'maxfilesize' rule returns false in case of file upload errors
+ (bug #2203)
+- Fixed Javascript generation for 'hierselect' elements (bug #1984)
+- Fixed examples using 'rangelength' rule (bug #2421)
+
+
+";}i:1;a:5:{s:7:"version";s:5:"3.2.3";s:12:"release_date";s:10:"2004-06-28";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:1511:"* Additions and changes
+- Implemented a new 'xbutton' element for HTML 4.0 <button> tag
+ (see also #1227)
+- HTML_QuickForm::toArray() method accepts a boolean value that is
+ passed to the constructor of HTML_QuickForm_Renderer_Array (see #1392)
+- ITDynamic renderer is now able to select a block for rendering based
+ on the element's name (see #1506)
+- Default template of Default Renderer now allows XHTML-compliant output
+ of hidden elements (see #1124)
+- Added Esperanto translation and French accents for date element
+ (Sebastien Montagne)
+- Implemented #553: ITStatic renderer can use special blocks for required
+ elements and errors, if present in the template
+- Implemented #1576: Enclose js code between //<![CDATA[ and //]]>
+ to make it xhtml compliant
+
+* Bugfixes
+- Fixed #1235 (setChecked() and setValue() did not work on advcheckbox)
+- Fixed #1389 (Invalid comments in autocomplete element's Javascript)
+- Fixed #1143 (Client validation should be turned off for frozen elements)
+- Fixed #1252 (File upload names were not cleaned when magic_quotes_gpc = On)
+- Fixed notices raised by hierselect element (reported by Herim Vasquez)
+- Fixed #1248 (Errors in hierselect's Javascript when element's name
+ begins with a number)
+- Fixed #1577 (hierselect's Javascript works with multiselects)
+- Fixed #1516 (Default Renderer should use separators even when
+ setGroupTemplate() is used)
+- Fixed #1694 (ITStatic renders hidden element with brackets correctly,
+ patch provided by milamamu)
+
+
+";}i:2;a:5:{s:7:"version";s:5:"3.2.2";s:12:"release_date";s:10:"2004-03-22";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:2382:"* On XHTML compliance:
+As of 3.2.2 QuickForm does not rely on presence of 'name' attribute
+in form tag (which is invalid in Strict XHTML), although it does
+set this attribute. If you desire Strict XHTML compliance, you can
+remove the attribute via removeAttribute() method.
+
+* On Flexy renderers:
+The Flexy renderers currently lack a maintainer. If one is not
+found, they will be marked as unsupported and may later be removed
+from the package. If you wish to look after these renderers, please
+contact the package maintainers.
+
+* Additions and changes
+- Added arrayMerge() method to HTML_QuickForm, this behaves like
+ builtin array_merge_recursive() but does not renumber numeric
+ array keys. The new method is now used throughout the QuickForm
+ instead of array_merge_recursive()
+- Added setDatasource() method
+- Implemented #552 (ITStatic renderer can group hidden fields in
+ output)
+- Added Estonian translation for date element (Ants Aasma)
+- Added Turkish translation for date element (Suat Imam-Oglu)
+- Added Norwegian translation for date element (Espen Carlsen)
+- All non-ASCII symbols in date translations are now encoded as
+ HTML Unicode entities to prevent encoding problems
+- Renamed labels-default.php example to multiple-labels.php
+ (suggested by Ignatius Reilly)
+- Date element accepts non numeric default values compatible with
+ function strtotime() (Kellan Elliot-Mccrea)
+- Added possibility to have more than just two selects in
+ the hierselect element (Herim Vasquez)
+
+
+* Bugfixes
+- Fixed #735 (applyFilter() failed for elements with "complex" names and
+ grouped elements)
+- Fixed #872 (possible get_class()-related issues in PHP5)
+- Fixed #925 (XHTML compliance, see the note)
+- Fixed #741 (frozen elements lost 'id' attribute)
+- Final fix for #202 (ArraySmarty renderer broke on elements with
+ brackets in names)
+- Fixed #645 (ArraySmarty renderer had problems with anonymous elements)
+- Fixed #734 (ArraySmarty renderer did not honour the change of Smarty
+ delimiters)
+- Fixed #745 (wrong removal of group name from elements)
+- Fixed #212 (ObjectFlexy renderer broke on elements with brackets in names)
+- Fixed #1032 (ITStatic used wrong placeholders for groups with
+ $appendName = false)
+- updateElementAttr() works correctly if passed an array of actual element
+ objects, also handles the case of multiple elements with the same name.
+
+
+";}i:3;a:5:{s:7:"version";s:5:"3.2.1";s:12:"release_date";s:10:"2004-01-03";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:1166:"* Additions and changes
+- The usage examples were significantly redone to better reflect current
+ package functionality.
+- Added Armenian translation for date element (Vahe Khachikyan)
+- Added Dutch translation for date element (Michiel Rook)
+- validate() throws a PEAR error if rule added via addFormRule() returns junk
+ (see also bugs #243 and #338)
+
+* Bugfixes
+- Fixed bug #351 (incorrect values set for groups with weird names)
+- Required elements in groups are once again marked correctly
+- Fixed setValue() method of date element (thanks to Orya and Mike Carter,
+ see also bug #388)
+- Fixed bug #403 (typo in Rule_Callback::getValidationScript())
+- Fixed bug #356 (javascript generation for groups with $appendName = false)
+- Fixed bug #328 (js values in hierselect not escaped properly)
+- Fixed bug #302 (better support for RFC 2821 and RFC 2822 in Rule_Email)
+- Fixed bug #253 (ArraySmarty renderer broken with radiobuttons)
+- Partial fix for bug #202 (ArraySmarty renderer broke on elements with
+ brackets in names)
+- Fixed bug #493 (non-existant method in setConstants())
+- Fixed bug #209 (Flexy renderer broken with groups having '_' in names)
+
+
+";}i:4;a:5:{s:7:"version";s:3:"3.2";s:12:"release_date";s:10:"2003-11-05";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:1973:"* Major changes in this release
+Refactored form validation using Strategy/Factory/Singleton patterns. Rules are now
+processed by subclasses of HTML_QuickForm_Rule class, HTML_QuickForm_RuleRegistry manages
+registering the rules and instantiating the rule classes. Backwards compatibility was
+completely preserved. (Bertrand Mansion)
+- Added ability to set a rule on several elements. Added 'compare' rule that compares the
+ values of two form elements to take advantage of this. (Alexey Borzov)
+- New 'callback' rule and an improved 'email' rule.
+- Function-based rules can now be used without registering.
+- Added 2 new examples for built-in and custom rules. (Alexey Borzov)
+- Javascript validation opportunities were enhanced, validation of groups, radios, checkboxes,
+ selects now works correctly (see also PEAR bug #184) (Alexey Borzov, Hodicska Gergely)
+
+* Other additions and changes
+- Added 'autocomplete' element. (Matteo Di Giovinazzo)
+- Added possibility to check whether form was actually submitted (see also bug #185). (Alexey Borzov)
+- QuickForm now correctly handles 'file' elements with [] in name, 'file' elements inside groups. (Alexey Borzov)
+- 'date' element will display year select in reverse order if 'minYear' > 'maxYear'. (Alexey Borzov)
+- Added possibility to set increment value for some 'date' element selects. (Alexey Borzov)
+- Added 'y' format to 'date' element. (Bertrand Mansion)
+
+* Migration to 3.2
+- Features that were marked deprecated in previous releases are REMOVED
+- 'emailorblank' rule was REMOVED, use 'email' rule (see also PEAR bug #34)
+
+* Bugfixes
+- Fixed PEAR bug #140 (empty label tags for checkboxes and radios)
+- Fixed PEAR bug #175 (error in 'date' element's German translation)
+- Fixed PEAR bug #70 (Flexy renderer didn't handle multiple labels for element)
+- Fixed PEAR bug #152 (JS function for 'hierselect' element not printed)
+- ArraySmarty renderer correctly processes groups with '_' in names (Ian Duggan)
+
+
+";}i:5;a:5:{s:7:"version";s:5:"3.1.1";s:12:"release_date";s:10:"2003-09-09";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:733:"* New additions
+- Added Flexy renderer with examples (Ron McClain)
+- Added QuickHtml renderer with example (Jason Rust)
+- Added possibility to add customized empty options on top of date element selects (Jason Rust)
+- Added Czech translation for date element (Tomas Zavodny)
+
+* Bugfixes
+- Fixed a bug in select element found by Pascal Thuet (Bertrand Mansion)
+- Fixed setting hierselect's values, reported by Keith Edmunds (Bertrand Mansion)
+- Fixed validate() not checking for presence of _formRules, reported by Justin Hendrickson (Alexey Borzov)
+- Fixed javascript for advcheckbox element (Jason Rust)
+
+Deprecated methods will be kept for 3.1.x releases, but will be removed in the next
+major one. Please update your code!
+
+
+";}i:6;a:5:{s:7:"version";s:3:"3.1";s:12:"release_date";s:10:"2003-07-19";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:2202:"* New additions
+- Added a new hierselect element, a group of two selects where the first select changes the options of the second select using javascript (Bertrand Mansion)
+- Added exportValue() and exportValues() methods, these should now be the preferred way to get form values (Alexey Borzov)
+- Added addFormRule() method, this adds the rule that validates the whole form (Alexey Borzov)
+- Added Italian date/dategroup names (Marco Piarulli)
+- Added Slovak date/dategroup names (Peter Sablica)
+- Added Icelandic date/dategroup names (Birgir Stefansson)
+
+* Changes
+- Rule-registering functions can now be called statically (Alexey Borzov)
+- File-related rules and methods are moved to HTML_QuickForm_file (Alexey Borzov)
+- Moved 'magic quotes' handling to QuickForm's constructor (Alexey Borzov)
+- Use strtr() instead of rawurlencode() for javascript messages (Bertrand Mansion)
+- Changed the nonzero rule to every numbers (negative or positive) except zero (David Sklar)
+- Date element has been entirely reworked and optimised (Alexey Borzov)
+- Updated the examples to use the latest changes (Alexey Borzov, Bertrand Mansion)
+
+* Bugfixes
+- Fixed a bug in HTML_QuickForm_group::getFrozenHtml() (Alexey Borzov, Wolfram Kriesing)
+- Fixed a problem in HTML_QuickForm_group::getValue(): value was not changed to array (Alexey Borzov)
+- Correctly set element's index in addElement() if some of the elements were previously removed (Alexey Borzov, Wolfram Kriesing)
+- Fixed notice in _findElementValue() for files (Bertrand Mansion)
+- Fixed passing a 'selected' attribute to addOption() (Alexey Borzov, Wolfram Kriesing)
+
+* Deprecated
+- setFormat(), setMinYear() and other accessors in date element are now deprecated. Set options and attributes in the constructor instead (Alexey Borzov)
+- Method getAttributesString() is deprecated (Alexey Borzov)
+- HTML_QuickForm's isUploadedFile(), moveUploadedFile(), getUploadeFile() are now deprecated (Alexey Borzov)
+
+Deprecated methods will be removed from QuickForm in the next release.
+In this release, they throw PEAR errors but still finish the job.
+Use this to catch them with an error handler function, the global error code is QUICKFORM_DEPRECATED.
+
+
+";}i:7;a:5:{s:7:"version";s:3:"3.0";s:12:"release_date";s:10:"2003-06-04";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:233:"* Fixes
+
+- do not use submit values for 'submit' elements also (reported by Thomas Schulz, Mike Carter)
+- assorted small fixes to HTML_QuickForm's methods and PHPDoc documentation
+- HTML_QuickForm::apiVersion() returns 3.0 now
+
+
+";}i:8;a:5:{s:7:"version";s:6:"3.0RC1";s:12:"release_date";s:10:"2003-05-28";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:664:"* Bugfixes
+
+- setMaxfileSize() does not throw pear error anymore (Alexey Borzov)
+- onQuickFormEvent 'updateValue' fixes, elements that cannot actually have a submit value do not try to use one (Alexey Borzov)
+- _loadElement() now returns a reference (Alexey Borzov)
+- Several fixes to usage examples (Alexey Borzov, Thomas Schulz, Joerg Bruckmann)
+
+* Changes
+
+- Added time to the date element (Jason Rust)
+- Required groups now show the asterisk (Bertrand Mansion)
+- Optimizations in HTML_QuickForm_group's methods (Alexey Borzov)
+- ArraySmarty renderer now requires a Smarty object and uses Smarty templates for 'required' and errors output (Thomas Schulz)
+
+
+";}i:9;a:5:{s:7:"version";s:8:"3.0Beta2";s:12:"release_date";s:10:"2003-05-13";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:1266:"- PHP 4.2+ is now required for file uploads
+
+* Bugfixes
+
+- Fixed bug with selected values in select element
+- Fixed bug when form method was changed from get to post by adding a file element
+- addRule() and addGroupRule() now return a PEAR error when the rule is not registered
+- <label> tags are no more generated for frozen checkboxes and radios
+
+* Changes
+
+- Added new Array-based renderer for Smarty, with examples (Thomas Schulz)
+- Array renderer generates more info on the form, possibility to set styles for elements (Thomas Schulz)
+- Example of usage for Array renderer with Smarty (Thomas Schulz)
+- Better XHTML compliance for Default renderer output (Klaus Guenther, Alexey Borzov)
+- Added possibility to have multiple labels for one element (Jon Wood)
+- Added support for multiple labels in ITStatic and ITDynamic renderers (Jon Wood, Alexey, Bertrand)
+
+- Added client-side validation parameter to addGroupRule() (Bertrand Mansion)
+
+- getSubmitValues() can now return uploaded files (Markus Wolff, Alexey Borzov)
+- Added new methods set/getMaxFileSize (Tommy Ipsen)
+
+- Removed unwanted linefeeds in frozen checkboxes (Thomas Schulz)
+- Added Spanish translation for date element (The PhP_KiD)
+- Added a setValue() method to static element (Jon Wood)
+
+
+";}i:10;a:5:{s:7:"version";s:8:"3.0Beta1";s:12:"release_date";s:10:"2003-04-17";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:3974:"* Major changes in this release
+
+- Renderers
+ QuickForm now uses external renderers to output the form. Currently
+ available renderers are: Default - based on old toHtml() code,
+ Array - based on old toArray() code, ITStatic and ITDynamic - these work
+ through an ITX/Sigma template engine. More renderers are expected to follow.
+- Validation
+ Validation rules are tested in the order they are set. Groups can
+ now be validated with custom functions. File validation only happens
+ if a file is required. It is also tested if the file has been uploaded.
+- Static elements
+ Headers and Data (html) elements are treated like other form elements.
+ Headers can have a name, this makes it possible to use different templates
+ for different headers.
+- XHTML
+ HTML_Common and QuickForm have been reworked to be more XHTML compliant.
+
+* Migration to 3.0
+
+Incompatibilities:
+
+- Filters registration is not used anymore. Just give applyFilter() the function name as a parameter.
+- toArray() now returns a different structure and calls an external renderer: elements in groups are rendered as seperate elements, every element has a numeric index for key.
+- Validation rules are validated in the order they were set.
+- File validation happens only when the file is required.
+- Direct calls to onQuickFormEvent() with 'setDefault' and 'setConstant' events won't work anymore, these events have been replaced by an 'updateValue' event.
+- Grouped elements index is now a numeric key only. This can break some group rules if you used their associative key in addGroupRule().
+- Groups must have a name. If you don't want the name to be appended to the elements names, set the appendName parameter to false.
+
+Deprecated features:
+- addHeader() and addData(), while still there, are deprecated. Use addElement('header') and addElement('html').
+- If you want to customize form output, do so by using a renderer object. setElementTemplate() and the like are now deprecated.
+
+* Changes
+
+- Added support for external renderers, added a default and an array renderer based on old QuickForm code (Alexey Borzov)
+- Added a dynamic ITX/Sigma renderer with examples (Alexey Borzov)
+- Added a static ITX/Sigma renderer with examples (Bertrand Mansion)
+
+- Added possibility to validate a group with a function using addRule() (Bertrand Mansion)
+- Added possibility to validate groups when appendName is false (Bertrand Mansion)
+- Rules are validated in the order they are set (Bertrand Mansion)
+- Fixed problems when validating a file not uploaded (Frederic Poeydomenge, Bertrand Mansion)
+
+- addHeader() and addData() now call addElement(), added new 'header' and 'html' elements (Alexey Borzov)
+- Got rid of registering filters, now only function existence is checked. First parameter to applyFilter can be an array (Alexey Borzov)
+- Automatically generate a name for group element when no name given. Added possibility to append the group name or not to grouped elements (Alexey Borzov)
+- process() now accepts a callback function to process the form (Bertrand Mansion)
+
+- Added XHTML compliant labels in radio and checkboxes (Alexey Borzov, Alexander Radivanovich)
+- XHTML for form tag (Bertrand Mansion)
+
+- Elements values are now set through a uniform 'updateValue' event (Alexey Borzov)
+- Elements with value 0 are not considered empty when validating (Bertrand Mansion)
+- Fix warning with uploaded files (Frederic Poeydomenge)
+- Changed _findElementValue to work with appendName (Bertrand Mansion)
+
+- Added Polish date names in date element (Robert Janeczek)
+- Added Hungarian date names in date element (Kovacs Gergely)
+- Added Russian date names in date element (Alexey Borzov)
+
+- Optimized select element addOption() and toHtml() methods (Alexey Borzov)
+- Added method getElementName() in group element (Bertrand Mansion)
+- Fix bug when setting attributes in date element (Bertrand Mansion)
+- Removed method display(), exists in parent class (Bertrand Mansion)
+
+
+";}i:11;a:5:{s:7:"version";s:4:"2.10";s:12:"release_date";s:10:"2003-03-07";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:960:"* Email validation regex accepts lower and uppercases (Derick Rethans).
+* Added getUploadedFile() and isUploadedFile() methods (Wotjek Gdela).
+* XHTML compliant for the form tags (Jules Szemere).
+* Fixed Javascript validation when empty elements are not required (Joern Barthel).
+* Added possibility to call setDefaults() and setConstants() anytime.
+* Fix problem with duplicate element names (ex: radio).
+* Added method updateElementAttr() to update the attributes of multiple elements (Dan Wilson).
+* Slovanian translation of date in date element (Matej Dakol).
+* Fix bug in date element preventing submitted values to be used.
+* Removed nbsp; replace in frozen textarea (Carsten Bleek, Dan Wilson).
+
+This is probably the last release of QuickForm without template support.
+The next release will have two BC breaks:
+- Use of filters will be easier, no registration needed.
+- Groups with no name will not be supported anymore, even if name is not used.
+
+
+";}i:12;a:5:{s:7:"version";s:3:"2.9";s:12:"release_date";s:10:"2003-02-20";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:444:"* Added htmlspecialchars() to frozen elements output in every case.
+* Elements are not validated against a function if they are empty and not required.
+ It is easier this way for QuickForm to work with external validators.
+ This is already the way it is done when using regex validation.
+* Added javascript validation for select multiple and checkboxes.
+* Added simple javascript validation for groups: values are imploded in a string.
+
+
+";}i:13;a:5:{s:7:"version";s:3:"2.8";s:12:"release_date";s:10:"2003-02-18";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:349:"* IMPORTANT: favour the use of addGroup() instead of addElementGroup().
+* Fix bug when validating grouped elements.
+* Removed useless call to toHtml() in toArray(), was disturbing the date element.
+* Fix bug with select multiple for display and validation.
+* Textarea element now uses nl2br() instead of pre tags so it won't mess your layout.
+
+
+";}i:14;a:5:{s:7:"version";s:3:"2.7";s:12:"release_date";s:10:"2003-02-12";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:982:"* Use moveUploadedFile() (PHP 4.1) for file uploads instead of copy() and unlink(). (Matthias Englert)
+* Added support for element names with brackets ie. element[name][firstname].
+* Added static element to add static text in form. (Wojciech Gdela)
+* Removed extra tabs created when wrapping form elements (Tim Glen)
+* Added validation for whole group with addRule('group') in complement to validation for group elements with addGroupRule('group').
+* Added ability to use a method from a class or from an object for validation using registerRule().
+* Fixed bug with radio button validation. (Jack Kline)
+* Fixed bug when validating checkboxes named with backets ie. checkbox[A]
+* Fixed a problem with stripslashes() in element.php
+* Fixed a bug in hidden.php preventing its value to be set under some conditions. (Arne Gellhaus)
+* In progress : fix bug with toArray() for the date element, use a layout object (templates), implement function validation for elements in group.
+
+
+";}i:15;a:5:{s:7:"version";s:3:"2.6";s:12:"release_date";s:10:"2003-01-29";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:398:"* IMPORTANT: addElement now returns a reference to the added element.
+* Added new date element to create formatted date selects.
+* Added example for the new date element in QuickForm_Example.php
+* Fixed a warning in checkbox element
+* Default values are always overidden by submitted values.
+* Email validation rules accept 4 chars extensions (.info, .name, ...)
+* Fixed bug in applyFilter()
+
+
+";}i:16;a:5:{s:7:"version";s:3:"2.5";s:12:"release_date";s:10:"2002-11-20";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:688:"* Refine the use of setDefaults and setContants.
+setDefaults is used to fill the form with defaults values that will be overriden by submitted values once the form is submitted.
+setConstants is used to overide default and submitted values for example if form is not valid when submitted. In this case, constant values will be shown again in the field.
+In all cases, values won't be overriden by the other method anymore.
+* Added grouped elements validation on the server-side.
+* Added grouped elements customization through templates.
+* Fixes in rules for file uploads on maxsize, mime... when element is not required.
+* Added a new example for groups customization and validation.
+
+
+";}i:17;a:5:{s:7:"version";s:3:"2.4";s:12:"release_date";s:10:"2002-10-21";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:1047:"* Use $_POST, $_GET and $_FILES instead of $HTTP_.
+* Better error checking in renderElement().
+* Fixed freeze() for removed elements.
+* Added new element 'hiddenselect'.
+* Added optional $in_data parameter to method toHtml so that data can be added to the form right before it is rendered. Useful when using with a template system.
+* Changed the way the key is obtained for _elementIndex in addElement() to accomodate with removeElement().
+* Some changes to _buildRules(): Made some of the javascript variables private which did not need have a global scope (such as frm).
+* Added new rule: emailorblank.
+* Fixes in HTML_QuickForm_Element.
+* Fixes in QuickForm_example2.php.
+* Added getElements() in HTML_QuickForm_Group which returns an array of all elements in group by reference.
+* Added getGroupType() in HTML_QuickForm_Group which returns the type of group as string or 'mixed' if group is composed of different element types.
+* Fixes in file upload validation rules.
+* Added a few new examples for filters, rules, file uploads usage.
+
+
+";}i:18;a:5:{s:7:"version";s:3:"2.3";s:12:"release_date";s:10:"2002-06-19";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:702:"* Important API change in the way select elements load arrays.
+* Added the possibility to set a template for an element only.
+* Fixes in the creation of select elements with multiple attribute.
+* Added consistency in the magic_quotes handling.
+* Added recursive filters on setDefaults an setConstants values.
+* Added getSubmitValue (same as getSubmitValues but for one value only).
+* Removed HTML_QuickForm_date element: it needs more work.
+* Added the HTML_QuickForm_advcheckbox element in package and in registered elements.
+* Added one example for the HTML_QuickForm_advcheckbox element.
+* Added setPersistantFreeze accessor method in element.php.
+* Fixed problems with password field values.
+
+
+";}i:19;a:5:{s:7:"version";s:3:"2.2";s:12:"release_date";s:10:"2002-05-29";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:71:"* Some fixes for file uploads.
+* Some fixes for hidden fields.
+
+
+";}i:20;a:5:{s:7:"version";s:3:"2.0";s:12:"release_date";s:10:"2002-04-15";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:1939:"New API for new version 2.0 !
+Here is a list of what's new in this version:
+* Moved all the elements from HTML/QuickForm/Elements/ to HTML/QuickForm/
+* Added $_elementIndex to keep track of the elements
+* Changed $_elementValues to $_defaultValues
+* Removed $_hidden and $_frozen
+* Added Filter support
+* Added templates for the _wrap functions
+* Changed loadDefault() to setDefaults()
+* Moved most of the logic to the element classes
+* Created an abstraction between the form class and the elements
+* Added getElement() and getElementValue()
+* Added toArray()
+* Cleaned up a lot of the code
+* Added the // {{{ markers to all files to make it for easier reading in vim
+* Added the error handlers and codes to make errors more descriptive
+* Added new method setConstants() which is like setDefaults() except that it overrides the POST or GET variables.
+* Added methods setElementTemplate(), setFormTemplate(), setHeaderTemplate() and setRequiredNotTemplate(). They are pretty self-explanatory.
+* Added method addData(). Same as before.
+* Added new method clearAllTemplates() which basically strips out all HTML. This is useful if you want to create a completely custom looking form using addData()
+* Fixed the toHtml() method so that it is a while statement so that freeze() works.
+* Fixed a bug in the hidden type, where it was setting the value when you created the element, thus not allowing the value to be set by POST or GET variables.
+* Took out the is_int test in select.php.
+* Made the caller a global variable in element.php, so that it can be accessed, if need be, in other element classes.
+* Changed it so that if an element is frozen and has no value it outputs '&nbsp;' instead of nothing which messes up old browsers like NS4.
+* Added renderElement() method which will return the html for one element.
+* Added removeElement() method which removes the element from the elements list. Both methods accept parameters.
+
+
+";}i:21;a:5:{s:7:"version";s:3:"2.1";s:12:"release_date";s:10:"2002-05-24";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:163:"* Fixes for many if not all E_ALL warnings.
+* Filter function is now recursive.
+* Better handling of registered rules.
+* New regex for numeric validation.
+
+
+";}i:22;a:5:{s:7:"version";s:4:"1.12";s:12:"release_date";s:10:"2002-03-22";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:55:"Changed directory structure for package installation.
+
+";}i:23;a:5:{s:7:"version";s:3:"1.0";s:12:"release_date";s:10:"2001-09-06";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:60:"This is the initial release of the HTML_QuickForm package.
+
+";}}s:13:"_lastmodified";i:1099255814;} \ No newline at end of file
diff --git a/pearlib/.registry/html_quickform_controller.reg b/pearlib/.registry/html_quickform_controller.reg
new file mode 100644
index 00000000..723f045e
--- /dev/null
+++ b/pearlib/.registry/html_quickform_controller.reg
@@ -0,0 +1,50 @@
+a:14:{s:8:"provides";a:0:{}s:8:"filelist";a:17:{s:10:"Action.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:14:"HTML/QuickForm";s:12:"installed_as";s:74:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Action.php";}s:14:"Controller.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:14:"HTML/QuickForm";s:12:"installed_as";s:78:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Controller.php";}s:8:"Page.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:14:"HTML/QuickForm";s:12:"installed_as";s:72:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Page.php";}s:15:"Action/Back.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:14:"HTML/QuickForm";s:12:"installed_as";s:79:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Action/Back.php";}s:17:"Action/Direct.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:14:"HTML/QuickForm";s:12:"installed_as";s:81:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Action/Direct.php";}s:18:"Action/Display.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:14:"HTML/QuickForm";s:12:"installed_as";s:82:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Action/Display.php";}s:15:"Action/Jump.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:14:"HTML/QuickForm";s:12:"installed_as";s:79:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Action/Jump.php";}s:15:"Action/Next.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:14:"HTML/QuickForm";s:12:"installed_as";s:79:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Action/Next.php";}s:17:"Action/Submit.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:14:"HTML/QuickForm";s:12:"installed_as";s:81:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Action/Submit.php";}s:19:"examples/simple.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:14:"HTML/QuickForm";s:12:"installed_as";s:95:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm_Controller/examples/simple.php";}s:19:"examples/tabbed.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:14:"HTML/QuickForm";s:12:"installed_as";s:95:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm_Controller/examples/tabbed.php";}s:19:"examples/wizard.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:14:"HTML/QuickForm";s:12:"installed_as";s:95:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm_Controller/examples/wizard.php";}s:22:"examples/regWizard.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:14:"HTML/QuickForm";s:12:"installed_as";s:98:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm_Controller/examples/regWizard.php";}s:19:"examples/upload.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:14:"HTML/QuickForm";s:12:"installed_as";s:95:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm_Controller/examples/upload.php";}s:30:"examples/templates/upload.html";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:14:"HTML/QuickForm";s:12:"installed_as";s:106:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm_Controller/examples/templates/upload.html";}s:25:"examples/statemachine.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:14:"HTML/QuickForm";s:12:"installed_as";s:101:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm_Controller/examples/statemachine.php";}s:7:"dirtree";a:5:{s:63:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm";b:1;s:53:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML";b:1;s:70:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/QuickForm/Action";b:1;s:84:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm_Controller/examples";b:1;s:94:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_QuickForm_Controller/examples/templates";b:1;}}s:7:"package";s:25:"HTML_QuickForm_Controller";s:7:"summary";s:76:"The add-on to HTML_QuickForm package that allows building of multipage forms";s:11:"description";s:510:"The package is essentially an implementation of a PageController pattern.
+Architecture:
+* Controller class that examines HTTP requests and manages form values persistence across requests.
+* Page class (subclass of QuickForm) representing a single page of the form.
+* Business logic is contained in subclasses of Action class.
+Cool features:
+* Includes several default Actions that allow easy building of multipage forms.
+* Includes usage examples for common usage cases (single-page form, wizard, tabbed form).";s:11:"maintainers";a:2:{i:0;a:4:{s:6:"handle";s:3:"avb";s:4:"name";s:13:"Alexey Borzov";s:5:"email";s:18:"[email protected]";s:4:"role";s:4:"lead";}i:1;a:4:{s:6:"handle";s:7:"mansion";s:4:"name";s:16:"Bertrand Mansion";s:5:"email";s:20:"[email protected]";s:4:"role";s:9:"developer";}}s:7:"version";s:5:"1.0.2";s:12:"release_date";s:10:"2004-03-23";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:349:"* HTML_QuickForm_Controller does not rely on form's "name" attribute anymore
+ (XHTML compliance fix, see also bug #925)
+* Use new HTML_QuickForm::arrayMerge() method in place of PHP's builtin
+ array_merge(), depend on QuickForm 3.2.2
+* Applied patch from #929, exportValues() does not overwrite elements with
+ the same names from different pages.";s:12:"release_deps";a:1:{i:1;a:4:{s:4:"type";s:3:"pkg";s:3:"rel";s:2:"ge";s:7:"version";s:5:"3.2.2";s:4:"name";s:14:"HTML_QuickForm";}}s:9:"changelog";a:6:{i:0;a:4:{s:7:"version";s:5:"1.0.1";s:12:"release_date";s:10:"2003-12-11";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:117:"* Added a new example showing how to proceed to either of the two pages
+ based on user input (Donald Lobo).
+
+
+";}i:1;a:4:{s:7:"version";s:3:"1.0";s:12:"release_date";s:10:"2003-11-05";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:304:"This release is done to update package's status to 'stable'. The code is now
+considered mature and tested enough to be used in production.
+
+Changes:
+* No changes to classes since 0.9.3
+* Added an example of wizard with file upload (Bertrand Mansion)
+* Depend on newest version of HTML_QuickForm
+
+
+";}i:2;a:4:{s:7:"version";s:5:"0.9.3";s:12:"release_date";s:10:"2003-10-24";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:404:"* Controller now differentiates between invalid and not-yet-validated pages. False is stored
+in container's ['valid'][$pageName] field for the former, null for the latter. This is
+a POTENTIAL BC BREAK, so pay attention if you wrote your own Actions.
+* Back action in modal forms does not validate the page anymore (the previous behaviour was
+non-intuitive, thanks to Bob Van Zant for reporting).
+
+
+";}i:3;a:4:{s:7:"version";s:5:"0.9.2";s:12:"release_date";s:10:"2003-10-02";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:192:"Added Page::setDefaultAction() method to fix the Controller's behaviour when user presses
+Enter instead of clicking on one of submit buttons. Thanks to Mike Carter for reporting this.
+
+
+";}i:4;a:4:{s:7:"version";s:5:"0.9.1";s:12:"release_date";s:10:"2003-09-19";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:427:"Changes and additions:
+* Added Controller::exportValue() method (thanks to Arnaud Limbourg)
+* Controller::exportValues() can return values for the single page (thanks to Arnaud Limbourg)
+* Added a parameter for Controller::container() that forces the container reset
+* Added a new example: registration wizard (Bertrand Mansion)
+
+Fixes:
+* Action_Next on the last page validates the whole form, not just the page itself
+
+
+";}i:5;a:4:{s:7:"version";s:3:"0.9";s:12:"release_date";s:10:"2003-08-29";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:29:"Initial PEAR release
+
+
+";}}s:13:"_lastmodified";i:1082371332;} \ No newline at end of file
diff --git a/pearlib/.registry/html_template_sigma.reg b/pearlib/.registry/html_template_sigma.reg
new file mode 100644
index 00000000..e9c823d5
--- /dev/null
+++ b/pearlib/.registry/html_template_sigma.reg
@@ -0,0 +1,76 @@
+a:14:{s:8:"provides";a:0:{}s:8:"filelist";a:28:{s:9:"Sigma.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:72:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/Template/Sigma.php";}s:29:"tests/templates/addblock.html";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:100:"/home/alexey.beta.postgresql.org/alexey/pear/tests/HTML_Template_Sigma/tests/templates/addblock.html";}s:35:"tests/templates/blockiteration.html";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:106:"/home/alexey.beta.postgresql.org/alexey/pear/tests/HTML_Template_Sigma/tests/templates/blockiteration.html";}s:27:"tests/templates/blocks.html";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:98:"/home/alexey.beta.postgresql.org/alexey/pear/tests/HTML_Template_Sigma/tests/templates/blocks.html";}s:29:"tests/templates/callback.html";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:100:"/home/alexey.beta.postgresql.org/alexey/pear/tests/HTML_Template_Sigma/tests/templates/callback.html";}s:28:"tests/templates/globals.html";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:99:"/home/alexey.beta.postgresql.org/alexey/pear/tests/HTML_Template_Sigma/tests/templates/globals.html";}s:30:"tests/templates/__include.html";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:101:"/home/alexey.beta.postgresql.org/alexey/pear/tests/HTML_Template_Sigma/tests/templates/__include.html";}s:28:"tests/templates/include.html";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:99:"/home/alexey.beta.postgresql.org/alexey/pear/tests/HTML_Template_Sigma/tests/templates/include.html";}s:37:"tests/templates/loadtemplatefile.html";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:108:"/home/alexey.beta.postgresql.org/alexey/pear/tests/HTML_Template_Sigma/tests/templates/loadtemplatefile.html";}s:33:"tests/templates/replaceblock.html";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:104:"/home/alexey.beta.postgresql.org/alexey/pear/tests/HTML_Template_Sigma/tests/templates/replaceblock.html";}s:30:"tests/Console_TestListener.php";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:101:"/home/alexey.beta.postgresql.org/alexey/pear/tests/HTML_Template_Sigma/tests/Console_TestListener.php";}s:28:"tests/Sigma_api_testcase.php";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:99:"/home/alexey.beta.postgresql.org/alexey/pear/tests/HTML_Template_Sigma/tests/Sigma_api_testcase.php";}s:30:"tests/Sigma_cache_testcase.php";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:101:"/home/alexey.beta.postgresql.org/alexey/pear/tests/HTML_Template_Sigma/tests/Sigma_cache_testcase.php";}s:30:"tests/Sigma_usage_testcase.php";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:101:"/home/alexey.beta.postgresql.org/alexey/pear/tests/HTML_Template_Sigma/tests/Sigma_usage_testcase.php";}s:14:"tests/test.php";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:85:"/home/alexey.beta.postgresql.org/alexey/pear/tests/HTML_Template_Sigma/tests/test.php";}s:18:"docs/example_1.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:88:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_Template_Sigma/docs/example_1.php";}s:18:"docs/example_2.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:88:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_Template_Sigma/docs/example_2.php";}s:18:"docs/example_3.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:88:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_Template_Sigma/docs/example_3.php";}s:18:"docs/example_4.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:88:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_Template_Sigma/docs/example_4.php";}s:29:"docs/templates/example_1.html";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:99:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_Template_Sigma/docs/templates/example_1.html";}s:29:"docs/templates/example_2.html";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:99:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_Template_Sigma/docs/templates/example_2.html";}s:29:"docs/templates/example_3.html";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:99:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_Template_Sigma/docs/templates/example_3.html";}s:33:"docs/templates/example_3_add.html";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:103:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_Template_Sigma/docs/templates/example_3_add.html";}s:37:"docs/templates/example_3_include.html";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:107:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_Template_Sigma/docs/templates/example_3_include.html";}s:39:"docs/templates/example_3_replace_1.html";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:109:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_Template_Sigma/docs/templates/example_3_replace_1.html";}s:39:"docs/templates/example_3_replace_2.html";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:109:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_Template_Sigma/docs/templates/example_3_replace_2.html";}s:29:"docs/templates/example_4.html";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:13:"HTML/Template";s:12:"installed_as";s:99:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_Template_Sigma/docs/templates/example_4.html";}s:7:"dirtree";a:6:{s:62:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML/Template";b:1;s:53:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTML";b:1;s:86:"/home/alexey.beta.postgresql.org/alexey/pear/tests/HTML_Template_Sigma/tests/templates";b:1;s:76:"/home/alexey.beta.postgresql.org/alexey/pear/tests/HTML_Template_Sigma/tests";b:1;s:74:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_Template_Sigma/docs";b:1;s:84:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTML_Template_Sigma/docs/templates";b:1;}}s:7:"package";s:19:"HTML_Template_Sigma";s:7:"summary";s:79:"An implementation of Integrated Templates API with template 'compilation' added";s:11:"description";s:1141:"HTML_Template_Sigma implements Integrated Templates API designed by Ulf Wendel.
+
+Features:
+* Nested blocks. Nesting is controlled by the engine.
+* Ability to include files from within template: &lt;!-- INCLUDE --&gt;
+* Automatic removal of empty blocks and unknown variables (methods to manually tweak/override this are also available)
+* Methods for runtime addition and replacement of blocks in templates
+* Ability to insert simple function calls into templates: func_uppercase('Hello world!') and to define callback functions for these
+* 'Compiled' templates: the engine has to parse a template file using regular expressions to find all the blocks and variable placeholders. This is a very "expensive" operation and is an overkill to do on every page request: templates seldom change on production websites. Thus this feature: an internal representation of the template structure is saved into a file and this file gets loaded instead of the source one on subsequent requests (unless the source changes)
+* PHPUnit-based tests to define correct behaviour
+* Usage examples for most of the features are available, look in the docs/ directory";s:11:"maintainers";a:1:{i:0;a:4:{s:6:"handle";s:3:"avb";s:4:"name";s:13:"Alexey Borzov";s:5:"email";s:11:"[email protected]";s:4:"role";s:4:"lead";}}s:7:"version";s:5:"1.1.2";s:12:"release_date";s:10:"2004-10-20";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:319:"Changed the code for parsing template function arguments. It will
+now correctly unescape the escaped quotes within quoted arguments
+(see bug #1840).
+The new code throws errors on bogus arguments. If you previously
+relied on undocumented behaviour of argument parsing method, your
+code may break: test before upgrading.";s:12:"release_deps";a:1:{i:1;a:3:{s:4:"type";s:3:"ext";s:3:"rel";s:3:"has";s:4:"name";s:5:"ctype";}}s:9:"changelog";a:8:{i:0;a:5:{s:7:"version";s:5:"1.1.1";s:12:"release_date";s:10:"2004-05-31";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:118:"Callbacks are not called if the block containing them is empty. Thanks
+to Sergey Vasiliev for the suggestion.
+
+
+";}i:1;a:5:{s:7:"version";s:5:"1.1.0";s:12:"release_date";s:10:"2004-04-14";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:422:"* Fixed #876 (get_class() and get_class_methods() usage with PHP5)
+ Unit tests run on PHP5, all tests pass.
+* Added clearVariables() method (see also #939)
+* Added shorthand syntax for callbacks: {var:callback} instead of
+ func_callback({var})
+* Added builtin callbacks
+ 'h' for htmlspecialchars()
+ 'u' for urlencode()
+ 'j' to escape the string for usage in Javascript
+
+This release is dedicated to Lukas Smith
+
+
+";}i:2;a:5:{s:7:"version";s:5:"1.0.2";s:12:"release_date";s:10:"2003-10-02";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:203:"* Removed _callbackExists() method, is_callable() is used instead
+* Check for SIGMA_OK instead of PEAR::isError() where possible
+* Fixed unit tests packaging. Should work OK in PEAR installation
+
+
+";}i:3;a:5:{s:7:"version";s:5:"1.0.1";s:12:"release_date";s:10:"2003-05-20";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:258:"* Added getBlockList() and getPlaceholderList() methods (thanks to Markus Wolff for the idea).
+* HTML_Template_IT compatibility: get() does not clear the parsed block contents by default.
+* Removed obsolete docs, current ones are in the PEAR manual.
+
+
+";}i:4;a:5:{s:7:"version";s:3:"1.0";s:12:"release_date";s:10:"2003-04-28";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:1569:"This release has Sigma's template functions/callbacks infrastructure significantly enhanced.
+This unfortunately means some backwards compatibility breaks. If you were using callbacks before, please carefully examine the release notes and test you scripts before upgrading to 1.0.
+If you weren't using callbacks due to their shortcomings it is a good time to give them a try, consult docs/example_4.php for some of their possible applications.
+
+Template functions/callbacks changes and incompatibilities:
+
+* Templates are parsed for function calls not only on setTemplate()/loadTemplatefile(), but on addBlock()/addBlockfile() and replaceBlock()/replaceBlockfile() as well.
+* The arguments to template functions can now contain variable placeholders: func_uppercase('Hello, {username}!).
+* Template functions are now compatible with template caching.
+* Method setCallbackFunction() now accepts a callback that is compatible with call_user_func() as its second argument. Its third argument is used to control variable substitution in function arguments.
+* Method performCallback() was removed. Callbacks are now called automatically when the block containing them is parse()'d.
+* Callbacks are now called via call_user_func_array() instead of call_user_func(). This makes it possible to use built-in functions as callbacks, but also means that you have to rewrite your old functions!
+* Usage example is available in docs/example_4.php
+
+Other changes:
+
+* Option 'use_preg' was removed.
+* Stricter error checking when doing addBlockfile() with a prepared template.
+
+
+";}i:5;a:5:{s:7:"version";s:5:"0.9.2";s:12:"release_date";s:10:"2003-04-21";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:239:"* Added usage examples
+* Added the cache FAQ
+* This is the last 0.9.x release unless some bugs are found. The 1.0 release will
+ have callbacks significantly redone and will require changes to your scripts if
+ you use callbacks.
+
+
+";}i:6;a:5:{s:7:"version";s:5:"0.9.1";s:12:"release_date";s:10:"2003-04-18";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:149:"* Fix: if source template does not exist, do not load the 'prepared' one, throw an error instead.
+ Thanks to Maxim Panasiuk for the report.
+
+
+";}i:7;a:5:{s:7:"version";s:3:"0.9";s:12:"release_date";s:10:"2003-04-04";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:22:"Initial PEAR release
+
+";}}s:13:"_lastmodified";i:1099255807;} \ No newline at end of file
diff --git a/pearlib/.registry/http_client.reg b/pearlib/.registry/http_client.reg
new file mode 100644
index 00000000..92873b9e
--- /dev/null
+++ b/pearlib/.registry/http_client.reg
@@ -0,0 +1,27 @@
+a:14:{s:8:"provides";a:0:{}s:8:"filelist";a:4:{s:10:"Client.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTTP";s:12:"installed_as";s:64:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTTP/Client.php";}s:24:"Client/CookieManager.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTTP";s:12:"installed_as";s:78:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTTP/Client/CookieManager.php";}s:25:"examples/link-checker.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTTP";s:12:"installed_as";s:87:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTTP_Client/examples/link-checker.php";}s:7:"dirtree";a:3:{s:53:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTTP";b:1;s:60:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTTP/Client";b:1;s:70:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTTP_Client/examples";b:1;}}s:7:"package";s:11:"HTTP_Client";s:7:"summary";s:68:"Easy way to perform multiple HTTP requests and process their results";s:11:"description";s:397:"The HTTP_Client class wraps around HTTP_Request and provides a higher level interface
+for performing multiple HTTP requests.
+
+Features:
+* Manages cookies and referrers between requests
+* Handles HTTP redirection
+* Has methods to set default headers and request parameters
+* Implements the Subject-Observer design pattern: the base class sends
+events to listeners that do the response processing.";s:11:"maintainers";a:1:{i:0;a:4:{s:6:"handle";s:3:"avb";s:4:"name";s:13:"Alexey Borzov";s:5:"email";s:11:"[email protected]";s:4:"role";s:4:"lead";}}s:7:"version";s:5:"1.0.0";s:12:"release_date";s:10:"2004-06-11";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:113:"Since no problems were discovered with release 1.0.0beta1, the same code
+is now re-released with "stable" status.";s:12:"release_deps";a:1:{i:1;a:4:{s:4:"type";s:3:"pkg";s:3:"rel";s:2:"ge";s:7:"version";s:3:"1.2";s:4:"name";s:12:"HTTP_Request";}}s:9:"changelog";a:3:{i:0;a:4:{s:7:"version";s:10:"1.0.0beta1";s:12:"release_date";s:10:"2004-04-30";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:230:"* REMOVED deprecated HTTP_Client_Listener
+* Applied patch from #1151, cookies with empty values are now deleted
+* Added method enableHistory() allowing to enable/disable saving of history,
+ suggested by Matthew Feinberg
+
+
+";}i:1;a:4:{s:7:"version";s:3:"0.2";s:12:"release_date";s:10:"2004-01-18";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:352:"* Depend on most recent version of HTTP_Request
+ - HTTP_Client_Listener is deprecated in favor of HTTP_Request_Listener
+ - Added possibilty to attach Listeners to created HTTP_Request objects
+ - Added support for file uploads in post() method
+* Better handling of secure cookies (thanks to Denis Malinovsky)
+* Added setMaxRedirects() method
+
+
+";}i:2;a:4:{s:7:"version";s:3:"0.1";s:12:"release_date";s:10:"2003-06-05";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:29:"Initial PEAR release
+
+
+";}}s:13:"_lastmodified";i:1093330313;} \ No newline at end of file
diff --git a/pearlib/.registry/http_request.reg b/pearlib/.registry/http_request.reg
new file mode 100644
index 00000000..bae7bd57
--- /dev/null
+++ b/pearlib/.registry/http_request.reg
@@ -0,0 +1,61 @@
+a:14:{s:8:"provides";a:0:{}s:8:"filelist";a:5:{s:11:"Request.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTTP";s:12:"installed_as";s:65:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTTP/Request.php";}s:20:"Request/Listener.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:4:"HTTP";s:12:"installed_as";s:74:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTTP/Request/Listener.php";}s:16:"docs/example.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTTP";s:12:"installed_as";s:79:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTTP_Request/docs/example.php";}s:26:"docs/download-progress.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:4:"HTTP";s:12:"installed_as";s:89:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTTP_Request/docs/download-progress.php";}s:7:"dirtree";a:3:{s:53:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTTP";b:1;s:61:"/home/alexey.beta.postgresql.org/alexey/pear/lib/HTTP/Request";b:1;s:67:"/home/alexey.beta.postgresql.org/alexey/pear/docs/HTTP_Request/docs";b:1;}}s:7:"package";s:12:"HTTP_Request";s:7:"summary";s:45:"Provides an easy way to perform HTTP requests";s:11:"description";s:114:"Supports GET/POST/HEAD/TRACE/PUT/DELETE, Basic authentication, Proxy,
+Proxy Authentication, SSL, file uploads etc.";s:11:"maintainers";a:2:{i:0;a:4:{s:6:"handle";s:7:"richard";s:4:"name";s:13:"Richard Heyes";s:5:"email";s:15:"[email protected]";s:4:"role";s:4:"lead";}i:1;a:4:{s:6:"handle";s:3:"avb";s:4:"name";s:13:"Alexey Borzov";s:5:"email";s:11:"[email protected]";s:4:"role";s:4:"lead";}}s:7:"version";s:5:"1.2.3";s:12:"release_date";s:10:"2004-10-01";s:15:"release_license";s:3:"BSD";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:453:"* Auth information is properly extracted from URLs of the form http://user:pass@host/
+ (bug #1507)
+* Connection to server is closed after performing request (bug #1692)
+* Use correct argument separator for generated query stings (bug #1857, see
+ also bug #704 for Net_URL)
+* Do not use gzip encoding if certain string functions are overloaded by
+ mbstring extension (bug #1781)
+* addPostData() now properly handles multidimensional arrays (bug #2233)";s:12:"release_deps";a:2:{i:1;a:4:{s:4:"type";s:3:"pkg";s:3:"rel";s:2:"ge";s:7:"version";s:6:"1.0.12";s:4:"name";s:7:"Net_URL";}i:2;a:4:{s:4:"type";s:3:"pkg";s:3:"rel";s:2:"ge";s:7:"version";s:5:"1.0.2";s:4:"name";s:10:"Net_Socket";}}s:9:"changelog";a:8:{i:0;a:5:{s:7:"version";s:5:"1.2.2";s:12:"release_date";s:10:"2004-05-19";s:15:"release_license";s:3:"BSD";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:304:"Bug fixes:
+* Fixed #1037 (unable to connect to port 80 through HTTPS). This relies
+ on fix for Net_URL bug #1036, thus Net_URL 1.0.12 is now required.
+* Fixed #1333 (sending POST data on non-POST requests).
+* Fixed #1433 (overwriting the variable name when adding multiple files
+ for upload).
+
+
+";}i:1;a:5:{s:7:"version";s:5:"1.2.1";s:12:"release_date";s:10:"2004-04-29";s:15:"release_license";s:3:"BSD";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:632:"Additions and changes:
+ * Applied patch from #851 (First parameter of constructor is now optional)
+ * Implemented #526 (It is now possible to set timeout on socket, via
+ parameter readTimeout)
+ * Implemented #1141 (It is now possible to pass options to socket via
+ parameter socketOptions, Net_Socket 1.0.2 is needed for this functionality)
+
+Fixes:
+ * Fixed #842 (Doc comments incorrectly described the possible return values)
+ * Fixed #1152 (Incorrect handling of cookies with '=' in value)
+ * Fixed #1158 (Cookie parameters are not necessarily lowercase)
+ * Fixed #1080 (Cookies should not be urlencoded/urldecoded)
+
+
+";}i:2;a:5:{s:7:"version";s:3:"1.2";s:12:"release_date";s:10:"2003-10-27";s:15:"release_license";s:3:"BSD";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:946:"Feature additions:
+ * Support for multipart/form-data POST requests and file uploads (partly based on Christian Stocker's work)
+ * Brackets [] after array variables are optional (on by default, controlled by useBrackets parameter)
+ * HTTP_Request now implements a Subject-Observer design pattern. It is possible to add Listeners
+ to the Request object to e.g. draw a progress bar when downloading a large file. This is partly
+ based on Stefan Walk's work. A usage example for this is available.
+
+Migration to 1.2:
+ * Redirect support is now OFF by default
+ * Redirect support is DEPRECATED
+ * Methods clearCookies(), clearPostData(), reset() are DEPRECATED
+
+Fixes:
+ * Fixed PEAR bug #18 (Lowercased headers, fix by Dave Mertens)
+ * Fixed PEAR bug #131 (Domain without trailing slash)
+ * Fixed PHP bug #25486 (100 Continue handling)
+ * Fixed PEAR bug #150 (Notices being generated)
+ * Fixed problems with HTTP responses without bodies
+
+
+";}i:3;a:5:{s:7:"version";s:5:"1.1.1";s:12:"release_date";s:10:"2003-01-30";s:15:"release_license";s:3:"BSD";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:56:"Added redirect support. Net_URL 1.0.7 is now required.
+
+";}i:4;a:5:{s:7:"version";s:5:"1.1.0";s:12:"release_date";s:10:"2003-01-20";s:15:"release_license";s:3:"BSD";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:108:"Added SSL support as long as you have PHP 4.3.0+ and the OpenSSL extension. Net_URL 1.0.6 is now required.
+
+";}i:5;a:5:{s:7:"version";s:5:"1.0.2";s:12:"release_date";s:10:"2002-09-16";s:15:"release_license";s:3:"BSD";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:22:"Added cookie support
+
+";}i:6;a:5:{s:7:"version";s:5:"1.0.1";s:12:"release_date";s:10:"2002-07-27";s:15:"release_license";s:3:"BSD";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:16:"License change
+
+";}i:7;a:4:{s:7:"version";s:3:"1.0";s:12:"release_date";s:10:"2002-02-17";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:46:"Initial release of the HTTP_Request package.
+
+";}}s:13:"_lastmodified";i:1099255812;} \ No newline at end of file
diff --git a/pearlib/.registry/log.reg b/pearlib/.registry/log.reg
new file mode 100644
index 00000000..91bab143
--- /dev/null
+++ b/pearlib/.registry/log.reg
@@ -0,0 +1,105 @@
+a:14:{s:8:"provides";a:0:{}s:8:"filelist";a:43:{s:14:"docs/guide.txt";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:68:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Log/docs/guide.txt";}s:27:"docs/examples/composite.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:81:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Log/docs/examples/composite.php";}s:25:"docs/examples/console.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:79:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Log/docs/examples/console.php";}s:25:"docs/examples/display.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:79:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Log/docs/examples/display.php";}s:27:"docs/examples/error_log.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:81:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Log/docs/examples/error_log.php";}s:22:"docs/examples/file.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:76:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Log/docs/examples/file.php";}s:22:"docs/examples/mail.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:76:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Log/docs/examples/mail.php";}s:22:"docs/examples/null.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:76:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Log/docs/examples/null.php";}s:31:"docs/examples/observer_mail.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:85:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Log/docs/examples/observer_mail.php";}s:36:"docs/examples/pear_error_handler.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:90:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Log/docs/examples/pear_error_handler.php";}s:35:"docs/examples/php_error_handler.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:89:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Log/docs/examples/php_error_handler.php";}s:21:"docs/examples/sql.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:75:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Log/docs/examples/sql.php";}s:24:"docs/examples/sqlite.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:78:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Log/docs/examples/sqlite.php";}s:24:"docs/examples/syslog.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:78:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Log/docs/examples/syslog.php";}s:21:"docs/examples/win.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:75:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Log/docs/examples/win.php";}s:17:"Log/composite.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:66:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Log/composite.php";}s:15:"Log/console.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:64:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Log/console.php";}s:15:"Log/display.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:64:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Log/display.php";}s:17:"Log/error_log.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:66:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Log/error_log.php";}s:12:"Log/file.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:61:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Log/file.php";}s:12:"Log/mail.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:61:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Log/mail.php";}s:12:"Log/mcal.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:61:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Log/mcal.php";}s:12:"Log/null.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:61:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Log/null.php";}s:16:"Log/observer.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:65:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Log/observer.php";}s:11:"Log/sql.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:60:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Log/sql.php";}s:14:"Log/sqlite.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:63:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Log/sqlite.php";}s:14:"Log/syslog.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:63:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Log/syslog.php";}s:11:"Log/win.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:60:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Log/win.php";}s:12:"misc/log.sql";a:3:{s:4:"role";s:4:"data";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:66:"/home/alexey.beta.postgresql.org/alexey/pear/data/Log/misc/log.sql";}s:20:"tests/composite.phpt";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:75:"/home/alexey.beta.postgresql.org/alexey/pear/tests/Log/tests/composite.phpt";}s:18:"tests/console.phpt";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:73:"/home/alexey.beta.postgresql.org/alexey/pear/tests/Log/tests/console.phpt";}s:18:"tests/display.phpt";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:73:"/home/alexey.beta.postgresql.org/alexey/pear/tests/Log/tests/display.phpt";}s:20:"tests/error_log.phpt";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:75:"/home/alexey.beta.postgresql.org/alexey/pear/tests/Log/tests/error_log.phpt";}s:18:"tests/extract.phpt";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:73:"/home/alexey.beta.postgresql.org/alexey/pear/tests/Log/tests/extract.phpt";}s:19:"tests/extract2.phpt";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:74:"/home/alexey.beta.postgresql.org/alexey/pear/tests/Log/tests/extract2.phpt";}s:18:"tests/factory.phpt";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:73:"/home/alexey.beta.postgresql.org/alexey/pear/tests/Log/tests/factory.phpt";}s:15:"tests/null.phpt";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:70:"/home/alexey.beta.postgresql.org/alexey/pear/tests/Log/tests/null.phpt";}s:19:"tests/priority.phpt";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:74:"/home/alexey.beta.postgresql.org/alexey/pear/tests/Log/tests/priority.phpt";}s:20:"tests/singleton.phpt";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:75:"/home/alexey.beta.postgresql.org/alexey/pear/tests/Log/tests/singleton.phpt";}s:17:"tests/sqlite.phpt";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:72:"/home/alexey.beta.postgresql.org/alexey/pear/tests/Log/tests/sqlite.phpt";}s:20:"tests/sql_ident.phpt";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:75:"/home/alexey.beta.postgresql.org/alexey/pear/tests/Log/tests/sql_ident.phpt";}s:7:"Log.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:1:"/";s:12:"installed_as";s:56:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Log.php";}s:7:"dirtree";a:6:{s:58:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Log/docs";b:1;s:67:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Log/docs/examples";b:1;s:52:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Log";b:1;s:58:"/home/alexey.beta.postgresql.org/alexey/pear/data/Log/misc";b:1;s:60:"/home/alexey.beta.postgresql.org/alexey/pear/tests/Log/tests";b:1;s:48:"/home/alexey.beta.postgresql.org/alexey/pear/lib";b:1;}}s:7:"package";s:3:"Log";s:7:"summary";s:17:"Logging utilities";s:11:"description";s:189:"The Log framework provides an abstracted logging system. It supports logging to console, file, syslog, SQL, Sqlite, mail and mcal targets. It also provides a subject - observer mechanism.";s:11:"maintainers";a:1:{i:0;a:4:{s:6:"handle";s:3:"jon";s:4:"name";s:10:"Jon Parise";s:5:"email";s:11:"[email protected]";s:4:"role";s:4:"lead";}}s:7:"version";s:5:"1.8.6";s:12:"release_date";s:10:"2004-09-08";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:161:"The maximum length of the 'sql' handler's 'ident' string is now configurable.
+
+Different instances of the 'win' handler can now address their own output windows.";s:12:"release_deps";a:3:{i:1;a:4:{s:4:"type";s:3:"pkg";s:3:"rel";s:3:"has";s:8:"optional";s:3:"yes";s:4:"name";s:2:"DB";}i:2;a:4:{s:4:"type";s:3:"ext";s:3:"rel";s:3:"has";s:8:"optional";s:3:"yes";s:4:"name";s:6:"sqlite";}i:3;a:4:{s:4:"type";s:3:"php";s:3:"rel";s:2:"ge";s:7:"version";s:5:"4.3.0";s:8:"optional";s:2:"no";}}s:9:"changelog";a:24:{i:0;a:5:{s:7:"version";s:5:"1.8.6";s:12:"release_date";s:10:"2004-09-08";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:230:"The maximum length of the 'sql' handler's 'ident' string is now configurable via the 'identLimit' configuration parameter. (Bug 2137)
+
+Different instances of the 'win' handler can now address their own output windows. (Bug 2212)
+
+";}i:1;a:5:{s:7:"version";s:5:"1.8.5";s:12:"release_date";s:10:"2004-08-09";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:682:"The 'sql' handler now enforces a maximum 'ident' length of 16 characters.
+
+The 'sql' handler can now use a user-defined sequence (via the 'sequence' configuration parameter). This allows each log table to use its own sequence. You will need to take appropriate steps to preserve your sequence numbering if that is important to your site; the ID sequence will be reinitialized to 0 the first time this updated handler is used. You may also need to explicitly drop the 'log_id_seq' sequence before using this new code.
+
+The 'composite' handler now properly closes all of its children when its close() method is called. It also guards against multiple calls to open(). (Bug 1947)
+
+";}i:2;a:5:{s:7:"version";s:5:"1.8.4";s:12:"release_date";s:10:"2004-02-16";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:592:"The Log package now requires PHP 4.3.0 or later.
+
+The _extractMessage() method no longer uses the serialize()'ed version of an event object if no string conversion method is available. Instead, the human-readable (via print_r()) representation of the object will be used.
+
+_extractMessage() can now handle arrays. Their human-readable representation will be used.
+
+Each Log instance now has a default priority level. This priority will be used by the log() method when no specific priority parameter is provided. It can be accessed using the new getPriority() and setPriority() methods.
+
+";}i:3;a:5:{s:7:"version";s:5:"1.8.3";s:12:"release_date";s:10:"2004-01-11";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:320:"The open() and close() methods now consistently return success or failure. Previously, some handlers' open() and close() methods did not return any result. Also, the log() methods will return failure when the handler cannot be opened.
+
+Bertrand Mansion contributed a new handler that logs using the Sqlite extension.
+
+";}i:4;a:5:{s:7:"version";s:5:"1.8.2";s:12:"release_date";s:10:"2004-01-01";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:432:"A new 'null' log handler that simply consumes log events has been added. The 'null' handler will still respect log masks and attached observers.
+
+The public Log API has grown a flush() method. This allows buffered log output to be explicitly flushed. At this time, the flush() method is only implemented by the console, file and mail handlers.
+
+New unit tests for the Factory and Singleton construction methods have been added.
+
+";}i:5;a:5:{s:7:"version";s:5:"1.8.1";s:12:"release_date";s:10:"2003-12-14";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:355:"The 'win' handler now handles newline sequences correctly (Bug 282).
+
+The Log_observer::factory() method has been updated to accept an optional associative array of configuration values, return the newly-created object by reference, and look for files named 'Log/observer_.php'. Backwards compatibility for the old-style conventions has been preserved.
+
+";}i:6;a:5:{s:7:"version";s:5:"1.8.0";s:12:"release_date";s:10:"2003-11-18";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:520:"The Log package now includes a users guide, example scripts and unit tests.
+
+A number of small improvements have been made to the 'win' log handler (based on suggestions from Paul Yanchenko).
+
+A new 'display' log handler has been added to the distribution. Contributed by Paul Yanchenko, this handler simply prints the error message back to the browser. It respects the 'error_prepend_string' and 'error_append_string' PHP INI values and is useful when using PEAR::setErrorHandling()'s PEAR_ERROR_CALLBACK mechanism.
+
+";}i:7;a:4:{s:7:"version";s:5:"1.7.1";s:12:"release_date";s:10:"2003-09-21";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:367:"The mail handler now uses "\n" to terminate lines instead of "\r\n".
+
+The file handler's path creation routines now guard against potentially infinite recursion.
+
+It is now possible to pass an object to the logging methods. If the object offers a PEAR_Error-style getMessage() or PHP5 exception-style toString() method, it will be used to extract the message text.
+
+";}i:8;a:4:{s:7:"version";s:5:"1.7.0";s:12:"release_date";s:10:"2003-08-02";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:745:"It is now possible to specific a specific mask that dictates exactly which levels will be logged.
+
+A number of convenient "shortcut" functions have been added for logging messages at specific log messages.
+
+Two new log handers have been added: the 'error_log' handler wraps PHP's native error_log() function; and the 'win' handler outputs log messages to a separate browser window.
+
+The file log handler has been completely rewritten based on suggestions from Roman Neuhauser. It no longer buffers log output in memory before writing to the log file.
+
+Greg Beaver submitted a change that allows callers to provide their own Log classes (that existing outside of the PEAR file hierarchy) by including them before invoking a factory() method.
+
+";}i:9;a:4:{s:7:"version";s:5:"1.6.7";s:12:"release_date";s:10:"2003-06-16";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:160:"It is now possible to set the identification string for all of a composite's child instances.
+
+The preamble of the mail handler's message is now configurable.
+
+";}i:10;a:4:{s:7:"version";s:5:"1.6.5";s:12:"release_date";s:10:"2003-04-26";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:127:"The mail log handler needs to register its own destructer now that the Log class no longer inherits from the PEAR base class.
+
+";}i:11;a:4:{s:7:"version";s:5:"1.6.4";s:12:"release_date";s:10:"2003-04-08";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:349:"The Log classes no longer inherits from the PEAR base class. Not including the PEAR base class gives us smaller, faster Log objects.
+
+The Log_file class now registers its own destructor to ensure that the logfile is correctly closed upon shutdown.
+
+A ZendEngine 1 incompatibility was introduced in the previous release. This has been corrrected.
+
+";}i:12;a:4:{s:7:"version";s:5:"1.6.3";s:12:"release_date";s:10:"2003-04-07";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:454:"A unique identifier is now stored inside each Log instance. Both the composite and subject-observer mechanisms use this identifier to index their object lists.
+
+The internals of the Log_observer system have been rewritten for general cleanliness. The external interface remains unchanged.
+
+The Log class now offers public setIdent() and getIdent() members for setting and getting the Log instance's identification string during the object's lifetime.
+
+";}i:13;a:4:{s:7:"version";s:5:"1.6.2";s:12:"release_date";s:10:"2003-04-06";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:120:"In the Log_observer class, operate directly on the reference to the Log instance (instead of on a copy of the object).
+
+";}i:14;a:4:{s:7:"version";s:5:"1.6.1";s:12:"release_date";s:10:"2003-04-02";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:49:"- Fix a typo that broke the Log_Observer class.
+
+";}i:15;a:4:{s:7:"version";s:5:"1.6.0";s:12:"release_date";s:10:"2003-02-17";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:147:"- The SQL handler now uses sequences instead of timestamps as unique identifiers.
+- The time format used by the file handler is now configurable.
+
+";}i:16;a:4:{s:7:"version";s:5:"1.5.3";s:12:"release_date";s:10:"2003-01-01";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:193:"- Improved singleton()'s signature generation.
+- The SQL handler no longer closes existing database connections.
+- The log() method now returns a boolean result indicating success or failure.
+
+";}i:17;a:4:{s:7:"version";s:5:"1.5.2";s:12:"release_date";s:10:"2002-11-28";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:91:"- Altered the singleton's "signature" construction to be shorter and more array-friendly.
+
+";}i:18;a:4:{s:7:"version";s:5:"1.5.1";s:12:"release_date";s:10:"2002-11-17";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:148:"- The mail handler now implements a destructor to ensure close() is called.
+- The syslog handler now requires $name to be a valid syslog facility.
+
+";}i:19;a:4:{s:7:"version";s:3:"1.5";s:12:"release_date";s:10:"2002-10-24";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:199:"- Adding the ability to set the mode of the logfile in the Log_file.
+- mail() failures in Log_mail are logged via error_log().
+- Replaced all remaining LOG_* constants with the PEAR_LOG_* versions.
+
+";}i:20;a:4:{s:7:"version";s:3:"1.4";s:12:"release_date";s:10:"2002-09-28";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:198:"- Altered the format of the SQL logging table to use a TIMESTAMP field.
+- The Log class now extends the PEAR base class.
+- New file logger that keeps the log file open for the life of the request.
+
+";}i:21;a:4:{s:7:"version";s:3:"1.3";s:12:"release_date";s:10:"2002-09-15";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:31:"Adding a mail logging target.
+
+";}i:22;a:4:{s:7:"version";s:3:"1.2";s:12:"release_date";s:10:"2002-07-24";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:37:"Introduce the PEAR_LOG_* constants.
+
+";}i:23;a:4:{s:7:"version";s:3:"1.1";s:12:"release_date";s:10:"2002-04-12";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:38:"Adding a new console logging target.
+
+";}}s:13:"_lastmodified";i:1099255811;} \ No newline at end of file
diff --git a/pearlib/.registry/net_socket.reg b/pearlib/.registry/net_socket.reg
new file mode 100644
index 00000000..a3c35f0a
--- /dev/null
+++ b/pearlib/.registry/net_socket.reg
@@ -0,0 +1,10 @@
+a:13:{s:8:"provides";a:0:{}s:8:"filelist";a:2:{s:10:"Socket.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:3:"Net";s:12:"installed_as";s:63:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Net/Socket.php";}s:7:"dirtree";a:1:{s:52:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Net";b:1;}}s:7:"package";s:10:"Net_Socket";s:7:"summary";s:24:"Network Socket Interface";s:11:"description";s:231:"Net_Socket is a class interface to TCP sockets. It provides blocking
+and non-blocking operation, with different reading and writing modes
+(byte-wise, block-wise, line-wise and special formats like network
+byte-order ip addresses).";s:11:"maintainers";a:2:{i:0;a:4:{s:6:"handle";s:3:"ssb";s:4:"name";s:18:"Stig S�ther Bakken";s:5:"email";s:12:"[email protected]";s:4:"role";s:4:"lead";}i:1;a:4:{s:6:"handle";s:8:"chagenbu";s:4:"name";s:15:"Chuck Hagenbuch";s:5:"email";s:15:"[email protected]";s:4:"role";s:4:"lead";}}s:7:"version";s:5:"1.0.2";s:12:"release_date";s:10:"2004-04-26";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:134:"Fixes for several longstanding bugs. Allow setting of stream
+context. Correctly read lines that only end in \n. Suppress PHP
+warnings.";s:9:"changelog";a:2:{i:0;a:4:{s:7:"version";s:3:"1.0";s:12:"release_date";s:10:"2002-04-01";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:42:"First independent release of Net_Socket.
+
+";}i:1;a:4:{s:7:"version";s:5:"1.0.1";s:12:"release_date";s:10:"2002-04-04";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:26:"Touch up error handling.
+
+";}}s:13:"_lastmodified";i:1086963273;} \ No newline at end of file
diff --git a/pearlib/.registry/net_url.reg b/pearlib/.registry/net_url.reg
new file mode 100644
index 00000000..a74f8fd9
--- /dev/null
+++ b/pearlib/.registry/net_url.reg
@@ -0,0 +1,31 @@
+a:13:{s:8:"provides";a:0:{}s:8:"filelist";a:3:{s:7:"URL.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:3:"Net";s:12:"installed_as";s:60:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Net/URL.php";}s:16:"docs/example.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:3:"Net";s:12:"installed_as";s:74:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Net_URL/docs/example.php";}s:7:"dirtree";a:2:{s:52:"/home/alexey.beta.postgresql.org/alexey/pear/lib/Net";b:1;s:62:"/home/alexey.beta.postgresql.org/alexey/pear/docs/Net_URL/docs";b:1;}}s:7:"package";s:7:"Net_URL";s:7:"summary";s:20:"Easy parsing of Urls";s:11:"description";s:58:"Provides easy parsing of URLs and their constituent parts.";s:11:"maintainers";a:1:{i:0;a:4:{s:6:"handle";s:7:"richard";s:4:"name";s:13:"Richard heyes";s:5:"email";s:15:"[email protected]";s:4:"role";s:4:"lead";}}s:7:"version";s:6:"1.0.14";s:12:"release_date";s:10:"2004-06-19";s:15:"release_license";s:3:"BSD";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:10:"Whitespace";s:9:"changelog";a:14:{i:0;a:5:{s:7:"version";s:6:"1.0.13";s:12:"release_date";s:10:"2004-06-05";s:15:"release_license";s:3:"BSD";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:14:"Fix bug 1558
+
+";}i:1;a:5:{s:7:"version";s:6:"1.0.12";s:12:"release_date";s:10:"2004-05-08";s:15:"release_license";s:3:"BSD";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:36:"Bug fixes release (#704 and #1036)
+
+";}i:2;a:5:{s:7:"version";s:6:"1.0.11";s:12:"release_date";s:10:"2004-01-17";s:15:"release_license";s:3:"BSD";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:34:"Bug fixes release (#83 and #471)
+
+";}i:3;a:5:{s:7:"version";s:6:"1.0.10";s:12:"release_date";s:10:"2002-04-06";s:15:"release_license";s:3:"BSD";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:47:"Be more flexible in what constitutes a scheme
+
+";}i:4;a:4:{s:7:"version";s:5:"1.0.9";s:12:"release_date";s:10:"2002-04-05";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:34:"Fix couple of absolute URL bugs.
+
+";}i:5;a:4:{s:7:"version";s:5:"1.0.8";s:12:"release_date";s:10:"2002-03-06";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:119:"Various bugs. Remove auto setting of default url to '/' if a url is supplied
+to the constructor. May cause BC issues.
+
+";}i:6;a:4:{s:7:"version";s:5:"1.0.7";s:12:"release_date";s:10:"2002-12-07";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:53:"Added method to resolve URL paths of //, ../ and ./
+
+";}i:7;a:4:{s:7:"version";s:5:"1.0.6";s:12:"release_date";s:10:"2002-12-07";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:27:"Make usage of [] optional
+
+";}i:8;a:4:{s:7:"version";s:5:"1.0.5";s:12:"release_date";s:10:"2002-11-14";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:32:"Allow for URLS such as ...?foo
+
+";}i:9;a:4:{s:7:"version";s:5:"1.0.4";s:12:"release_date";s:10:"2002-07-27";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:17:"License change
+
+
+";}i:10;a:4:{s:7:"version";s:5:"1.0.3";s:12:"release_date";s:10:"2002-06-20";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:34:"Now uses HTTP_HOST if available.
+
+";}i:11;a:4:{s:7:"version";s:5:"1.0.2";s:12:"release_date";s:10:"2002-04-28";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:59:"updated to fix a minor irritation when running on windows
+
+";}i:12;a:4:{s:7:"version";s:5:"1.0.1";s:12:"release_date";s:10:"2002-04-28";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:67:"Maintenance release. Bugs fixed with path detection and defaults.
+
+";}i:13;a:4:{s:7:"version";s:3:"1.0";s:12:"release_date";s:10:"2002-02-17";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:53:"This is the initial release of the Net_URL package.
+
+";}}s:13:"_lastmodified";i:1093337104;} \ No newline at end of file
diff --git a/pearlib/.registry/pear.reg b/pearlib/.registry/pear.reg
new file mode 100644
index 00000000..d47a400d
--- /dev/null
+++ b/pearlib/.registry/pear.reg
@@ -0,0 +1,54 @@
+a:14:{s:8:"provides";a:3:{s:14:"class;OS_Guess";a:3:{s:4:"type";s:5:"class";s:4:"name";s:8:"OS_Guess";s:8:"explicit";b:1;}s:12:"class;System";a:3:{s:4:"type";s:5:"class";s:4:"name";s:6:"System";s:8:"explicit";b:1;}s:17:"function;md5_file";a:3:{s:4:"type";s:8:"function";s:4:"name";s:8:"md5_file";s:8:"explicit";b:1;}}s:8:"filelist";a:31:{s:12:"OS/Guess.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"7f552f5a5476a5ef8d180290d7d2a90f";s:12:"installed_as";s:61:"/home/alexey.beta.postgresql.org/alexey/pear/lib/OS/Guess.php";}s:21:"PEAR/Command/Auth.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"f257b9252172a6e174b36499296bb972";s:12:"installed_as";s:70:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/Command/Auth.php";}s:22:"PEAR/Command/Build.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"b0c210a914fb6c25507bfb948ff71bac";s:12:"installed_as";s:71:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/Command/Build.php";}s:23:"PEAR/Command/Common.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"d90bfb54cf2505747999d8ad1f6c470f";s:12:"installed_as";s:72:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/Command/Common.php";}s:23:"PEAR/Command/Config.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"303bbf44d112d510dd3a87ea7e55becf";s:12:"installed_as";s:72:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/Command/Config.php";}s:24:"PEAR/Command/Install.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"6fee5ff129e8846d32e54dd5952c214d";s:12:"installed_as";s:73:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/Command/Install.php";}s:24:"PEAR/Command/Package.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"c29b8af8311b90056012ea60a668ba8f";s:12:"installed_as";s:73:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/Command/Package.php";}s:25:"PEAR/Command/Registry.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"87a9582c0ba5ec6c9fbaba2d518e33dd";s:12:"installed_as";s:74:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/Command/Registry.php";}s:23:"PEAR/Command/Remote.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"db11793e282f070ad9dcadf2a644aeec";s:12:"installed_as";s:72:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/Command/Remote.php";}s:23:"PEAR/Command/Mirror.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"a0f44e37e237f81404c6f73819a58206";s:12:"installed_as";s:72:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/Command/Mirror.php";}s:21:"PEAR/Frontend/CLI.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"8e310f4f947bf7079778ef0a71fcc5b3";s:12:"installed_as";s:70:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/Frontend/CLI.php";}s:19:"PEAR/Autoloader.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"3940b7d27d339d72f019b8ab7e8e81b0";s:12:"installed_as";s:68:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/Autoloader.php";}s:16:"PEAR/Command.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"7fe4074ba2914cea3d17913b96c0088c";s:12:"installed_as";s:65:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/Command.php";}s:15:"PEAR/Common.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"8fd9f166b60412a61a1f8838c8e38a01";s:12:"installed_as";s:64:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/Common.php";}s:15:"PEAR/Config.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"cea7df54a1491f7acf6d5290d68cd4ae";s:12:"installed_as";s:64:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/Config.php";}s:19:"PEAR/Dependency.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"4fd0e12e75e064524b16a983b8150f3a";s:12:"installed_as";s:68:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/Dependency.php";}s:19:"PEAR/Downloader.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"bd1e073d4d42516164fe9da30bad9e75";s:12:"installed_as";s:68:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/Downloader.php";}s:18:"PEAR/Exception.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"1b1b3e13ee526560b50c97413fb4ec81";s:12:"installed_as";s:67:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/Exception.php";}s:19:"PEAR/ErrorStack.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"a487a318793138abae3a294af8aea0d0";s:12:"installed_as";s:68:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/ErrorStack.php";}s:16:"PEAR/Builder.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"cfffe3a0577e4c3c14479b6b962b9f51";s:12:"installed_as";s:65:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/Builder.php";}s:18:"PEAR/Installer.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"3d3f8c71261fe5c7fa8571b1ccf962fb";s:12:"installed_as";s:67:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/Installer.php";}s:17:"PEAR/Packager.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"cf9a5b9cbd6cf1d43bbb6151c77a5b4c";s:12:"installed_as";s:66:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/Packager.php";}s:17:"PEAR/Registry.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"6840ca9ca43e611da23aee935657a67d";s:12:"installed_as";s:66:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/Registry.php";}s:15:"PEAR/Remote.php";a:3:{s:4:"role";s:3:"php";s:6:"md5sum";s:32:"a2a46e11af74a5b73cd1095f54ad5e51";s:12:"installed_as";s:64:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/Remote.php";}s:15:"scripts/pear.sh";a:6:{s:4:"role";s:6:"script";s:14:"baseinstalldir";s:1:"/";s:6:"md5sum";s:32:"a3bc543b3f7174ab74108449496cad8b";s:10:"install-as";s:4:"pear";s:12:"replacements";a:4:{i:0;a:3:{s:4:"from";s:9:"@php_bin@";s:2:"to";s:7:"php_bin";s:4:"type";s:11:"pear-config";}i:1;a:3:{s:4:"from";s:9:"@php_dir@";s:2:"to";s:7:"php_dir";s:4:"type";s:11:"pear-config";}i:2;a:3:{s:4:"from";s:14:"@pear_version@";s:2:"to";s:7:"version";s:4:"type";s:12:"package-info";}i:3;a:3:{s:4:"from";s:14:"@include_path@";s:2:"to";s:7:"php_dir";s:4:"type";s:11:"pear-config";}}s:12:"installed_as";s:53:"/home/alexey.beta.postgresql.org/alexey/pear/bin/pear";}s:19:"scripts/pearcmd.php";a:6:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:1:"/";s:6:"md5sum";s:32:"b6a1c8ef58d9cf93882b2d407da07b4d";s:10:"install-as";s:11:"pearcmd.php";s:12:"replacements";a:4:{i:0;a:3:{s:4:"from";s:9:"@php_bin@";s:2:"to";s:7:"php_bin";s:4:"type";s:11:"pear-config";}i:1;a:3:{s:4:"from";s:9:"@php_dir@";s:2:"to";s:7:"php_dir";s:4:"type";s:11:"pear-config";}i:2;a:3:{s:4:"from";s:14:"@pear_version@";s:2:"to";s:7:"version";s:4:"type";s:12:"package-info";}i:3;a:3:{s:4:"from";s:14:"@include_path@";s:2:"to";s:7:"php_dir";s:4:"type";s:11:"pear-config";}}s:12:"installed_as";s:60:"/home/alexey.beta.postgresql.org/alexey/pear/lib/pearcmd.php";}s:11:"package.dtd";a:4:{s:4:"role";s:4:"data";s:14:"baseinstalldir";s:1:"/";s:6:"md5sum";s:32:"9a200033206173168832b22bc5d20053";s:12:"installed_as";s:66:"/home/alexey.beta.postgresql.org/alexey/pear/data/PEAR/package.dtd";}s:13:"template.spec";a:4:{s:4:"role";s:4:"data";s:14:"baseinstalldir";s:1:"/";s:6:"md5sum";s:32:"f2abf8db08a36295645d19b51e319a32";s:12:"installed_as";s:68:"/home/alexey.beta.postgresql.org/alexey/pear/data/PEAR/template.spec";}s:8:"PEAR.php";a:4:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:1:"/";s:6:"md5sum";s:32:"a76a0886034eb3c1a94db8fd2bf9ffcc";s:12:"installed_as";s:57:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR.php";}s:10:"System.php";a:4:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:1:"/";s:6:"md5sum";s:32:"57012786babadc058fab98c6e6468689";s:12:"installed_as";s:59:"/home/alexey.beta.postgresql.org/alexey/pear/lib/System.php";}s:7:"dirtree";a:7:{s:51:"/home/alexey.beta.postgresql.org/alexey/pear/lib/OS";b:1;s:61:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/Command";b:1;s:53:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR";b:1;s:62:"/home/alexey.beta.postgresql.org/alexey/pear/lib/PEAR/Frontend";b:1;s:48:"/home/alexey.beta.postgresql.org/alexey/pear/bin";b:1;s:48:"/home/alexey.beta.postgresql.org/alexey/pear/lib";b:1;s:54:"/home/alexey.beta.postgresql.org/alexey/pear/data/PEAR";b:1;}}s:7:"package";s:4:"PEAR";s:7:"summary";s:16:"PEAR Base System";s:11:"description";s:470:"The PEAR package contains:
+ * the PEAR installer, for creating, distributing
+ and installing packages
+ * the alpha-quality PEAR_Exception php5-only exception class
+ * the beta-quality PEAR_ErrorStack advanced error handling mechanism
+ * the PEAR_Error error handling mechanism
+ * the OS_Guess class for retrieving info about the OS
+ where PHP is running on
+ * the System class for quick handling common operations
+ with files and directories
+ * the PEAR base class";s:11:"maintainers";a:5:{i:0;a:4:{s:6:"handle";s:3:"ssb";s:4:"name";s:11:"Stig Bakken";s:5:"email";s:12:"[email protected]";s:4:"role";s:4:"lead";}i:1;a:4:{s:6:"handle";s:3:"cox";s:4:"name";s:13:"Tomas V.V.Cox";s:5:"email";s:15:"[email protected]";s:4:"role";s:4:"lead";}i:2;a:4:{s:6:"handle";s:6:"cellog";s:4:"name";s:11:"Greg Beaver";s:5:"email";s:14:"[email protected]";s:4:"role";s:4:"lead";}i:3;a:4:{s:6:"handle";s:6:"pajoye";s:4:"name";s:17:"Pierre-Alain Joye";s:5:"email";s:17:"[email protected]";s:4:"role";s:4:"lead";}i:4;a:4:{s:6:"handle";s:2:"mj";s:4:"name";s:13:"Martin Jansen";s:5:"email";s:10:"[email protected]";s:4:"role";s:9:"developer";}}s:7:"version";s:5:"1.3.3";s:12:"release_date";s:10:"2004-10-28";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:672:"Installer:
+ * fix Bug #1186 raise a notice error on PEAR::Common $_packageName
+ * fix Bug #1249 display the right state when using --force option
+ * fix Bug #2189 upgrade-all stops if dependancy fails
+ * fix Bug #1637 The use of interface causes warnings when packaging with PEAR
+ * fix Bug #1420 Parser bug for T_DOUBLE_COLON
+ * fix Request #2220 pear5 build fails on dual php4/php5 system
+ * fix Bug #1163 pear makerpm fails with packages that supply role="doc"
+
+Other:
+ * add PEAR_Exception class for PHP5 users
+ * fix critical problem in package.xml for linux in 1.3.2
+ * fix staticPopCallback() in PEAR_ErrorStack
+ * fix warning in PEAR_Registry for windows 98 users";s:12:"release_deps";a:6:{i:1;a:3:{s:4:"type";s:3:"php";s:3:"rel";s:2:"ge";s:7:"version";s:3:"4.2";}i:2;a:4:{s:4:"type";s:3:"pkg";s:3:"rel";s:2:"ge";s:7:"version";s:3:"1.1";s:4:"name";s:11:"Archive_Tar";}i:3;a:4:{s:4:"type";s:3:"pkg";s:3:"rel";s:2:"ge";s:7:"version";s:3:"1.2";s:4:"name";s:14:"Console_Getopt";}i:4;a:4:{s:4:"type";s:3:"pkg";s:3:"rel";s:2:"ge";s:7:"version";s:5:"1.0.4";s:4:"name";s:7:"XML_RPC";}i:5;a:3:{s:4:"type";s:3:"ext";s:3:"rel";s:3:"has";s:4:"name";s:3:"xml";}i:6;a:3:{s:4:"type";s:3:"ext";s:3:"rel";s:3:"has";s:4:"name";s:4:"pcre";}}s:9:"changelog";a:2:{i:0;a:4:{s:7:"version";s:5:"1.3.1";s:12:"release_date";s:10:"2004-04-06";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:480:"PEAR Installer:
+
+ * Bug #534 pear search doesn't list unstable releases
+ * Bug #933 CMD Usability Patch
+ * Bug #937 throwError() treats every call as static
+ * Bug #964 PEAR_ERROR_EXCEPTION causes fatal error
+ * Bug #1008 safe mode raises warning
+
+PEAR_ErrorStack:
+
+ * Added experimental error handling, designed to eventually replace
+ PEAR_Error. It should be considered experimental until explicitly marked
+ stable. require_once 'PEAR/ErrorStack.php' to use.
+
+
+
+";}i:1;a:4:{s:7:"version";s:3:"1.3";s:12:"release_date";s:10:"2004-02-20";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:621:"PEAR Installer:
+
+* Bug #171 --alldeps with a rel="eq" should install the required version, if possible
+* Bug #249 installing from an url doesnt work
+* Bug #248 --force command does not work as expected
+* Bug #293 [Patch] PEAR_Error not calling static method callbacks for error-handler
+* Bug #324 pear -G gives Fatal Error (PHP-GTK not installed, but error is at engine level)
+* Bug #594 PEAR_Common::analyzeSourceCode fails on string with $var and {
+* Bug #521 Incorrect filename in naming warnings
+* Moved download code into its own class
+* Fully unit tested the installer, packager, downloader, and PEAR_Common
+
+
+
+";}}s:13:"_lastmodified";i:1099255809;} \ No newline at end of file
diff --git a/pearlib/.registry/xml_parser.reg b/pearlib/.registry/xml_parser.reg
new file mode 100644
index 00000000..8c9db767
--- /dev/null
+++ b/pearlib/.registry/xml_parser.reg
@@ -0,0 +1,91 @@
+a:14:{s:8:"provides";a:0:{}s:8:"filelist";a:18:{s:28:"examples\xml_parser_file.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:3:"XML";s:12:"installed_as";s:89:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Parser/examples/xml_parser_file.php";}s:28:"examples\xml_parser_file.xml";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:3:"XML";s:12:"installed_as";s:89:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Parser/examples/xml_parser_file.xml";}s:31:"examples\xml_parser_handler.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:3:"XML";s:12:"installed_as";s:92:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Parser/examples/xml_parser_handler.php";}s:31:"examples\xml_parser_simple1.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:3:"XML";s:12:"installed_as";s:92:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Parser/examples/xml_parser_simple1.php";}s:31:"examples\xml_parser_simple1.xml";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:3:"XML";s:12:"installed_as";s:92:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Parser/examples/xml_parser_simple1.xml";}s:31:"examples\xml_parser_simple2.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:3:"XML";s:12:"installed_as";s:92:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Parser/examples/xml_parser_simple2.php";}s:31:"examples\xml_parser_simple2.xml";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:3:"XML";s:12:"installed_as";s:92:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Parser/examples/xml_parser_simple2.xml";}s:38:"examples\xml_parser_simple_handler.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:3:"XML";s:12:"installed_as";s:99:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Parser/examples/xml_parser_simple_handler.php";}s:17:"Parser\Simple.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:3:"XML";s:12:"installed_as";s:70:"/home/alexey.beta.postgresql.org/alexey/pear/lib/XML/Parser/Simple.php";}s:14:"tests\001.phpt";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:3:"XML";s:12:"installed_as";s:76:"/home/alexey.beta.postgresql.org/alexey/pear/tests/XML_Parser/tests/001.phpt";}s:14:"tests\002.phpt";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:3:"XML";s:12:"installed_as";s:76:"/home/alexey.beta.postgresql.org/alexey/pear/tests/XML_Parser/tests/002.phpt";}s:14:"tests\003.phpt";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:3:"XML";s:12:"installed_as";s:76:"/home/alexey.beta.postgresql.org/alexey/pear/tests/XML_Parser/tests/003.phpt";}s:14:"tests\004.phpt";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:3:"XML";s:12:"installed_as";s:76:"/home/alexey.beta.postgresql.org/alexey/pear/tests/XML_Parser/tests/004.phpt";}s:14:"tests\005.phpt";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:3:"XML";s:12:"installed_as";s:76:"/home/alexey.beta.postgresql.org/alexey/pear/tests/XML_Parser/tests/005.phpt";}s:15:"tests\test2.xml";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:3:"XML";s:12:"installed_as";s:77:"/home/alexey.beta.postgresql.org/alexey/pear/tests/XML_Parser/tests/test2.xml";}s:15:"tests\test3.xml";a:3:{s:4:"role";s:4:"test";s:14:"baseinstalldir";s:3:"XML";s:12:"installed_as";s:77:"/home/alexey.beta.postgresql.org/alexey/pear/tests/XML_Parser/tests/test3.xml";}s:10:"Parser.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:3:"XML";s:12:"installed_as";s:63:"/home/alexey.beta.postgresql.org/alexey/pear/lib/XML/Parser.php";}s:7:"dirtree";a:4:{s:69:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Parser/examples";b:1;s:59:"/home/alexey.beta.postgresql.org/alexey/pear/lib/XML/Parser";b:1;s:52:"/home/alexey.beta.postgresql.org/alexey/pear/lib/XML";b:1;s:67:"/home/alexey.beta.postgresql.org/alexey/pear/tests/XML_Parser/tests";b:1;}}s:7:"package";s:10:"XML_Parser";s:7:"summary";s:46:"XML parsing class based on PHP's bundled expat";s:11:"description";s:634:"This is an XML parser based on PHPs built-in xml extension.
+It supports two basic modes of operation: "func" and "event". In "func" mode, it will look for a function named after each element (xmltag_ELEMENT for start tags and xmltag_ELEMENT_ for end tags), and in "event" mode it uses a set of generic callbacks.
+
+Since version 1.2.0 there's a new XML_Parser_Simple class that makes parsing of most XML documents easier, by automatically providing a stack for the elements.
+Furthermore its now possible to split the parser from the handler object, so you do not have to extend XML_Parser anymore in order to parse a document with it.";s:11:"maintainers";a:3:{i:0;a:4:{s:6:"handle";s:5:"schst";s:4:"name";s:15:"Stephan Schmidt";s:5:"email";s:19:"[email protected]";s:4:"role";s:4:"lead";}i:1;a:4:{s:6:"handle";s:3:"ssb";s:4:"name";s:18:"Stig S�ther Bakken";s:5:"email";s:12:"[email protected]";s:4:"role";s:9:"developer";}i:2;a:4:{s:6:"handle";s:3:"cox";s:4:"name";s:13:"Tomas V.V.Cox";s:5:"email";s:11:"[email protected]";s:4:"role";s:9:"developer";}}s:7:"version";s:5:"1.2.1";s:12:"release_date";s:10:"2004-10-04";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:91:"fixed bug #2442: Call to "xmltag_ELEMENT_" not correctly managed in function funcEndHandler";s:12:"release_deps";a:2:{i:1;a:4:{s:4:"type";s:3:"php";s:3:"rel";s:2:"ge";s:7:"version";s:5:"4.2.0";s:8:"optional";s:2:"no";}i:2;a:4:{s:4:"type";s:3:"pkg";s:3:"rel";s:3:"has";s:8:"optional";s:2:"no";s:4:"name";s:4:"PEAR";}}s:9:"changelog";a:8:{i:0;a:5:{s:7:"version";s:10:"1.1.0beta1";s:12:"release_date";s:10:"2004-04-16";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:941:"- Fixed memory leaks parsing many documents or big files (mroch)
+- Fixed setInput() url detection regex (mroch)
+- Added setInputString() method, allowing strings to be passed as input (schst)
+- Error handling rewritten (cox)
+- Increased the overall parsing speed (cox)
+- Added free() method (schst
+- Added reset() method, that is called when parsing a document so it is possible to parse more than one document per instance (schst)
+- Added error codes (schst)
+- revamped documentation (cox, schst)
+- Fixed bug #516 (url fopen and safe mode) (schst)
+- Fixed bug #637 (dependency on PEAR) (schst)
+- improved parse() and parseString() to be able to parse more than one document (schst)
+- added PHP5 constructor (schst)
+- moved xml_parser_create() to _create() for PHP5 compatibility (schst)
+- added dependency on PHP 4.2
+
+Thanks to Marshall Roch for commments and contributions and Tomas V.V. Cox
+for applying a lot of fixes and improvements.
+
+";}i:1;a:5:{s:7:"version";s:10:"1.1.0beta2";s:12:"release_date";s:10:"2004-04-18";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:987:"beta2:
+- Fixed calling of __construct
+
+beta1:
+- Fixed memory leaks parsing many documents or big files (mroch)
+- Fixed setInput() url detection regex (mroch)
+- Added setInputString() method, allowing strings to be passed as input (schst)
+- Error handling rewritten (cox)
+- Increased the overall parsing speed (cox)
+- Added free() method (schst
+- Added reset() method, that is called when parsing a document so it is possible to parse more than one document per instance (schst)
+- Added error codes (schst)
+- revamped documentation (cox, schst)
+- Fixed bug #516 (url fopen and safe mode) (schst)
+- Fixed bug #637 (dependency on PEAR) (schst)
+- improved parse() and parseString() to be able to parse more than one document (schst)
+- added PHP5 constructor (schst)
+- moved xml_parser_create() to _create() for PHP5 compatibility (schst)
+- added dependency on PHP 4.2
+
+Thanks to Marshall Roch for commments and contributions and Tomas V.V. Cox
+for applying a lot of fixes and improvements.
+
+";}i:2;a:5:{s:7:"version";s:5:"1.1.0";s:12:"release_date";s:10:"2004-04-23";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:941:"- Fixed memory leaks parsing many documents or big files (mroch)
+- Fixed setInput() url detection regex (mroch)
+- Added setInputString() method, allowing strings to be passed as input (schst)
+- Error handling rewritten (cox)
+- Increased the overall parsing speed (cox)
+- Added free() method (schst
+- Added reset() method, that is called when parsing a document so it is possible to parse more than one document per instance (schst)
+- Added error codes (schst)
+- revamped documentation (cox, schst)
+- Fixed bug #516 (url fopen and safe mode) (schst)
+- Fixed bug #637 (dependency on PEAR) (schst)
+- improved parse() and parseString() to be able to parse more than one document (schst)
+- added PHP5 constructor (schst)
+- moved xml_parser_create() to _create() for PHP5 compatibility (schst)
+- added dependency on PHP 4.2
+
+Thanks to Marshall Roch for commments and contributions and Tomas V.V. Cox
+for applying a lot of fixes and improvements.
+
+";}i:3;a:5:{s:7:"version";s:10:"1.2.0beta1";s:12:"release_date";s:10:"2004-05-17";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:150:"added new class XML_Parser_Simple that provides a stack for the elements so the user only needs to implement one method to handle the tag and cdata.
+
+";}i:4;a:5:{s:7:"version";s:10:"1.2.0beta2";s:12:"release_date";s:10:"2004-05-24";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:312:"XML_Parser:
+- fixed bug with setMode()
+- moved the init routines for the handlers in _initHandlers()
+XML_Parser_Simple:
+- fixed bug with character data (did not get parsed)
+- fixed bug with setMode()
+- some refactoring
+- added getCurrentDepth() to retrieve the tag depth
+- added addToData()
+- added new example
+
+";}i:5;a:5:{s:7:"version";s:10:"1.2.0beta3";s:12:"release_date";s:10:"2004-05-25";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:95:"- added setHandlerObj() which allows you to have the parser separate from the handler methods
+
+";}i:6;a:5:{s:7:"version";s:5:"1.2.0";s:12:"release_date";s:10:"2004-05-28";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:448:"- added setHandlerObj() which allows you to have the parser separate from the handler methods
+- fixed bug with setMode()
+- moved the init routines for the handlers in _initHandlers()
+- added new examples
+- fixed test files so they do not fail because of different resource ids
+XML_Parser_Simple:
+- added new class XML_Parser_Simple that provides a stack for the elements so the user only needs to implement one method to handle the tag and cdata.
+
+";}i:7;a:5:{s:7:"version";s:5:"1.2.1";s:12:"release_date";s:10:"2004-10-04";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:93:"fixed bug #2442: Call to "xmltag_ELEMENT_" not correctly managed in function funcEndHandler
+
+";}}s:13:"_lastmodified";i:1099255810;} \ No newline at end of file
diff --git a/pearlib/.registry/xml_rpc.reg b/pearlib/.registry/xml_rpc.reg
new file mode 100644
index 00000000..6a88d25b
--- /dev/null
+++ b/pearlib/.registry/xml_rpc.reg
@@ -0,0 +1,21 @@
+a:13:{s:8:"provides";a:0:{}s:8:"filelist";a:3:{s:7:"RPC.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:3:"XML";s:12:"installed_as";s:60:"/home/alexey.beta.postgresql.org/alexey/pear/lib/XML/RPC.php";}s:10:"Server.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:7:"XML/RPC";s:12:"installed_as";s:67:"/home/alexey.beta.postgresql.org/alexey/pear/lib/XML/RPC/Server.php";}s:7:"dirtree";a:2:{s:52:"/home/alexey.beta.postgresql.org/alexey/pear/lib/XML";b:1;s:56:"/home/alexey.beta.postgresql.org/alexey/pear/lib/XML/RPC";b:1;}}s:7:"package";s:7:"XML_RPC";s:7:"summary";s:42:"PHP implementation of the XML-RPC protocol";s:11:"description";s:125:"This is a PEAR-ified version of Useful inc's XML-RPC
+for PHP. It has support for HTTP transport, proxies and authentication.";s:11:"maintainers";a:1:{i:0;a:4:{s:6:"handle";s:3:"ssb";s:4:"name";s:18:"Stig S�ther Bakken";s:5:"email";s:12:"[email protected]";s:4:"role";s:4:"lead";}}s:7:"version";s:5:"1.1.0";s:12:"release_date";s:10:"2003-03-15";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:517:"- Added support for sequential arrays to XML_RPC_encode() (mroch)
+- Cleaned up new XML_RPC_encode() changes a bit (mroch, pierre)
+- Remove "require_once 'PEAR.php'", include only when needed to raise an error
+- Replace echo and error_log() with raiseError() (mroch)
+- Make all classes extend XML_RPC_Base, which will handle common functions (mroch)
+- be tolerant of junk after methodResponse (Luca Mariano, mroch)
+- Silent notice even in the error log (pierre)
+- fix include of shared xml extension on win32 (pierre)";s:9:"changelog";a:4:{i:0;a:4:{s:7:"version";s:5:"1.0.4";s:12:"release_date";s:10:"2002-10-02";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:70:"* added HTTP proxy authorization support (thanks to Arnaud Limbourg)
+
+";}i:1;a:4:{s:7:"version";s:5:"1.0.3";s:12:"release_date";s:10:"2002-05-19";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:54:"* fix bug when parsing responses with boolean types
+
+
+";}i:2;a:4:{s:7:"version";s:5:"1.0.2";s:12:"release_date";s:10:"2002-04-16";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:51:"* E_ALL fixes
+* fix HTTP response header parsing
+
+
+";}i:3;a:4:{s:7:"version";s:5:"1.0.1";s:12:"release_date";s:10:"2001-09-25";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:134:"This is a PEAR-ified version of Useful Inc's 1.0.1 release.
+Includes an urgent security fix identified by Dan Libby <[email protected]>.
+
+";}}s:13:"_lastmodified";i:1082371281;} \ No newline at end of file
diff --git a/pearlib/.registry/xml_serializer.reg b/pearlib/.registry/xml_serializer.reg
new file mode 100644
index 00000000..ecc34aec
--- /dev/null
+++ b/pearlib/.registry/xml_serializer.reg
@@ -0,0 +1,69 @@
+a:14:{s:8:"provides";a:13:{s:20:"class;XML_Serializer";a:4:{s:4:"type";s:5:"class";s:4:"name";s:14:"XML_Serializer";s:7:"extends";s:4:"PEAR";s:8:"explicit";b:1;}s:35:"function;XML_Serializer::apiVersion";a:3:{s:4:"type";s:8:"function";s:4:"name";s:26:"XML_Serializer::apiVersion";s:8:"explicit";b:1;}s:37:"function;XML_Serializer::resetOptions";a:3:{s:4:"type";s:8:"function";s:4:"name";s:28:"XML_Serializer::resetOptions";s:8:"explicit";b:1;}s:34:"function;XML_Serializer::setOption";a:3:{s:4:"type";s:8:"function";s:4:"name";s:25:"XML_Serializer::setOption";s:8:"explicit";b:1;}s:34:"function;XML_Serializer::serialize";a:3:{s:4:"type";s:8:"function";s:4:"name";s:25:"XML_Serializer::serialize";s:8:"explicit";b:1;}s:42:"function;XML_Serializer::getSerializedData";a:3:{s:4:"type";s:8:"function";s:4:"name";s:33:"XML_Serializer::getSerializedData";s:8:"explicit";b:1;}s:22:"class;XML_Unserializer";a:4:{s:4:"type";s:5:"class";s:4:"name";s:16:"XML_Unserializer";s:7:"extends";s:10:"XML_Parser";s:8:"explicit";b:1;}s:37:"function;XML_Unserializer::apiVersion";a:3:{s:4:"type";s:8:"function";s:4:"name";s:28:"XML_Unserializer::apiVersion";s:8:"explicit";b:1;}s:39:"function;XML_Unserializer::resetOptions";a:3:{s:4:"type";s:8:"function";s:4:"name";s:30:"XML_Unserializer::resetOptions";s:8:"explicit";b:1;}s:36:"function;XML_Unserializer::setOption";a:3:{s:4:"type";s:8:"function";s:4:"name";s:27:"XML_Unserializer::setOption";s:8:"explicit";b:1;}s:38:"function;XML_Unserializer::unserialize";a:3:{s:4:"type";s:8:"function";s:4:"name";s:29:"XML_Unserializer::unserialize";s:8:"explicit";b:1;}s:46:"function;XML_Unserializer::getUnserializedData";a:3:{s:4:"type";s:8:"function";s:4:"name";s:37:"XML_Unserializer::getUnserializedData";s:8:"explicit";b:1;}s:38:"function;XML_Unserializer::getRootName";a:3:{s:4:"type";s:8:"function";s:4:"name";s:29:"XML_Unserializer::getRootName";s:8:"explicit";b:1;}}s:8:"filelist";a:15:{s:12:"doc/todo.txt";a:4:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:3:"XML";s:6:"md5sum";s:32:"5715d23f0125e0c0ce23e9c3670565aa";s:12:"installed_as";s:77:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Serializer/doc/todo.txt";}s:20:"examples/example.xml";a:4:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:3:"XML";s:6:"md5sum";s:32:"e9eabbdfdcfc442be88d446648206c3c";s:12:"installed_as";s:85:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Serializer/examples/example.xml";}s:34:"examples/serializeIndexedArray.php";a:4:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:3:"XML";s:6:"md5sum";s:32:"fbe6b72ad3f0faf855bbc6adf3fc9990";s:12:"installed_as";s:99:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Serializer/examples/serializeIndexedArray.php";}s:28:"examples/serializeObject.php";a:4:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:3:"XML";s:6:"md5sum";s:32:"501899ed838441d12fb8bd1495d6bb96";s:12:"installed_as";s:93:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Serializer/examples/serializeObject.php";}s:25:"examples/serializeRDF.php";a:4:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:3:"XML";s:6:"md5sum";s:32:"94cec5d98ba262f9332d22c929272f87";s:12:"installed_as";s:90:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Serializer/examples/serializeRDF.php";}s:36:"examples/serializeWithAttributes.php";a:4:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:3:"XML";s:6:"md5sum";s:32:"c301e3dfee0899abfbd5017049864a19";s:12:"installed_as";s:101:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Serializer/examples/serializeWithAttributes.php";}s:29:"examples/serializeWithDtd.php";a:4:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:3:"XML";s:6:"md5sum";s:32:"09d739357fbba5e29d017c2dba21b8d6";s:12:"installed_as";s:94:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Serializer/examples/serializeWithDtd.php";}s:44:"examples/serializeWithIndentedAttributes.php";a:4:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:3:"XML";s:6:"md5sum";s:32:"5bebe2bcb9ca5888b79a22b32aa22e41";s:12:"installed_as";s:109:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Serializer/examples/serializeWithIndentedAttributes.php";}s:30:"examples/unserializeAnyXML.php";a:4:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:3:"XML";s:6:"md5sum";s:32:"bb153dd8f553dd137978b6e6f55bf569";s:12:"installed_as";s:95:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Serializer/examples/unserializeAnyXML.php";}s:30:"examples/unserializeObject.php";a:4:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:3:"XML";s:6:"md5sum";s:32:"1f4d25e75944f47ce9e415e07c4a3b73";s:12:"installed_as";s:95:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Serializer/examples/unserializeObject.php";}s:27:"examples/unserializeRDF.php";a:4:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:3:"XML";s:6:"md5sum";s:32:"a6d8093c5be25c635d168b4268c2ac4c";s:12:"installed_as";s:92:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Serializer/examples/unserializeRDF.php";}s:38:"examples/unserializeWithAttributes.php";a:4:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:3:"XML";s:6:"md5sum";s:32:"bcf1a95eff1cc55f66b48c7ae35d9d0b";s:12:"installed_as";s:103:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Serializer/examples/unserializeWithAttributes.php";}s:14:"Serializer.php";a:4:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:3:"XML";s:6:"md5sum";s:32:"d4a034471af42df299b950dcd929c113";s:12:"installed_as";s:67:"/home/alexey.beta.postgresql.org/alexey/pear/lib/XML/Serializer.php";}s:16:"Unserializer.php";a:4:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:3:"XML";s:6:"md5sum";s:32:"d8da21a6f9ced2e34608075c4ac92c3f";s:12:"installed_as";s:69:"/home/alexey.beta.postgresql.org/alexey/pear/lib/XML/Unserializer.php";}s:7:"dirtree";a:3:{s:68:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Serializer/doc";b:1;s:73:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Serializer/examples";b:1;s:52:"/home/alexey.beta.postgresql.org/alexey/pear/lib/XML";b:1;}}s:7:"package";s:14:"XML_Serializer";s:7:"summary";s:106:"Swiss-army knive for reading and writing XML files. Creates XML files from data structures and vice versa.";s:11:"description";s:842:"XML_Serializer serializes complex data structures like arrays or object as XML documents.
+This class helps you generating any XML document you require without the need for DOM.
+Furthermore this package can be used as a replacement to serialize() und unserialize() as it comes with a matching XML_Unserializer that is able to create PHP data strcutures (like arrays and objects) from XML documents, if type hints are available.
+If you use the XML_Unserialzer on standard XML files, it will try to guess how it has to be unserialized. In most cases it does exactly what you expect it to do.
+Try reading a RSS file with XML_Unserializer and you have the whole RSS file in a structured array or even a collection of objects, similar to XML_RSS.
+
+Since version 0.8 the package is able to treat XML documents like the simplexml extension of PHP 5.";s:11:"maintainers";a:1:{i:0;a:4:{s:6:"handle";s:5:"schst";s:4:"name";s:15:"Stephan Schmidt";s:5:"email";s:13:"[email protected]";s:4:"role";s:4:"lead";}}s:7:"version";s:5:"0.9.1";s:12:"release_date";s:10:"2003-10-26";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:14:"fixed bug #105";s:12:"release_deps";a:2:{i:1;a:4:{s:4:"type";s:3:"pkg";s:3:"rel";s:2:"ge";s:7:"version";s:5:"0.4.1";s:4:"name";s:8:"XML_Util";}i:2;a:3:{s:4:"type";s:3:"pkg";s:3:"rel";s:3:"has";s:4:"name";s:10:"XML_Parser";}}s:9:"changelog";a:8:{i:0;a:4:{s:7:"version";s:3:"0.2";s:12:"release_date";s:10:"2003-08-03";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:16:"inital release
+
+";}i:1;a:4:{s:7:"version";s:3:"0.4";s:12:"release_date";s:10:"2003-08-12";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:233:"added Unserializer
+removed bug with object typehint
+removed bug with unnameditem w/o _originalKey attribute
+uses XML_Util to validate tagname
+serializer does not break on resources anymore
+added support for __sleep() and __wakeup()
+
+";}i:2;a:5:{s:7:"version";s:3:"0.5";s:12:"release_date";s:10:"2003-08-14";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:281:"Unserializer is now able to unserialize XML files w/o type hints by automatically transforming multiple occurences of the same tag to an indexed array
+This is now similar to ext/simpleXML in PHP5 that means it can be used to parse any XML file, try it with your favorite RSS feed
+
+";}i:3;a:5:{s:7:"version";s:3:"0.6";s:12:"release_date";s:10:"2003-08-21";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:394:"Unserializer is able to return the name of the root tag,
+added 'complexType' option to XML_Unserializer,
+added possibility to create nested objects from any XML file,
+tagname is used as classname if no other class is specified,
+added 'overrideOptions' option that can be used to restore the default options for a serialization,
+added 'keyAttribute' option to XML_Unserializer,
+added Todo list
+
+";}i:4;a:5:{s:7:"version";s:3:"0.8";s:12:"release_date";s:10:"2003-09-23";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:1528:"XML_Serializer:
+* added option: scalarAsAttribute to create tags with attributes from an array or object,
+* added option: prependAttributes to enable a prepend string for attributes (ensure they are different from type hints),
+* added option: attributeIndent to allow multiline tags, if a tag has several attributes; this makes it easier to read (requested by Yavor Shahpasov),
+* now requires XML_Util 0.4.1 or newer,
+* added option: mode => simplexml, allows you to serialize indexed arrays like simpleXML of PHP5 does: tags use name of the parent structure and are in the same depth (this is still in alpha state, not thoroughly tested)
+
+XML_Unserializer:
+* added option: parseAttributes which allows you to parse tags with attributes,
+* added option: attributesArray to specify the name of the array in which attributes should be stored,
+* added option: prependAttributes to specify a prepend string for the generated array keys,
+* added option: contentName to specify the key for the content of a tag that contains also attributes
+* added option: tagMap, to change the names of the tags while parsing
+
+Both:
+* removed bug that occured when instantiating w/o options (reported by Alan Knowles),
+* added options: keyAttribute, typeAttribute and classAtribute to allow user to specify attribute names for typehints,
+* added resetOptions(),
+* added setOption(),
+* added option: overrideOptions that allows you to reset to defaults for one call of serialize or unserialize,
+* added some very small docs to defaultOptions arrays
+
+";}i:5;a:5:{s:7:"version";s:5:"0.8.1";s:12:"release_date";s:10:"2003-09-23";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:373:"Bugfix release
+
+XML_Serializer:
+* removed bug when using mode => simplexml and indexed arrays contained only scalar values,
+* removed bug with missing _originalKey attribute when serializing objects
+
+XML_Unserializer:
+* added check for set{$propName} method to use methods to set properties in Unserializer instead of direct access
+
+";}i:6;a:5:{s:7:"version";s:3:"0.9";s:12:"release_date";s:10:"2003-10-13";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:366:"XML_Serializer:
++ added support for doctype declaration (requested by Lapo 'Raist' Luchini),
+* changed tagName option to rootName option (tagName is still supported but deprecated),
++ added rootAttributes option (thanks to Lapo 'Raist' Luchini),
++ added rootName option for serializing objects
+
+XML_Unserializer:
+* removed bug with unserializing aggregated objects
+
+";}i:7;a:5:{s:7:"version";s:5:"0.9.1";s:12:"release_date";s:10:"2003-10-26";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:16:"fixed bug #105
+
+";}}s:13:"_lastmodified";i:1082371404;} \ No newline at end of file
diff --git a/pearlib/.registry/xml_util.reg b/pearlib/.registry/xml_util.reg
new file mode 100644
index 00000000..9304799f
--- /dev/null
+++ b/pearlib/.registry/xml_util.reg
@@ -0,0 +1,40 @@
+a:14:{s:8:"provides";a:0:{}s:8:"filelist";a:4:{s:20:"examples\example.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:3:"XML";s:12:"installed_as";s:79:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Util/examples/example.php";}s:21:"examples\example2.php";a:3:{s:4:"role";s:3:"doc";s:14:"baseinstalldir";s:3:"XML";s:12:"installed_as";s:80:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Util/examples/example2.php";}s:8:"Util.php";a:3:{s:4:"role";s:3:"php";s:14:"baseinstalldir";s:3:"XML";s:12:"installed_as";s:61:"/home/alexey.beta.postgresql.org/alexey/pear/lib/XML/Util.php";}s:7:"dirtree";a:2:{s:67:"/home/alexey.beta.postgresql.org/alexey/pear/docs/XML_Util/examples";b:1;s:52:"/home/alexey.beta.postgresql.org/alexey/pear/lib/XML";b:1;}}s:7:"package";s:8:"XML_Util";s:7:"summary";s:18:"XML utility class.";s:11:"description";s:191:"Selection of methods that are often needed when working with XML documents. Functionality includes creating of attribute lists from arrays, creation of tags, validation of XML names and more.";s:11:"maintainers";a:2:{i:0;a:4:{s:6:"handle";s:5:"schst";s:4:"name";s:15:"Stephan Schmidt";s:5:"email";s:19:"[email protected]";s:4:"role";s:4:"lead";}i:1;a:4:{s:6:"handle";s:5:"davey";s:4:"name";s:12:"Davey Shafik";s:5:"email";s:13:"[email protected]";s:4:"role";s:6:"helper";}}s:7:"version";s:5:"1.0.0";s:12:"release_date";s:10:"2004-10-28";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:41:"- Added reverseEntities() (request #2639)";s:12:"release_deps";a:3:{i:1;a:4:{s:4:"type";s:3:"ext";s:3:"rel";s:3:"has";s:8:"optional";s:2:"no";s:4:"name";s:4:"pcre";}i:2;a:4:{s:4:"type";s:3:"pkg";s:3:"rel";s:3:"has";s:8:"optional";s:2:"no";s:4:"name";s:4:"PEAR";}i:3;a:4:{s:4:"type";s:3:"php";s:3:"rel";s:2:"ge";s:7:"version";s:5:"4.2.0";s:8:"optional";s:2:"no";}}s:9:"changelog";a:13:{i:0;a:4:{s:7:"version";s:3:"0.1";s:12:"release_date";s:10:"2003-08-01";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:16:"inital release
+
+";}i:1;a:4:{s:7:"version";s:5:"0.1.1";s:12:"release_date";s:10:"2003-08-02";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:43:"bugfix: removed bug in createTagFromArray
+
+";}i:2;a:5:{s:7:"version";s:3:"0.2";s:12:"release_date";s:10:"2003-08-12";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:41:"added XML_Util::getDocTypeDeclaration()
+
+";}i:3;a:5:{s:7:"version";s:5:"0.2.1";s:12:"release_date";s:10:"2003-09-05";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:72:"fixed bug with zero as tag content in createTagFromArray and createTag
+
+";}i:4;a:5:{s:7:"version";s:3:"0.3";s:12:"release_date";s:10:"2003-09-12";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:51:"added createStartElement() and createEndElement()
+
+";}i:5;a:5:{s:7:"version";s:3:"0.4";s:12:"release_date";s:10:"2003-09-21";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:134:"added createCDataSection(),
+added support for CData sections in createTag* methods,
+fixed bug #23,
+fixed bug in splitQualifiedName()
+
+";}i:6;a:5:{s:7:"version";s:3:"0.5";s:12:"release_date";s:10:"2003-09-23";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:172:"added support for multiline attributes in attributesToString(), createTag*() and createStartElement (requested by Yavor Shahpasov for XML_Serializer),
+added createComment
+
+";}i:7;a:5:{s:7:"version";s:5:"0.5.1";s:12:"release_date";s:10:"2003-09-26";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:104:"added default namespace parameter (optional) in splitQualifiedName() (requested by Sebastian Bergmann)
+
+";}i:8;a:5:{s:7:"version";s:5:"0.5.2";s:12:"release_date";s:10:"2003-11-22";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:80:"now creates XHTML compliant empty tags (Davey),
+minor whitespace fixes (Davey)
+
+";}i:9;a:5:{s:7:"version";s:10:"0.6.0beta1";s:12:"release_date";s:10:"2004-05-24";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:4:"beta";s:13:"release_notes";s:569:"- Fixed bug 1438 (namespaces not accepted for isValidName()) (thanks to davey)
+- added optional parameter to replaceEntities() to define the set of entities to replace
+- added optional parameter to attributesToString() to define, whether entities should be replaced (requested by Sebastian Bergmann)
+- allowed second parameter to XML_Util::attributesToString() to be an array containing options (easier to use, if you only need to set the last parameter)
+- introduced XML_Util::raiseError() to avoid the necessity of including PEAR.php, will only be included on error
+
+";}i:10;a:5:{s:7:"version";s:5:"0.6.0";s:12:"release_date";s:10:"2004-06-07";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:569:"- Fixed bug 1438 (namespaces not accepted for isValidName()) (thanks to davey)
+- added optional parameter to replaceEntities() to define the set of entities to replace
+- added optional parameter to attributesToString() to define, whether entities should be replaced (requested by Sebastian Bergmann)
+- allowed second parameter to XML_Util::attributesToString() to be an array containing options (easier to use, if you only need to set the last parameter)
+- introduced XML_Util::raiseError() to avoid the necessity of including PEAR.php, will only be included on error
+
+";}i:11;a:5:{s:7:"version";s:5:"0.6.1";s:12:"release_date";s:10:"2004-10-28";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:105:"- Added check for tag name (either as local part or qualified name) in createTagFromArray() (bug #1083)
+
+";}i:12;a:5:{s:7:"version";s:5:"1.0.0";s:12:"release_date";s:10:"2004-10-28";s:15:"release_license";s:11:"PHP License";s:13:"release_state";s:6:"stable";s:13:"release_notes";s:43:"- Added reverseEntities() (request #2639)
+
+";}}s:13:"_lastmodified";i:1099255810;} \ No newline at end of file
diff --git a/pearlib/Archive/Tar.php b/pearlib/Archive/Tar.php
new file mode 100644
index 00000000..1ec72023
--- /dev/null
+++ b/pearlib/Archive/Tar.php
@@ -0,0 +1,1660 @@
+<?php
+/* vim: set ts=4 sw=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Vincent Blavet <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Tar.php,v 1.19 2004/05/08 09:56:26 vblavet Exp $
+
+require_once 'PEAR.php';
+
+
+define ('ARCHIVE_TAR_ATT_SEPARATOR', 90001);
+
+/**
+* Creates a (compressed) Tar archive
+*
+* @author Vincent Blavet <[email protected]>
+* @version $Revision: 1.19 $
+* @package Archive
+*/
+class Archive_Tar extends PEAR
+{
+ /**
+ * @var string Name of the Tar
+ */
+ var $_tarname='';
+
+ /**
+ * @var boolean if true, the Tar file will be gzipped
+ */
+ var $_compress=false;
+
+ /**
+ * @var string Type of compression : 'none', 'gz' or 'bz2'
+ */
+ var $_compress_type='none';
+
+ /**
+ * @var string Explode separator
+ */
+ var $_separator=' ';
+
+ /**
+ * @var file descriptor
+ */
+ var $_file=0;
+
+ /**
+ * @var string Local Tar name of a remote Tar (http:// or ftp://)
+ */
+ var $_temp_tarname='';
+
+ // {{{ constructor
+ /**
+ * Archive_Tar Class constructor. This flavour of the constructor only
+ * declare a new Archive_Tar object, identifying it by the name of the
+ * tar file.
+ * If the compress argument is set the tar will be read or created as a
+ * gzip or bz2 compressed TAR file.
+ *
+ * @param string $p_tarname The name of the tar archive to create
+ * @param string $p_compress can be null, 'gz' or 'bz2'. This
+ * parameter indicates if gzip or bz2 compression
+ * is required. For compatibility reason the
+ * boolean value 'true' means 'gz'.
+ * @access public
+ */
+ function Archive_Tar($p_tarname, $p_compress = null)
+ {
+ $this->PEAR();
+ $this->_compress = false;
+ $this->_compress_type = 'none';
+ if ($p_compress === null) {
+ if (@file_exists($p_tarname)) {
+ if ($fp = @fopen($p_tarname, "rb")) {
+ // look for gzip magic cookie
+ $data = fread($fp, 2);
+ fclose($fp);
+ if ($data == "\37\213") {
+ $this->_compress = true;
+ $this->_compress_type = 'gz';
+ // No sure it's enought for a magic code ....
+ } elseif ($data == "BZ") {
+ $this->_compress = true;
+ $this->_compress_type = 'bz2';
+ }
+ }
+ } else {
+ // probably a remote file or some file accessible
+ // through a stream interface
+ if (substr($p_tarname, -2) == 'gz') {
+ $this->_compress = true;
+ $this->_compress_type = 'gz';
+ } elseif ((substr($p_tarname, -3) == 'bz2') ||
+ (substr($p_tarname, -2) == 'bz')) {
+ $this->_compress = true;
+ $this->_compress_type = 'bz2';
+ }
+ }
+ } else {
+ if (($p_compress === true) || ($p_compress == 'gz')) {
+ $this->_compress = true;
+ $this->_compress_type = 'gz';
+ } else if ($p_compress == 'bz2') {
+ $this->_compress = true;
+ $this->_compress_type = 'bz2';
+ }
+ }
+ $this->_tarname = $p_tarname;
+ if ($this->_compress) { // assert zlib or bz2 extension support
+ if ($this->_compress_type == 'gz')
+ $extname = 'zlib';
+ else if ($this->_compress_type == 'bz2')
+ $extname = 'bz2';
+
+ if (!extension_loaded($extname)) {
+ PEAR::loadExtension($extname);
+ }
+ if (!extension_loaded($extname)) {
+ die("The extension '$extname' couldn't be found.\n".
+ "Please make sure your version of PHP was built ".
+ "with '$extname' support.\n");
+ return false;
+ }
+ }
+ }
+ // }}}
+
+ // {{{ destructor
+ function _Archive_Tar()
+ {
+ $this->_close();
+ // ----- Look for a local copy to delete
+ if ($this->_temp_tarname != '')
+ @unlink($this->_temp_tarname);
+ $this->_PEAR();
+ }
+ // }}}
+
+ // {{{ create()
+ /**
+ * This method creates the archive file and add the files / directories
+ * that are listed in $p_filelist.
+ * If a file with the same name exist and is writable, it is replaced
+ * by the new tar.
+ * The method return false and a PEAR error text.
+ * The $p_filelist parameter can be an array of string, each string
+ * representing a filename or a directory name with their path if
+ * needed. It can also be a single string with names separated by a
+ * single blank.
+ * For each directory added in the archive, the files and
+ * sub-directories are also added.
+ * See also createModify() method for more details.
+ *
+ * @param array $p_filelist An array of filenames and directory names, or a single
+ * string with names separated by a single blank space.
+ * @return true on success, false on error.
+ * @see createModify()
+ * @access public
+ */
+ function create($p_filelist)
+ {
+ return $this->createModify($p_filelist, '', '');
+ }
+ // }}}
+
+ // {{{ add()
+ /**
+ * This method add the files / directories that are listed in $p_filelist in
+ * the archive. If the archive does not exist it is created.
+ * The method return false and a PEAR error text.
+ * The files and directories listed are only added at the end of the archive,
+ * even if a file with the same name is already archived.
+ * See also createModify() method for more details.
+ *
+ * @param array $p_filelist An array of filenames and directory names, or a single
+ * string with names separated by a single blank space.
+ * @return true on success, false on error.
+ * @see createModify()
+ * @access public
+ */
+ function add($p_filelist)
+ {
+ return $this->addModify($p_filelist, '', '');
+ }
+ // }}}
+
+ // {{{ extract()
+ function extract($p_path='')
+ {
+ return $this->extractModify($p_path, '');
+ }
+ // }}}
+
+ // {{{ listContent()
+ function listContent()
+ {
+ $v_list_detail = array();
+
+ if ($this->_openRead()) {
+ if (!$this->_extractList('', $v_list_detail, "list", '', '')) {
+ unset($v_list_detail);
+ $v_list_detail = 0;
+ }
+ $this->_close();
+ }
+
+ return $v_list_detail;
+ }
+ // }}}
+
+ // {{{ createModify()
+ /**
+ * This method creates the archive file and add the files / directories
+ * that are listed in $p_filelist.
+ * If the file already exists and is writable, it is replaced by the
+ * new tar. It is a create and not an add. If the file exists and is
+ * read-only or is a directory it is not replaced. The method return
+ * false and a PEAR error text.
+ * The $p_filelist parameter can be an array of string, each string
+ * representing a filename or a directory name with their path if
+ * needed. It can also be a single string with names separated by a
+ * single blank.
+ * The path indicated in $p_remove_dir will be removed from the
+ * memorized path of each file / directory listed when this path
+ * exists. By default nothing is removed (empty path '')
+ * The path indicated in $p_add_dir will be added at the beginning of
+ * the memorized path of each file / directory listed. However it can
+ * be set to empty ''. The adding of a path is done after the removing
+ * of path.
+ * The path add/remove ability enables the user to prepare an archive
+ * for extraction in a different path than the origin files are.
+ * See also addModify() method for file adding properties.
+ *
+ * @param array $p_filelist An array of filenames and directory names, or a single
+ * string with names separated by a single blank space.
+ * @param string $p_add_dir A string which contains a path to be added to the
+ * memorized path of each element in the list.
+ * @param string $p_remove_dir A string which contains a path to be removed from
+ * the memorized path of each element in the list, when
+ * relevant.
+ * @return boolean true on success, false on error.
+ * @access public
+ * @see addModify()
+ */
+ function createModify($p_filelist, $p_add_dir, $p_remove_dir='')
+ {
+ $v_result = true;
+
+ if (!$this->_openWrite())
+ return false;
+
+ if ($p_filelist != '') {
+ if (is_array($p_filelist))
+ $v_list = $p_filelist;
+ elseif (is_string($p_filelist))
+ $v_list = explode($this->_separator, $p_filelist);
+ else {
+ $this->_cleanFile();
+ $this->_error('Invalid file list');
+ return false;
+ }
+
+ $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir);
+ }
+
+ if ($v_result) {
+ $this->_writeFooter();
+ $this->_close();
+ } else
+ $this->_cleanFile();
+
+ return $v_result;
+ }
+ // }}}
+
+ // {{{ addModify()
+ /**
+ * This method add the files / directories listed in $p_filelist at the
+ * end of the existing archive. If the archive does not yet exists it
+ * is created.
+ * The $p_filelist parameter can be an array of string, each string
+ * representing a filename or a directory name with their path if
+ * needed. It can also be a single string with names separated by a
+ * single blank.
+ * The path indicated in $p_remove_dir will be removed from the
+ * memorized path of each file / directory listed when this path
+ * exists. By default nothing is removed (empty path '')
+ * The path indicated in $p_add_dir will be added at the beginning of
+ * the memorized path of each file / directory listed. However it can
+ * be set to empty ''. The adding of a path is done after the removing
+ * of path.
+ * The path add/remove ability enables the user to prepare an archive
+ * for extraction in a different path than the origin files are.
+ * If a file/dir is already in the archive it will only be added at the
+ * end of the archive. There is no update of the existing archived
+ * file/dir. However while extracting the archive, the last file will
+ * replace the first one. This results in a none optimization of the
+ * archive size.
+ * If a file/dir does not exist the file/dir is ignored. However an
+ * error text is send to PEAR error.
+ * If a file/dir is not readable the file/dir is ignored. However an
+ * error text is send to PEAR error.
+ *
+ * @param array $p_filelist An array of filenames and directory names, or a single
+ * string with names separated by a single blank space.
+ * @param string $p_add_dir A string which contains a path to be added to the
+ * memorized path of each element in the list.
+ * @param string $p_remove_dir A string which contains a path to be removed from
+ * the memorized path of each element in the list, when
+ * relevant.
+ * @return true on success, false on error.
+ * @access public
+ */
+ function addModify($p_filelist, $p_add_dir, $p_remove_dir='')
+ {
+ $v_result = true;
+
+ if (!@is_file($this->_tarname))
+ $v_result = $this->createModify($p_filelist, $p_add_dir, $p_remove_dir);
+ else {
+ if (is_array($p_filelist))
+ $v_list = $p_filelist;
+ elseif (is_string($p_filelist))
+ $v_list = explode($this->_separator, $p_filelist);
+ else {
+ $this->_error('Invalid file list');
+ return false;
+ }
+
+ $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir);
+ }
+
+ return $v_result;
+ }
+ // }}}
+
+ // {{{ addString()
+ /**
+ * This method add a single string as a file at the
+ * end of the existing archive. If the archive does not yet exists it
+ * is created.
+ *
+ * @param string $p_filename A string which contains the full filename path
+ * that will be associated with the string.
+ * @param string $p_string The content of the file added in the archive.
+ * @return true on success, false on error.
+ * @access public
+ */
+ function addString($p_filename, $p_string)
+ {
+ $v_result = true;
+
+ if (!@is_file($this->_tarname)) {
+ if (!$this->_openWrite()) {
+ return false;
+ }
+ $this->_close();
+ }
+
+ if (!$this->_openAppend())
+ return false;
+
+ // Need to check the get back to the temporary file ? ....
+ $v_result = $this->_addString($p_filename, $p_string);
+
+ $this->_writeFooter();
+
+ $this->_close();
+
+ return $v_result;
+ }
+ // }}}
+
+ // {{{ extractModify()
+ /**
+ * This method extract all the content of the archive in the directory
+ * indicated by $p_path. When relevant the memorized path of the
+ * files/dir can be modified by removing the $p_remove_path path at the
+ * beginning of the file/dir path.
+ * While extracting a file, if the directory path does not exists it is
+ * created.
+ * While extracting a file, if the file already exists it is replaced
+ * without looking for last modification date.
+ * While extracting a file, if the file already exists and is write
+ * protected, the extraction is aborted.
+ * While extracting a file, if a directory with the same name already
+ * exists, the extraction is aborted.
+ * While extracting a directory, if a file with the same name already
+ * exists, the extraction is aborted.
+ * While extracting a file/directory if the destination directory exist
+ * and is write protected, or does not exist but can not be created,
+ * the extraction is aborted.
+ * If after extraction an extracted file does not show the correct
+ * stored file size, the extraction is aborted.
+ * When the extraction is aborted, a PEAR error text is set and false
+ * is returned. However the result can be a partial extraction that may
+ * need to be manually cleaned.
+ *
+ * @param string $p_path The path of the directory where the files/dir need to by
+ * extracted.
+ * @param string $p_remove_path Part of the memorized path that can be removed if
+ * present at the beginning of the file/dir path.
+ * @return boolean true on success, false on error.
+ * @access public
+ * @see extractList()
+ */
+ function extractModify($p_path, $p_remove_path)
+ {
+ $v_result = true;
+ $v_list_detail = array();
+
+ if ($v_result = $this->_openRead()) {
+ $v_result = $this->_extractList($p_path, $v_list_detail, "complete", 0, $p_remove_path);
+ $this->_close();
+ }
+
+ return $v_result;
+ }
+ // }}}
+
+ // {{{ extractInString()
+ /**
+ * This method extract from the archive one file identified by $p_filename.
+ * The return value is a string with the file content, or NULL on error.
+ * @param string $p_filename The path of the file to extract in a string.
+ * @return a string with the file content or NULL.
+ * @access public
+ */
+ function extractInString($p_filename)
+ {
+ if ($this->_openRead()) {
+ $v_result = $this->_extractInString($p_filename);
+ $this->_close();
+ } else {
+ $v_result = NULL;
+ }
+
+ return $v_result;
+ }
+ // }}}
+
+ // {{{ extractList()
+ /**
+ * This method extract from the archive only the files indicated in the
+ * $p_filelist. These files are extracted in the current directory or
+ * in the directory indicated by the optional $p_path parameter.
+ * If indicated the $p_remove_path can be used in the same way as it is
+ * used in extractModify() method.
+ * @param array $p_filelist An array of filenames and directory names, or a single
+ * string with names separated by a single blank space.
+ * @param string $p_path The path of the directory where the files/dir need to by
+ * extracted.
+ * @param string $p_remove_path Part of the memorized path that can be removed if
+ * present at the beginning of the file/dir path.
+ * @return true on success, false on error.
+ * @access public
+ * @see extractModify()
+ */
+ function extractList($p_filelist, $p_path='', $p_remove_path='')
+ {
+ $v_result = true;
+ $v_list_detail = array();
+
+ if (is_array($p_filelist))
+ $v_list = $p_filelist;
+ elseif (is_string($p_filelist))
+ $v_list = explode($this->_separator, $p_filelist);
+ else {
+ $this->_error('Invalid string list');
+ return false;
+ }
+
+ if ($v_result = $this->_openRead()) {
+ $v_result = $this->_extractList($p_path, $v_list_detail, "partial", $v_list, $p_remove_path);
+ $this->_close();
+ }
+
+ return $v_result;
+ }
+ // }}}
+
+ // {{{ setAttribute()
+ /**
+ * This method set specific attributes of the archive. It uses a variable
+ * list of parameters, in the format attribute code + attribute values :
+ * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ',');
+ * @param mixed $argv variable list of attributes and values
+ * @return true on success, false on error.
+ * @access public
+ */
+ function setAttribute()
+ {
+ $v_result = true;
+
+ // ----- Get the number of variable list of arguments
+ if (($v_size = func_num_args()) == 0) {
+ return true;
+ }
+
+ // ----- Get the arguments
+ $v_att_list = &func_get_args();
+
+ // ----- Read the attributes
+ $i=0;
+ while ($i<$v_size) {
+
+ // ----- Look for next option
+ switch ($v_att_list[$i]) {
+ // ----- Look for options that request a string value
+ case ARCHIVE_TAR_ATT_SEPARATOR :
+ // ----- Check the number of parameters
+ if (($i+1) >= $v_size) {
+ $this->_error('Invalid number of parameters for attribute ARCHIVE_TAR_ATT_SEPARATOR');
+ return false;
+ }
+
+ // ----- Get the value
+ $this->_separator = $v_att_list[$i+1];
+ $i++;
+ break;
+
+ default :
+ $this->_error('Unknow attribute code '.$v_att_list[$i].'');
+ return false;
+ }
+
+ // ----- Next attribute
+ $i++;
+ }
+
+ return $v_result;
+ }
+ // }}}
+
+ // {{{ _error()
+ function _error($p_message)
+ {
+ // ----- To be completed
+ $this->raiseError($p_message);
+ }
+ // }}}
+
+ // {{{ _warning()
+ function _warning($p_message)
+ {
+ // ----- To be completed
+ $this->raiseError($p_message);
+ }
+ // }}}
+
+ // {{{ _openWrite()
+ function _openWrite()
+ {
+ if ($this->_compress_type == 'gz')
+ $this->_file = @gzopen($this->_tarname, "wb");
+ else if ($this->_compress_type == 'bz2')
+ $this->_file = @bzopen($this->_tarname, "wb");
+ else if ($this->_compress_type == 'none')
+ $this->_file = @fopen($this->_tarname, "wb");
+ else
+ $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
+
+ if ($this->_file == 0) {
+ $this->_error('Unable to open in write mode \''.$this->_tarname.'\'');
+ return false;
+ }
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _openRead()
+ function _openRead()
+ {
+ if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') {
+
+ // ----- Look if a local copy need to be done
+ if ($this->_temp_tarname == '') {
+ $this->_temp_tarname = uniqid('tar').'.tmp';
+ if (!$v_file_from = @fopen($this->_tarname, 'rb')) {
+ $this->_error('Unable to open in read mode \''.$this->_tarname.'\'');
+ $this->_temp_tarname = '';
+ return false;
+ }
+ if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) {
+ $this->_error('Unable to open in write mode \''.$this->_temp_tarname.'\'');
+ $this->_temp_tarname = '';
+ return false;
+ }
+ while ($v_data = @fread($v_file_from, 1024))
+ @fwrite($v_file_to, $v_data);
+ @fclose($v_file_from);
+ @fclose($v_file_to);
+ }
+
+ // ----- File to open if the local copy
+ $v_filename = $this->_temp_tarname;
+
+ } else
+ // ----- File to open if the normal Tar file
+ $v_filename = $this->_tarname;
+
+ if ($this->_compress_type == 'gz')
+ $this->_file = @gzopen($v_filename, "rb");
+ else if ($this->_compress_type == 'bz2')
+ $this->_file = @bzopen($v_filename, "rb");
+ else if ($this->_compress_type == 'none')
+ $this->_file = @fopen($v_filename, "rb");
+ else
+ $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
+
+ if ($this->_file == 0) {
+ $this->_error('Unable to open in read mode \''.$v_filename.'\'');
+ return false;
+ }
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _openReadWrite()
+ function _openReadWrite()
+ {
+ if ($this->_compress_type == 'gz')
+ $this->_file = @gzopen($this->_tarname, "r+b");
+ else if ($this->_compress_type == 'bz2')
+ $this->_file = @bzopen($this->_tarname, "r+b");
+ else if ($this->_compress_type == 'none')
+ $this->_file = @fopen($this->_tarname, "r+b");
+ else
+ $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
+
+ if ($this->_file == 0) {
+ $this->_error('Unable to open in read/write mode \''.$this->_tarname.'\'');
+ return false;
+ }
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _close()
+ function _close()
+ {
+ //if (isset($this->_file)) {
+ if (is_resource($this->_file)) {
+ if ($this->_compress_type == 'gz')
+ @gzclose($this->_file);
+ else if ($this->_compress_type == 'bz2')
+ @bzclose($this->_file);
+ else if ($this->_compress_type == 'none')
+ @fclose($this->_file);
+ else
+ $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
+
+ $this->_file = 0;
+ }
+
+ // ----- Look if a local copy need to be erase
+ // Note that it might be interesting to keep the url for a time : ToDo
+ if ($this->_temp_tarname != '') {
+ @unlink($this->_temp_tarname);
+ $this->_temp_tarname = '';
+ }
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _cleanFile()
+ function _cleanFile()
+ {
+ $this->_close();
+
+ // ----- Look for a local copy
+ if ($this->_temp_tarname != '') {
+ // ----- Remove the local copy but not the remote tarname
+ @unlink($this->_temp_tarname);
+ $this->_temp_tarname = '';
+ } else {
+ // ----- Remove the local tarname file
+ @unlink($this->_tarname);
+ }
+ $this->_tarname = '';
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _writeBlock()
+ function _writeBlock($p_binary_data, $p_len=null)
+ {
+ if (is_resource($this->_file)) {
+ if ($p_len === null) {
+ if ($this->_compress_type == 'gz')
+ @gzputs($this->_file, $p_binary_data);
+ else if ($this->_compress_type == 'bz2')
+ @bzwrite($this->_file, $p_binary_data);
+ else if ($this->_compress_type == 'none')
+ @fputs($this->_file, $p_binary_data);
+ else
+ $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
+ } else {
+ if ($this->_compress_type == 'gz')
+ @gzputs($this->_file, $p_binary_data, $p_len);
+ else if ($this->_compress_type == 'bz2')
+ @bzwrite($this->_file, $p_binary_data, $p_len);
+ else if ($this->_compress_type == 'none')
+ @fputs($this->_file, $p_binary_data, $p_len);
+ else
+ $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
+
+ }
+ }
+ return true;
+ }
+ // }}}
+
+ // {{{ _readBlock()
+ function _readBlock($p_len=null)
+ {
+ $v_block = null;
+ if (is_resource($this->_file)) {
+ if ($p_len === null)
+ $p_len = 512;
+
+ if ($this->_compress_type == 'gz')
+ $v_block = @gzread($this->_file, 512);
+ else if ($this->_compress_type == 'bz2')
+ $v_block = @bzread($this->_file, 512);
+ else if ($this->_compress_type == 'none')
+ $v_block = @fread($this->_file, 512);
+ else
+ $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
+
+ }
+ return $v_block;
+ }
+ // }}}
+
+ // {{{ _jumpBlock()
+ function _jumpBlock($p_len=null)
+ {
+ if (is_resource($this->_file)) {
+ if ($p_len === null)
+ $p_len = 1;
+
+ if ($this->_compress_type == 'gz')
+ @gzseek($this->_file, @gztell($this->_file)+($p_len*512));
+ else if ($this->_compress_type == 'bz2') {
+ // ----- Replace missing bztell() and bzseek()
+ for ($i=0; $i<$p_len; $i++)
+ $this->_readBlock();
+ } else if ($this->_compress_type == 'none')
+ @fseek($this->_file, @ftell($this->_file)+($p_len*512));
+ else
+ $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
+
+ }
+ return true;
+ }
+ // }}}
+
+ // {{{ _writeFooter()
+ function _writeFooter()
+ {
+ if (is_resource($this->_file)) {
+ // ----- Write the last 0 filled block for end of archive
+ $v_binary_data = pack("a512", '');
+ $this->_writeBlock($v_binary_data);
+ }
+ return true;
+ }
+ // }}}
+
+ // {{{ _addList()
+ function _addList($p_list, $p_add_dir, $p_remove_dir)
+ {
+ $v_result=true;
+ $v_header = array();
+
+ // ----- Remove potential windows directory separator
+ $p_add_dir = $this->_translateWinPath($p_add_dir);
+ $p_remove_dir = $this->_translateWinPath($p_remove_dir, false);
+
+ if (!$this->_file) {
+ $this->_error('Invalid file descriptor');
+ return false;
+ }
+
+ if (sizeof($p_list) == 0)
+ return true;
+
+ for ($j=0; ($j<count($p_list)) && ($v_result); $j++) {
+ $v_filename = $p_list[$j];
+
+ // ----- Skip the current tar name
+ if ($v_filename == $this->_tarname)
+ continue;
+
+ if ($v_filename == '')
+ continue;
+
+ if (!file_exists($v_filename)) {
+ $this->_warning("File '$v_filename' does not exist");
+ continue;
+ }
+
+ // ----- Add the file or directory header
+ if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir))
+ return false;
+
+ if (@is_dir($v_filename)) {
+ if (!($p_hdir = opendir($v_filename))) {
+ $this->_warning("Directory '$v_filename' can not be read");
+ continue;
+ }
+ $p_hitem = readdir($p_hdir); // '.' directory
+ $p_hitem = readdir($p_hdir); // '..' directory
+ while (false !== ($p_hitem = readdir($p_hdir))) {
+ if ($v_filename != ".")
+ $p_temp_list[0] = $v_filename.'/'.$p_hitem;
+ else
+ $p_temp_list[0] = $p_hitem;
+
+ $v_result = $this->_addList($p_temp_list, $p_add_dir, $p_remove_dir);
+ }
+
+ unset($p_temp_list);
+ unset($p_hdir);
+ unset($p_hitem);
+ }
+ }
+
+ return $v_result;
+ }
+ // }}}
+
+ // {{{ _addFile()
+ function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir)
+ {
+ if (!$this->_file) {
+ $this->_error('Invalid file descriptor');
+ return false;
+ }
+
+ if ($p_filename == '') {
+ $this->_error('Invalid file name');
+ return false;
+ }
+
+ // ----- Calculate the stored filename
+ $p_filename = $this->_translateWinPath($p_filename, false);;
+ $v_stored_filename = $p_filename;
+ if (strcmp($p_filename, $p_remove_dir) == 0) {
+ return true;
+ }
+ if ($p_remove_dir != '') {
+ if (substr($p_remove_dir, -1) != '/')
+ $p_remove_dir .= '/';
+
+ if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir)
+ $v_stored_filename = substr($p_filename, strlen($p_remove_dir));
+ }
+ $v_stored_filename = $this->_translateWinPath($v_stored_filename);
+ if ($p_add_dir != '') {
+ if (substr($p_add_dir, -1) == '/')
+ $v_stored_filename = $p_add_dir.$v_stored_filename;
+ else
+ $v_stored_filename = $p_add_dir.'/'.$v_stored_filename;
+ }
+
+ $v_stored_filename = $this->_pathReduction($v_stored_filename);
+
+ if (is_file($p_filename)) {
+ if (($v_file = @fopen($p_filename, "rb")) == 0) {
+ $this->_warning("Unable to open file '$p_filename' in binary read mode");
+ return true;
+ }
+
+ if (!$this->_writeHeader($p_filename, $v_stored_filename))
+ return false;
+
+ while (($v_buffer = fread($v_file, 512)) != '') {
+ $v_binary_data = pack("a512", "$v_buffer");
+ $this->_writeBlock($v_binary_data);
+ }
+
+ fclose($v_file);
+
+ } else {
+ // ----- Only header for dir
+ if (!$this->_writeHeader($p_filename, $v_stored_filename))
+ return false;
+ }
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _addString()
+ function _addString($p_filename, $p_string)
+ {
+ if (!$this->_file) {
+ $this->_error('Invalid file descriptor');
+ return false;
+ }
+
+ if ($p_filename == '') {
+ $this->_error('Invalid file name');
+ return false;
+ }
+
+ // ----- Calculate the stored filename
+ $p_filename = $this->_translateWinPath($p_filename, false);;
+
+ if (!$this->_writeHeaderBlock($p_filename, strlen($p_string), 0, 0, "", 0, 0))
+ return false;
+
+ $i=0;
+ while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') {
+ $v_binary_data = pack("a512", $v_buffer);
+ $this->_writeBlock($v_binary_data);
+ }
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _writeHeader()
+ function _writeHeader($p_filename, $p_stored_filename)
+ {
+ if ($p_stored_filename == '')
+ $p_stored_filename = $p_filename;
+ $v_reduce_filename = $this->_pathReduction($p_stored_filename);
+
+ if (strlen($v_reduce_filename) > 99) {
+ if (!$this->_writeLongHeader($v_reduce_filename))
+ return false;
+ }
+
+ $v_info = stat($p_filename);
+ $v_uid = sprintf("%6s ", DecOct($v_info[4]));
+ $v_gid = sprintf("%6s ", DecOct($v_info[5]));
+ $v_perms = sprintf("%6s ", DecOct(fileperms($p_filename)));
+
+ $v_mtime = sprintf("%11s", DecOct(filemtime($p_filename)));
+
+ if (@is_dir($p_filename)) {
+ $v_typeflag = "5";
+ $v_size = sprintf("%11s ", DecOct(0));
+ } else {
+ $v_typeflag = '';
+ clearstatcache();
+ $v_size = sprintf("%11s ", DecOct(filesize($p_filename)));
+ }
+
+ $v_linkname = '';
+
+ $v_magic = '';
+
+ $v_version = '';
+
+ $v_uname = '';
+
+ $v_gname = '';
+
+ $v_devmajor = '';
+
+ $v_devminor = '';
+
+ $v_prefix = '';
+
+ $v_binary_data_first = pack("a100a8a8a8a12A12", $v_reduce_filename, $v_perms, $v_uid, $v_gid, $v_size, $v_mtime);
+ $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", $v_typeflag, $v_linkname, $v_magic, $v_version, $v_uname, $v_gname, $v_devmajor, $v_devminor, $v_prefix, '');
+
+ // ----- Calculate the checksum
+ $v_checksum = 0;
+ // ..... First part of the header
+ for ($i=0; $i<148; $i++)
+ $v_checksum += ord(substr($v_binary_data_first,$i,1));
+ // ..... Ignore the checksum value and replace it by ' ' (space)
+ for ($i=148; $i<156; $i++)
+ $v_checksum += ord(' ');
+ // ..... Last part of the header
+ for ($i=156, $j=0; $i<512; $i++, $j++)
+ $v_checksum += ord(substr($v_binary_data_last,$j,1));
+
+ // ----- Write the first 148 bytes of the header in the archive
+ $this->_writeBlock($v_binary_data_first, 148);
+
+ // ----- Write the calculated checksum
+ $v_checksum = sprintf("%6s ", DecOct($v_checksum));
+ $v_binary_data = pack("a8", $v_checksum);
+ $this->_writeBlock($v_binary_data, 8);
+
+ // ----- Write the last 356 bytes of the header in the archive
+ $this->_writeBlock($v_binary_data_last, 356);
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _writeHeaderBlock()
+ function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0, $p_type='', $p_uid=0, $p_gid=0)
+ {
+ $p_filename = $this->_pathReduction($p_filename);
+
+ if (strlen($p_filename) > 99) {
+ if (!$this->_writeLongHeader($p_filename))
+ return false;
+ }
+
+ if ($p_type == "5") {
+ $v_size = sprintf("%11s ", DecOct(0));
+ } else {
+ $v_size = sprintf("%11s ", DecOct($p_size));
+ }
+
+ $v_uid = sprintf("%6s ", DecOct($p_uid));
+ $v_gid = sprintf("%6s ", DecOct($p_gid));
+ $v_perms = sprintf("%6s ", DecOct($p_perms));
+
+ $v_mtime = sprintf("%11s", DecOct($p_mtime));
+
+ $v_linkname = '';
+
+ $v_magic = '';
+
+ $v_version = '';
+
+ $v_uname = '';
+
+ $v_gname = '';
+
+ $v_devmajor = '';
+
+ $v_devminor = '';
+
+ $v_prefix = '';
+
+ $v_binary_data_first = pack("a100a8a8a8a12A12", $p_filename, $v_perms, $v_uid, $v_gid, $v_size, $v_mtime);
+ $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", $p_type, $v_linkname, $v_magic, $v_version, $v_uname, $v_gname, $v_devmajor, $v_devminor, $v_prefix, '');
+
+ // ----- Calculate the checksum
+ $v_checksum = 0;
+ // ..... First part of the header
+ for ($i=0; $i<148; $i++)
+ $v_checksum += ord(substr($v_binary_data_first,$i,1));
+ // ..... Ignore the checksum value and replace it by ' ' (space)
+ for ($i=148; $i<156; $i++)
+ $v_checksum += ord(' ');
+ // ..... Last part of the header
+ for ($i=156, $j=0; $i<512; $i++, $j++)
+ $v_checksum += ord(substr($v_binary_data_last,$j,1));
+
+ // ----- Write the first 148 bytes of the header in the archive
+ $this->_writeBlock($v_binary_data_first, 148);
+
+ // ----- Write the calculated checksum
+ $v_checksum = sprintf("%6s ", DecOct($v_checksum));
+ $v_binary_data = pack("a8", $v_checksum);
+ $this->_writeBlock($v_binary_data, 8);
+
+ // ----- Write the last 356 bytes of the header in the archive
+ $this->_writeBlock($v_binary_data_last, 356);
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _writeLongHeader()
+ function _writeLongHeader($p_filename)
+ {
+ $v_size = sprintf("%11s ", DecOct(strlen($p_filename)));
+
+ $v_typeflag = 'L';
+
+ $v_linkname = '';
+
+ $v_magic = '';
+
+ $v_version = '';
+
+ $v_uname = '';
+
+ $v_gname = '';
+
+ $v_devmajor = '';
+
+ $v_devminor = '';
+
+ $v_prefix = '';
+
+ $v_binary_data_first = pack("a100a8a8a8a12A12", '././@LongLink', 0, 0, 0, $v_size, 0);
+ $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", $v_typeflag, $v_linkname, $v_magic, $v_version, $v_uname, $v_gname, $v_devmajor, $v_devminor, $v_prefix, '');
+
+ // ----- Calculate the checksum
+ $v_checksum = 0;
+ // ..... First part of the header
+ for ($i=0; $i<148; $i++)
+ $v_checksum += ord(substr($v_binary_data_first,$i,1));
+ // ..... Ignore the checksum value and replace it by ' ' (space)
+ for ($i=148; $i<156; $i++)
+ $v_checksum += ord(' ');
+ // ..... Last part of the header
+ for ($i=156, $j=0; $i<512; $i++, $j++)
+ $v_checksum += ord(substr($v_binary_data_last,$j,1));
+
+ // ----- Write the first 148 bytes of the header in the archive
+ $this->_writeBlock($v_binary_data_first, 148);
+
+ // ----- Write the calculated checksum
+ $v_checksum = sprintf("%6s ", DecOct($v_checksum));
+ $v_binary_data = pack("a8", $v_checksum);
+ $this->_writeBlock($v_binary_data, 8);
+
+ // ----- Write the last 356 bytes of the header in the archive
+ $this->_writeBlock($v_binary_data_last, 356);
+
+ // ----- Write the filename as content of the block
+ $i=0;
+ while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') {
+ $v_binary_data = pack("a512", "$v_buffer");
+ $this->_writeBlock($v_binary_data);
+ }
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _readHeader()
+ function _readHeader($v_binary_data, &$v_header)
+ {
+ if (strlen($v_binary_data)==0) {
+ $v_header['filename'] = '';
+ return true;
+ }
+
+ if (strlen($v_binary_data) != 512) {
+ $v_header['filename'] = '';
+ $this->_error('Invalid block size : '.strlen($v_binary_data));
+ return false;
+ }
+
+ // ----- Calculate the checksum
+ $v_checksum = 0;
+ // ..... First part of the header
+ for ($i=0; $i<148; $i++)
+ $v_checksum+=ord(substr($v_binary_data,$i,1));
+ // ..... Ignore the checksum value and replace it by ' ' (space)
+ for ($i=148; $i<156; $i++)
+ $v_checksum += ord(' ');
+ // ..... Last part of the header
+ for ($i=156; $i<512; $i++)
+ $v_checksum+=ord(substr($v_binary_data,$i,1));
+
+ $v_data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor", $v_binary_data);
+
+ // ----- Extract the checksum
+ $v_header['checksum'] = OctDec(trim($v_data['checksum']));
+ if ($v_header['checksum'] != $v_checksum) {
+ $v_header['filename'] = '';
+
+ // ----- Look for last block (empty block)
+ if (($v_checksum == 256) && ($v_header['checksum'] == 0))
+ return true;
+
+ $this->_error('Invalid checksum for file "'.$v_data['filename'].'" : '.$v_checksum.' calculated, '.$v_header['checksum'].' expected');
+ return false;
+ }
+
+ // ----- Extract the properties
+ $v_header['filename'] = trim($v_data['filename']);
+ $v_header['mode'] = OctDec(trim($v_data['mode']));
+ $v_header['uid'] = OctDec(trim($v_data['uid']));
+ $v_header['gid'] = OctDec(trim($v_data['gid']));
+ $v_header['size'] = OctDec(trim($v_data['size']));
+ $v_header['mtime'] = OctDec(trim($v_data['mtime']));
+ if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
+ $v_header['size'] = 0;
+ }
+ /* ----- All these fields are removed form the header because they do not carry interesting info
+ $v_header[link] = trim($v_data[link]);
+ $v_header[magic] = trim($v_data[magic]);
+ $v_header[version] = trim($v_data[version]);
+ $v_header[uname] = trim($v_data[uname]);
+ $v_header[gname] = trim($v_data[gname]);
+ $v_header[devmajor] = trim($v_data[devmajor]);
+ $v_header[devminor] = trim($v_data[devminor]);
+ */
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _readLongHeader()
+ function _readLongHeader(&$v_header)
+ {
+ $v_filename = '';
+ $n = floor($v_header['size']/512);
+ for ($i=0; $i<$n; $i++) {
+ $v_content = $this->_readBlock();
+ $v_filename .= $v_content;
+ }
+ if (($v_header['size'] % 512) != 0) {
+ $v_content = $this->_readBlock();
+ $v_filename .= $v_content;
+ }
+
+ // ----- Read the next header
+ $v_binary_data = $this->_readBlock();
+
+ if (!$this->_readHeader($v_binary_data, $v_header))
+ return false;
+
+ $v_header['filename'] = $v_filename;
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _extractInString()
+ /**
+ * This method extract from the archive one file identified by $p_filename.
+ * The return value is a string with the file content, or NULL on error.
+ * @param string $p_filename The path of the file to extract in a string.
+ * @return a string with the file content or NULL.
+ * @access private
+ */
+ function _extractInString($p_filename)
+ {
+ $v_result_str = "";
+
+ While (strlen($v_binary_data = $this->_readBlock()) != 0)
+ {
+ if (!$this->_readHeader($v_binary_data, $v_header))
+ return NULL;
+
+ if ($v_header['filename'] == '')
+ continue;
+
+ // ----- Look for long filename
+ if ($v_header['typeflag'] == 'L') {
+ if (!$this->_readLongHeader($v_header))
+ return NULL;
+ }
+
+ if ($v_header['filename'] == $p_filename) {
+ if ($v_header['typeflag'] == "5") {
+ $this->_error('Unable to extract in string a directory entry {'.$v_header['filename'].'}');
+ return NULL;
+ } else {
+ $n = floor($v_header['size']/512);
+ for ($i=0; $i<$n; $i++) {
+ $v_result_str .= $this->_readBlock();
+ }
+ if (($v_header['size'] % 512) != 0) {
+ $v_content = $this->_readBlock();
+ $v_result_str .= substr($v_content, 0, ($v_header['size'] % 512));
+ }
+ return $v_result_str;
+ }
+ } else {
+ $this->_jumpBlock(ceil(($v_header['size']/512)));
+ }
+ }
+
+ return NULL;
+ }
+ // }}}
+
+ // {{{ _extractList()
+ function _extractList($p_path, &$p_list_detail, $p_mode, $p_file_list, $p_remove_path)
+ {
+ $v_result=true;
+ $v_nb = 0;
+ $v_extract_all = true;
+ $v_listing = false;
+
+ $p_path = $this->_translateWinPath($p_path, false);
+ if ($p_path == '' || (substr($p_path, 0, 1) != '/' && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) {
+ $p_path = "./".$p_path;
+ }
+ $p_remove_path = $this->_translateWinPath($p_remove_path);
+
+ // ----- Look for path to remove format (should end by /)
+ if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/'))
+ $p_remove_path .= '/';
+ $p_remove_path_size = strlen($p_remove_path);
+
+ switch ($p_mode) {
+ case "complete" :
+ $v_extract_all = TRUE;
+ $v_listing = FALSE;
+ break;
+ case "partial" :
+ $v_extract_all = FALSE;
+ $v_listing = FALSE;
+ break;
+ case "list" :
+ $v_extract_all = FALSE;
+ $v_listing = TRUE;
+ break;
+ default :
+ $this->_error('Invalid extract mode ('.$p_mode.')');
+ return false;
+ }
+
+ clearstatcache();
+
+ While (strlen($v_binary_data = $this->_readBlock()) != 0)
+ {
+ $v_extract_file = FALSE;
+ $v_extraction_stopped = 0;
+
+ if (!$this->_readHeader($v_binary_data, $v_header))
+ return false;
+
+ if ($v_header['filename'] == '')
+ continue;
+
+ // ----- Look for long filename
+ if ($v_header['typeflag'] == 'L') {
+ if (!$this->_readLongHeader($v_header))
+ return false;
+ }
+
+ if ((!$v_extract_all) && (is_array($p_file_list))) {
+ // ----- By default no unzip if the file is not found
+ $v_extract_file = false;
+
+ for ($i=0; $i<sizeof($p_file_list); $i++) {
+ // ----- Look if it is a directory
+ if (substr($p_file_list[$i], -1) == '/') {
+ // ----- Look if the directory is in the filename path
+ if ((strlen($v_header['filename']) > strlen($p_file_list[$i])) && (substr($v_header['filename'], 0, strlen($p_file_list[$i])) == $p_file_list[$i])) {
+ $v_extract_file = TRUE;
+ break;
+ }
+ }
+
+ // ----- It is a file, so compare the file names
+ elseif ($p_file_list[$i] == $v_header['filename']) {
+ $v_extract_file = TRUE;
+ break;
+ }
+ }
+ } else {
+ $v_extract_file = TRUE;
+ }
+
+ // ----- Look if this file need to be extracted
+ if (($v_extract_file) && (!$v_listing))
+ {
+ if (($p_remove_path != '')
+ && (substr($v_header['filename'], 0, $p_remove_path_size) == $p_remove_path))
+ $v_header['filename'] = substr($v_header['filename'], $p_remove_path_size);
+ if (($p_path != './') && ($p_path != '/')) {
+ while (substr($p_path, -1) == '/')
+ $p_path = substr($p_path, 0, strlen($p_path)-1);
+
+ if (substr($v_header['filename'], 0, 1) == '/')
+ $v_header['filename'] = $p_path.$v_header['filename'];
+ else
+ $v_header['filename'] = $p_path.'/'.$v_header['filename'];
+ }
+ if (file_exists($v_header['filename'])) {
+ if ((@is_dir($v_header['filename'])) && ($v_header['typeflag'] == '')) {
+ $this->_error('File '.$v_header['filename'].' already exists as a directory');
+ return false;
+ }
+ if ((is_file($v_header['filename'])) && ($v_header['typeflag'] == "5")) {
+ $this->_error('Directory '.$v_header['filename'].' already exists as a file');
+ return false;
+ }
+ if (!is_writeable($v_header['filename'])) {
+ $this->_error('File '.$v_header['filename'].' already exists and is write protected');
+ return false;
+ }
+ if (filemtime($v_header['filename']) > $v_header['mtime']) {
+ // To be completed : An error or silent no replace ?
+ }
+ }
+
+ // ----- Check the directory availability and create it if necessary
+ elseif (($v_result = $this->_dirCheck(($v_header['typeflag'] == "5"?$v_header['filename']:dirname($v_header['filename'])))) != 1) {
+ $this->_error('Unable to create path for '.$v_header['filename']);
+ return false;
+ }
+
+ if ($v_extract_file) {
+ if ($v_header['typeflag'] == "5") {
+ if (!@file_exists($v_header['filename'])) {
+ if (!@mkdir($v_header['filename'], 0777)) {
+ $this->_error('Unable to create directory {'.$v_header['filename'].'}');
+ return false;
+ }
+ }
+ } else {
+ if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) {
+ $this->_error('Error while opening {'.$v_header['filename'].'} in write binary mode');
+ return false;
+ } else {
+ $n = floor($v_header['size']/512);
+ for ($i=0; $i<$n; $i++) {
+ $v_content = $this->_readBlock();
+ fwrite($v_dest_file, $v_content, 512);
+ }
+ if (($v_header['size'] % 512) != 0) {
+ $v_content = $this->_readBlock();
+ fwrite($v_dest_file, $v_content, ($v_header['size'] % 512));
+ }
+
+ @fclose($v_dest_file);
+
+ // ----- Change the file mode, mtime
+ @touch($v_header['filename'], $v_header['mtime']);
+ // To be completed
+ //chmod($v_header[filename], DecOct($v_header[mode]));
+ }
+
+ // ----- Check the file size
+ clearstatcache();
+ if (filesize($v_header['filename']) != $v_header['size']) {
+ $this->_error('Extracted file '.$v_header['filename'].' does not have the correct file size \''.filesize($v_filename).'\' ('.$v_header['size'].' expected). Archive may be corrupted.');
+ return false;
+ }
+ }
+ } else {
+ $this->_jumpBlock(ceil(($v_header['size']/512)));
+ }
+ } else {
+ $this->_jumpBlock(ceil(($v_header['size']/512)));
+ }
+
+ /* TBC : Seems to be unused ...
+ if ($this->_compress)
+ $v_end_of_file = @gzeof($this->_file);
+ else
+ $v_end_of_file = @feof($this->_file);
+ */
+
+ if ($v_listing || $v_extract_file || $v_extraction_stopped) {
+ // ----- Log extracted files
+ if (($v_file_dir = dirname($v_header['filename'])) == $v_header['filename'])
+ $v_file_dir = '';
+ if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == ''))
+ $v_file_dir = '/';
+
+ $p_list_detail[$v_nb++] = $v_header;
+ }
+ }
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _openAppend()
+ function _openAppend()
+ {
+ if (filesize($this->_tarname) == 0)
+ return $this->_openWrite();
+
+ if ($this->_compress) {
+ $this->_close();
+
+ if (!@rename($this->_tarname, $this->_tarname.".tmp")) {
+ $this->_error('Error while renaming \''.$this->_tarname.'\' to temporary file \''.$this->_tarname.'.tmp\'');
+ return false;
+ }
+
+ if ($this->_compress_type == 'gz')
+ $v_temp_tar = @gzopen($this->_tarname.".tmp", "rb");
+ elseif ($this->_compress_type == 'bz2')
+ $v_temp_tar = @bzopen($this->_tarname.".tmp", "rb");
+
+ if ($v_temp_tar == 0) {
+ $this->_error('Unable to open file \''.$this->_tarname.'.tmp\' in binary read mode');
+ @rename($this->_tarname.".tmp", $this->_tarname);
+ return false;
+ }
+
+ if (!$this->_openWrite()) {
+ @rename($this->_tarname.".tmp", $this->_tarname);
+ return false;
+ }
+
+ if ($this->_compress_type == 'gz') {
+ $v_buffer = @gzread($v_temp_tar, 512);
+
+ // ----- Read the following blocks but not the last one
+ if (!@gzeof($v_temp_tar)) {
+ do{
+ $v_binary_data = pack("a512", $v_buffer);
+ $this->_writeBlock($v_binary_data);
+ $v_buffer = @gzread($v_temp_tar, 512);
+
+ } while (!@gzeof($v_temp_tar));
+ }
+
+ @gzclose($v_temp_tar);
+ }
+ elseif ($this->_compress_type == 'bz2') {
+ $v_buffered_lines = array();
+ $v_buffered_lines[] = @bzread($v_temp_tar, 512);
+
+ // ----- Read the following blocks but not the last one
+ while (strlen($v_buffered_lines[] = @bzread($v_temp_tar, 512)) > 0) {
+ $v_binary_data = pack("a512", array_shift($v_buffered_lines));
+ $this->_writeBlock($v_binary_data);
+ }
+
+ @bzclose($v_temp_tar);
+ }
+
+ if (!@unlink($this->_tarname.".tmp")) {
+ $this->_error('Error while deleting temporary file \''.$this->_tarname.'.tmp\'');
+ }
+
+ } else {
+ // ----- For not compressed tar, just add files before the last 512 bytes block
+ if (!$this->_openReadWrite())
+ return false;
+
+ clearstatcache();
+ $v_size = filesize($this->_tarname);
+ fseek($this->_file, $v_size-512);
+ }
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _append()
+ function _append($p_filelist, $p_add_dir='', $p_remove_dir='')
+ {
+ if (!$this->_openAppend())
+ return false;
+
+ if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir))
+ $this->_writeFooter();
+
+ $this->_close();
+
+ return true;
+ }
+ // }}}
+
+ // {{{ _dirCheck()
+
+ /**
+ * Check if a directory exists and create it (including parent
+ * dirs) if not.
+ *
+ * @param string $p_dir directory to check
+ *
+ * @return bool TRUE if the directory exists or was created
+ */
+ function _dirCheck($p_dir)
+ {
+ if ((@is_dir($p_dir)) || ($p_dir == ''))
+ return true;
+
+ $p_parent_dir = dirname($p_dir);
+
+ if (($p_parent_dir != $p_dir) &&
+ ($p_parent_dir != '') &&
+ (!$this->_dirCheck($p_parent_dir)))
+ return false;
+
+ if (!@mkdir($p_dir, 0777)) {
+ $this->_error("Unable to create directory '$p_dir'");
+ return false;
+ }
+
+ return true;
+ }
+
+ // }}}
+
+ // {{{ _pathReduction()
+
+ /**
+ * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar", and
+ * remove double slashes.
+ *
+ * @param string $p_dir path to reduce
+ *
+ * @return string reduced path
+ *
+ * @access private
+ *
+ */
+ function _pathReduction($p_dir)
+ {
+ $v_result = '';
+
+ // ----- Look for not empty path
+ if ($p_dir != '') {
+ // ----- Explode path by directory names
+ $v_list = explode('/', $p_dir);
+
+ // ----- Study directories from last to first
+ for ($i=sizeof($v_list)-1; $i>=0; $i--) {
+ // ----- Look for current path
+ if ($v_list[$i] == ".") {
+ // ----- Ignore this directory
+ // Should be the first $i=0, but no check is done
+ }
+ else if ($v_list[$i] == "..") {
+ // ----- Ignore it and ignore the $i-1
+ $i--;
+ }
+ else if (($v_list[$i] == '') && ($i!=(sizeof($v_list)-1)) && ($i!=0)) {
+ // ----- Ignore only the double '//' in path,
+ // but not the first and last /
+ } else {
+ $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?'/'.$v_result:'');
+ }
+ }
+ }
+ $v_result = strtr($v_result, '\\', '/');
+ return $v_result;
+ }
+
+ // }}}
+
+ // {{{ _translateWinPath()
+ function _translateWinPath($p_path, $p_remove_disk_letter=true)
+ {
+ if (OS_WINDOWS) {
+ // ----- Look for potential disk letter
+ if (($p_remove_disk_letter) && (($v_position = strpos($p_path, ':')) != false)) {
+ $p_path = substr($p_path, $v_position+1);
+ }
+ // ----- Change potential windows directory separator
+ if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {
+ $p_path = strtr($p_path, '\\', '/');
+ }
+ }
+ return $p_path;
+ }
+ // }}}
+
+}
+?>
diff --git a/pearlib/Benchmark/Iterate.php b/pearlib/Benchmark/Iterate.php
new file mode 100644
index 00000000..cfe76ac4
--- /dev/null
+++ b/pearlib/Benchmark/Iterate.php
@@ -0,0 +1,155 @@
+<?php
+//
+// +------------------------------------------------------------------------+
+// | PEAR :: Benchmark |
+// +------------------------------------------------------------------------+
+// | Copyright (c) 2001-2003 Sebastian Bergmann <[email protected]>. |
+// +------------------------------------------------------------------------+
+// | This source file is subject to version 3.00 of the PHP License, |
+// | that is available at http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +------------------------------------------------------------------------+
+//
+// $Id: Iterate.php,v 1.6 2003/01/01 17:57:52 sterling Exp $
+//
+
+require_once 'Benchmark/Timer.php';
+
+/**
+* Benchmark::Benchmark_Iterate
+*
+* Purpose:
+*
+* Benchmarking
+*
+* Example:
+* a)
+* require_once 'Benchmark/Iterate.php';
+* $benchmark = new Benchmark_Iterate;
+*
+* function foo($string) {
+* print $string . '<br>';
+* }
+*
+* $benchmark->run(100, 'foo', 'test');
+* $result = $benchmark->get();
+*
+* b)
+* require_once 'Benchmark/Iterate.php';
+* $benchmark = new Benchmark_Iterate;
+*
+* class myclass{
+*
+* function foo($string) {
+* print $string . '<br>';
+* }
+* }
+*
+* $benchmark->run(100, 'myclass::foo', 'test');
+* $result = $benchmark->get();
+*
+* c)
+* require_once 'Benchmark/Iterate.php';
+* $benchmark = new Benchmark_Iterate;
+*
+* class myclass{
+*
+* function foo($string) {
+* print $string . '<br>';
+* }
+* }
+*
+* $myobj = new myclass();
+*
+* $benchmark->run(100, 'myobj->foo', 'test');
+* $result = $benchmark->get();
+*
+* @author Sebastian Bergmann <[email protected]>
+* @version $Revision: 1.6 $
+* @access public
+*/
+
+class Benchmark_Iterate extends Benchmark_Timer {
+ /**
+ * Benchmarks a function.
+ *
+ * @access public
+ */
+
+ function run() {
+ $arguments = func_get_args();
+
+ $iterations = array_shift($arguments);
+ $function_name = array_shift($arguments);
+
+ if (strstr($function_name, '::')) {
+ $function_name = explode('::', $function_name);
+ $objectmethod = $function_name[1];
+ }
+
+ // If we're calling a method on an object use call_user_func
+ if (strstr($function_name, '->')) {
+ $function_name = explode('->', $function_name);
+ $objectname = $function_name[0];
+
+ global ${$objectname};
+ $objectmethod = $function_name[1];
+
+ for ($i = 1; $i <= $iterations; $i++) {
+ $this->setMarker('start_' . $i);
+ call_user_func_array(array(${$objectname}, $function_name[1]), $arguments);
+ $this->setMarker('end_' . $i);
+ }
+ return(0);
+ }
+
+ for ($i = 1; $i <= $iterations; $i++) {
+ $this->setMarker('start_' . $i);
+ call_user_func_array($function_name, $arguments);
+ $this->setMarker('end_' . $i);
+ }
+ }
+
+ /**
+ * Returns benchmark result.
+ *
+ * $result[x ] = execution time of iteration x
+ * $result['mean' ] = mean execution time
+ * $result['iterations'] = number of iterations
+ *
+ * @return array $result
+ * @access public
+ */
+
+ function get() {
+ $result = array();
+ $total = 0;
+
+ $iterations = count($this->markers)/2;
+
+ for ($i = 1; $i <= $iterations; $i++) {
+ $time = $this->timeElapsed('start_'.$i , 'end_'.$i);
+
+ if (extension_loaded('bcmath')) {
+ $total = bcadd($total, $time, 6);
+ } else {
+ $total = $total + $time;
+ }
+
+ $result[$i] = $time;
+ }
+
+ if (extension_loaded('bcmath')) {
+ $result['mean'] = bcdiv($total, $iterations, 6);
+ } else {
+ $result['mean'] = $total / $iterations;
+ }
+
+ $result['iterations'] = $iterations;
+
+ return $result;
+ }
+}
+?>
diff --git a/pearlib/Benchmark/Profiler.php b/pearlib/Benchmark/Profiler.php
new file mode 100644
index 00000000..37770d12
--- /dev/null
+++ b/pearlib/Benchmark/Profiler.php
@@ -0,0 +1,371 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PEAR :: Benchmark |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 2002-2003 Matthias Englert <[email protected]>. |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.00 of the PHP License, |
+// | that is available at http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+//
+// $Id: Profiler.php,v 1.6 2003/04/24 15:33:53 matthias Exp $
+//
+
+/**
+ * Benchmark::Profiler
+ *
+ * Purpose:
+ *
+ * Timing Script Execution, Generating Profiling Information
+ *
+ * Example with automatic profiling start, stop, and output:
+ *
+ * $profiler =& new Benchmark_Profiler(true);
+ * function myFunction() {
+ * $profiler->enterSection('myFunction');
+ * //do something
+ * $profiler->leaveSection('myFunction');
+ * return;
+ * }
+ * //do something
+ * myFunction();
+ * //do more
+ *
+ *
+ * Example without automatic profiling:
+ *
+ * $profiler =& new Benchmark_Profiler();
+ * function myFunction() {
+ * $profiler->enterSection('myFunction');
+ * //do something
+ * $profiler->leaveSection('myFunction');
+ * return;
+ * }
+ * $profiler->start();
+ * //do something
+ * myFunction();
+ * //do more
+ * $profiler->stop();
+ * $profiler->display();
+ *
+ * @author Matthias Englert <[email protected]>
+ * @version $Revision: 1.6 $
+ * @access public
+ */
+
+require_once 'PEAR.php';
+
+class Benchmark_Profiler extends PEAR {
+
+ /**
+ * Contains the total ex. time of each section
+ *
+ * @var array
+ * @access private
+ */
+ var $_sections = array();
+
+ /**
+ * Calling stack
+ *
+ * @var array
+ * @access private
+ */
+ var $_stack = array();
+
+ /**
+ * Notes how often a section was entered
+ *
+ * @var array
+ * @access private
+ */
+ var $_num_calls = array();
+
+ /**
+ * Notes for each section how much time is spend in sub-sections
+ *
+ * @var array
+ * @access private
+ */
+ var $_sub_sections_time = array();
+
+ /**
+ * Notes for each section how often it calls which section
+ *
+ * @var array
+ * @access private
+ */
+ var $_calls = array();
+
+ /**
+ * Notes for each section how often it was called by which section
+ *
+ * @var array
+ * @access private
+ */
+ var $_callers = array();
+
+ /**
+ * Auto-starts and stops profiler
+ *
+ * @var boolean
+ * @access private
+ */
+ var $_auto = false;
+
+ /**
+ * Max marker name length for non-html output
+ *
+ * @var integer
+ * @access private
+ */
+ var $_strlen_max = 0;
+
+ /**
+ * Constructor, starts profiling recording
+ *
+ * @access public
+ */
+ function Benchmark_Profiler($auto = false) {
+ $this->PEAR();
+ if ($auto) {
+ $this->auto = $auto;
+ $this->start();
+ }
+ }
+
+ /**
+ * Destructor, stops profiling recording
+ *
+ * @access private
+ */
+ function _Benchmark_Profiler() {
+ if (isset($this->auto)) {
+ $this->stop();
+ $this->display();
+ }
+ }
+
+ /**
+ * Returns profiling informations for a given section.
+ *
+ * @access public
+ */
+ function getSectionInformations($section = 'Global') {
+ if (isset($this->_sections[$section])) {
+
+ $calls = array();
+ if (isset($this->_calls[$section])) {
+ $calls = $this->_calls[$section];
+ }
+
+ $callers = array();
+ if (isset($this->_callers[$section])) {
+ $callers = $this->_callers[$section];
+ }
+
+ $informations = array();
+ $informations['time'] = $this->_sections[$section];
+ $informations['percentage'] = number_format(100 * $this->_sections[$section] / $this->_sections['Global'], 2, '.', '');
+ $informations['calls'] = $calls;
+ $informations['num_calls'] = $this->_num_calls[$section];
+ $informations['callers'] = $callers;
+ if (isset($this->_sub_sections_time[$section])) {
+ $informations['netto_time'] = $this->_sections[$section] - $this->_sub_sections_time[$section];
+ } else {
+ $informations['netto_time'] = $this->_sections[$section];
+ }
+
+ return $informations;
+ } else {
+ $this->raiseError("The section '$section' does not exists.\n", null, PEAR_ERROR_TRIGGER, E_USER_WARNING);
+ }
+ }
+
+ /**
+ * Returns profiling informations for all sections.
+ *
+ * @access public
+ */
+ function getAllSectionsInformations() {
+ $informations = array();
+ foreach($this->_sections as $section => $time) {
+ $informations[$section] = $this->getSectionInformations($section);
+ }
+
+ return $informations;
+ }
+
+ /**
+ * Returns formatted profiling information.
+ *
+ * @see display()
+ * @access private
+ */
+ function _getOutput() {
+ if (function_exists('version_compare') &&
+ version_compare(phpversion(), '4.1', 'ge')) {
+ $http = isset($_SERVER['SERVER_PROTOCOL']);
+ } else {
+ global $HTTP_SERVER_VARS;
+ $http = isset($HTTP_SERVER_VARS['SERVER_PROTOCOL']);
+ }
+ if ($http) {
+ $out = "<table border=1>\n";
+ $out .=
+ '<tr><td>&nbsp;</td><td align="center"><b>total ex. time</b></td>'.
+ '<td align="center"><b>netto ex. time</b></td>'.
+ '<td align="center"><b>#calls</b></td><td align="center"><b>%</b></td>'.
+ '<td align="center"><b>calls</b></td><td align="center"><b>callers</b></td></tr>'.
+ "\n";
+ } else {
+ $dashes = $out =
+ str_pad("\n", ($this->_strlen_max + 52), '-',
+ STR_PAD_LEFT);
+ $out .= str_pad('section', $this->_strlen_max);
+ $out .= str_pad("total ex time", 22);
+ $out .= str_pad("netto ex time", 22);
+ $out .= str_pad("#calls", 22);
+ $out .= "perct\n";
+ $out .= $dashes;
+ }
+
+ $informations = $this->getAllSectionsInformations();
+ foreach($informations as $name => $values) {
+ $percentage = $values['percentage'];
+ $calls_str = "";
+ foreach($values['calls'] as $key => $val) {
+ if ($calls_str) {
+ $calls_str .= ", ";
+ }
+ $calls_str .= "$key ($val)";
+ }
+ $callers_str = "";
+ foreach($values['callers'] as $key => $val) {
+ if ($callers_str) {
+ $callers_str .= ", ";
+ }
+ $callers_str .= "$key ($val)";
+ }
+ if ($http) {
+ $out .=
+ "<tr><td><b>$name</b></td><td>{$values['time']}</td><td>{$values['netto_time']}</td><td>{$values['num_calls']}</td>".
+ "<td align=\"right\">{$values['percentage']}%</td>\n";
+ $out .= "<td>$calls_str</td><td>$callers_str</td></tr>";
+ } else {
+ $out .= str_pad($name, $this->_strlen_max, ' ');
+ $out .= str_pad($values['time'], 22);
+ $out .= str_pad($values['netto_time'], 22);
+ $out .= str_pad($values['num_calls'], 22);
+ $out .=
+ str_pad($values['percentage']."%\n", 8, ' ',
+ STR_PAD_LEFT);
+ }
+ }
+ $out .= "</table>";
+ return $out;
+ }
+
+ /**
+ * Returns formatted profiling information.
+ *
+ * @access public
+ */
+ function display() {
+ echo $this->_getOutput();
+ }
+
+ /**
+ * Enters "Global" section.
+ *
+ * @see enterSection(), stop()
+ * @access public
+ */
+ function start() {
+ $this->enterSection('Global');
+ }
+
+ /**
+ * Leaves "Global" section.
+ *
+ * @see leaveSection(), start()
+ * @access public
+ */
+ function stop() {
+ $this->leaveSection('Global');
+ }
+
+ /**
+ * Enters code section.
+ *
+ * @param string name of the code section
+ * @see start(), leaveSection()
+ * @access public
+ */
+ function enterSection($name) {
+ if (count($this->_stack)) {
+ if (isset($this->_callers[$name][$this->_stack[count($this->_stack) - 1]["name"]])) {
+ $this->_callers[$name][$this->_stack[count($this->_stack) - 1]["name"]]++;
+ } else {
+ $this->_callers[$name][$this->_stack[count($this->_stack) - 1]["name"]] = 1;
+ }
+
+ if (isset($this->_calls[$this->_stack[count($this->_stack) - 1]["name"]][$name])) {
+ $this->_calls[$this->_stack[count($this->_stack) - 1]["name"]][$name]++;
+ } else {
+ $this->_calls[$this->_stack[count($this->_stack) - 1]["name"]][$name] = 1;
+ }
+ }
+
+ if (isset($this->_num_calls[$name])) {
+ $this->_num_calls[$name]++;
+ } else {
+ $this->_num_calls[$name] = 1;
+ }
+
+ $microtime = explode(" ", microtime());
+ $microtime = $microtime[1].substr($microtime[0], 1);
+ array_push($this->_stack,
+ array("name" => $name, "time" => $microtime));
+ }
+
+ /**
+ * Leaves code section.
+ *
+ * @param string name of the marker to be set
+ * @see stop(), enterSection()
+ * @access public
+ */
+ function leaveSection($name) {
+ $microtime = explode(" ", microtime());
+ $microtime = $microtime[1].substr($microtime[0], 1);
+ $x = array_pop($this->_stack);
+ if ($x["name"] != $name) {
+ $this->raiseError("reached end of section $name but expecting end of ".
+ $x["name"]."\n",null,PEAR_ERROR_DIE);
+ }
+
+ if (isset($this->_sections[$name])) {
+ $this->_sections[$name] += $microtime - $x["time"];
+ } else {
+ $this->_sections[$name] = $microtime - $x["time"];
+ }
+
+ $parent = array_pop($this->_stack);
+ if (isset($parent)) {
+ if (isset($this->_sub_sections_time[$parent['name']])) {
+ $this->_sub_sections_time[$parent['name']] += $microtime - $x['time'];
+ } else {
+ $this->_sub_sections_time[$parent['name']] = $microtime - $x['time'];
+ }
+ array_push($this->_stack, $parent);
+ }
+ }
+
+}
+
+?> \ No newline at end of file
diff --git a/pearlib/Benchmark/Timer.php b/pearlib/Benchmark/Timer.php
new file mode 100644
index 00000000..6f990fd1
--- /dev/null
+++ b/pearlib/Benchmark/Timer.php
@@ -0,0 +1,254 @@
+<?php
+//
+// +------------------------------------------------------------------------+
+// | PEAR :: Benchmark |
+// +------------------------------------------------------------------------+
+// | Copyright (c) 2001-2003 Sebastian Bergmann <[email protected]>. |
+// +------------------------------------------------------------------------+
+// | This source file is subject to version 3.00 of the PHP License, |
+// | that is available at http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +------------------------------------------------------------------------+
+//
+// $Id: Timer.php,v 1.7 2002/12/11 17:14:17 sebastian Exp $
+//
+
+/**
+ * Benchmark::Timer
+ *
+ * Purpose:
+ *
+ * Timing Script Execution, Generating Profiling Information
+ *
+ * Example with automatic profiling start, stop, and output:
+ *
+ * $timer =& new Benchmark_Timer(true);
+ * $timer->setMarker('Marker 1');
+ *
+ * Example without automatic profiling:
+ *
+ * $timer =& new Benchmark_Timer();
+ *
+ * $timer->start();
+ * $timer->setMarker('Marker 1');
+ * $timer->stop();
+ *
+ * $profiling = $timer->getProfiling();
+ * echo $profiling->getOutput(); // or $timer->display()
+ *
+ * Contributors:
+ * - Ludovico Magnocavallo <[email protected]>
+ * auto profiling and get_output() method
+ *
+ * @author Sebastian Bergmann <[email protected]>
+ * @version $Revision: 1.7 $
+ * @access public
+ */
+
+require_once 'PEAR.php';
+
+class Benchmark_Timer extends PEAR
+{
+ /**
+ * Contains the markers
+ *
+ * @var array
+ * @access private
+ */
+ var $markers = array();
+
+ /**
+ * Auto-start and stop timer
+ *
+ * @var boolean
+ * @access private
+ */
+ var $auto = false;
+
+ /**
+ * Max marker name length for non-html output
+ *
+ * @var integer
+ * @access private
+ */
+ var $strlen_max = 0;
+
+ /**
+ * Constructor, starts profiling recording
+ *
+ * @access public
+ */
+ function Benchmark_Timer($auto = false) {
+ $this->PEAR();
+ if ($auto) {
+ $this->auto = $auto;
+ $this->start();
+ }
+ }
+
+ /**
+ * Destructor, stops profiling recording
+ *
+ * @access private
+ */
+ function _Benchmark_Timer() {
+ if ($this->auto) {
+ $this->stop();
+ $this->display();
+ }
+ }
+
+ /**
+ * Return formatted profiling information.
+ *
+ * @see getProfiling()
+ * @access public
+ */
+ function getOutput()
+ {
+ if (function_exists('version_compare') &&
+ version_compare(phpversion(), '4.1', 'ge'))
+ {
+ $http = isset($_SERVER['SERVER_PROTOCOL']);
+ } else {
+ global $HTTP_SERVER_VARS;
+ $http = isset($HTTP_SERVER_VARS['SERVER_PROTOCOL']);
+ }
+ $total = $this->TimeElapsed();
+ $result = $this->getProfiling();
+ $dashes = "";
+ if ($http) {
+ $out = "<table border=1>\n";
+ $out .= '<tr><td>&nbsp;</td><td align="center"><b>time index</b></td><td align="center"><b>ex time</b></td><td align="center"><b>%</b></td></tr>'."\n";
+ } else {
+ $dashes = $out = str_pad("\n", ($this->strlen_max + 52), '-', STR_PAD_LEFT);
+ $out .= str_pad('marker', $this->strlen_max);
+ $out .= str_pad("time index", 22);
+ $out .= str_pad("ex time", 22);
+ $out .= "perct\n";
+ $out .= $dashes;
+ }
+ foreach ($result as $k => $v) {
+ $perc = (($v['diff'] * 100) / $total);
+ if ($http) {
+ $out .= "<tr><td><b>" . $v['name'] . "</b></td><td>" . $v['time'] . "</td><td>" . $v['diff'] . "</td><td align=\"right\">" . number_format($perc, 2, '.', '') . "%</td></tr>\n";
+ } else {
+ $out .= str_pad($v['name'], $this->strlen_max, ' ');
+ $out .= str_pad($v['time'], 22);
+ $out .= str_pad($v['diff'], 22);
+ $out .= str_pad(number_format($perc, 2, '.', '') . "%\n", 8, ' ', STR_PAD_LEFT);
+ }
+ $out .= $dashes;
+ }
+ if ($http) {
+ $out .= "<tr style='background: silver;'><td><b>total</b></td><td>-</td><td>${total}</td><td>100.00%</td></tr>\n";
+ $out .= "</table>\n";
+ } else {
+ $out .= str_pad('total', $this->strlen_max);
+ $out .= str_pad('-', 22);
+ $out .= str_pad($total, 22);
+ $out .= "100.00%\n";
+ $out .= $dashes;
+ }
+ return $out;
+ }
+
+ /**
+ * Prints the information returned by getOutput
+ */
+ function display()
+ {
+ print $this->getOutput();
+ }
+
+ /**
+ * Set "Start" marker.
+ *
+ * @see setMarker(), stop()
+ * @access public
+ */
+ function start() {
+ $this->setMarker('Start');
+ }
+
+ /**
+ * Set "Stop" marker.
+ *
+ * @see setMarker(), start()
+ * @access public
+ */
+ function stop() {
+ $this->setMarker('Stop');
+ }
+
+ /**
+ * Set marker.
+ *
+ * @param string name of the marker to be set
+ * @see start(), stop()
+ * @access public
+ */
+ function setMarker($name) {
+ $microtime = explode(' ', microtime());
+ $this->markers[$name] = $microtime[1] . substr($microtime[0], 1);
+ }
+
+ /**
+ * Returns the time elapsed betweens two markers.
+ *
+ * @param string $start start marker, defaults to "Start"
+ * @param string $end end marker, defaults to "Stop"
+ * @return double $time_elapsed time elapsed between $start and $end
+ * @access public
+ */
+ function timeElapsed($start = 'Start', $end = 'Stop') {
+ if (extension_loaded('bcmath')) {
+ return bcsub($this->markers[$end], $this->markers[$start], 6);
+ } else {
+ return $this->markers[$end] - $this->markers[$start];
+ }
+ }
+
+ /**
+ * Returns profiling information.
+ *
+ * $profiling[x]['name'] = name of marker x
+ * $profiling[x]['time'] = time index of marker x
+ * $profiling[x]['diff'] = execution time from marker x-1 to this marker x
+ * $profiling[x]['total'] = total execution time up to marker x
+ *
+ * @return array $profiling
+ * @access public
+ */
+ function getProfiling() {
+ $i = $total = $temp = 0;
+ $result = array();
+
+ foreach ($this->markers as $marker => $time) {
+ if (extension_loaded('bcmath')) {
+ $diff = bcsub($time, $temp, 6);
+ $total = bcadd($total, $diff, 6);
+ } else {
+ $diff = $time - $temp;
+ $total = $total + $diff;
+ }
+
+ $result[$i]['name'] = $marker;
+ $result[$i]['time'] = $time;
+ $result[$i]['diff'] = $diff;
+ $result[$i]['total'] = $total;
+
+ $this->strlen_max = (strlen($marker) > $this->strlen_max ? strlen($marker) + 1 : $this->strlen_max);
+
+ $temp = $time;
+ $i++;
+ }
+ $result[0]['diff'] = '-';
+ $this->strlen_max = (strlen('total') > $this->strlen_max ? strlen('total') : $this->strlen_max);
+ $this->strlen_max += 4;
+ return $result;
+ }
+}
+?>
diff --git a/pearlib/Console/Getargs.php b/pearlib/Console/Getargs.php
new file mode 100644
index 00000000..9c48c4b3
--- /dev/null
+++ b/pearlib/Console/Getargs.php
@@ -0,0 +1,1019 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Getargs.php,v 1.5 2004/10/07 23:31:46 scottmattocks Exp $
+
+require_once 'PEAR.php';
+
+/**#@+
+ * Error Constants
+ */
+/**
+ * Wrong configuration
+ *
+ * This error will be TRIGGERed when a configuration error is found,
+ * it will also issue a WARNING.
+ */
+define('CONSOLE_GETARGS_ERROR_CONFIG', -1);
+
+/**
+ * User made an error
+ *
+ * This error will be RETURNed when a bad parameter
+ * is found in the command line, for example an unknown parameter
+ * or a parameter with an invalid number of options.
+ */
+define('CONSOLE_GETARGS_ERROR_USER', -2);
+
+/**
+ * Help text wanted
+ *
+ * This error will be RETURNed when the user asked to
+ * see the help by using <kbd>-h</kbd> or <kbd>--help</kbd> in the command line, you can then print
+ * the help ascii art text by using the {@link Console_Getargs::getHelp()} method
+ */
+define('CONSOLE_GETARGS_HELP', -3);
+
+/**
+ * Option name for application "parameters"
+ *
+ * Parameters are the options an application needs to function.
+ * The two files passed to the diff command would be considered
+ * the parameters. These are different from other options in that
+ * they do not need an option name passed on the command line.
+ */
+define('CONSOLE_GETARGS_PARAMS', 'parameters');
+/**#@-*/
+
+/**
+ * Command-line arguments parsing class
+ *
+ * This implementation was freely inspired by a python module called
+ * getargs by Vinod Vijayarajan and a perl CPAN module called
+ * Getopt::Simple by Ron Savage
+ *
+ * This class implements a Command Line Parser that your cli applications
+ * can use to parse command line arguments found in $_SERVER['argv'] or a
+ * user defined array.
+ *
+ * It gives more flexibility and error checking than Console_Getopt. It also
+ * performs some arguments validation and is capable to return a formatted
+ * help text to the user, based on the configuration it is given.
+ *
+ * The class provides the following capabilities:
+ * - Each command line option can take an arbitrary number of arguments.
+ * - Makes the distinction between switches (options without arguments)
+ * and options that require arguments.
+ * - Recognizes 'single-argument-options' and 'default-if-set' options.
+ * - Switches and options with arguments can be interleaved in the command
+ * line.
+ * - You can specify the maximum and minimum number of arguments an option
+ * can take. Use -1 if you don't want to specify an upper bound.
+ * - Specify the default arguments to an option
+ * - Short options can be more than one letter in length.
+ * - A given option may be invoked by multiple names (aliases).
+ * - Understands by default the --help, -h options
+ * - Can return a formatted help text
+ * - Arguments may be specified using the '=' syntax also.
+ * - Short option names may be concatenated (-dvw 100 == -d -v -w 100)
+ * - Can define a default option that will take any arguments added without
+ * an option name
+ * - Can pass in a user defined array of arguments instead of using
+ * $_SERVER['argv']
+ *
+ * @todo Implement the parsing of comma delimited arguments
+ * @todo Implement method for turning assocative arrays into command
+ * line arguments (ex. array('d' => true, 'v' => 2) -->
+ * array('-d', '-v', 2))
+ *
+ * @author Bertrand Mansion <[email protected]>
+ * @copyright 2004
+ * @license http://www.php.net/license/3_0.txt PHP License 3.0
+ * @version @VER@
+ * @package Console_Getargs
+ */
+class Console_Getargs
+{
+ /**
+ * Factory creates a new {@link Console_Getargs_Options} object
+ *
+ * This method will return a new {@link Console_Getargs_Options}
+ * built using the given configuration options. If the configuration
+ * or the command line options contain errors, the returned object will
+ * in fact be a PEAR_Error explaining the cause of the error.
+ *
+ * Factory expects an array as parameter.
+ * The format for this array is:
+ * <pre>
+ * array(
+ * longname => array('short' => Short option name,
+ * 'max' => Maximum arguments for option,
+ * 'min' => Minimum arguments for option,
+ * 'default' => Default option argument,
+ * 'desc' => Option description)
+ * )
+ * </pre>
+ *
+ * If an option can be invoked by more than one name, they have to be defined
+ * by using | as a separator. For example: name1|name2
+ * This works both in long and short names.
+ *
+ * max/min are the most/least number of arguments an option accepts.
+ *
+ * The 'defaults' field is optional and is used to specify default
+ * arguments to an option. These will be assigned to the option if
+ * it is *not* used in the command line.
+ * Default arguments can be:
+ * - a single value for options that require a single argument,
+ * - an array of values for options with more than one possible arguments.
+ * Default argument(s) are mandatory for 'default-if-set' options.
+ *
+ * If max is 0 (option is just a switch), min is ignored.
+ * If max is -1, then the option can have an unlimited number of arguments
+ * greater or equal to min.
+ *
+ * If max == min == 1, the option is treated as a single argument option.
+ *
+ * If max >= 1 and min == 0, the option is treated as a
+ * 'default-if-set' option. This implies that it will get the default argument
+ * only if the option is used in the command line without any value.
+ * (Note: defaults *must* be specified for 'default-if-set' options)
+ *
+ * If the option is not in the command line, the defaults are
+ * *not* applied. If an argument for the option is specified on the command
+ * line, then the given argument is assigned to the option.
+ * Thus:
+ * - a --debug in the command line would cause debug = 'default argument'
+ * - a --debug 2 in the command line would result in debug = 2
+ * if not used in the command line, debug will not be defined.
+ *
+ * Example 1.
+ * <code>
+ * require_once 'Console_Getargs.php';
+ *
+ * $args =& Console_Getargs::factory($config);
+ *
+ * if (PEAR::isError($args)) {
+ * if ($args->getCode() === CONSOLE_GETARGS_ERROR_USER) {
+ * echo Console_Getargs::getHelp($config, null, $args->getMessage())."\n";
+ * } else if ($args->getCode() === CONSOLE_GETARGS_HELP) {
+ * echo Console_Getargs::getHelp($config)."\n";
+ * }
+ * exit;
+ * }
+ *
+ * echo 'Verbose: '.$args->getValue('verbose')."\n";
+ * if ($args->isDefined('bs')) {
+ * echo 'Block-size: '.(is_array($args->getValue('bs')) ? implode(', ', $args->getValue('bs'))."\n" : $args->getValue('bs')."\n");
+ * } else {
+ * echo "Block-size: undefined\n";
+ * }
+ * echo 'Files: '.($args->isDefined('file') ? implode(', ', $args->getValue('file'))."\n" : "undefined\n");
+ * if ($args->isDefined('n')) {
+ * echo 'Nodes: '.(is_array($args->getValue('n')) ? implode(', ', $args->getValue('n'))."\n" : $args->getValue('n')."\n");
+ * } else {
+ * echo "Nodes: undefined\n";
+ * }
+ * echo 'Log: '.$args->getValue('log')."\n";
+ * echo 'Debug: '.($args->isDefined('d') ? "YES\n" : "NO\n");
+ *
+ * </code>
+ *
+ * If you don't want to require any option name for a set of arguments,
+ * or if you would like any "leftover" arguments assigned by default,
+ * you can create an option named CONSOLE_GETARGS_PARAMS that will
+ * grab any arguments that cannot be assigned to another option. The
+ * rules for CONSOLE_GETARGS_PARAMS are still the same. If you specify
+ * that two values must be passed then two values must be passed. See
+ * the example script for a complete example.
+ *
+ * @param array $config associative array with keys being the
+ * options long name
+ * @param array $arguments numeric array of command line arguments
+ * @access public
+ * @return object|PEAR_Error a newly created Console_Getargs_Options
+ * object or a PEAR_Error object on error
+ */
+ function &factory($config = array(), $arguments = array())
+ {
+ // Create the options object.
+ $obj =& new Console_Getargs_Options();
+
+ // Try to set up the arguments.
+ $err = $obj->init($config, $arguments);
+ if ($err !== true) {
+ return $err;
+ }
+
+ // Try to set up the options.
+ $err = $obj->buildMaps();
+ if ($err !== true) {
+ return $err;
+ }
+
+ // Get the options and arguments from the command line.
+ $err = $obj->parseArgs();
+ if ($err !== true) {
+ return $err;
+ }
+
+ // Set arguments for options that have defaults.
+ $err = $obj->setDefaults();
+ if ($err !== true) {
+ return $err;
+ }
+
+ // All is good.
+ return $obj;
+ }
+
+ /**
+ * Returns an ascii art version of the help
+ *
+ * This method uses the given configuration and parameters
+ * to create and format an help text for the options you defined
+ * in your config parameter. You can supply a header and a footer
+ * as well as the maximum length of a line. If you supplied
+ * descriptions for your options, they will be used as well.
+ *
+ * By default, it returns something like this:
+ * <pre>
+ * Usage: myscript.php [-dv --formats] <-fw --filters>
+ *
+ * -f --files values(2) Set the source and destination image files.
+ * -w --width=&lt;value&gt; Set the new width of the image.
+ * -d --debug Switch to debug mode.
+ * --formats values(1-3) Set the image destination format. (jpegbig,
+ * jpegsmall)
+ * -fi --filters values(1-...) Set the filters to be applied to the image upon
+ * conversion. The filters will be used in the order
+ * they are set.
+ * -v --verbose (optional)value Set the verbose level. (3)
+ * </pre>
+ *
+ * @access public
+ * @param array your args configuration
+ * @param string the header for the help. If it is left null,
+ * a default header will be used, starting by Usage:
+ * @param string the footer for the help. This could be used
+ * to supply a description of the error the user made
+ * @param int help lines max length
+ * @return string the formatted help text
+ */
+ function getHelp($config, $helpHeader = null, $helpFooter = '', $maxlength = 78)
+ {
+ // Start with an empty help message and build it piece by piece
+ $help = '';
+
+ // If no user defined header, build the default header.
+ if (!isset($helpHeader)) {
+ // Get the optional, required and "paramter" names for this config.
+ list($optional, $required, $params) = Console_Getargs::getOptionalRequired($config);
+ // Start with the file name.
+ $helpHeader = 'Usage: '. basename($_SERVER['SCRIPT_NAME']) . ' ';
+ // Add the optional arguments and required arguments.
+ $helpHeader.= $optional . ' ' . $required . ' ';
+ // Add any parameters that are needed.
+ $helpHeader.= $params . "\n\n";
+ }
+
+ // Build the list of options and definitions.
+ $i = 0;
+ foreach ($config as $long => $def) {
+
+ // Break the names up if there is more than one for an option.
+ $shortArr = array();
+ if (isset($def['short'])) {
+ $shortArr = explode('|', $def['short']);
+ }
+ $longArr = explode('|', $long);
+
+ // Column one is the option name displayed as "-short, --long [additional info]"
+ // Add the short option name.
+ $col1[$i] = !empty($shortArr) ? '-'.$shortArr[0].' ' : '';
+ // Add the long option name.
+ $col1[$i] .= '--'.$longArr[0];
+
+ // Get the min and max to show needed/optional values.
+ $max = $def['max'];
+ $min = isset($def['min']) ? $def['min'] : $max;
+
+ if ($max === 1 && $min === 1) {
+ // One value required.
+ $col1[$i] .= '=<value>';
+ } else if ($max > 1) {
+ if ($min === $max) {
+ // More than one value needed.
+ $col1[$i] .= ' values('.$max.')';
+ } else if ($min === 0) {
+ // Argument takes optional value(s).
+ $col1[$i] .= ' values(optional)';
+ } else {
+ // Argument takes a range of values.
+ $col1[$i] .= ' values('.$min.'-'.$max.')';
+ }
+ } else if ($max === 1 && $min === 0) {
+ // Argument can take at most one value.
+ $col1[$i] .= ' (optional)value';
+ } else if ($max === -1) {
+ // Argument can take unlimited values.
+ if ($min > 0) {
+ $col1[$i] .= ' values('.$min.'-...)';
+ } else {
+ $col1[$i] .= ' (optional)values';
+ }
+ }
+
+ // Column two is the description if available.
+ if (isset($def['desc'])) {
+ $col2[$i] = $def['desc'];
+ } else {
+ $col2[$i] = '';
+ }
+ // Add the default value(s) if there are any/
+ if (isset($def['default'])) {
+ if (is_array($def['default'])) {
+ $col2[$i] .= ' ('.implode(', ', $def['default']).')';
+ } else {
+ $col2[$i] .= ' ('.$def['default'].')';
+ }
+ }
+ $i++;
+ }
+
+ // Figure out the maximum length for column one.
+ $arglen = 0;
+ foreach ($col1 as $txt) {
+ $length = strlen($txt);
+ if ($length > $arglen) {
+ $arglen = $length;
+ }
+ }
+
+ // The maximum length for each description line.
+ $desclen = $maxlength - $arglen;
+ $padding = str_repeat(' ', $arglen);
+ foreach ($col1 as $k => $txt) {
+ // Wrap the descriptions.
+ if (strlen($col2[$k]) > $desclen) {
+ $desc = wordwrap($col2[$k], $desclen, "\n ".$padding);
+ } else {
+ $desc = $col2[$k];
+ }
+ // Push everything together.
+ $help .= str_pad($txt, $arglen).' '.$desc."\n";
+ }
+
+ // Put it all together.
+ return $helpHeader.$help.$helpFooter;
+ }
+
+ /**
+ * Parse the config array to determine which flags are
+ * optional and which are required.
+ *
+ * To make the help header more descriptive, the options
+ * are shown seperated into optional and required flags.
+ * When possible the short flag is used for readability.
+ * Optional items (including "parameters") are surrounded
+ * in square braces ([-vd]). Required flags are surrounded
+ * in angle brackets (<-wf>).
+ *
+ * This method may be called statically.
+ *
+ * @access public
+ * @param &$config The config array.
+ * @return array
+ * @author Scott Mattocks
+ * @package Console_Getargs
+ */
+ function getOptionalRequired(&$config)
+ {
+ // Parse the config array and look for optional/required
+ // tags.
+ $optional = '';
+ $optionalHasShort = false;
+ $required = '';
+ $requiredHasShort = false;
+
+ ksort($config);
+ foreach ($config as $long => $def) {
+
+ // We only really care about the first option name.
+ $long = reset(explode('|', $long));
+
+ // Treat the "parameters" specially.
+ if ($long == CONSOLE_GETARGS_PARAMS) {
+ continue;
+ }
+ // We only really care about the first option name.
+ $def['short'] = reset(explode('|', $def['short']));
+
+ if (!isset($def['min']) || $def['min'] == 0 || isset($def['default'])) {
+ // This argument is optional.
+ if (isset($def['short']) && strlen($def['short']) == 1) {
+ $optional = $def['short'] . $optional;
+ $optionalHasShort = true;
+ } else {
+ $optional.= ' --' . $long;
+ }
+ } else {
+ // This argument is required.
+ if (isset($def['short']) && strlen($def['short']) == 1) {
+ $required = $def['short'] . $required;
+ $requiredHasShort = true;
+ } else {
+ $required.= ' --' . $long;
+ }
+ }
+ }
+
+ // Check for "parameters" option.
+ $params = '';
+ if (isset($config[CONSOLE_GETARGS_PARAMS])) {
+ for ($i = 1; $i <= max($config[CONSOLE_GETARGS_PARAMS]['max'], $config[CONSOLE_GETARGS_PARAMS]['min']); ++$i) {
+ if ($config[CONSOLE_GETARGS_PARAMS]['max'] == -1 ||
+ ($i > $config[CONSOLE_GETARGS_PARAMS]['min'] &&
+ $i <= $config[CONSOLE_GETARGS_PARAMS]['max']) ||
+ isset($config[CONSOLE_GETARGS_PARAMS]['default'])) {
+ // Parameter is optional.
+ $params.= '[param' . $i .'] ';
+ } else {
+ // Parameter is required.
+ $params.= 'param' . $i . ' ';
+ }
+ }
+ }
+ // Add a leading - if needed.
+ if ($optionalHasShort) {
+ $optional = '-' . $optional;
+ }
+
+ if ($requiredHasShort) {
+ $required = '-' . $required;
+ }
+
+ // Add the extra characters if needed.
+ if (!empty($optional)) {
+ $optional = '[' . $optional . ']';
+ }
+ if (!empty($required)) {
+ $required = '<' . $required . '>';
+ }
+
+ return array($optional, $required, $params);
+ }
+} // end class Console_Getargs
+
+/**
+ * This class implements a wrapper to the command line options and arguments.
+ *
+ * @author Bertrand Mansion <[email protected]>
+ * @package Console_Getargs
+ */
+class Console_Getargs_Options
+{
+
+ /**
+ * Lookup to match short options name with long ones
+ * @var array
+ * @access private
+ */
+ var $_shortLong = array();
+
+ /**
+ * Lookup to match alias options name with long ones
+ * @var array
+ * @access private
+ */
+ var $_aliasLong = array();
+
+ /**
+ * Arguments set for the options
+ * @var array
+ * @access private
+ */
+ var $_longLong = array();
+
+ /**
+ * Configuration set at initialization time
+ * @var array
+ * @access private
+ */
+ var $_config = array();
+
+ /**
+ * A read/write copy of argv
+ * @var array
+ * @access private
+ */
+ var $args = array();
+
+ /**
+ * Initializes the Console_Getargs_Options object
+ * @param array configuration options
+ * @access private
+ * @throws CONSOLE_GETARGS_ERROR_CONFIG
+ * @return true|PEAR_Error
+ */
+ function init($config, $arguments = array())
+ {
+ if (is_array($arguments) && count($arguments)) {
+ // Use the user defined argument list.
+ $this->args = $arguments;
+ } else {
+ // Command line arguments must be available.
+ if (!isset($_SERVER['argv']) || !is_array($_SERVER['argv'])) {
+ return PEAR::raiseError("Could not read argv", CONSOLE_GETARGS_ERROR_CONFIG,
+ PEAR_ERROR_TRIGGER, E_USER_WARNING, 'Console_Getargs_Options::init()');
+ }
+ $this->args = $_SERVER['argv'];
+ }
+
+ // Drop the first argument if it doesn't begin with a '-'.
+ if (isset($this->args[0]{0}) && $this->args[0]{0} != '-') {
+ array_shift($this->args);
+ }
+ $this->_config = $config;
+ return true;
+ }
+
+ /**
+ * Makes the lookup arrays for alias and short name mapping with long names
+ * @access private
+ * @throws CONSOLE_GETARGS_ERROR_CONFIG
+ * @return true|PEAR_Error
+ */
+ function buildMaps()
+ {
+ foreach($this->_config as $long => $def) {
+
+ $longArr = explode('|', $long);
+ $longname = $longArr[0];
+
+ if (count($longArr) > 1) {
+ // The fisrt item in the list is "the option".
+ // The rest are aliases.
+ array_shift($longArr);
+ foreach($longArr as $alias) {
+ // Watch out for duplicate aliases.
+ if (isset($this->_aliasLong[$alias])) {
+ return PEAR::raiseError('Duplicate alias for long option '.$alias, CONSOLE_GETARGS_ERROR_CONFIG,
+ PEAR_ERROR_TRIGGER, E_USER_WARNING, 'Console_Getargs_Options::buildMaps()');
+
+ }
+ $this->_aliasLong[$alias] = $longname;
+ }
+ // Add the real option name and defintion.
+ $this->_config[$longname] = $def;
+ // Get rid of the old version (name|alias1|...)
+ unset($this->_config[$long]);
+ }
+
+ // Add the (optional) short option names.
+ if (!empty($def['short'])) {
+ // Short names
+ $shortArr = explode('|', $def['short']);
+ $short = $shortArr[0];
+ if (count($shortArr) > 1) {
+ // The first item is "the option".
+ // The rest are aliases.
+ array_shift($shortArr);
+ foreach ($shortArr as $alias) {
+ // Watch out for duplicate aliases.
+ if (isset($this->_shortLong[$alias])) {
+ return PEAR::raiseError('Duplicate alias for short option '.$alias, CONSOLE_GETARGS_ERROR_CONFIG,
+ PEAR_ERROR_TRIGGER, E_USER_WARNING, 'Console_Getargs_Options::buildMaps()');
+ }
+ $this->_shortLong[$alias] = $longname;
+ }
+ }
+ // Add the real short option name.
+ $this->_shortLong[$short] = $longname;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Parses the given options/arguments one by one
+ * @access private
+ * @throws CONSOLE_GETARGS_HELP
+ * @throws CONSOLE_GETARGS_ERROR_USER
+ * @return true|PEAR_Error
+ */
+ function parseArgs()
+ {
+ // Go through the options and parse the arguments for each.
+ for ($i = 0, $count = count($this->args); $i < $count; $i++) {
+ $arg = $this->args[$i];
+
+ if ($arg === '--') {
+ // '--' alone breaks the loop
+ break;
+ }
+ if ($arg === '--help' || $arg === '-h') {
+ // Asking for help breaks the loop.
+ return PEAR::raiseError(null, CONSOLE_GETARGS_HELP, PEAR_ERROR_RETURN);
+ }
+ if (strlen($arg) > 1 && $arg{1} == '-') {
+ // Long name used (--option)
+ $err = $this->parseArg(substr($arg, 2), true, $i);
+ } else if (strlen($arg) > 1 && $arg{0} == '-') {
+ // Short name used (-o)
+ $err = $this->parseArg(substr($arg, 1), false, $i);
+ if ($err === -1) {
+ break;
+ }
+ } elseif (isset($this->_config[CONSOLE_GETARGS_PARAMS])) {
+ // No flags at all. Try the parameters option.
+ $tempI = &$i - 1;
+ $err = $this->parseArg(CONSOLE_GETARGS_PARAMS, true, $tempI);
+ } else {
+ $err = PEAR::raiseError('Unknown argument '.$arg,
+ CONSOLE_GETARGS_ERROR_USER, PEAR_ERROR_RETURN,
+ null, 'Console_Getargs_Options::parseArgs()');
+ }
+ if ($err !== true) {
+ return $err;
+ }
+ }
+ // Check to see if we need to reload the arguments
+ // due to concatenated short names.
+ if (isset($err) && $err === -1) {
+ return $this->parseArgs();
+ }
+
+ return true;
+ }
+
+ /**
+ * Parses one option/argument
+ * @access private
+ * @throws CONSOLE_GETARGS_ERROR_USER
+ * @return true|PEAR_Error
+ */
+ function parseArg($arg, $isLong, &$pos)
+ {
+ // If the whole short option isn't in the shortLong array
+ // then break it into a bunch of switches.
+ if (!$isLong && !isset($this->_shortLong[$arg]) && strlen($arg) > 1) {
+ $newArgs = array();
+ for ($i = 0; $i < strlen($arg); $i++) {
+ if (array_key_exists($arg{$i}, $this->_shortLong)) {
+ $newArgs[] = '-' . $arg{$i};
+ } else {
+ $newArgs[] = $arg{$i};
+ }
+ }
+ // Add the new args to the array.
+ array_splice($this->args, $pos, 1, $newArgs);
+
+ // Reset the option values.
+ $this->_longLong = array();
+
+ // Then reparse the arguments.
+ return -1;
+ }
+
+ $opt = '';
+ for ($i = 0; $i < strlen($arg); $i++) {
+ // Build the option name one char at a time looking for a match.
+ $opt .= $arg{$i};
+ if ($isLong === false && isset($this->_shortLong[$opt])) {
+ // Found a match in the short option names.
+ $cmp = $opt;
+ $long = $this->_shortLong[$opt];
+ } else if ($isLong === true && isset($this->_config[$opt])) {
+ // Found a match in the long option names.
+ $long = $cmp = $opt;
+ }
+ if ($arg{$i} === '=') {
+ // End of the option name when '=' is found.
+ break;
+ }
+ }
+
+ if (isset($long)) {
+ // A match was found.
+ if (strlen($arg) > strlen($cmp)) {
+ // Seperate the argument from the option.
+ // Ex: php test.php -f=image.png
+ // $cmp = 'f'
+ // $arg = 'f=image.png'
+ $arg = substr($arg, strlen($cmp));
+ // Now $arg = '=image.png'
+ if ($arg{0} === '=') {
+ $arg = substr($arg, 1);
+ // Now $arg = 'image.png'
+ }
+ } else {
+ // No argument passed for option.
+ $arg = '';
+ }
+ // Set the options value.
+ return $this->setValue($long, $arg, $pos);
+ }
+ return PEAR::raiseError('Unknown argument '.$opt,
+ CONSOLE_GETARGS_ERROR_USER, PEAR_ERROR_RETURN,
+ null, 'Console_Getargs_Options::parseArg()');
+ }
+
+ /**
+ * Set the option arguments
+ * @access private
+ * @throws CONSOLE_GETARGS_ERROR_CONFIG
+ * @throws CONSOLE_GETARGS_ERROR_USER
+ * @return true|PEAR_Error
+ */
+ function setValue($optname, $value, &$pos)
+ {
+ if (!isset($this->_config[$optname]['max'])) {
+ // Max must be set for every option even if it is zero or -1.
+ return PEAR::raiseError('No max parameter set for '.$optname,
+ CONSOLE_GETARGS_ERROR_CONFIG, PEAR_ERROR_TRIGGER,
+ E_USER_WARNING, 'Console_Getargs_Options::setValue()');
+ }
+
+ $max = $this->_config[$optname]['max'];
+ $min = isset($this->_config[$optname]['min']) ? $this->_config[$optname]['min']: $max;
+
+ // A value was passed after the option.
+ if ($value !== '') {
+ // Argument is like -v5
+ if ($min == 1 && $max > 0) {
+ // At least one argument is required for option.
+ $this->updateValue($optname, $value);
+ return true;
+ }
+ if ($max === 0) {
+ // Argument passed but not expected.
+ return PEAR::raiseError('Argument '.$optname.' does not take any value',
+ CONSOLE_GETARGS_ERROR_USER, PEAR_ERROR_RETURN,
+ null, 'Console_Getargs_Options::setValue()');
+ }
+ // Not enough arguments passed for this option.
+ return PEAR::raiseError('Argument '.$optname.' expects more than one value',
+ CONSOLE_GETARGS_ERROR_USER, PEAR_ERROR_RETURN,
+ null, 'Console_Getargs_Options::setValue()');
+ }
+
+ if ($min === 1 && $max === 1) {
+ // Argument requires 1 value
+ // If optname is "parameters" take a step back.
+ if ($optname == CONSOLE_GETARGS_PARAMS) {
+ $pos--;
+ }
+ if (isset($this->args[$pos+1]) && $this->isValue($this->args[$pos+1])) {
+ // Set the option value and increment the position.
+ $this->updateValue($optname, $this->args[$pos+1]);
+ $pos++;
+ return true;
+ }
+ // What we thought was the argument was really the next option.
+ return PEAR::raiseError('Argument '.$optname.' expects one value',
+ CONSOLE_GETARGS_ERROR_USER, PEAR_ERROR_RETURN,
+ null, 'Console_Getargs_Options::setValue()');
+
+ } else if ($max === 0) {
+ // Argument is a switch
+ if (isset($this->args[$pos+1]) && $this->isValue($this->args[$pos+1])) {
+ // What we thought was the next option was really an argument for this option.
+ // First update the value
+ $this->updateValue($optname, true);
+ // Then try to assign values to parameters.
+ if (isset($this->_config[CONSOLE_GETARGS_PARAMS])) {
+ return $this->setValue(CONSOLE_GETARGS_PARAMS, '', ++$pos);
+ } else {
+ return PEAR::raiseError('Argument '.$optname.' does not take any value',
+ CONSOLE_GETARGS_ERROR_USER, PEAR_ERROR_RETURN,
+ null, 'Console_Getargs_Options::setValue()');
+ }
+ }
+ // Set the switch to on.
+ $this->updateValue($optname, true);
+ return true;
+
+ } else if ($max >= 1 && $min === 0) {
+ // Argument has a default-if-set value
+ if (!isset($this->_config[$optname]['default'])) {
+ // A default value MUST be assigned when config is loaded.
+ return PEAR::raiseError('No default value defined for '.$optname,
+ CONSOLE_GETARGS_ERROR_CONFIG, PEAR_ERROR_TRIGGER,
+ E_USER_WARNING, 'Console_Getargs_Options::setValue()');
+ }
+ if (is_array($this->_config[$optname]['default'])) {
+ // Default value cannot be an array.
+ return PEAR::raiseError('Default value for '.$optname.' must be scalar',
+ CONSOLE_GETARGS_ERROR_CONFIG, PEAR_ERROR_TRIGGER,
+ E_USER_WARNING, 'Console_Getargs_Options::setValue()');
+ }
+
+ // If optname is "parameters" take a step back.
+ if ($optname == CONSOLE_GETARGS_PARAMS) {
+ $pos--;
+ }
+
+ if (isset($this->args[$pos+1]) && $this->isValue($this->args[$pos+1])) {
+ // Assign the option the value from the command line if there is one.
+ $this->updateValue($optname, $this->args[$pos+1]);
+ $pos++;
+ return true;
+ }
+ // Otherwise use the default value.
+ $this->updateValue($optname, $this->_config[$optname]['default']);
+ return true;
+ }
+
+ // Argument takes one or more values
+ $added = 0;
+ // If trying to assign values to parameters, must go back one position.
+ if ($optname == CONSOLE_GETARGS_PARAMS) {
+ $pos = max($pos - 1, -1);
+ }
+ for ($i = $pos + 1; $i <= count($this->args); $i++) {
+ if (isset($this->args[$i]) && $this->isValue($this->args[$i])) {
+ // Add the argument value until the next option is hit.
+ $this->updateValue($optname, $this->args[$i]);
+ $added++;
+ $pos++;
+ // Only keep trying if we haven't filled up yet.
+ // or there is no limit
+ if ($added < $max || $max < 0) {
+ continue;
+ }
+ }
+ if ($min > $added) {
+ // There aren't enough arguments for this option.
+ return PEAR::raiseError('Argument '.$optname.' expects at least '.$min.(($min > 1) ? ' values' : ' value'),
+ CONSOLE_GETARGS_ERROR_USER, PEAR_ERROR_RETURN,
+ null, 'Console_Getargs_Options::setValue()');
+ } else if ($max !== -1 && $added >= $max) {
+ // Too many arguments for this option.
+ // Try to add the extra options to parameters.
+ if (isset($this->_config[CONSOLE_GETARGS_PARAMS]) && $optname != CONSOLE_GETARGS_PARAMS) {
+ return $this->setValue(CONSOLE_GETARGS_PARAMS, '', ++$pos);
+ } elseif ($optname == CONSOLE_GETARGS_PARAMS && !isset($this->args[$i + 1])) {
+ $pos += $added;
+ break;
+ } else {
+ return PEAR::raiseError('Argument '.$optname.' expects maximum '.$max.' values',
+ CONSOLE_GETARGS_ERROR_USER, PEAR_ERROR_RETURN,
+ null, 'Console_Getargs_Options::setValue()');
+ }
+ }
+ break;
+ }
+ // Everything went well.
+ return true;
+ }
+
+ /**
+ * Checks whether the given parameter is an argument or an option
+ * @access private
+ * @return boolean
+ */
+ function isValue($arg)
+ {
+ if (strlen($arg) > 1 && $arg{1} == '-' ||
+ strlen($arg) > 1 && $arg{0} == '-') {
+ // The next argument is really an option.
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Adds the argument to the option
+ *
+ * If the argument for the option is already set,
+ * the option arguments will be changed to an array
+ * @access private
+ * @return void
+ */
+ function updateValue($optname, $value)
+ {
+ if (isset($this->_longLong[$optname])) {
+ if (is_array($this->_longLong[$optname])) {
+ // Add this value to the list of values for this option.
+ $this->_longLong[$optname][] = $value;
+ } else {
+ // There is already one value set. Turn everything into a list of values.
+ $prevValue = $this->_longLong[$optname];
+ $this->_longLong[$optname] = array($prevValue);
+ $this->_longLong[$optname][] = $value;
+ }
+ } else {
+ // This is the first value for this option.
+ $this->_longLong[$optname] = $value;
+ }
+ }
+
+ /**
+ * Sets the option default arguments when necessary
+ * @access private
+ * @return true
+ */
+ function setDefaults()
+ {
+ foreach ($this->_config as $longname => $def) {
+ // Add the default value only if the default is defined
+ // and the option requires at least one argument.
+ if (isset($def['default']) && $def['min'] !== 0 && !isset($this->_longLong[$longname])) {
+ $this->_longLong[$longname] = $def['default'];
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Checks whether the given option is defined
+ *
+ * An option will be defined if an argument was assigned to it using
+ * the command line options. You can use the short, the long or
+ * an alias name as parameter.
+ *
+ * @access public
+ * @param string the name of the option to be checked
+ * @return boolean true if the option is defined
+ */
+ function isDefined($optname)
+ {
+ $longname = $this->getLongName($optname);
+ if (isset($this->_longLong[$longname])) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the long version of the given parameter
+ *
+ * If the given name is not found, it will return the name that
+ * was given, without further ensuring that the option
+ * actually exists
+ *
+ * @access private
+ * @param string the name of the option
+ * @return string long version of the option name
+ */
+ function getLongName($optname)
+ {
+ if (isset($this->_shortLong[$optname])) {
+ // Short version was passed.
+ $longname = $this->_shortLong[$optname];
+ } else if (isset($this->_aliasLong[$optname])) {
+ // An alias was passed.
+ $longname = $this->_aliasLong[$optname];
+ } else {
+ // No further validation is done.
+ $longname = $optname;
+ }
+ return $longname;
+ }
+
+ /**
+ * Returns the argument of the given option
+ *
+ * You can use the short, alias or long version of the option name.
+ * This method will try to find the argument(s) of the given option name.
+ * If it is not found it will return null. If the arg has more than
+ * one argument, an array of arguments will be returned.
+ *
+ * @access public
+ * @param string the name of the option
+ * @return array|string|null argument(s) associated with the option
+ */
+ function getValue($optname)
+ {
+ if ($this->isDefined($optname)) {
+ // Option is defined. Return its value
+ $longname = $this->getLongName($optname);
+ return $this->_longLong[$longname];
+ }
+ // Option is not defined.
+ return null;
+ }
+} // end class Console_Getargs_Options
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+?>
diff --git a/pearlib/Console/Getopt.php b/pearlib/Console/Getopt.php
new file mode 100644
index 00000000..7966d1ac
--- /dev/null
+++ b/pearlib/Console/Getopt.php
@@ -0,0 +1,251 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Andrei Zmievski <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Getopt.php,v 1.21.4.7 2003/12/05 21:57:01 andrei Exp $
+
+require_once 'PEAR.php';
+
+/**
+ * Command-line options parsing class.
+ *
+ * @author Andrei Zmievski <[email protected]>
+ *
+ */
+class Console_Getopt {
+ /**
+ * Parses the command-line options.
+ *
+ * The first parameter to this function should be the list of command-line
+ * arguments without the leading reference to the running program.
+ *
+ * The second parameter is a string of allowed short options. Each of the
+ * option letters can be followed by a colon ':' to specify that the option
+ * requires an argument, or a double colon '::' to specify that the option
+ * takes an optional argument.
+ *
+ * The third argument is an optional array of allowed long options. The
+ * leading '--' should not be included in the option name. Options that
+ * require an argument should be followed by '=', and options that take an
+ * option argument should be followed by '=='.
+ *
+ * The return value is an array of two elements: the list of parsed
+ * options and the list of non-option command-line arguments. Each entry in
+ * the list of parsed options is a pair of elements - the first one
+ * specifies the option, and the second one specifies the option argument,
+ * if there was one.
+ *
+ * Long and short options can be mixed.
+ *
+ * Most of the semantics of this function are based on GNU getopt_long().
+ *
+ * @param array $args an array of command-line arguments
+ * @param string $short_options specifies the list of allowed short options
+ * @param array $long_options specifies the list of allowed long options
+ *
+ * @return array two-element array containing the list of parsed options and
+ * the non-option arguments
+ *
+ * @access public
+ *
+ */
+ function getopt2($args, $short_options, $long_options = null)
+ {
+ return Console_Getopt::doGetopt(2, $args, $short_options, $long_options);
+ }
+
+ /**
+ * This function expects $args to start with the script name (POSIX-style).
+ * Preserved for backwards compatibility.
+ * @see getopt2()
+ */
+ function getopt($args, $short_options, $long_options = null)
+ {
+ return Console_Getopt::doGetopt(1, $args, $short_options, $long_options);
+ }
+
+ /**
+ * The actual implementation of the argument parsing code.
+ */
+ function doGetopt($version, $args, $short_options, $long_options = null)
+ {
+ // in case you pass directly readPHPArgv() as the first arg
+ if (PEAR::isError($args)) {
+ return $args;
+ }
+ if (empty($args)) {
+ return array(array(), array());
+ }
+ $opts = array();
+ $non_opts = array();
+
+ settype($args, 'array');
+
+ if ($long_options) {
+ sort($long_options);
+ }
+
+ /*
+ * Preserve backwards compatibility with callers that relied on
+ * erroneous POSIX fix.
+ */
+ if ($version < 2) {
+ if (isset($args[0]{0}) && $args[0]{0} != '-') {
+ array_shift($args);
+ }
+ }
+
+ reset($args);
+ while (list($i, $arg) = each($args)) {
+
+ /* The special element '--' means explicit end of
+ options. Treat the rest of the arguments as non-options
+ and end the loop. */
+ if ($arg == '--') {
+ $non_opts = array_merge($non_opts, array_slice($args, $i + 1));
+ break;
+ }
+
+ if ($arg{0} != '-' || (strlen($arg) > 1 && $arg{1} == '-' && !$long_options)) {
+ $non_opts = array_merge($non_opts, array_slice($args, $i));
+ break;
+ } elseif (strlen($arg) > 1 && $arg{1} == '-') {
+ $error = Console_Getopt::_parseLongOption(substr($arg, 2), $long_options, $opts, $args);
+ if (PEAR::isError($error))
+ return $error;
+ } else {
+ $error = Console_Getopt::_parseShortOption(substr($arg, 1), $short_options, $opts, $args);
+ if (PEAR::isError($error))
+ return $error;
+ }
+ }
+
+ return array($opts, $non_opts);
+ }
+
+ /**
+ * @access private
+ *
+ */
+ function _parseShortOption($arg, $short_options, &$opts, &$args)
+ {
+ for ($i = 0; $i < strlen($arg); $i++) {
+ $opt = $arg{$i};
+ $opt_arg = null;
+
+ /* Try to find the short option in the specifier string. */
+ if (($spec = strstr($short_options, $opt)) === false || $arg{$i} == ':')
+ {
+ return PEAR::raiseError("Console_Getopt: unrecognized option -- $opt");
+ }
+
+ if (strlen($spec) > 1 && $spec{1} == ':') {
+ if (strlen($spec) > 2 && $spec{2} == ':') {
+ if ($i + 1 < strlen($arg)) {
+ /* Option takes an optional argument. Use the remainder of
+ the arg string if there is anything left. */
+ $opts[] = array($opt, substr($arg, $i + 1));
+ break;
+ }
+ } else {
+ /* Option requires an argument. Use the remainder of the arg
+ string if there is anything left. */
+ if ($i + 1 < strlen($arg)) {
+ $opts[] = array($opt, substr($arg, $i + 1));
+ break;
+ } else if (list(, $opt_arg) = each($args))
+ /* Else use the next argument. */;
+ else
+ return PEAR::raiseError("Console_Getopt: option requires an argument -- $opt");
+ }
+ }
+
+ $opts[] = array($opt, $opt_arg);
+ }
+ }
+
+ /**
+ * @access private
+ *
+ */
+ function _parseLongOption($arg, $long_options, &$opts, &$args)
+ {
+ @list($opt, $opt_arg) = explode('=', $arg);
+ $opt_len = strlen($opt);
+
+ for ($i = 0; $i < count($long_options); $i++) {
+ $long_opt = $long_options[$i];
+ $opt_start = substr($long_opt, 0, $opt_len);
+
+ /* Option doesn't match. Go on to the next one. */
+ if ($opt_start != $opt)
+ continue;
+
+ $opt_rest = substr($long_opt, $opt_len);
+
+ /* Check that the options uniquely matches one of the allowed
+ options. */
+ if ($opt_rest != '' && $opt{0} != '=' &&
+ $i + 1 < count($long_options) &&
+ $opt == substr($long_options[$i+1], 0, $opt_len)) {
+ return PEAR::raiseError("Console_Getopt: option --$opt is ambiguous");
+ }
+
+ if (substr($long_opt, -1) == '=') {
+ if (substr($long_opt, -2) != '==') {
+ /* Long option requires an argument.
+ Take the next argument if one wasn't specified. */;
+ if (!strlen($opt_arg) && !(list(, $opt_arg) = each($args))) {
+ return PEAR::raiseError("Console_Getopt: option --$opt requires an argument");
+ }
+ }
+ } else if ($opt_arg) {
+ return PEAR::raiseError("Console_Getopt: option --$opt doesn't allow an argument");
+ }
+
+ $opts[] = array('--' . $opt, $opt_arg);
+ return;
+ }
+
+ return PEAR::raiseError("Console_Getopt: unrecognized option --$opt");
+ }
+
+ /**
+ * Safely read the $argv PHP array across different PHP configurations.
+ * Will take care on register_globals and register_argc_argv ini directives
+ *
+ * @access public
+ * @return mixed the $argv PHP array or PEAR error if not registered
+ */
+ function readPHPArgv()
+ {
+ global $argv;
+ if (!is_array($argv)) {
+ if (!@is_array($_SERVER['argv'])) {
+ if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
+ return PEAR::raiseError("Console_Getopt: Could not read cmd args (register_argc_argv=Off?)");
+ }
+ return $GLOBALS['HTTP_SERVER_VARS']['argv'];
+ }
+ return $_SERVER['argv'];
+ }
+ return $argv;
+ }
+
+}
+
+?>
diff --git a/pearlib/HTML/Common.php b/pearlib/HTML/Common.php
new file mode 100644
index 00000000..fcf255f6
--- /dev/null
+++ b/pearlib/HTML/Common.php
@@ -0,0 +1,419 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Adam Daniel <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Common.php,v 1.8 2003/06/19 18:52:50 mansion Exp $
+
+/**
+ * Base class for all HTML classes
+ *
+ * @author Adam Daniel <[email protected]>
+ * @version 1.7
+ * @since PHP 4.0.3pl1
+ * @abstract
+ */
+class HTML_Common {
+
+ /**
+ * Associative array of table attributes
+ * @var array
+ * @access private
+ */
+ var $_attributes = array();
+
+ /**
+ * Tab offset of the table
+ * @var int
+ * @access private
+ */
+ var $_tabOffset = 0;
+
+ /**
+ * Tab string
+ * @var string
+ * @since 1.7
+ * @access private
+ */
+ var $_tab = "\11";
+
+ /**
+ * Contains the line end string
+ * @var string
+ * @since 1.7
+ * @access private
+ */
+ var $_lineEnd = "\12";
+
+ /**
+ * HTML comment on the object
+ * @var string
+ * @since 1.5
+ * @access private
+ */
+ var $_comment = '';
+
+ /**
+ * Class constructor
+ * @param mixed $attributes Associative array of table tag attributes
+ * or HTML attributes name="value" pairs
+ * @param int $tabOffset Indent offset in tabs
+ * @access public
+ */
+ function HTML_Common($attributes = null, $tabOffset = 0)
+ {
+ $this->setAttributes($attributes);
+ $this->setTabOffset($tabOffset);
+ } // end constructor
+
+ /**
+ * Returns the current API version
+ * @access public
+ * @returns double
+ */
+ function apiVersion()
+ {
+ return 1.7;
+ } // end func apiVersion
+
+ /**
+ * Returns the lineEnd
+ *
+ * @since 1.7
+ * @access private
+ * @return string
+ * @throws
+ */
+ function _getLineEnd()
+ {
+ return $this->_lineEnd;
+ } // end func getLineEnd
+
+ /**
+ * Returns a string containing the unit for indenting HTML
+ *
+ * @since 1.7
+ * @access private
+ * @return string
+ */
+ function _getTab()
+ {
+ return $this->_tab;
+ } // end func _getTab
+
+ /**
+ * Returns a string containing the offset for the whole HTML code
+ *
+ * @return string
+ * @access private
+ */
+ function _getTabs()
+ {
+ return str_repeat($this->_getTab(), $this->_tabOffset);
+ } // end func _getTabs
+
+ /**
+ * Returns an HTML formatted attribute string
+ * @param array $attributes
+ * @return string
+ * @access private
+ */
+ function _getAttrString($attributes)
+ {
+ $strAttr = '';
+
+ if (is_array($attributes)) {
+ foreach ($attributes as $key => $value) {
+ $strAttr .= ' ' . $key . '="' . htmlspecialchars($value) . '"';
+ }
+ }
+ return $strAttr;
+ } // end func _getAttrString
+
+ /**
+ * Returns a valid atrributes array from either a string or array
+ * @param mixed $attributes Either a typical HTML attribute string or an associative array
+ * @access private
+ */
+ function _parseAttributes($attributes)
+ {
+ if (is_array($attributes)) {
+ $ret = array();
+ foreach ($attributes as $key => $value) {
+ if (is_int($key)) {
+ $key = $value = strtolower($value);
+ } else {
+ $key = strtolower($key);
+ }
+ $ret[$key] = $value;
+ }
+ return $ret;
+
+ } elseif (is_string($attributes)) {
+ $preg = "/(([A-Za-z_:]|[^\\x00-\\x7F])([A-Za-z0-9_:.-]|[^\\x00-\\x7F])*)" .
+ "([ \\n\\t\\r]+)?(=([ \\n\\t\\r]+)?(\"[^\"]*\"|'[^']*'|[^ \\n\\t\\r]*))?/";
+ if (preg_match_all($preg, $attributes, $regs)) {
+ for ($counter=0; $counter<count($regs[1]); $counter++) {
+ $name = $regs[1][$counter];
+ $check = $regs[0][$counter];
+ $value = $regs[7][$counter];
+ if (trim($name) == trim($check)) {
+ $arrAttr[strtolower(trim($name))] = strtolower(trim($name));
+ } else {
+ if (substr($value, 0, 1) == "\"" || substr($value, 0, 1) == "'") {
+ $value = substr($value, 1, -1);
+ }
+ $arrAttr[strtolower(trim($name))] = trim($value);
+ }
+ }
+ return $arrAttr;
+ }
+ }
+ } // end func _parseAttributes
+
+ /**
+ * Returns the array key for the given non-name-value pair attribute
+ *
+ * @param string $attr Attribute
+ * @param array $attributes Array of attribute
+ * @since 1.0
+ * @access private
+ * @return array key
+ * @throws
+ */
+ function _getAttrKey($attr, $attributes)
+ {
+ if (isset($attributes[strtolower($attr)])) {
+ return true;
+ } else {
+ return null;
+ }
+ } //end func _getAttrKey
+
+ /**
+ * Updates the attributes in $attr1 with the values in $attr2 without changing the other existing attributes
+ * @param array $attr1 Original attributes array
+ * @param array $attr2 New attributes array
+ * @access private
+ * @return array
+ */
+ function _updateAttrArray(&$attr1, $attr2)
+ {
+ if (!is_array($attr2)) {
+ return false;
+ }
+ foreach ($attr2 as $key => $value) {
+ $attr1[$key] = $value;
+ }
+ } // end func _updateAtrrArray
+
+ /**
+ * Removes the given attribute from the given array
+ *
+ * @param string $attr Attribute name
+ * @param array $attributes Attribute array
+ * @since 1.4
+ * @access public
+ * @return void
+ * @throws
+ */
+ function _removeAttr($attr, &$attributes)
+ {
+ $attr = strtolower($attr);
+ if (isset($attributes[$attr])) {
+ unset($attributes[$attr]);
+ }
+ } //end func _removeAttr
+
+ /**
+ * Returns the value of the given attribute
+ *
+ * @param string $attr Attribute name
+ * @since 1.5
+ * @access public
+ * @return void
+ * @throws
+ */
+ function getAttribute($attr)
+ {
+ $attr = strtolower($attr);
+ if (isset($this->_attributes[$attr])) {
+ return $this->_attributes[$attr];
+ }
+ return null;
+ } //end func getAttribute
+
+ /**
+ * Sets the HTML attributes
+ * @param mixed $attributes Either a typical HTML attribute string or an associative array
+ * @access public
+ */
+ function setAttributes($attributes)
+ {
+ $this->_attributes = $this->_parseAttributes($attributes);
+ } // end func setAttributes
+
+ /**
+ * Returns the assoc array (default) or string of attributes
+ *
+ * @param bool Whether to return the attributes as string
+ * @since 1.6
+ * @access public
+ * @return mixed attributes
+ */
+ function getAttributes($asString = false)
+ {
+ if ($asString) {
+ return $this->_getAttrString($this->_attributes);
+ } else {
+ return $this->_attributes;
+ }
+ } //end func getAttributes
+
+ /**
+ * Updates the passed attributes without changing the other existing attributes
+ * @param mixed $attributes Either a typical HTML attribute string or an associative array
+ * @access public
+ */
+ function updateAttributes($attributes)
+ {
+ $this->_updateAttrArray($this->_attributes, $this->_parseAttributes($attributes));
+ } // end func updateAttributes
+
+ /**
+ * Removes an attribute
+ *
+ * @param string $attr Attribute name
+ * @since 1.4
+ * @access public
+ * @return void
+ * @throws
+ */
+ function removeAttribute($attr)
+ {
+ $this->_removeAttr($attr, $this->_attributes);
+ } //end func removeAttribute
+
+ /**
+ * Sets the line end style to Windows, Mac, Unix or a custom string.
+ *
+ * @param string $style "win", "mac", "unix" or custom string.
+ * @since 1.7
+ * @access public
+ * @return void
+ */
+ function setLineEnd($style)
+ {
+ switch ($style) {
+ case 'win':
+ $this->_lineEnd = "\15\12";
+ break;
+ case 'unix':
+ $this->_lineEnd = "\12";
+ break;
+ case 'mac';
+ $this->_lineEnd = "\15";
+ break;
+ default:
+ $this->_lineEnd = $style;
+ }
+ } // end func setLineEnd
+
+ /**
+ * Sets the tab offset
+ *
+ * @param int $offset
+ * @access public
+ */
+ function setTabOffset($offset)
+ {
+ $this->_tabOffset = $offset;
+ } // end func setTabOffset
+
+ /**
+ * Returns the tabOffset
+ *
+ * @since 1.5
+ * @access public
+ * @return int
+ */
+ function getTabOffset()
+ {
+ return $this->_tabOffset;
+ } //end func getTabOffset
+
+ /**
+ * Sets the string used to indent HTML
+ *
+ * @since 1.7
+ * @param string $string String used to indent ("\11", "\t", ' ', etc.).
+ * @access public
+ * @return void
+ */
+ function setTab($string)
+ {
+ $this->_tab = $string;
+ } // end func setTab
+
+ /**
+ * Sets the HTML comment to be displayed at the beginning of the HTML string
+ *
+ * @param string
+ * @since 1.4
+ * @access public
+ * @return void
+ */
+ function setComment($comment)
+ {
+ $this->_comment = $comment;
+ } // end func setHtmlComment
+
+ /**
+ * Returns the HTML comment
+ *
+ * @since 1.5
+ * @access public
+ * @return string
+ */
+ function getComment()
+ {
+ return $this->_comment;
+ } //end func getComment
+
+ /**
+ * Abstract method. Must be extended to return the objects HTML
+ *
+ * @access public
+ * @return string
+ * @abstract
+ */
+ function toHtml()
+ {
+ return '';
+ } // end func toHtml
+
+ /**
+ * Displays the HTML to the screen
+ *
+ * @access public
+ */
+ function display()
+ {
+ print $this->toHtml();
+ } // end func display
+
+} // end class HTML_Common
+?> \ No newline at end of file
diff --git a/pearlib/HTML/QuickForm.php b/pearlib/HTML/QuickForm.php
new file mode 100644
index 00000000..20f0f3e2
--- /dev/null
+++ b/pearlib/HTML/QuickForm.php
@@ -0,0 +1,1911 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Adam Daniel <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: QuickForm.php,v 1.151 2004/10/20 18:43:56 avb Exp $
+
+require_once('PEAR.php');
+require_once('HTML/Common.php');
+
+$GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'] =
+ array(
+ 'group' =>array('HTML/QuickForm/group.php','HTML_QuickForm_group'),
+ 'hidden' =>array('HTML/QuickForm/hidden.php','HTML_QuickForm_hidden'),
+ 'reset' =>array('HTML/QuickForm/reset.php','HTML_QuickForm_reset'),
+ 'checkbox' =>array('HTML/QuickForm/checkbox.php','HTML_QuickForm_checkbox'),
+ 'file' =>array('HTML/QuickForm/file.php','HTML_QuickForm_file'),
+ 'image' =>array('HTML/QuickForm/image.php','HTML_QuickForm_image'),
+ 'password' =>array('HTML/QuickForm/password.php','HTML_QuickForm_password'),
+ 'radio' =>array('HTML/QuickForm/radio.php','HTML_QuickForm_radio'),
+ 'button' =>array('HTML/QuickForm/button.php','HTML_QuickForm_button'),
+ 'submit' =>array('HTML/QuickForm/submit.php','HTML_QuickForm_submit'),
+ 'select' =>array('HTML/QuickForm/select.php','HTML_QuickForm_select'),
+ 'hiddenselect' =>array('HTML/QuickForm/hiddenselect.php','HTML_QuickForm_hiddenselect'),
+ 'text' =>array('HTML/QuickForm/text.php','HTML_QuickForm_text'),
+ 'textarea' =>array('HTML/QuickForm/textarea.php','HTML_QuickForm_textarea'),
+ 'link' =>array('HTML/QuickForm/link.php','HTML_QuickForm_link'),
+ 'advcheckbox' =>array('HTML/QuickForm/advcheckbox.php','HTML_QuickForm_advcheckbox'),
+ 'date' =>array('HTML/QuickForm/date.php','HTML_QuickForm_date'),
+ 'static' =>array('HTML/QuickForm/static.php','HTML_QuickForm_static'),
+ 'header' =>array('HTML/QuickForm/header.php', 'HTML_QuickForm_header'),
+ 'html' =>array('HTML/QuickForm/html.php', 'HTML_QuickForm_html'),
+ 'hierselect' =>array('HTML/QuickForm/hierselect.php', 'HTML_QuickForm_hierselect'),
+ 'autocomplete' =>array('HTML/QuickForm/autocomplete.php', 'HTML_QuickForm_autocomplete'),
+ 'xbutton' =>array('HTML/QuickForm/xbutton.php','HTML_QuickForm_xbutton')
+ );
+
+$GLOBALS['_HTML_QuickForm_registered_rules'] = array(
+ 'required' => array('html_quickform_rule_required', 'HTML/QuickForm/Rule/Required.php'),
+ 'maxlength' => array('html_quickform_rule_range', 'HTML/QuickForm/Rule/Range.php'),
+ 'minlength' => array('html_quickform_rule_range', 'HTML/QuickForm/Rule/Range.php'),
+ 'rangelength' => array('html_quickform_rule_range', 'HTML/QuickForm/Rule/Range.php'),
+ 'email' => array('html_quickform_rule_email', 'HTML/QuickForm/Rule/Email.php'),
+ 'regex' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
+ 'lettersonly' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
+ 'alphanumeric' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
+ 'numeric' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
+ 'nopunctuation' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
+ 'nonzero' => array('html_quickform_rule_regex', 'HTML/QuickForm/Rule/Regex.php'),
+ 'callback' => array('html_quickform_rule_callback', 'HTML/QuickForm/Rule/Callback.php'),
+ 'compare' => array('html_quickform_rule_compare', 'HTML/QuickForm/Rule/Compare.php')
+);
+
+// {{{ error codes
+
+/*
+ * Error codes for the QuickForm interface, which will be mapped to textual messages
+ * in the QuickForm::errorMessage() function. If you are to add a new error code, be
+ * sure to add the textual messages to the QuickForm::errorMessage() function as well
+ */
+
+define('QUICKFORM_OK', 1);
+define('QUICKFORM_ERROR', -1);
+define('QUICKFORM_INVALID_RULE', -2);
+define('QUICKFORM_NONEXIST_ELEMENT', -3);
+define('QUICKFORM_INVALID_FILTER', -4);
+define('QUICKFORM_UNREGISTERED_ELEMENT', -5);
+define('QUICKFORM_INVALID_ELEMENT_NAME', -6);
+define('QUICKFORM_INVALID_PROCESS', -7);
+define('QUICKFORM_DEPRECATED', -8);
+define('QUICKFORM_INVALID_DATASOURCE', -9);
+
+// }}}
+
+/**
+* Create, validate and process HTML forms
+*
+* @author Adam Daniel <[email protected]>
+* @author Bertrand Mansion <[email protected]>
+* @version 2.0
+* @since PHP 4.0.3pl1
+*/
+class HTML_QuickForm extends HTML_Common {
+ // {{{ properties
+
+ /**
+ * Array containing the form fields
+ * @since 1.0
+ * @var array
+ * @access private
+ */
+ var $_elements = array();
+
+ /**
+ * Array containing element name to index map
+ * @since 1.1
+ * @var array
+ * @access private
+ */
+ var $_elementIndex = array();
+
+ /**
+ * Array containing indexes of duplicate elements
+ * @since 2.10
+ * @var array
+ * @access private
+ */
+ var $_duplicateIndex = array();
+
+ /**
+ * Array containing required field IDs
+ * @since 1.0
+ * @var array
+ * @access private
+ */
+ var $_required = array();
+
+ /**
+ * Prefix message in javascript alert if error
+ * @since 1.0
+ * @var string
+ * @access public
+ */
+ var $_jsPrefix = 'Invalid information entered.';
+
+ /**
+ * Postfix message in javascript alert if error
+ * @since 1.0
+ * @var string
+ * @access public
+ */
+ var $_jsPostfix = 'Please correct these fields.';
+
+ /**
+ * Datasource object implementing the informal
+ * datasource protocol
+ * @since 3.3
+ * @var object
+ * @access private
+ */
+ var $_datasource;
+
+ /**
+ * Array of default form values
+ * @since 2.0
+ * @var array
+ * @access private
+ */
+ var $_defaultValues = array();
+
+ /**
+ * Array of constant form values
+ * @since 2.0
+ * @var array
+ * @access private
+ */
+ var $_constantValues = array();
+
+ /**
+ * Array of submitted form values
+ * @since 1.0
+ * @var array
+ * @access private
+ */
+ var $_submitValues = array();
+
+ /**
+ * Array of submitted form files
+ * @since 1.0
+ * @var integer
+ * @access public
+ */
+ var $_submitFiles = array();
+
+ /**
+ * Value for maxfilesize hidden element if form contains file input
+ * @since 1.0
+ * @var integer
+ * @access public
+ */
+ var $_maxFileSize = 1048576; // 1 Mb = 1048576
+
+ /**
+ * Flag to know if all fields are frozen
+ * @since 1.0
+ * @var boolean
+ * @access private
+ */
+ var $_freezeAll = false;
+
+ /**
+ * Array containing the form rules
+ * @since 1.0
+ * @var array
+ * @access private
+ */
+ var $_rules = array();
+
+ /**
+ * Form rules, global variety
+ * @var array
+ * @access private
+ */
+ var $_formRules = array();
+
+ /**
+ * Array containing the validation errors
+ * @since 1.0
+ * @var array
+ * @access private
+ */
+ var $_errors = array();
+
+ /**
+ * Note for required fields in the form
+ * @var string
+ * @since 1.0
+ * @access public
+ */
+ var $_requiredNote = '<span style="font-size:80%; color:#ff0000;">*</span><span style="font-size:80%;"> denotes required field</span>';
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ * @param string $formName Form's name.
+ * @param string $method (optional)Form's method defaults to 'POST'
+ * @param string $action (optional)Form's action
+ * @param string $target (optional)Form's target defaults to '_self'
+ * @param mixed $attributes (optional)Extra attributes for <form> tag
+ * @param bool $trackSubmit (optional)Whether to track if the form was submitted by adding a special hidden field
+ * @access public
+ */
+ function HTML_QuickForm($formName='', $method='post', $action='', $target='_self', $attributes=null, $trackSubmit = false)
+ {
+ HTML_Common::HTML_Common($attributes);
+ $method = (strtoupper($method) == 'GET') ? 'get' : 'post';
+ $action = ($action == '') ? $_SERVER['PHP_SELF'] : $action;
+ $target = (empty($target) || $target == '_self') ? array() : array('target' => $target);
+ $attributes = array('action'=>$action, 'method'=>$method, 'name'=>$formName, 'id'=>$formName) + $target;
+ $this->updateAttributes($attributes);
+ if (!$trackSubmit || isset($_REQUEST['_qf__' . $formName])) {
+ if (1 == get_magic_quotes_gpc()) {
+ $this->_submitValues = $this->_recursiveFilter('stripslashes', 'get' == $method? $_GET: $_POST);
+ foreach ($_FILES as $keyFirst => $valFirst) {
+ foreach ($valFirst as $keySecond => $valSecond) {
+ if ('name' == $keySecond) {
+ $this->_submitFiles[$keyFirst][$keySecond] = $this->_recursiveFilter('stripslashes', $valSecond);
+ } else {
+ $this->_submitFiles[$keyFirst][$keySecond] = $valSecond;
+ }
+ }
+ }
+ } else {
+ $this->_submitValues = 'get' == $method? $_GET: $_POST;
+ $this->_submitFiles = $_FILES;
+ }
+ }
+ if ($trackSubmit) {
+ unset($this->_submitValues['_qf__' . $formName]);
+ $this->addElement('hidden', '_qf__' . $formName, null);
+ }
+ } // end constructor
+
+ // }}}
+ // {{{ apiVersion()
+
+ /**
+ * Returns the current API version
+ *
+ * @since 1.0
+ * @access public
+ * @return float
+ */
+ function apiVersion()
+ {
+ return 3.2;
+ } // end func apiVersion
+
+ // }}}
+ // {{{ registerElementType()
+
+ /**
+ * Registers a new element type
+ *
+ * @param string $typeName Name of element type
+ * @param string $include Include path for element type
+ * @param string $className Element class name
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function registerElementType($typeName, $include, $className)
+ {
+ $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][strtolower($typeName)] = array($include, $className);
+ } // end func registerElementType
+
+ // }}}
+ // {{{ registerRule()
+
+ /**
+ * Registers a new validation rule
+ *
+ * @param string $ruleName Name of validation rule
+ * @param string $type Either: 'regex', 'function' or 'rule' for an HTML_QuickForm_Rule object
+ * @param string $data1 Name of function, regular expression or HTML_QuickForm_Rule classname
+ * @param string $data2 Object parent of above function or HTML_QuickForm_Rule file path
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function registerRule($ruleName, $type, $data1, $data2 = null)
+ {
+ include_once('HTML/QuickForm/RuleRegistry.php');
+ $registry =& HTML_QuickForm_RuleRegistry::singleton();
+ $registry->registerRule($ruleName, $type, $data1, $data2);
+ } // end func registerRule
+
+ // }}}
+ // {{{ elementExists()
+
+ /**
+ * Returns true if element is in the form
+ *
+ * @param string $element form name of element to check
+ * @since 1.0
+ * @access public
+ * @return boolean
+ */
+ function elementExists($element=null)
+ {
+ return isset($this->_elementIndex[$element]);
+ } // end func elementExists
+
+ // }}}
+ // {{{ setDatasource()
+
+ /**
+ * Sets a datasource object for this form object
+ *
+ * Datasource default and constant values will feed the QuickForm object if
+ * the datasource implements defaultValues() and constantValues() methods.
+ *
+ * @param object $datasource datasource object implementing the informal datasource protocol
+ * @param mixed $defaultsFilter string or array of filter(s) to apply to default values
+ * @param mixed $constantsFilter string or array of filter(s) to apply to constants values
+ * @since 3.3
+ * @access public
+ * @return void
+ */
+ function setDatasource(&$datasource, $defaultsFilter = null, $constantsFilter = null)
+ {
+ if (is_object($datasource)) {
+ $this->_datasource =& $datasource;
+ if (is_callable(array($datasource, 'defaultValues'))) {
+ $this->setDefaults($datasource->defaultValues($this), $defaultsFilter);
+ }
+ if (is_callable(array($datasource, 'constantValues'))) {
+ $this->setConstants($datasource->constantValues($this), $constantsFilter);
+ }
+ } else {
+ return PEAR::raiseError(null, QUICKFORM_INVALID_DATASOURCE, null, E_USER_WARNING, "Datasource is not an object in QuickForm::setDatasource()", 'HTML_QuickForm_Error', true);
+ }
+ } // end func setDatasource
+
+ // }}}
+ // {{{ setDefaults()
+
+ /**
+ * Initializes default form values
+ *
+ * @param array $defaultValues values used to fill the form
+ * @param mixed $filter (optional) filter(s) to apply to all default values
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setDefaults($defaultValues = null, $filter = null)
+ {
+ if (is_array($defaultValues)) {
+ if (isset($filter)) {
+ if (is_array($filter) && (2 != count($filter) || !is_callable($filter))) {
+ foreach ($filter as $val) {
+ if (!is_callable($val)) {
+ return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setDefaults()", 'HTML_QuickForm_Error', true);
+ } else {
+ $defaultValues = $this->_recursiveFilter($val, $defaultValues);
+ }
+ }
+ } elseif (!is_callable($filter)) {
+ return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setDefaults()", 'HTML_QuickForm_Error', true);
+ } else {
+ $defaultValues = $this->_recursiveFilter($filter, $defaultValues);
+ }
+ }
+ $this->_defaultValues = HTML_QuickForm::arrayMerge($this->_defaultValues, $defaultValues);
+ foreach (array_keys($this->_elements) as $key) {
+ $this->_elements[$key]->onQuickFormEvent('updateValue', null, $this);
+ }
+ }
+ } // end func setDefaults
+
+ // }}}
+ // {{{ setConstants()
+
+ /**
+ * Initializes constant form values.
+ * These values won't get overridden by POST or GET vars
+ *
+ * @param array $constantValues values used to fill the form
+ * @param mixed $filter (optional) filter(s) to apply to all default values
+ *
+ * @since 2.0
+ * @access public
+ * @return void
+ */
+ function setConstants($constantValues = null, $filter = null)
+ {
+ if (is_array($constantValues)) {
+ if (isset($filter)) {
+ if (is_array($filter) && (2 != count($filter) || !is_callable($filter))) {
+ foreach ($filter as $val) {
+ if (!is_callable($val)) {
+ return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setConstants()", 'HTML_QuickForm_Error', true);
+ } else {
+ $constantValues = $this->_recursiveFilter($val, $constantValues);
+ }
+ }
+ } elseif (!is_callable($filter)) {
+ return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::setConstants()", 'HTML_QuickForm_Error', true);
+ } else {
+ $constantValues = $this->_recursiveFilter($filter, $constantValues);
+ }
+ }
+ $this->_constantValues = HTML_QuickForm::arrayMerge($this->_constantValues, $constantValues);
+ foreach (array_keys($this->_elements) as $key) {
+ $this->_elements[$key]->onQuickFormEvent('updateValue', null, $this);
+ }
+ }
+ } // end func setConstants
+
+ // }}}
+ // {{{ setMaxFileSize()
+
+ /**
+ * Sets the value of MAX_FILE_SIZE hidden element
+ *
+ * @param int $bytes Size in bytes
+ * @since 3.0
+ * @access public
+ * @return void
+ */
+ function setMaxFileSize($bytes = 0)
+ {
+ if ($bytes > 0) {
+ $this->_maxFileSize = $bytes;
+ }
+ if (!$this->elementExists('MAX_FILE_SIZE')) {
+ $this->addElement('hidden', 'MAX_FILE_SIZE', $this->_maxFileSize);
+ } else {
+ $el =& $this->getElement('MAX_FILE_SIZE');
+ $el->updateAttributes(array('value' => $this->_maxFileSize));
+ }
+ } // end func setMaxFileSize
+
+ // }}}
+ // {{{ getMaxFileSize()
+
+ /**
+ * Returns the value of MAX_FILE_SIZE hidden element
+ *
+ * @since 3.0
+ * @access public
+ * @return int max file size in bytes
+ */
+ function getMaxFileSize()
+ {
+ return $this->_maxFileSize;
+ } // end func getMaxFileSize
+
+ // }}}
+ // {{{ &createElement()
+
+ /**
+ * Creates a new form element of the given type.
+ *
+ * This method accepts variable number of parameters, their
+ * meaning and count depending on $elementType
+ *
+ * @param string $elementType type of element to add (text, textarea, file...)
+ * @since 1.0
+ * @access public
+ * @return object extended class of HTML_element
+ * @throws HTML_QuickForm_Error
+ */
+ function &createElement($elementType)
+ {
+ $args = func_get_args();
+ return HTML_QuickForm::_loadElement('createElement', $elementType, array_slice($args, 1));
+ } // end func createElement
+
+ // }}}
+ // {{{ _loadElement()
+
+ /**
+ * Returns a form element of the given type
+ *
+ * @param string $event event to send to newly created element ('createElement' or 'addElement')
+ * @param string $type element type
+ * @param array $args arguments for event
+ * @since 2.0
+ * @access private
+ * @return object a new element
+ * @throws HTML_QuickForm_Error
+ */
+ function &_loadElement($event, $type, $args)
+ {
+ $type = strtolower($type);
+ if (!HTML_QuickForm::isTypeRegistered($type)) {
+ return PEAR::raiseError(null, QUICKFORM_UNREGISTERED_ELEMENT, null, E_USER_WARNING, "Element '$type' does not exist in HTML_QuickForm::_loadElement()", 'HTML_QuickForm_Error', true);
+ }
+ $className = $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][$type][1];
+ $includeFile = $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][$type][0];
+ include_once($includeFile);
+ $elementObject =& new $className();
+ for ($i = 0; $i < 5; $i++) {
+ if (!isset($args[$i])) {
+ $args[$i] = null;
+ }
+ }
+ $err = $elementObject->onQuickFormEvent($event, $args, $this);
+ if ($err !== true) {
+ return $err;
+ }
+ return $elementObject;
+ } // end func _loadElement
+
+ // }}}
+ // {{{ addElement()
+
+ /**
+ * Adds an element into the form
+ *
+ * If $element is a string representing element type, then this
+ * method accepts variable number of parameters, their meaning
+ * and count depending on $element
+ *
+ * @param mixed $element element object or type of element to add (text, textarea, file...)
+ * @since 1.0
+ * @return object reference to element
+ * @access public
+ * @throws HTML_QuickForm_Error
+ */
+ function &addElement($element)
+ {
+ if (is_object($element) && is_subclass_of($element, 'html_quickform_element')) {
+ $elementObject = &$element;
+ $elementObject->onQuickFormEvent('updateValue', null, $this);
+ } else {
+ $args = func_get_args();
+ $elementObject =& $this->_loadElement('addElement', $element, array_slice($args, 1));
+ if (PEAR::isError($elementObject)) {
+ return $elementObject;
+ }
+ }
+ $elementName = $elementObject->getName();
+
+ // Add the element if it is not an incompatible duplicate
+ if (!empty($elementName) && isset($this->_elementIndex[$elementName])) {
+ if ($this->_elements[$this->_elementIndex[$elementName]]->getType() ==
+ $elementObject->getType()) {
+ $this->_elements[] =& $elementObject;
+ $this->_duplicateIndex[$elementName][] = end(array_keys($this->_elements));
+ } else {
+ return PEAR::raiseError(null, QUICKFORM_INVALID_ELEMENT_NAME, null, E_USER_WARNING, "Element '$elementName' already exists in HTML_QuickForm::addElement()", 'HTML_QuickForm_Error', true);
+ }
+ } else {
+ $this->_elements[] =& $elementObject;
+ $this->_elementIndex[$elementName] = end(array_keys($this->_elements));
+ }
+ if ($this->_freezeAll) {
+ $elementObject->freeze();
+ }
+
+ return $elementObject;
+ } // end func addElement
+
+ // }}}
+ // {{{ insertElementBefore()
+
+ /**
+ * Inserts a new element right before the other element
+ *
+ * Warning: it is not possible to check whether the $element is already
+ * added to the form, therefore if you want to move the existing form
+ * element to a new position, you'll have to use removeElement():
+ * $form->insertElementBefore($form->removeElement('foo', false), 'bar');
+ *
+ * @access public
+ * @since 3.2.4
+ * @param object HTML_QuickForm_element Element to insert
+ * @param string Name of the element before which the new one is inserted
+ * @return object HTML_QuickForm_element reference to inserted element
+ * @throws HTML_QuickForm_Error
+ */
+ function &insertElementBefore(&$element, $nameAfter)
+ {
+ if (!empty($this->_duplicateIndex[$nameAfter])) {
+ return PEAR::raiseError(null, QUICKFORM_INVALID_ELEMENT_NAME, null, E_USER_WARNING, 'Several elements named "' . $nameAfter . '" exist in HTML_QuickForm::insertElementBefore().', 'HTML_QuickForm_Error', true);
+ } elseif (!$this->elementExists($nameAfter)) {
+ return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$nameAfter' does not exist in HTML_QuickForm::insertElementBefore()", 'HTML_QuickForm_Error', true);
+ }
+ $elementName = $element->getName();
+ $targetIdx = $this->_elementIndex[$nameAfter];
+ $duplicate = false;
+ // Like in addElement(), check that it's not an incompatible duplicate
+ if (!empty($elementName) && isset($this->_elementIndex[$elementName])) {
+ if ($this->_elements[$this->_elementIndex[$elementName]]->getType() != $element->getType()) {
+ return PEAR::raiseError(null, QUICKFORM_INVALID_ELEMENT_NAME, null, E_USER_WARNING, "Element '$elementName' already exists in HTML_QuickForm::insertElementBefore()", 'HTML_QuickForm_Error', true);
+ }
+ $duplicate = true;
+ }
+ // Move all the elements after added back one place, reindex _elementIndex and/or _duplicateIndex
+ for ($i = end(array_keys($this->_elements)); $i >= $targetIdx; $i--) {
+ if (isset($this->_elements[$i])) {
+ $currentName = $this->_elements[$i]->getName();
+ $this->_elements[$i + 1] =& $this->_elements[$i];
+ if ($this->_elementIndex[$currentName] == $i) {
+ $this->_elementIndex[$currentName] = $i + 1;
+ } else {
+ $dupIdx = array_search($i, $this->_duplicateIndex[$currentName]);
+ $this->_duplicateIndex[$currentName][$dupIdx] = $i + 1;
+ }
+ unset($this->_elements[$i]);
+ }
+ }
+ // Put the element in place finally
+ $this->_elements[$targetIdx] =& $element;
+ if (!$duplicate) {
+ $this->_elementIndex[$elementName] = $targetIdx;
+ } else {
+ $this->_duplicateIndex[$elementName][] = $targetIdx;
+ }
+ $element->onQuickFormEvent('updateValue', null, $this);
+ if ($this->_freezeAll) {
+ $element->freeze();
+ }
+ // If not done, the elements will appear in reverse order
+ ksort($this->_elements);
+ return $element;
+ }
+
+ // }}}
+ // {{{ addGroup()
+
+ /**
+ * Adds an element group
+ * @param array $elements array of elements composing the group
+ * @param string $name (optional)group name
+ * @param string $groupLabel (optional)group label
+ * @param string $separator (optional)string to separate elements
+ * @param string $appendName (optional)specify whether the group name should be
+ * used in the form element name ex: group[element]
+ * @return object reference to added group of elements
+ * @since 2.8
+ * @access public
+ * @throws PEAR_Error
+ */
+ function &addGroup($elements, $name=null, $groupLabel='', $separator=null, $appendName = true)
+ {
+ static $anonGroups = 1;
+
+ if (0 == strlen($name)) {
+ $name = 'qf_group_' . $anonGroups++;
+ $appendName = false;
+ }
+ return $this->addElement('group', $name, $groupLabel, $elements, $separator, $appendName);
+ } // end func addGroup
+
+ // }}}
+ // {{{ &getElement()
+
+ /**
+ * Returns a reference to the element
+ *
+ * @param string $element Element name
+ * @since 2.0
+ * @access public
+ * @return object reference to element
+ * @throws HTML_QuickForm_Error
+ */
+ function &getElement($element)
+ {
+ if (isset($this->_elementIndex[$element])) {
+ return $this->_elements[$this->_elementIndex[$element]];
+ } else {
+ return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::getElement()", 'HTML_QuickForm_Error', true);
+ }
+ } // end func getElement
+
+ // }}}
+ // {{{ &getElementValue()
+
+ /**
+ * Returns the element's raw value
+ *
+ * This returns the value as submitted by the form (not filtered)
+ * or set via setDefaults() or setConstants()
+ *
+ * @param string $element Element name
+ * @since 2.0
+ * @access public
+ * @return mixed element value
+ * @throws HTML_QuickForm_Error
+ */
+ function &getElementValue($element)
+ {
+ if (!isset($this->_elementIndex[$element])) {
+ return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::getElementValue()", 'HTML_QuickForm_Error', true);
+ }
+ $value = $this->_elements[$this->_elementIndex[$element]]->getValue();
+ if (isset($this->_duplicateIndex[$element])) {
+ foreach ($this->_duplicateIndex[$element] as $index) {
+ if (null !== ($v = $this->_elements[$index]->getValue())) {
+ if (is_array($value)) {
+ $value[] = $v;
+ } else {
+ $value = (null === $value)? $v: array($value, $v);
+ }
+ }
+ }
+ }
+ return $value;
+ } // end func getElementValue
+
+ // }}}
+ // {{{ getSubmitValue()
+
+ /**
+ * Returns the elements value after submit and filter
+ *
+ * @param string Element name
+ * @since 2.0
+ * @access public
+ * @return mixed submitted element value or null if not set
+ */
+ function getSubmitValue($elementName)
+ {
+ $value = null;
+ if (isset($this->_submitValues[$elementName]) || isset($this->_submitFiles[$elementName])) {
+ $value = isset($this->_submitValues[$elementName])? $this->_submitValues[$elementName]: array();
+ if (is_array($value) && isset($this->_submitFiles[$elementName])) {
+ foreach ($this->_submitFiles[$elementName] as $k => $v) {
+ $value = HTML_QuickForm::arrayMerge($value, $this->_reindexFiles($this->_submitFiles[$elementName][$k], $k));
+ }
+ }
+
+ } elseif ('file' == $this->getElementType($elementName)) {
+ return $this->getElementValue($elementName);
+
+ } elseif ('group' == $this->getElementType($elementName)) {
+ $group =& $this->getElement($elementName);
+ $elements =& $group->getElements();
+ foreach (array_keys($elements) as $key) {
+ $name = $group->getElementName($key);
+ // filter out radios
+ if ($name != $elementName) {
+ if (null !== ($v = $this->getSubmitValue($name))) {
+ $value[$name] = $v;
+ }
+ }
+ }
+
+ } elseif (false !== ($pos = strpos($elementName, '['))) {
+ $base = substr($elementName, 0, $pos);
+ $idx = "['" . str_replace(array(']', '['), array('', "']['"), substr($elementName, $pos + 1, -1)) . "']";
+ if (isset($this->_submitValues[$base])) {
+ $value = eval("return (isset(\$this->_submitValues['{$base}']{$idx})) ? \$this->_submitValues['{$base}']{$idx} : null;");
+ }
+
+ if (null === $value && isset($this->_submitFiles[$base])) {
+ $props = array('name', 'type', 'size', 'tmp_name', 'error');
+ $code = "if (!isset(\$this->_submitFiles['{$base}']['name']{$idx})) {\n" .
+ " return null;\n" .
+ "} else {\n" .
+ " \$v = array();\n";
+ foreach ($props as $prop) {
+ $code .= " \$v['{$prop}'] = \$this->_submitFiles['{$base}']['{$prop}']{$idx};\n";
+ }
+ $value = eval($code . " return \$v;\n}\n");
+ }
+ }
+ return $value;
+ } // end func getSubmitValue
+
+ // }}}
+ // {{{ _reindexFiles()
+
+ /**
+ * A helper function to change the indexes in $_FILES array
+ *
+ * @param mixed Some value from the $_FILES array
+ * @param string The key from the $_FILES array that should be appended
+ * @return array
+ */
+ function _reindexFiles($value, $key)
+ {
+ if (!is_array($value)) {
+ return array($key => $value);
+ } else {
+ $ret = array();
+ foreach ($value as $k => $v) {
+ $ret[$k] = $this->_reindexFiles($v, $key);
+ }
+ return $ret;
+ }
+ }
+
+ // }}}
+ // {{{ getElementError()
+
+ /**
+ * Returns error corresponding to validated element
+ *
+ * @param string $element Name of form element to check
+ * @since 1.0
+ * @access public
+ * @return string error message corresponding to checked element
+ */
+ function getElementError($element)
+ {
+ if (isset($this->_errors[$element])) {
+ return $this->_errors[$element];
+ }
+ } // end func getElementError
+
+ // }}}
+ // {{{ setElementError()
+
+ /**
+ * Set error message for a form element
+ *
+ * @param string $element Name of form element to set error for
+ * @param string $message Error message
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setElementError($element,$message)
+ {
+ $this->_errors[$element] = $message;
+ } // end func setElementError
+
+ // }}}
+ // {{{ getElementType()
+
+ /**
+ * Returns the type of the given element
+ *
+ * @param string $element Name of form element
+ * @since 1.1
+ * @access public
+ * @return string Type of the element, false if the element is not found
+ */
+ function getElementType($element)
+ {
+ if (isset($this->_elementIndex[$element])) {
+ return $this->_elements[$this->_elementIndex[$element]]->getType();
+ }
+ return false;
+ } // end func getElementType
+
+ // }}}
+ // {{{ updateElementAttr()
+
+ /**
+ * Updates Attributes for one or more elements
+ *
+ * @param mixed $elements Array of element names/objects or string of elements to be updated
+ * @param mixed $attrs Array or sting of html attributes
+ * @since 2.10
+ * @access public
+ * @return void
+ */
+ function updateElementAttr($elements, $attrs)
+ {
+ if (is_string($elements)) {
+ $elements = split('[ ]?,[ ]?', $elements);
+ }
+ foreach (array_keys($elements) as $key) {
+ if (is_object($elements[$key]) && is_a($elements[$key], 'HTML_QuickForm_element')) {
+ $elements[$key]->updateAttributes($attrs);
+ } elseif (isset($this->_elementIndex[$elements[$key]])) {
+ $this->_elements[$this->_elementIndex[$elements[$key]]]->updateAttributes($attrs);
+ if (isset($this->_duplicateIndex[$elements[$key]])) {
+ foreach ($this->_duplicateIndex[$elements[$key]] as $index) {
+ $this->_elements[$index]->updateAttributes($attrs);
+ }
+ }
+ }
+ }
+ } // end func updateElementAttr
+
+ // }}}
+ // {{{ removeElement()
+
+ /**
+ * Removes an element
+ *
+ * The method "unlinks" an element from the form, returning the reference
+ * to the element object. If several elements named $elementName exist,
+ * it removes the first one, leaving the others intact.
+ *
+ * @param string $elementName The element name
+ * @param boolean $removeRules True if rules for this element are to be removed too
+ * @access public
+ * @since 2.0
+ * @return object HTML_QuickForm_element a reference to the removed element
+ * @throws HTML_QuickForm_Error
+ */
+ function &removeElement($elementName, $removeRules = true)
+ {
+ if (!isset($this->_elementIndex[$elementName])) {
+ return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$elementName' does not exist in HTML_QuickForm::removeElement()", 'HTML_QuickForm_Error', true);
+ }
+ $el =& $this->_elements[$this->_elementIndex[$elementName]];
+ unset($this->_elements[$this->_elementIndex[$elementName]]);
+ if (empty($this->_duplicateIndex[$elementName])) {
+ unset($this->_elementIndex[$elementName]);
+ } else {
+ $this->_elementIndex[$elementName] = array_shift($this->_duplicateIndex[$elementName]);
+ }
+ if ($removeRules) {
+ unset($this->_rules[$elementName]);
+ }
+ return $el;
+ } // end func removeElement
+
+ // }}}
+ // {{{ addRule()
+
+ /**
+ * Adds a validation rule for the given field
+ *
+ * If the element is in fact a group, it will be considered as a whole.
+ * To validate grouped elements as separated entities,
+ * use addGroupRule instead of addRule.
+ *
+ * @param string $element Form element name
+ * @param string $message Message to display for invalid data
+ * @param string $type Rule type, use getRegisteredRules() to get types
+ * @param string $format (optional)Required for extra rule data
+ * @param string $validation (optional)Where to perform validation: "server", "client"
+ * @param boolean $reset Client-side validation: reset the form element to its original value if there is an error?
+ * @param boolean $force Force the rule to be applied, even if the target form element does not exist
+ * @since 1.0
+ * @access public
+ * @throws HTML_QuickForm_Error
+ */
+ function addRule($element, $message, $type, $format=null, $validation='server', $reset = false, $force = false)
+ {
+ if (!$force) {
+ if (!is_array($element) && !$this->elementExists($element)) {
+ return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true);
+ } elseif (is_array($element)) {
+ foreach ($element as $el) {
+ if (!$this->elementExists($el)) {
+ return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$el' does not exist in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true);
+ }
+ }
+ }
+ }
+ if (false === ($newName = $this->isRuleRegistered($type, true))) {
+ return PEAR::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addRule()", 'HTML_QuickForm_Error', true);
+ } elseif (is_string($newName)) {
+ $type = $newName;
+ }
+ if (is_array($element)) {
+ $dependent = $element;
+ $element = array_shift($dependent);
+ } else {
+ $dependent = null;
+ }
+ if ($type == 'required' || $type == 'uploadedfile') {
+ $this->_required[] = $element;
+ }
+ if (!isset($this->_rules[$element])) {
+ $this->_rules[$element] = array();
+ }
+ if ($validation == 'client') {
+ $this->updateAttributes(array('onsubmit'=>'return validate_'.$this->_attributes['id'] . '(this);'));
+ }
+ $this->_rules[$element][] = array(
+ 'type' => $type,
+ 'format' => $format,
+ 'message' => $message,
+ 'validation' => $validation,
+ 'reset' => $reset,
+ 'dependent' => $dependent
+ );
+ } // end func addRule
+
+ // }}}
+ // {{{ addGroupRule()
+
+ /**
+ * Adds a validation rule for the given group of elements
+ *
+ * Only groups with a name can be assigned a validation rule
+ * Use addGroupRule when you need to validate elements inside the group.
+ * Use addRule if you need to validate the group as a whole. In this case,
+ * the same rule will be applied to all elements in the group.
+ * Use addRule if you need to validate the group against a function.
+ *
+ * @param string $group Form group name
+ * @param mixed $arg1 Array for multiple elements or error message string for one element
+ * @param string $type (optional)Rule type use getRegisteredRules() to get types
+ * @param string $format (optional)Required for extra rule data
+ * @param int $howmany (optional)How many valid elements should be in the group
+ * @param string $validation (optional)Where to perform validation: "server", "client"
+ * @param bool $reset Client-side: whether to reset the element's value to its original state if validation failed.
+ * @since 2.5
+ * @access public
+ * @throws HTML_QuickForm_Error
+ */
+ function addGroupRule($group, $arg1, $type='', $format=null, $howmany=0, $validation = 'server', $reset = false)
+ {
+ if (!$this->elementExists($group)) {
+ return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Group '$group' does not exist in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true);
+ }
+
+ $groupObj =& $this->getElement($group);
+ if (is_array($arg1)) {
+ $required = 0;
+ foreach ($arg1 as $elementIndex => $rules) {
+ $elementName = $groupObj->getElementName($elementIndex);
+ foreach ($rules as $rule) {
+ $format = (isset($rule[2])) ? $rule[2] : null;
+ $validation = (isset($rule[3]) && 'client' == $rule[3])? 'client': 'server';
+ $reset = isset($rule[4]) && $rule[4];
+ $type = $rule[1];
+ if (false === ($newName = $this->isRuleRegistered($type, true))) {
+ return PEAR::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true);
+ } elseif (is_string($newName)) {
+ $type = $newName;
+ }
+
+ $this->_rules[$elementName][] = array(
+ 'type' => $type,
+ 'format' => $format,
+ 'message' => $rule[0],
+ 'validation' => $validation,
+ 'reset' => $reset,
+ 'group' => $group);
+
+ if ('required' == $type || 'uploadedfile' == $type) {
+ $groupObj->_required[] = $elementName;
+ $this->_required[] = $elementName;
+ $required++;
+ }
+ if ('client' == $validation) {
+ $this->updateAttributes(array('onsubmit'=>'return validate_'.$this->_attributes['id'] . '(this);'));
+ }
+ }
+ }
+ if ($required > 0 && count($groupObj->getElements()) == $required) {
+ $this->_required[] = $group;
+ }
+ } elseif (is_string($arg1)) {
+ if (false === ($newName = $this->isRuleRegistered($type, true))) {
+ return PEAR::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, "Rule '$type' is not registered in HTML_QuickForm::addGroupRule()", 'HTML_QuickForm_Error', true);
+ } elseif (is_string($newName)) {
+ $type = $newName;
+ }
+
+ // addGroupRule() should also handle <select multiple>
+ if (is_a($groupObj, 'html_quickform_group')) {
+ // Radios need to be handled differently when required
+ if ($type == 'required' && $groupObj->getGroupType() == 'radio') {
+ $howmany = ($howmany == 0) ? 1 : $howmany;
+ } else {
+ $howmany = ($howmany == 0) ? count($groupObj->getElements()) : $howmany;
+ }
+ }
+
+ $this->_rules[$group][] = array('type' => $type,
+ 'format' => $format,
+ 'message' => $arg1,
+ 'validation' => $validation,
+ 'howmany' => $howmany,
+ 'reset' => $reset);
+ if ($type == 'required') {
+ $this->_required[] = $group;
+ }
+ if ($validation == 'client') {
+ $this->updateAttributes(array('onsubmit'=>'return validate_'.$this->_attributes['id'] . '(this);'));
+ }
+ }
+ } // end func addGroupRule
+
+ // }}}
+ // {{{ addFormRule()
+
+ /**
+ * Adds a global validation rule
+ *
+ * This should be used when for a rule involving several fields or if
+ * you want to use some completely custom validation for your form.
+ * The rule function/method should return true in case of successful
+ * validation and array('element name' => 'error') when there were errors.
+ *
+ * @access public
+ * @param mixed Callback, either function name or array(&$object, 'method')
+ * @throws HTML_QuickForm_Error
+ */
+ function addFormRule($rule)
+ {
+ if (!is_callable($rule)) {
+ return PEAR::raiseError(null, QUICKFORM_INVALID_RULE, null, E_USER_WARNING, 'Callback function does not exist in HTML_QuickForm::addFormRule()', 'HTML_QuickForm_Error', true);
+ }
+ $this->_formRules[] = $rule;
+ }
+
+ // }}}
+ // {{{ applyFilter()
+
+ /**
+ * Applies a data filter for the given field(s)
+ *
+ * @param mixed $element Form element name or array of such names
+ * @param mixed $filter Callback, either function name or array(&$object, 'method')
+ * @since 2.0
+ * @access public
+ */
+ function applyFilter($element, $filter)
+ {
+ if (!is_callable($filter)) {
+ return PEAR::raiseError(null, QUICKFORM_INVALID_FILTER, null, E_USER_WARNING, "Callback function does not exist in QuickForm::applyFilter()", 'HTML_QuickForm_Error', true);
+ }
+ if ($element == '__ALL__') {
+ $this->_submitValues = $this->_recursiveFilter($filter, $this->_submitValues);
+ } else {
+ if (!is_array($element)) {
+ $element = array($element);
+ }
+ foreach ($element as $elName) {
+ $value = $this->getSubmitValue($elName);
+ if (null !== $value) {
+ if (false === strpos($elName, '[')) {
+ $this->_submitValues[$elName] = $this->_recursiveFilter($filter, $value);
+ } else {
+ $idx = "['" . str_replace(array(']', '['), array('', "']['"), $elName) . "']";
+ eval("\$this->_submitValues{$idx} = \$this->_recursiveFilter(\$filter, \$value);");
+ }
+ }
+ }
+ }
+ } // end func applyFilter
+
+ // }}}
+ // {{{ _recursiveFilter()
+
+ /**
+ * Recursively apply a filter function
+ *
+ * @param string $filter filter to apply
+ * @param mixed $value submitted values
+ * @since 2.0
+ * @access private
+ * @return cleaned values
+ */
+ function _recursiveFilter($filter, $value)
+ {
+ if (is_array($value)) {
+ $cleanValues = array();
+ foreach ($value as $k => $v) {
+ $cleanValues[$k] = $this->_recursiveFilter($filter, $value[$k]);
+ }
+ return $cleanValues;
+ } else {
+ return call_user_func($filter, $value);
+ }
+ } // end func _recursiveFilter
+
+ // }}}
+ // {{{ arrayMerge()
+
+ /**
+ * Merges two arrays
+ *
+ * Merges two array like the PHP function array_merge but recursively.
+ * The main difference is that existing keys will not be renumbered
+ * if they are integers.
+ *
+ * @access puplic
+ * @param array $a original array
+ * @param array $b array which will be merged into first one
+ * @return array merged array
+ */
+ function arrayMerge($a, $b)
+ {
+ foreach ($b as $k => $v) {
+ if (is_array($v)) {
+ if (isset($a[$k]) && !is_array($a[$k])) {
+ $a[$k] = $v;
+ } else {
+ if (!isset($a[$k])) {
+ $a[$k] = array();
+ }
+ $a[$k] = HTML_QuickForm::arrayMerge($a[$k], $v);
+ }
+ } else {
+ $a[$k] = $v;
+ }
+ }
+ return $a;
+ } // end func arrayMerge
+
+ // }}}
+ // {{{ isTypeRegistered()
+
+ /**
+ * Returns whether or not the form element type is supported
+ *
+ * @param string $type Form element type
+ * @since 1.0
+ * @access public
+ * @return boolean
+ */
+ function isTypeRegistered($type)
+ {
+ return isset($GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][$type]);
+ } // end func isTypeRegistered
+
+ // }}}
+ // {{{ getRegisteredTypes()
+
+ /**
+ * Returns an array of registered element types
+ *
+ * @since 1.0
+ * @access public
+ * @return array
+ */
+ function getRegisteredTypes()
+ {
+ return array_keys($GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES']);
+ } // end func getRegisteredTypes
+
+ // }}}
+ // {{{ isRuleRegistered()
+
+ /**
+ * Returns whether or not the given rule is supported
+ *
+ * @param string $name Validation rule name
+ * @param bool Whether to automatically register subclasses of HTML_QuickForm_Rule
+ * @since 1.0
+ * @access public
+ * @return mixed true if previously registered, false if not, new rule name if auto-registering worked
+ */
+ function isRuleRegistered($name, $autoRegister = false)
+ {
+ if (is_scalar($name) && isset($GLOBALS['_HTML_QuickForm_registered_rules'][$name])) {
+ return true;
+ } elseif (!$autoRegister) {
+ return false;
+ }
+ // automatically register the rule if requested
+ include_once 'HTML/QuickForm/RuleRegistry.php';
+ $ruleName = false;
+ if (is_object($name) && is_a($name, 'html_quickform_rule')) {
+ $ruleName = !empty($name->name)? $name->name: strtolower(get_class($name));
+ } elseif (is_string($name) && class_exists($name)) {
+ $parent = strtolower($name);
+ do {
+ if ('html_quickform_rule' == strtolower($parent)) {
+ $ruleName = strtolower($name);
+ break;
+ }
+ } while ($parent = get_parent_class($parent));
+ }
+ if ($ruleName) {
+ $registry =& HTML_QuickForm_RuleRegistry::singleton();
+ $registry->registerRule($ruleName, null, $name);
+ }
+ return $ruleName;
+ } // end func isRuleRegistered
+
+ // }}}
+ // {{{ getRegisteredRules()
+
+ /**
+ * Returns an array of registered validation rules
+ *
+ * @since 1.0
+ * @access public
+ * @return array
+ */
+ function getRegisteredRules()
+ {
+ return array_keys($GLOBALS['_HTML_QuickForm_registered_rules']);
+ } // end func getRegisteredRules
+
+ // }}}
+ // {{{ isElementRequired()
+
+ /**
+ * Returns whether or not the form element is required
+ *
+ * @param string $element Form element name
+ * @since 1.0
+ * @access public
+ * @return boolean
+ */
+ function isElementRequired($element)
+ {
+ return in_array($element, $this->_required, true);
+ } // end func isElementRequired
+
+ // }}}
+ // {{{ isElementFrozen()
+
+ /**
+ * Returns whether or not the form element is frozen
+ *
+ * @param string $element Form element name
+ * @since 1.0
+ * @access public
+ * @return boolean
+ */
+ function isElementFrozen($element)
+ {
+ if (isset($this->_elementIndex[$element])) {
+ return $this->_elements[$this->_elementIndex[$element]]->isFrozen();
+ }
+ return false;
+ } // end func isElementFrozen
+
+ // }}}
+ // {{{ setJsWarnings()
+
+ /**
+ * Sets JavaScript warning messages
+ *
+ * @param string $pref Prefix warning
+ * @param string $post Postfix warning
+ * @since 1.1
+ * @access public
+ * @return void
+ */
+ function setJsWarnings($pref, $post)
+ {
+ $this->_jsPrefix = $pref;
+ $this->_jsPostfix = $post;
+ } // end func setJsWarnings
+
+ // }}}
+ // {{{ setRequiredNote()
+
+ /**
+ * Sets required-note
+ *
+ * @param string $note Message indicating some elements are required
+ * @since 1.1
+ * @access public
+ * @return void
+ */
+ function setRequiredNote($note)
+ {
+ $this->_requiredNote = $note;
+ } // end func setRequiredNote
+
+ // }}}
+ // {{{ getRequiredNote()
+
+ /**
+ * Returns the required note
+ *
+ * @since 2.0
+ * @access public
+ * @return string
+ */
+ function getRequiredNote()
+ {
+ return $this->_requiredNote;
+ } // end func getRequiredNote
+
+ // }}}
+ // {{{ validate()
+
+ /**
+ * Performs the server side validation
+ * @access public
+ * @since 1.0
+ * @return boolean true if no error found
+ */
+ function validate()
+ {
+ if (count($this->_rules) == 0 && count($this->_formRules) == 0 &&
+ (count($this->_submitValues) > 0 || count($this->_submitFiles) > 0)) {
+ return true;
+ } elseif (count($this->_submitValues) == 0 && count($this->_submitFiles) == 0) {
+ return false;
+ }
+
+ include_once('HTML/QuickForm/RuleRegistry.php');
+ $registry =& HTML_QuickForm_RuleRegistry::singleton();
+
+ foreach ($this->_rules as $target => $rules) {
+ $submitValue = $this->getSubmitValue($target);
+
+ foreach ($rules as $elementName => $rule) {
+ if ((isset($rule['group']) && isset($this->_errors[$rule['group']])) ||
+ isset($this->_errors[$target])) {
+ continue 2;
+ }
+ if ((!isset($submitValue) || $submitValue == '') &&
+ !$this->isElementRequired($target)) {
+ // Element is not required
+ continue 2;
+ }
+ if (isset($rule['dependent']) && is_array($rule['dependent'])) {
+ $values = array($submitValue);
+ foreach ($rule['dependent'] as $elName) {
+ $values[] = $this->getSubmitValue($elName);
+ }
+ $result = $registry->validate($rule['type'], $values, $rule['format'], true);
+ } elseif (is_array($submitValue) && !isset($rule['howmany'])) {
+ $result = $registry->validate($rule['type'], $submitValue, $rule['format'], true);
+ } else {
+ $result = $registry->validate($rule['type'], $submitValue, $rule['format'], false);
+ }
+
+ if (!$result || (!empty($rule['howmany']) && $rule['howmany'] > (int)$result)) {
+ if (isset($rule['group'])) {
+ $this->_errors[$rule['group']] = $rule['message'];
+ } else {
+ $this->_errors[$target] = $rule['message'];
+ }
+ }
+ }
+ }
+
+ // process the global rules now
+ foreach ($this->_formRules as $rule) {
+ if (true !== ($res = call_user_func($rule, $this->_submitValues, $this->_submitFiles))) {
+ if (is_array($res)) {
+ $this->_errors += $res;
+ } else {
+ return PEAR::raiseError(null, QUICKFORM_ERROR, null, E_USER_WARNING, 'Form rule callback returned invalid value in HTML_QuickForm::validate()', 'HTML_QuickForm_Error', true);
+ }
+ }
+ }
+
+ return (0 == count($this->_errors));
+ } // end func validate
+
+ // }}}
+ // {{{ freeze()
+
+ /**
+ * Displays elements without HTML input tags
+ *
+ * @param mixed $elementList array or string of element(s) to be frozen
+ * @since 1.0
+ * @access public
+ * @throws HTML_QuickForm_Error
+ */
+ function freeze($elementList=null)
+ {
+ if (!isset($elementList)) {
+ $this->_freezeAll = true;
+ $elementList = array();
+ } else {
+ if (!is_array($elementList)) {
+ $elementList = preg_split('/[ ]*,[ ]*/', $elementList);
+ }
+ $elementList = array_flip($elementList);
+ }
+
+ foreach (array_keys($this->_elements) as $key) {
+ $name = $this->_elements[$key]->getName();
+ if ($this->_freezeAll || isset($elementList[$name])) {
+ $this->_elements[$key]->freeze();
+ unset($elementList[$name]);
+ }
+ }
+
+ if (!empty($elementList)) {
+ return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Nonexistant element(s): '" . implode("', '", array_keys($elementList)) . "' in HTML_QuickForm::freeze()", 'HTML_QuickForm_Error', true);
+ }
+ return true;
+ } // end func freeze
+
+ // }}}
+ // {{{ isFrozen()
+
+ /**
+ * Returns whether or not the whole form is frozen
+ *
+ * @since 3.0
+ * @access public
+ * @return boolean
+ */
+ function isFrozen()
+ {
+ return $this->_freezeAll;
+ } // end func isFrozen
+
+ // }}}
+ // {{{ process()
+
+ /**
+ * Performs the form data processing
+ *
+ * @param mixed $callback Callback, either function name or array(&$object, 'method')
+ * @param bool $mergeFiles Whether uploaded files should be processed too
+ * @since 1.0
+ * @access public
+ * @throws HTML_QuickForm_Error
+ */
+ function process($callback, $mergeFiles = true)
+ {
+ if (!is_callable($callback)) {
+ return PEAR::raiseError(null, QUICKFORM_INVALID_PROCESS, null, E_USER_WARNING, "Callback function does not exist in QuickForm::process()", 'HTML_QuickForm_Error', true);
+ }
+ $values = ($mergeFiles === true) ? HTML_QuickForm::arrayMerge($this->_submitValues, $this->_submitFiles) : $this->_submitValues;
+ return call_user_func($callback, $values);
+ } // end func process
+
+ // }}}
+ // {{{ accept()
+
+ /**
+ * Accepts a renderer
+ *
+ * @param object An HTML_QuickForm_Renderer object
+ * @since 3.0
+ * @access public
+ * @return void
+ */
+ function accept(&$renderer)
+ {
+ $renderer->startForm($this);
+ foreach (array_keys($this->_elements) as $key) {
+ $element =& $this->_elements[$key];
+ $elementName = $element->getName();
+ $required = ($this->isElementRequired($elementName) && !$element->isFrozen());
+ $error = $this->getElementError($elementName);
+ $element->accept($renderer, $required, $error);
+ }
+ $renderer->finishForm($this);
+ } // end func accept
+
+ // }}}
+ // {{{ defaultRenderer()
+
+ /**
+ * Returns a reference to default renderer object
+ *
+ * @access public
+ * @since 3.0
+ * @return object a default renderer object
+ */
+ function &defaultRenderer()
+ {
+ if (!isset($GLOBALS['_HTML_QuickForm_default_renderer'])) {
+ include_once('HTML/QuickForm/Renderer/Default.php');
+ $GLOBALS['_HTML_QuickForm_default_renderer'] =& new HTML_QuickForm_Renderer_Default();
+ }
+ return $GLOBALS['_HTML_QuickForm_default_renderer'];
+ } // end func defaultRenderer
+
+ // }}}
+ // {{{ toHtml ()
+
+ /**
+ * Returns an HTML version of the form
+ *
+ * @param string $in_data (optional) Any extra data to insert right
+ * before form is rendered. Useful when using templates.
+ *
+ * @return string Html version of the form
+ * @since 1.0
+ * @access public
+ */
+ function toHtml ($in_data = null)
+ {
+ if (!is_null($in_data)) {
+ $this->addElement('html', $in_data);
+ }
+ $renderer =& $this->defaultRenderer();
+ $this->accept($renderer);
+ return $renderer->toHtml();
+ } // end func toHtml
+
+ // }}}
+ // {{{ getValidationScript()
+
+ /**
+ * Returns the client side validation script
+ *
+ * @since 2.0
+ * @access public
+ * @return string Javascript to perform validation, empty string if no 'client' rules were added
+ */
+ function getValidationScript()
+ {
+ if (empty($this->_rules) || empty($this->_attributes['onsubmit'])) {
+ return '';
+ }
+
+ include_once('HTML/QuickForm/RuleRegistry.php');
+ $registry =& HTML_QuickForm_RuleRegistry::singleton();
+ $test = array();
+ $js_escape = array(
+ "\r" => '\r',
+ "\n" => '\n',
+ "\t" => '\t',
+ "'" => "\\'",
+ '"' => '\"',
+ '\\' => '\\\\'
+ );
+
+ foreach ($this->_rules as $elementName => $rules) {
+ foreach ($rules as $rule) {
+ if ('client' == $rule['validation']) {
+ $dependent = isset($rule['dependent']) && is_array($rule['dependent']);
+ $rule['message'] = strtr($rule['message'], $js_escape);
+
+ if (isset($rule['group'])) {
+ $group =& $this->getElement($rule['group']);
+ // No JavaScript validation for frozen elements
+ if ($group->isFrozen()) {
+ continue 2;
+ }
+ $elements =& $group->getElements();
+ foreach (array_keys($elements) as $key) {
+ if ($elementName == $group->getElementName($key)) {
+ $element =& $elements[$key];
+ break;
+ }
+ }
+ } elseif ($dependent) {
+ $element = array();
+ $element[] =& $this->getElement($elementName);
+ foreach ($rule['dependent'] as $idx => $elName) {
+ $element[] =& $this->getElement($elName);
+ }
+ } else {
+ $element =& $this->getElement($elementName);
+ }
+ // No JavaScript validation for frozen elements
+ if (is_object($element) && $element->isFrozen()) {
+ continue 2;
+ } elseif (is_array($element)) {
+ foreach (array_keys($element) as $key) {
+ if ($element[$key]->isFrozen()) {
+ continue 3;
+ }
+ }
+ }
+
+ $test[] = $registry->getValidationScript($element, $elementName, $rule);
+ unset($element);
+ }
+ }
+ }
+ if (count($test) > 0) {
+ return
+ "\n<script type=\"text/javascript\">\n" .
+ "//<![CDATA[\n" .
+ "function validate_" . $this->_attributes['id'] . "(frm) {\n" .
+ " var value = '';\n" .
+ " var errFlag = new Array();\n" .
+ " _qfMsg = '';\n\n" .
+ join("\n", $test) .
+ "\n if (_qfMsg != '') {\n" .
+ " _qfMsg = '" . strtr($this->_jsPrefix, $js_escape) . "' + _qfMsg;\n" .
+ " _qfMsg = _qfMsg + '\\n" . strtr($this->_jsPostfix, $js_escape) . "';\n" .
+ " alert(_qfMsg);\n" .
+ " return false;\n" .
+ " }\n" .
+ " return true;\n" .
+ "}\n" .
+ "//]]>\n" .
+ "</script>";
+ }
+ return '';
+ } // end func getValidationScript
+
+ // }}}
+ // {{{ getSubmitValues()
+
+ /**
+ * Returns the values submitted by the form
+ *
+ * @since 2.0
+ * @access public
+ * @param bool Whether uploaded files should be returned too
+ * @return array
+ */
+ function getSubmitValues($mergeFiles = false)
+ {
+ return $mergeFiles? HTML_QuickForm::arrayMerge($this->_submitValues, $this->_submitFiles): $this->_submitValues;
+ } // end func getSubmitValues
+
+ // }}}
+ // {{{ toArray()
+
+ /**
+ * Returns the form's contents in an array.
+ *
+ * The description of the array structure is in HTML_QuickForm_Renderer_Array docs
+ *
+ * @since 2.0
+ * @access public
+ * @param bool Whether to collect hidden elements (passed to the Renderer's constructor)
+ * @return array of form contents
+ */
+ function toArray($collectHidden = false)
+ {
+ include_once 'HTML/QuickForm/Renderer/Array.php';
+ $renderer =& new HTML_QuickForm_Renderer_Array($collectHidden);
+ $this->accept($renderer);
+ return $renderer->toArray();
+ } // end func toArray
+
+ // }}}
+ // {{{ exportValue()
+
+ /**
+ * Returns a 'safe' element's value
+ *
+ * This method first tries to find a cleaned-up submitted value,
+ * it will return a value set by setValue()/setDefaults()/setConstants()
+ * if submitted value does not exist for the given element.
+ *
+ * @param string Name of an element
+ * @access public
+ * @return mixed
+ */
+ function exportValue($element)
+ {
+ if (!isset($this->_elementIndex[$element])) {
+ return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Element '$element' does not exist in HTML_QuickForm::getElementValue()", 'HTML_QuickForm_Error', true);
+ }
+ $value = $this->_elements[$this->_elementIndex[$element]]->exportValue($this->_submitValues, false);
+ if (isset($this->_duplicateIndex[$element])) {
+ foreach ($this->_duplicateIndex[$element] as $index) {
+ if (null !== ($v = $this->_elements[$index]->exportValue($this->_submitValues, false))) {
+ if (is_array($value)) {
+ $value[] = $v;
+ } else {
+ $value = (null === $value)? $v: array($value, $v);
+ }
+ }
+ }
+ }
+ return $value;
+ }
+
+ // }}}
+ // {{{ exportValues()
+
+ /**
+ * Returns 'safe' elements' values
+ *
+ * Unlike getSubmitValues(), this will return only the values
+ * corresponding to the elements present in the form.
+ *
+ * @param mixed Array/string of element names, whose values we want. If not set then return all elements.
+ * @access public
+ * @return array An assoc array of elements' values
+ * @throws HTML_QuickForm_Error
+ */
+ function exportValues($elementList = null)
+ {
+ $values = array();
+ if (null === $elementList) {
+ // iterate over all elements, calling their exportValue() methods
+ foreach (array_keys($this->_elements) as $key) {
+ $value = $this->_elements[$key]->exportValue($this->_submitValues, true);
+ if (is_array($value)) {
+ // This shit throws a bogus warning in PHP 4.3.x
+ $values = HTML_QuickForm::arrayMerge($values, $value);
+ }
+ }
+ } else {
+ if (!is_array($elementList)) {
+ $elementList = array_map('trim', explode(',', $elementList));
+ }
+ foreach ($elementList as $elementName) {
+ $value = $this->exportValue($elementName);
+ if (PEAR::isError($value)) {
+ return $value;
+ }
+ $values[$elementName] = $value;
+ }
+ }
+ return $values;
+ }
+
+ // }}}
+ // {{{ isError()
+
+ /**
+ * Tell whether a result from a QuickForm method is an error (an instance of HTML_QuickForm_Error)
+ *
+ * @access public
+ * @param mixed result code
+ * @return bool whether $value is an error
+ */
+ function isError($value)
+ {
+ return (is_object($value) && is_a($value, 'html_quickform_error'));
+ } // end func isError
+
+ // }}}
+ // {{{ errorMessage()
+
+ /**
+ * Return a textual error message for an QuickForm error code
+ *
+ * @access public
+ * @param int error code
+ * @return string error message
+ */
+ function errorMessage($value)
+ {
+ // make the variable static so that it only has to do the defining on the first call
+ static $errorMessages;
+
+ // define the varies error messages
+ if (!isset($errorMessages)) {
+ $errorMessages = array(
+ QUICKFORM_OK => 'no error',
+ QUICKFORM_ERROR => 'unknown error',
+ QUICKFORM_INVALID_RULE => 'the rule does not exist as a registered rule',
+ QUICKFORM_NONEXIST_ELEMENT => 'nonexistent html element',
+ QUICKFORM_INVALID_FILTER => 'invalid filter',
+ QUICKFORM_UNREGISTERED_ELEMENT => 'unregistered element',
+ QUICKFORM_INVALID_ELEMENT_NAME => 'element already exists',
+ QUICKFORM_INVALID_PROCESS => 'process callback does not exist',
+ QUICKFORM_DEPRECATED => 'method is deprecated',
+ QUICKFORM_INVALID_DATASOURCE => 'datasource is not an object'
+ );
+ }
+
+ // If this is an error object, then grab the corresponding error code
+ if (HTML_QuickForm::isError($value)) {
+ $value = $value->getCode();
+ }
+
+ // return the textual error message corresponding to the code
+ return isset($errorMessages[$value]) ? $errorMessages[$value] : $errorMessages[QUICKFORM_ERROR];
+ } // end func errorMessage
+
+ // }}}
+} // end class HTML_QuickForm
+
+class HTML_QuickForm_Error extends PEAR_Error {
+
+ // {{{ properties
+
+ /**
+ * Prefix for all error messages
+ * @var string
+ */
+ var $error_message_prefix = 'QuickForm Error: ';
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * Creates a quickform error object, extending the PEAR_Error class
+ *
+ * @param int $code the error code
+ * @param int $mode the reaction to the error, either return, die or trigger/callback
+ * @param int $level intensity of the error (PHP error code)
+ * @param mixed $debuginfo any information that can inform user as to nature of the error
+ */
+ function HTML_QuickForm_Error($code = QUICKFORM_ERROR, $mode = PEAR_ERROR_RETURN,
+ $level = E_USER_NOTICE, $debuginfo = null)
+ {
+ if (is_int($code)) {
+ $this->PEAR_Error(HTML_QuickForm::errorMessage($code), $code, $mode, $level, $debuginfo);
+ } else {
+ $this->PEAR_Error("Invalid error code: $code", QUICKFORM_ERROR, $mode, $level, $debuginfo);
+ }
+ }
+
+ // }}}
+} // end class HTML_QuickForm_Error
+?> \ No newline at end of file
diff --git a/pearlib/HTML/QuickForm/Action.php b/pearlib/HTML/QuickForm/Action.php
new file mode 100644
index 00000000..53673702
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Action.php
@@ -0,0 +1,47 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Alexey Borzov <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Action.php,v 1.1 2003/08/29 12:19:41 avb Exp $
+
+/**
+ * Class representing an action to perform on HTTP request. The Controller
+ * will select the appropriate Action to call on the request and call its
+ * perform() method. The subclasses of this class should implement all the
+ * necessary business logic.
+ *
+ * @author Alexey Borzov <[email protected]>
+ * @package HTML_QuickForm_Controller
+ * @version $Revision: 1.1 $
+ */
+class HTML_QuickForm_Action
+{
+ /**
+ * Processes the request. This method should be overriden by child classes to
+ * provide the necessary logic.
+ *
+ * @access public
+ * @param object HTML_QuickForm_Page the current form-page
+ * @param string Current action name, as one Action object can serve multiple actions
+ */
+ function perform(&$page, $actionName)
+ {
+ }
+}
+
+?>
diff --git a/pearlib/HTML/QuickForm/Action/Back.php b/pearlib/HTML/QuickForm/Action/Back.php
new file mode 100644
index 00000000..39c43aaa
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Action/Back.php
@@ -0,0 +1,54 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Alexey Borzov <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Back.php,v 1.3 2004/03/02 21:15:45 avb Exp $
+
+require_once 'HTML/QuickForm/Action.php';
+
+/**
+ * The action for a 'back' button of wizard-type multipage form.
+ *
+ * @author Alexey Borzov <[email protected]>
+ * @package HTML_QuickForm_Controller
+ * @version $Revision: 1.3 $
+ */
+class HTML_QuickForm_Action_Back extends HTML_QuickForm_Action
+{
+ function perform(&$page, $actionName)
+ {
+ // save the form values and validation status to the session
+ $page->isFormBuilt() or $page->buildForm();
+ $pageName = $page->getAttribute('id');
+ $data =& $page->controller->container();
+ $data['values'][$pageName] = $page->exportValues();
+ if (!$page->controller->isModal()) {
+ $data['valid'][$pageName] = $page->validate();
+ }
+
+ // get the previous page and go to it
+ // we don't check validation status here, 'jump' handler should
+ if (null === ($prevName = $page->controller->getPrevName($pageName))) {
+ $page->handle('jump');
+ } else {
+ $prev =& $page->controller->getPage($prevName);
+ $prev->handle('jump');
+ }
+ }
+}
+
+?>
diff --git a/pearlib/HTML/QuickForm/Action/Direct.php b/pearlib/HTML/QuickForm/Action/Direct.php
new file mode 100644
index 00000000..3942b11b
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Action/Direct.php
@@ -0,0 +1,52 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Alexey Borzov <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Direct.php,v 1.2 2004/03/02 21:15:45 avb Exp $
+
+require_once 'HTML/QuickForm/Action.php';
+
+/**
+ * This action allows to go to a specific page of a multipage form.
+ *
+ * Please note that the name for this action in addAction() should NOT be
+ * 'direct', but the name of the page you wish to go to.
+ *
+ * @author Alexey Borzov <[email protected]>
+ * @package HTML_QuickForm_Controller
+ * @version $Revision: 1.2 $
+ */
+class HTML_QuickForm_Action_Direct extends HTML_QuickForm_Action
+{
+ function perform(&$page, $actionName)
+ {
+ // save the form values and validation status to the session
+ $page->isFormBuilt() or $page->buildForm();
+ $pageName = $page->getAttribute('id');
+ $data =& $page->controller->container();
+ $data['values'][$pageName] = $page->exportValues();
+ $data['valid'][$pageName] = $page->validate();
+
+ $target =& $page->controller->getPage($actionName);
+ if (PEAR::isError($target)) {
+ return $target;
+ } else {
+ return $target->handle('jump');
+ }
+ }
+}
+?>
diff --git a/pearlib/HTML/QuickForm/Action/Display.php b/pearlib/HTML/QuickForm/Action/Display.php
new file mode 100644
index 00000000..e004eebf
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Action/Display.php
@@ -0,0 +1,74 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Alexey Borzov <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Display.php,v 1.3 2004/03/02 21:15:45 avb Exp $
+
+require_once 'HTML/QuickForm/Action.php';
+
+/**
+ * This action handles the output of the form.
+ *
+ * If you want to customize the form display, subclass this class and
+ * override the _renderForm() method, you don't need to change the perform()
+ * method itself.
+ *
+ * @author Alexey Borzov <[email protected]>
+ * @package HTML_QuickForm_Controller
+ * @version $Revision: 1.3 $
+ */
+class HTML_QuickForm_Action_Display extends HTML_QuickForm_Action
+{
+ function perform(&$page, $actionName)
+ {
+ $pageName = $page->getAttribute('id');
+ // If the original action was 'display' and we have values in container then we load them
+ // BTW, if the page was invalid, we should later call validate() to get the errors
+ list(, $oldName) = $page->controller->getActionName();
+ if ('display' == $oldName) {
+ $data =& $page->controller->container();
+ if (!empty($data['values'][$pageName])) {
+ $page->loadValues($data['values'][$pageName]);
+ $validate = false === $data['valid'][$pageName];
+ }
+ }
+ // set "common" defaults and constants
+ $page->controller->applyDefaults($pageName);
+ $page->isFormBuilt() or $page->buildForm();
+ // if we had errors we should show them again
+ if (isset($validate) && $validate) {
+ $page->validate();
+ }
+ $this->_renderForm($page);
+ }
+
+
+ /**
+ * Actually outputs the form.
+ *
+ * If you want to customize the form's appearance (you most certainly will),
+ * then you should override this method. There is no need to override perform()
+ *
+ * @access public
+ * @param object HTML_QuickForm_Page the page being processed
+ */
+ function _renderForm(&$page)
+ {
+ $page->display();
+ }
+}
+?>
diff --git a/pearlib/HTML/QuickForm/Action/Jump.php b/pearlib/HTML/QuickForm/Action/Jump.php
new file mode 100644
index 00000000..af034145
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Action/Jump.php
@@ -0,0 +1,57 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Alexey Borzov <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Jump.php,v 1.3 2004/03/02 21:15:45 avb Exp $
+
+require_once 'HTML/QuickForm/Action.php';
+
+/**
+ * The action handles the HTTP redirect to a specific page.
+ *
+ * @author Alexey Borzov <[email protected]>
+ * @package HTML_QuickForm_Controller
+ * @version $Revision: 1.3 $
+ */
+class HTML_QuickForm_Action_Jump extends HTML_QuickForm_Action
+{
+ function perform(&$page, $actionName)
+ {
+ // check whether the page is valid before trying to go to it
+ if ($page->controller->isModal()) {
+ // we check whether *all* pages up to current are valid
+ // if there is an invalid page we go to it, instead of the
+ // requested one
+ $pageName = $page->getAttribute('id');
+ if (!$page->controller->isValid($pageName)) {
+ $pageName = $page->controller->findInvalid();
+ }
+ $current =& $page->controller->getPage($pageName);
+
+ } else {
+ $current =& $page;
+ }
+ // generate the URL for the page 'display' event and redirect to it
+ $action = $current->getAttribute('action');
+ $url = $action . (false === strpos($action, '?')? '?': '&') .
+ $current->getButtonName('display') . '=true' .
+ ((!defined('SID') || '' == SID)? '': '&' . SID);
+ header('Location: ' . $url);
+ exit;
+ }
+}
+?>
diff --git a/pearlib/HTML/QuickForm/Action/Next.php b/pearlib/HTML/QuickForm/Action/Next.php
new file mode 100644
index 00000000..9d53acba
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Action/Next.php
@@ -0,0 +1,63 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Alexey Borzov <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Next.php,v 1.3 2004/03/02 21:15:45 avb Exp $
+
+require_once 'HTML/QuickForm/Action.php';
+
+/**
+ * The action for a 'next' button of wizard-type multipage form.
+ *
+ * @author Alexey Borzov <[email protected]>
+ * @package HTML_QuickForm_Controller
+ * @version $Revision: 1.3 $
+ */
+class HTML_QuickForm_Action_Next extends HTML_QuickForm_Action
+{
+ function perform(&$page, $actionName)
+ {
+ // save the form values and validation status to the session
+ $page->isFormBuilt() or $page->buildForm();
+ $pageName = $page->getAttribute('id');
+ $data =& $page->controller->container();
+ $data['values'][$pageName] = $page->exportValues();
+ $data['valid'][$pageName] = $page->validate();
+
+ // Modal form and page is invalid: don't go further
+ if ($page->controller->isModal() && !$data['valid'][$pageName]) {
+ return $page->handle('display');
+ }
+ // More pages?
+ if (null !== ($nextName = $page->controller->getNextName($pageName))) {
+ $next =& $page->controller->getPage($nextName);
+ $next->handle('jump');
+ // Consider this a 'finish' button, if there is no explicit one
+ } elseif($page->controller->isModal()) {
+ if ($page->controller->isValid()) {
+ $page->handle('process');
+ } else {
+ // this should redirect to the first invalid page
+ $page->handle('jump');
+ }
+ } else {
+ $page->handle('display');
+ }
+ }
+}
+
+?>
diff --git a/pearlib/HTML/QuickForm/Action/Submit.php b/pearlib/HTML/QuickForm/Action/Submit.php
new file mode 100644
index 00000000..fe26183c
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Action/Submit.php
@@ -0,0 +1,57 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Alexey Borzov <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Submit.php,v 1.2 2004/03/02 21:15:45 avb Exp $
+
+require_once 'HTML/QuickForm/Action.php';
+
+/**
+ * The action for a 'submit' button.
+ *
+ * @author Alexey Borzov <[email protected]>
+ * @package HTML_QuickForm_Controller
+ * @version $Revision: 1.2 $
+ */
+class HTML_QuickForm_Action_Submit extends HTML_QuickForm_Action
+{
+ function perform(&$page, $actionName)
+ {
+ // save the form values and validation status to the session
+ $page->isFormBuilt() or $page->buildForm();
+ $pageName = $page->getAttribute('id');
+ $data =& $page->controller->container();
+ $data['values'][$pageName] = $page->exportValues();
+ $data['valid'][$pageName] = $page->validate();
+
+ // All pages are valid, process
+ if ($page->controller->isValid()) {
+ $page->handle('process');
+
+ // Current page is invalid, display it
+ } elseif (!$data['valid'][$pageName]) {
+ $page->handle('display');
+
+ // Some other page is invalid, redirect to it
+ } else {
+ $target =& $page->controller->getPage($page->controller->findInvalid());
+ $target->handle('jump');
+ }
+ }
+}
+
+?> \ No newline at end of file
diff --git a/pearlib/HTML/QuickForm/Controller.php b/pearlib/HTML/QuickForm/Controller.php
new file mode 100644
index 00000000..4d4c79d7
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Controller.php
@@ -0,0 +1,441 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Alexey Borzov <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Controller.php,v 1.9 2004/03/18 12:44:36 mansion Exp $
+
+require_once 'HTML/QuickForm/Page.php';
+
+/**
+ * The class representing a Controller of MVC design pattern.
+ *
+ * This class keeps track of pages and (default) action handlers for the form,
+ * it manages keeping the form values in session, setting defaults and
+ * constants for the form as a whole and getting its submit values.
+ *
+ * Generally you don't need to subclass this.
+ *
+ * @author Alexey Borzov <[email protected]>
+ * @package HTML_QuickForm_Controller
+ * @version $Revision: 1.9 $
+ */
+class HTML_QuickForm_Controller
+{
+ /**
+ * Contains the pages (HTML_QuickForm_Page objects) of the miultipage form
+ * @var array
+ */
+ var $_pages = array();
+
+ /**
+ * Contains the mapping of actions to corresponding HTML_QuickForm_Action objects
+ * @var array
+ */
+ var $_actions = array();
+
+ /**
+ * Name of the form, used to store the values in session
+ * @var string
+ */
+ var $_name;
+
+ /**
+ * Whether the form is modal
+ * @var bool
+ */
+ var $_modal = true;
+
+ /**
+ * The action extracted from HTTP request: array('page', 'action')
+ * @var array
+ */
+ var $_actionName = null;
+
+ /**
+ * Class constructor.
+ *
+ * Sets the form name and modal/non-modal behaviuor. Different multipage
+ * forms should have different names, as they are used to store form
+ * values in session. Modal forms allow passing to the next page only when
+ * all of the previous pages are valid.
+ *
+ * @access public
+ * @param string form name
+ * @param bool whether the form is modal
+ */
+ function HTML_QuickForm_Controller($name, $modal = true)
+ {
+ $this->_name = $name;
+ $this->_modal = $modal;
+ }
+
+
+ /**
+ * Returns a reference to a session variable containing the form-page
+ * values and pages' validation status.
+ *
+ * This is a "low-level" method, use exportValues() if you want just to
+ * get the form's values.
+ *
+ * @access public
+ * @param bool If true, then reset the container: clear all default, constant and submitted values
+ * @return array
+ */
+ function &container($reset = false)
+ {
+ $name = '_' . $this->_name . '_container';
+ if (!isset($_SESSION[$name]) || $reset) {
+ $_SESSION[$name] = array(
+ 'defaults' => array(),
+ 'constants' => array(),
+ 'values' => array(),
+ 'valid' => array()
+ );
+ }
+ foreach (array_keys($this->_pages) as $pageName) {
+ if (!isset($_SESSION[$name]['values'][$pageName])) {
+ $_SESSION[$name]['values'][$pageName] = array();
+ $_SESSION[$name]['valid'][$pageName] = null;
+ }
+ }
+ return $_SESSION[$name];
+ }
+
+
+ /**
+ * Processes the request.
+ *
+ * This finds the current page, the current action and passes the action
+ * to the page's handle() method.
+ *
+ * @access public
+ */
+ function run()
+ {
+ // the names of the action and page should be saved
+ list($page, $action) = $this->_actionName = $this->getActionName();
+ return $this->_pages[$page]->handle($action);
+ }
+
+
+ /**
+ * Registers a handler for a specific action.
+ *
+ * @access public
+ * @param string name of the action
+ * @param object HTML_QuickForm_Action the handler for the action
+ */
+ function addAction($actionName, &$action)
+ {
+ $this->_actions[$actionName] =& $action;
+ }
+
+
+ /**
+ * Adds a new page to the form
+ *
+ * @access public
+ * @param object HTML_QuickForm_Page
+ */
+ function addPage(&$page)
+ {
+ $page->controller =& $this;
+ $this->_pages[$page->getAttribute('id')] =& $page;
+ }
+
+
+ /**
+ * Returns a page
+ *
+ * @access public
+ * @param string Name of a page
+ * @return object HTML_QuickForm_Page A reference to the page
+ */
+ function &getPage($pageName)
+ {
+ if (!isset($this->_pages[$pageName])) {
+ return PEAR::raiseError('HTML_QuickForm_Controller: Unknown page "' . $pageName . '"');
+ }
+ return $this->_pages[$pageName];
+ }
+
+
+ /**
+ * Handles an action.
+ *
+ * This will be called if the page itself does not have a handler
+ * to a specific action. The method also loads and uses default handlers
+ * for common actions, if specific ones were not added.
+ *
+ * @access public
+ * @param object HTML_QuickForm_Page The page that failed to handle the action
+ * @param string Name of the action
+ */
+ function handle(&$page, $actionName)
+ {
+ if (isset($this->_actions[$actionName])) {
+ return $this->_actions[$actionName]->perform($page, $actionName);
+ }
+ switch ($actionName) {
+ case 'next':
+ case 'back':
+ case 'submit':
+ case 'display':
+ case 'jump':
+ include_once 'HTML/QuickForm/Action/' . ucfirst($actionName) . '.php';
+ $className = 'HTML_QuickForm_Action_' . $actionName;
+ $this->_actions[$actionName] =& new $className();
+ return $this->_actions[$actionName]->perform($page, $actionName);
+ break;
+ default:
+ return PEAR::raiseError('HTML_QuickForm_Controller: Unhandled action "' . $actionName . '" in page "' . $page->getAttribute('id') . '"');
+ } // switch
+ }
+
+
+ /**
+ * Checks whether the form is modal.
+ *
+ * @access public
+ * @return bool
+ */
+ function isModal()
+ {
+ return $this->_modal;
+ }
+
+
+ /**
+ * Checks whether the pages of the controller are valid
+ *
+ * @access public
+ * @param string If set, check only the pages before (not including) that page
+ * @return bool
+ */
+ function isValid($pageName = null)
+ {
+ $data =& $this->container();
+ foreach (array_keys($this->_pages) as $key) {
+ if (isset($pageName) && $pageName == $key) {
+ return true;
+ } elseif (!$data['valid'][$key]) {
+ // We should handle the possible situation when the user has never
+ // seen a page of a non-modal multipage form
+ if (!$this->isModal() && null === $data['valid'][$key]) {
+ $page =& $this->_pages[$key];
+ // Use controller's defaults and constants, if present
+ $this->applyDefaults($key);
+ $page->isFormBuilt() or $page->BuildForm();
+ // We use defaults and constants as if they were submitted
+ $data['values'][$key] = $page->exportValues();
+ $page->loadValues($data['values'][$key]);
+ // Is the page now valid?
+ if (true === ($data['valid'][$key] = $page->validate())) {
+ continue;
+ }
+ }
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ /**
+ * Returns the name of the page before the given.
+ *
+ * @access public
+ * @param string
+ * @return string
+ */
+ function getPrevName($pageName)
+ {
+ $prev = null;
+ foreach (array_keys($this->_pages) as $key) {
+ if ($key == $pageName) {
+ return $prev;
+ }
+ $prev = $key;
+ }
+ }
+
+
+ /**
+ * Returns the name of the page after the given.
+ *
+ * @access public
+ * @param string
+ * @return string
+ */
+ function getNextName($pageName)
+ {
+ $prev = null;
+ foreach (array_keys($this->_pages) as $key) {
+ if ($prev == $pageName) {
+ return $key;
+ }
+ $prev = $key;
+ }
+ return null;
+ }
+
+
+ /**
+ * Finds the (first) invalid page
+ *
+ * @access public
+ * @return string Name of an invalid page
+ */
+ function findInvalid()
+ {
+ $data =& $this->container();
+ foreach (array_keys($this->_pages) as $key) {
+ if (!$data['valid'][$key]) {
+ return $key;
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * Extracts the names of the current page and the current action from
+ * HTTP request data.
+ *
+ * @access public
+ * @return array first element is page name, second is action name
+ */
+ function getActionName()
+ {
+ if (is_array($this->_actionName)) {
+ return $this->_actionName;
+ }
+ $names = array_map('preg_quote', array_keys($this->_pages));
+ $regex = '/^_qf_(' . implode('|', $names) . ')_(.+?)(_x)?$/';
+ foreach (array_keys($_REQUEST) as $key) {
+ if (preg_match($regex, $key, $matches)) {
+ return array($matches[1], $matches[2]);
+ }
+ }
+ if (isset($_REQUEST['_qf_default'])) {
+ $matches = explode(':', $_REQUEST['_qf_default'], 2);
+ if (isset($this->_pages[$matches[0]])) {
+ return $matches;
+ }
+ }
+ reset($this->_pages);
+ return array(key($this->_pages), 'display');
+ }
+
+
+ /**
+ * Initializes default form values.
+ *
+ * @access public
+ * @param array default values
+ */
+ function setDefaults($defaultValues = null)
+ {
+ if (is_array($defaultValues)) {
+ $data =& $this->container();
+ $data['defaults'] = HTML_QuickForm::arrayMerge($data['defaults'], $defaultValues);
+ }
+ }
+
+
+ /**
+ * Initializes constant form values.
+ * These values won't get overridden by POST or GET vars
+ *
+ * @access public
+ * @param array constant values
+ */
+ function setConstants($constantValues = null)
+ {
+ if (is_array($constantValues)) {
+ $data =& $this->container();
+ $data['constants'] = HTML_QuickForm::arrayMerge($data['constants'], $constantValues);
+ }
+ }
+
+
+ /**
+ * Sets the default values for the given page
+ *
+ * @access public
+ * @param string Name of a page
+ */
+ function applyDefaults($pageName)
+ {
+ $data =& $this->container();
+ if (!empty($data['defaults'])) {
+ $this->_pages[$pageName]->setDefaults($data['defaults']);
+ }
+ if (!empty($data['constants'])) {
+ $this->_pages[$pageName]->setConstants($data['constants']);
+ }
+ }
+
+
+ /**
+ * Returns the form's values
+ *
+ * @access public
+ * @param string name of the page, if not set then returns values for all pages
+ * @return array
+ */
+ function exportValues($pageName = null)
+ {
+ $data =& $this->container();
+ $values = array();
+ if (isset($pageName)) {
+ $pages = array($pageName);
+ } else {
+ $pages = array_keys($data['values']);
+ }
+ foreach ($pages as $page) {
+ // skip elements representing actions
+ foreach ($data['values'][$page] as $key => $value) {
+ if (0 !== strpos($key, '_qf_')) {
+ if (isset($values[$key]) && is_array($value)) {
+ $values[$key] = HTML_QuickForm::arrayMerge($values[$key], $value);
+ } else {
+ $values[$key] = $value;
+ }
+ }
+ }
+ }
+ return $values;
+ }
+
+
+ /**
+ * Returns the element's value
+ *
+ * @access public
+ * @param string name of the page
+ * @param string name of the element in the page
+ * @return mixed value for the element
+ */
+ function exportValue($pageName, $elementName)
+ {
+ $data =& $this->container();
+ return isset($data['values'][$pageName][$elementName])? $data['values'][$pageName][$elementName]: null;
+ }
+}
+?>
diff --git a/pearlib/HTML/QuickForm/Page.php b/pearlib/HTML/QuickForm/Page.php
new file mode 100644
index 00000000..79230311
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Page.php
@@ -0,0 +1,180 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Alexey Borzov <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Page.php,v 1.3 2004/03/02 21:15:52 avb Exp $
+
+require_once 'HTML/QuickForm.php';
+
+/**
+ * The class represents a page of a multipage form.
+ *
+ * Generally you'll need to subclass this and define your buildForm()
+ * method that will build the form. While it is also possible to instantiate
+ * this class and build the form manually, this is not the recommended way.
+ *
+ * @author Alexey Borzov <[email protected]>
+ * @package HTML_QuickForm_Controller
+ * @version $Revision: 1.3 $
+ */
+class HTML_QuickForm_Page extends HTML_QuickForm
+{
+ /**
+ * Contains the mapping of actions to corresponding HTML_QuickForm_Action objects
+ * @var array
+ */
+ var $_actions = array();
+
+ /**
+ * Contains a reference to a Controller object containing this page
+ * @var object HTML_QuickForm_Controller
+ * @access public
+ */
+ var $controller = null;
+
+ /**
+ * Should be set to true on first call to buildForm()
+ * @var bool
+ */
+ var $_formBuilt = false;
+
+ /**
+ * Class constructor
+ *
+ * @access public
+ */
+ function HTML_QuickForm_Page($formName, $method = 'post', $target = '_self', $attributes = null)
+ {
+ $this->HTML_QuickForm($formName, $method, '', $target, $attributes);
+ }
+
+
+ /**
+ * Registers a handler for a specific action.
+ *
+ * @access public
+ * @param string name of the action
+ * @param object HTML_QuickForm_Action the handler for the action
+ */
+ function addAction($actionName, &$action)
+ {
+ $this->_actions[$actionName] =& $action;
+ }
+
+
+ /**
+ * Handles an action.
+ *
+ * If an Action object was not registered here, controller's handle()
+ * method will be called.
+ *
+ * @access public
+ * @param string Name of the action
+ */
+ function handle($actionName)
+ {
+ if (isset($this->_actions[$actionName])) {
+ return $this->_actions[$actionName]->perform($this, $actionName);
+ } else {
+ return $this->controller->handle($this, $actionName);
+ }
+ }
+
+
+ /**
+ * Returns a name for a submit button that will invoke a specific action.
+ *
+ * @access public
+ * @param string Name of the action
+ * @return string "name" attribute for a submit button
+ */
+ function getButtonName($actionName)
+ {
+ return '_qf_' . $this->getAttribute('id') . '_' . $actionName;
+ }
+
+
+ /**
+ * Loads the submit values from the array.
+ *
+ * The method is NOT intended for general usage.
+ *
+ * @param array 'submit' values
+ * @access public
+ */
+ function loadValues($values)
+ {
+ $this->_submitValues = $values;
+ foreach (array_keys($this->_elements) as $key) {
+ $this->_elements[$key]->onQuickFormEvent('updateValue', null, $this);
+ }
+ }
+
+
+ /**
+ * Builds a form.
+ *
+ * You should override this method when you subclass HTML_QuickForm_Page,
+ * it should contain all the necessary addElement(), applyFilter(), addRule()
+ * and possibly setDefaults() and setConstants() calls. The method will be
+ * called on demand, so please be sure to set $_formBuilt property to true to
+ * assure that the method works only once.
+ *
+ * @access public
+ * @abstract
+ */
+ function buildForm()
+ {
+ $this->_formBuilt = true;
+ }
+
+
+ /**
+ * Checks whether the form was already built.
+ *
+ * @access public
+ * @return bool
+ */
+ function isFormBuilt()
+ {
+ return $this->_formBuilt;
+ }
+
+
+ /**
+ * Sets the default action invoked on page-form submit
+ *
+ * This is necessary as the user may just press Enter instead of
+ * clicking one of the named submit buttons and then no action name will
+ * be passed to the script.
+ *
+ * @access public
+ * @param string default action name
+ */
+ function setDefaultAction($actionName)
+ {
+ if ($this->elementExists('_qf_default')) {
+ $element =& $this->getElement('_qf_default');
+ $element->setValue($this->getAttribute('id') . ':' . $actionName);
+ } else {
+ $this->addElement('hidden', '_qf_default', $this->getAttribute('id') . ':' . $actionName);
+ }
+ }
+}
+
+?>
diff --git a/pearlib/HTML/QuickForm/Renderer.php b/pearlib/HTML/QuickForm/Renderer.php
new file mode 100644
index 00000000..25ee841d
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Renderer.php
@@ -0,0 +1,150 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Alexey Borzov <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Renderer.php,v 1.1 2003/03/08 17:02:07 mansion Exp $
+
+/**
+ * An abstract base class for QuickForm renderers
+ *
+ * The class implements a Visitor design pattern
+ *
+ * @abstract
+ * @author Alexey Borzov <[email protected]>
+ */
+class HTML_QuickForm_Renderer
+{
+ /**
+ * Constructor
+ *
+ * @access public
+ */
+ function HTML_QuickForm_Renderer()
+ {
+ } // end constructor
+
+ /**
+ * Called when visiting a form, before processing any form elements
+ *
+ * @param object An HTML_QuickForm object being visited
+ * @access public
+ * @return void
+ * @abstract
+ */
+ function startForm(&$form)
+ {
+ return;
+ } // end func startForm
+
+ /**
+ * Called when visiting a form, after processing all form elements
+ *
+ * @param object An HTML_QuickForm object being visited
+ * @access public
+ * @return void
+ * @abstract
+ */
+ function finishForm(&$form)
+ {
+ return;
+ } // end func finishForm
+
+ /**
+ * Called when visiting a header element
+ *
+ * @param object An HTML_QuickForm_header element being visited
+ * @access public
+ * @return void
+ * @abstract
+ */
+ function renderHeader(&$header)
+ {
+ return;
+ } // end func renderHeader
+
+ /**
+ * Called when visiting an element
+ *
+ * @param object An HTML_QuickForm_element object being visited
+ * @param bool Whether an element is required
+ * @param string An error message associated with an element
+ * @access public
+ * @return void
+ * @abstract
+ */
+ function renderElement(&$element, $required, $error)
+ {
+ return;
+ } // end func renderElement
+
+ /**
+ * Called when visiting a hidden element
+ *
+ * @param object An HTML_QuickForm_hidden object being visited
+ * @access public
+ * @return void
+ * @abstract
+ */
+ function renderHidden(&$element)
+ {
+ return;
+ } // end func renderHidden
+
+ /**
+ * Called when visiting a raw HTML/text pseudo-element
+ *
+ * Seems that this should not be used when using a template-based renderer
+ *
+ * @param object An HTML_QuickForm_html element being visited
+ * @access public
+ * @return void
+ * @abstract
+ */
+ function renderHtml(&$data)
+ {
+ return;
+ } // end func renderHtml
+
+ /**
+ * Called when visiting a group, before processing any group elements
+ *
+ * @param object An HTML_QuickForm_group object being visited
+ * @param bool Whether a group is required
+ * @param string An error message associated with a group
+ * @access public
+ * @return void
+ * @abstract
+ */
+ function startGroup(&$group, $required, $error)
+ {
+ return;
+ } // end func startGroup
+
+ /**
+ * Called when visiting a group, after processing all group elements
+ *
+ * @param object An HTML_QuickForm_group object being visited
+ * @access public
+ * @return void
+ * @abstract
+ */
+ function finishGroup(&$group)
+ {
+ return;
+ } // end func finishGroup
+} // end class HTML_QuickForm_Renderer
+?>
diff --git a/pearlib/HTML/QuickForm/Renderer/Array.php b/pearlib/HTML/QuickForm/Renderer/Array.php
new file mode 100644
index 00000000..04163ae7
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Renderer/Array.php
@@ -0,0 +1,319 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Alexey Borzov <[email protected]> |
+// | Adam Daniel <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// | Thomas Schulz <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Array.php,v 1.9 2004/10/15 20:00:48 ths Exp $
+
+require_once 'HTML/QuickForm/Renderer.php';
+
+/**
+ * A concrete renderer for HTML_QuickForm, makes an array of form contents
+ *
+ * Based on old toArray() code.
+ *
+ * The form array structure is the following:
+ * array(
+ * 'frozen' => 'whether the form is frozen',
+ * 'javascript' => 'javascript for client-side validation',
+ * 'attributes' => 'attributes for <form> tag',
+ * 'requirednote => 'note about the required elements',
+ * // if we set the option to collect hidden elements
+ * 'hidden' => 'collected html of all hidden elements',
+ * // if there were some validation errors:
+ * 'errors' => array(
+ * '1st element name' => 'Error for the 1st element',
+ * ...
+ * 'nth element name' => 'Error for the nth element'
+ * ),
+ * // if there are no headers in the form:
+ * 'elements' => array(
+ * element_1,
+ * ...
+ * element_N
+ * )
+ * // if there are headers in the form:
+ * 'sections' => array(
+ * array(
+ * 'header' => 'Header text for the first header',
+ * 'name' => 'Header name for the first header',
+ * 'elements' => array(
+ * element_1,
+ * ...
+ * element_K1
+ * )
+ * ),
+ * ...
+ * array(
+ * 'header' => 'Header text for the Mth header',
+ * 'name' => 'Header name for the Mth header',
+ * 'elements' => array(
+ * element_1,
+ * ...
+ * element_KM
+ * )
+ * )
+ * )
+ * );
+ *
+ * where element_i is an array of the form:
+ * array(
+ * 'name' => 'element name',
+ * 'value' => 'element value',
+ * 'type' => 'type of the element',
+ * 'frozen' => 'whether element is frozen',
+ * 'label' => 'label for the element',
+ * 'required' => 'whether element is required',
+ * 'error' => 'error associated with the element',
+ * 'style' => 'some information about element style (e.g. for Smarty)',
+ * // if element is not a group
+ * 'html' => 'HTML for the element'
+ * // if element is a group
+ * 'separator' => 'separator for group elements',
+ * 'elements' => array(
+ * element_1,
+ * ...
+ * element_N
+ * )
+ * );
+ *
+ * @access public
+ */
+class HTML_QuickForm_Renderer_Array extends HTML_QuickForm_Renderer
+{
+ /**
+ * An array being generated
+ * @var array
+ */
+ var $_ary;
+
+ /**
+ * Number of sections in the form (i.e. number of headers in it)
+ * @var integer
+ */
+ var $_sectionCount;
+
+ /**
+ * Current section number
+ * @var integer
+ */
+ var $_currentSection;
+
+ /**
+ * Array representing current group
+ * @var array
+ */
+ var $_currentGroup = null;
+
+ /**
+ * Additional style information for different elements
+ * @var array
+ */
+ var $_elementStyles = array();
+
+ /**
+ * true: collect all hidden elements into string; false: process them as usual form elements
+ * @var bool
+ */
+ var $_collectHidden = false;
+
+ /**
+ * true: render an array of labels to many labels, $key 0 named 'label', the rest "label_$key"
+ * false: leave labels as defined
+ * @var bool
+ */
+ var $staticLabels = false;
+
+ /**
+ * Constructor
+ *
+ * @param bool true: collect all hidden elements into string; false: process them as usual form elements
+ * @param bool true: render an array of labels to many labels, $key 0 to 'label' and the oterh to "label_$key"
+ * @access public
+ */
+ function HTML_QuickForm_Renderer_Array($collectHidden = false, $staticLabels = false)
+ {
+ $this->HTML_QuickForm_Renderer();
+ $this->_collectHidden = $collectHidden;
+ $this->_staticLabels = $staticLabels;
+ } // end constructor
+
+
+ /**
+ * Returns the resultant array
+ *
+ * @access public
+ * @return array
+ */
+ function toArray()
+ {
+ return $this->_ary;
+ }
+
+
+ function startForm(&$form)
+ {
+ $this->_ary = array(
+ 'frozen' => $form->isFrozen(),
+ 'javascript' => $form->getValidationScript(),
+ 'attributes' => $form->getAttributes(true),
+ 'requirednote' => $form->getRequiredNote(),
+ 'errors' => array()
+ );
+ if ($this->_collectHidden) {
+ $this->_ary['hidden'] = '';
+ }
+ $this->_elementIdx = 1;
+ $this->_currentSection = null;
+ $this->_sectionCount = 0;
+ } // end func startForm
+
+
+ function renderHeader(&$header)
+ {
+ $this->_ary['sections'][$this->_sectionCount] = array(
+ 'header' => $header->toHtml(),
+ 'name' => $header->getName()
+ );
+ $this->_currentSection = $this->_sectionCount++;
+ } // end func renderHeader
+
+
+ function renderElement(&$element, $required, $error)
+ {
+ $elAry = $this->_elementToArray($element, $required, $error);
+ if (!empty($error)) {
+ $this->_ary['errors'][$elAry['name']] = $error;
+ }
+ $this->_storeArray($elAry);
+ } // end func renderElement
+
+
+ function renderHidden(&$element)
+ {
+ if ($this->_collectHidden) {
+ $this->_ary['hidden'] .= $element->toHtml() . "\n";
+ } else {
+ $this->renderElement($element, false, null);
+ }
+ } // end func renderHidden
+
+
+ function startGroup(&$group, $required, $error)
+ {
+ $this->_currentGroup = $this->_elementToArray($group, $required, $error);
+ if (!empty($error)) {
+ $this->_ary['errors'][$this->_currentGroup['name']] = $error;
+ }
+ } // end func startGroup
+
+
+ function finishGroup(&$group)
+ {
+ $this->_storeArray($this->_currentGroup);
+ $this->_currentGroup = null;
+ } // end func finishGroup
+
+
+ /**
+ * Creates an array representing an element
+ *
+ * @access private
+ * @param object An HTML_QuickForm_element object
+ * @param bool Whether an element is required
+ * @param string Error associated with the element
+ * @return array
+ */
+ function _elementToArray(&$element, $required, $error)
+ {
+ $ret = array(
+ 'name' => $element->getName(),
+ 'value' => $element->getValue(),
+ 'type' => $element->getType(),
+ 'frozen' => $element->isFrozen(),
+ 'required' => $required,
+ 'error' => $error
+ );
+ // render label(s)
+ $labels = $element->getLabel();
+ if (is_array($labels) && $this->_staticLabels) {
+ foreach($labels as $key => $label) {
+ $key = is_int($key)? $key + 1: $key;
+ if (1 === $key) {
+ $ret['label'] = $label;
+ } else {
+ $ret['label_' . $key] = $label;
+ }
+ }
+ } else {
+ $ret['label'] = $labels;
+ }
+
+ // set the style for the element
+ if (isset($this->_elementStyles[$ret['name']])) {
+ $ret['style'] = $this->_elementStyles[$ret['name']];
+ }
+ if ('group' == $ret['type']) {
+ $ret['separator'] = $element->_separator;
+ $ret['elements'] = array();
+ } else {
+ $ret['html'] = $element->toHtml();
+ }
+ return $ret;
+ }
+
+
+ /**
+ * Stores an array representation of an element in the form array
+ *
+ * @access private
+ * @param array Array representation of an element
+ * @return void
+ */
+ function _storeArray($elAry)
+ {
+ // where should we put this element...
+ if (is_array($this->_currentGroup) && ('group' != $elAry['type'])) {
+ $this->_currentGroup['elements'][] = $elAry;
+ } elseif (isset($this->_currentSection)) {
+ $this->_ary['sections'][$this->_currentSection]['elements'][] = $elAry;
+ } else {
+ $this->_ary['elements'][] = $elAry;
+ }
+ }
+
+
+ /**
+ * Sets a style to use for element rendering
+ *
+ * @param mixed element name or array ('element name' => 'style name')
+ * @param string style name if $elementName is not an array
+ * @access public
+ * @return void
+ */
+ function setElementStyle($elementName, $styleName = null)
+ {
+ if (is_array($elementName)) {
+ $this->_elementStyles = array_merge($this->_elementStyles, $elementName);
+ } else {
+ $this->_elementStyles[$elementName] = $styleName;
+ }
+ }
+}
+?> \ No newline at end of file
diff --git a/pearlib/HTML/QuickForm/Renderer/ArraySmarty.php b/pearlib/HTML/QuickForm/Renderer/ArraySmarty.php
new file mode 100644
index 00000000..9a8fb5c6
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Renderer/ArraySmarty.php
@@ -0,0 +1,376 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Alexey Borzov <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// | Thomas Schulz <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: ArraySmarty.php,v 1.9 2004/10/15 20:00:48 ths Exp $
+
+require_once 'HTML/QuickForm/Renderer/Array.php';
+
+/**
+ * A static renderer for HTML_QuickForm, makes an array of form content
+ * useful for an Smarty template
+ *
+ * Based on old toArray() code and ITStatic renderer.
+ *
+ * The form array structure is the following:
+ * Array (
+ * [frozen] => whether the complete form is frozen'
+ * [javascript] => javascript for client-side validation
+ * [attributes] => attributes for <form> tag
+ * [hidden] => html of all hidden elements
+ * [requirednote] => note about the required elements
+ * [errors] => Array
+ * (
+ * [1st_element_name] => Error for the 1st element
+ * ...
+ * [nth_element_name] => Error for the nth element
+ * )
+ *
+ * [header] => Array
+ * (
+ * [1st_header_name] => Header text for the 1st header
+ * ...
+ * [nth_header_name] => Header text for the nth header
+ * )
+ *
+ * [1st_element_name] => Array for the 1st element
+ * ...
+ * [nth_element_name] => Array for the nth element
+ *
+ * // where an element array has the form:
+ * (
+ * [name] => element name
+ * [value] => element value,
+ * [type] => type of the element
+ * [frozen] => whether element is frozen
+ * [label] => label for the element
+ * [required] => whether element is required
+ * // if element is not a group:
+ * [html] => HTML for the element
+ * // if element is a group:
+ * [separator] => separator for group elements
+ * [1st_gitem_name] => Array for the 1st element in group
+ * ...
+ * [nth_gitem_name] => Array for the nth element in group
+ * )
+ * )
+ *
+ * @access public
+ */
+class HTML_QuickForm_Renderer_ArraySmarty extends HTML_QuickForm_Renderer_Array
+{
+ /**
+ * The Smarty template engine instance
+ * @var object
+ */
+ var $_tpl = null;
+
+ /**
+ * Current element index
+ * @var integer
+ */
+ var $_elementIdx = 0;
+
+ /**
+ * The current element index inside a group
+ * @var integer
+ */
+ var $_groupElementIdx = 0;
+
+ /**
+ * How to handle the required tag for required fields
+ * @var string
+ * @see setRequiredTemplate()
+ */
+ var $_required = '';
+
+ /**
+ * How to handle error messages in form validation
+ * @var string
+ * @see setErrorTemplate()
+ */
+ var $_error = '';
+
+ /**
+ * Constructor
+ *
+ * @param object reference to the Smarty template engine instance
+ * @param bool true: render an array of labels to many labels, $key 0 to 'label' and the oterh to "label_$key"
+ * @access public
+ */
+ function HTML_QuickForm_Renderer_ArraySmarty(&$tpl, $staticLabels = false)
+ {
+ $this->HTML_QuickForm_Renderer_Array(true, $staticLabels);
+ $this->_tpl =& $tpl;
+ } // end constructor
+
+ /**
+ * Called when visiting a header element
+ *
+ * @param object An HTML_QuickForm_header element being visited
+ * @access public
+ * @return void
+ */
+ function renderHeader(&$header)
+ {
+ if ($name = $header->getName()) {
+ $this->_ary['header'][$name] = $header->toHtml();
+ } else {
+ $this->_ary['header'][$this->_sectionCount] = $header->toHtml();
+ }
+ $this->_currentSection = $this->_sectionCount++;
+ } // end func renderHeader
+
+ /**
+ * Called when visiting a group, before processing any group elements
+ *
+ * @param object An HTML_QuickForm_group object being visited
+ * @param bool Whether a group is required
+ * @param string An error message associated with a group
+ * @access public
+ * @return void
+ */
+ function startGroup(&$group, $required, $error)
+ {
+ parent::startGroup($group, $required, $error);
+ $this->_groupElementIdx = 1;
+ } // end func startGroup
+
+ /**
+ * Creates an array representing an element containing
+ * the key for storing this
+ *
+ * @access private
+ * @param object An HTML_QuickForm_element object
+ * @param bool Whether an element is required
+ * @param string Error associated with the element
+ * @return array
+ */
+ function _elementToArray(&$element, $required, $error)
+ {
+ $ret = parent::_elementToArray($element, $required, $error);
+
+ if ('group' == $ret['type']) {
+ $ret['html'] = $element->toHtml();
+ // we don't need the elements, see the array structure
+ unset($ret['elements']);
+ }
+ if (!empty($this->_required)){
+ $this->_renderRequired($ret['label'], $ret['html'], $required, $error);
+ }
+ if (!empty($this->_error)) {
+ $this->_renderError($ret['label'], $ret['html'], $error);
+ $ret['error'] = $error;
+ }
+ // create keys for elements grouped by native group or name
+ if (strstr($ret['name'], '[') or $this->_currentGroup) {
+ preg_match('/([^]]*)\\[([^]]*)\\]/', $ret['name'], $matches);
+ if (isset($matches[1])) {
+ $sKeysSub = substr_replace($ret['name'], '', 0, strlen($matches[1]));
+ $sKeysSub = str_replace(
+ array('[' , ']', '[\'\']'),
+ array('[\'', '\']', '[]' ),
+ $sKeysSub
+ );
+ $sKeys = '[\'' . $matches[1] . '\']' . $sKeysSub;
+ } else {
+ $sKeys = '[\'' . $ret['name'] . '\']';
+ }
+ // special handling for elements in native groups
+ if ($this->_currentGroup) {
+ // skip unnamed group items unless radios: no name -> no static access
+ // identification: have the same key string as the parent group
+ if ($this->_currentGroup['keys'] == $sKeys and 'radio' != $ret['type']) {
+ return false;
+ }
+ // reduce string of keys by remove leading group keys
+ if (0 === strpos($sKeys, $this->_currentGroup['keys'])) {
+ $sKeys = substr_replace($sKeys, '', 0, strlen($this->_currentGroup['keys']));
+ }
+ }
+ // element without a name
+ } elseif ($ret['name'] == '') {
+ $sKeys = '[\'element_' . $this->_elementIdx . '\']';
+ // other elements
+ } else {
+ $sKeys = '[\'' . $ret['name'] . '\']';
+ }
+ // for radios: add extra key from value
+ if ('radio' == $ret['type'] and substr($sKeys, -2) != '[]') {
+ $sKeys .= '[\'' . $ret['value'] . '\']';
+ }
+ $this->_elementIdx++;
+ $ret['keys'] = $sKeys;
+ return $ret;
+ } // end func _elementToArray
+
+ /**
+ * Stores an array representation of an element in the form array
+ *
+ * @access private
+ * @param array Array representation of an element
+ * @return void
+ */
+ function _storeArray($elAry)
+ {
+ if ($elAry) {
+ $sKeys = $elAry['keys'];
+ unset($elAry['keys']);
+ // where should we put this element...
+ if (is_array($this->_currentGroup) && ('group' != $elAry['type'])) {
+ $toEval = '$this->_currentGroup' . $sKeys . ' = $elAry;';
+ } else {
+ $toEval = '$this->_ary' . $sKeys . ' = $elAry;';
+ }
+ eval($toEval);
+ }
+ return;
+ }
+
+ /**
+ * Called when an element is required
+ *
+ * This method will add the required tag to the element label and/or the element html
+ * such as defined with the method setRequiredTemplate.
+ *
+ * @param string The element label
+ * @param string The element html rendering
+ * @param boolean The element required
+ * @param string The element error
+ * @see setRequiredTemplate()
+ * @access private
+ * @return void
+ */
+ function _renderRequired(&$label, &$html, &$required, &$error)
+ {
+ $this->_tpl->assign(array(
+ 'label' => $label,
+ 'html' => $html,
+ 'required' => $required,
+ 'error' => $error
+ ));
+ if (!empty($label) && strpos($this->_required, $this->_tpl->left_delimiter . '$label') !== false) {
+ $label = $this->_tplFetch($this->_required);
+ }
+ if (!empty($html) && strpos($this->_required, $this->_tpl->left_delimiter . '$html') !== false) {
+ $html = $this->_tplFetch($this->_required);
+ }
+ $this->_tpl->clear_assign(array('label', 'html', 'required'));
+ } // end func _renderRequired
+
+ /**
+ * Called when an element has a validation error
+ *
+ * This method will add the error message to the element label or the element html
+ * such as defined with the method setErrorTemplate. If the error placeholder is not found
+ * in the template, the error will be displayed in the form error block.
+ *
+ * @param string The element label
+ * @param string The element html rendering
+ * @param string The element error
+ * @see setErrorTemplate()
+ * @access private
+ * @return void
+ */
+ function _renderError(&$label, &$html, &$error)
+ {
+ $this->_tpl->assign(array('label' => '', 'html' => '', 'error' => $error));
+ $error = $this->_tplFetch($this->_error);
+ $this->_tpl->assign(array('label' => $label, 'html' => $html));
+
+ if (!empty($label) && strpos($this->_error, $this->_tpl->left_delimiter . '$label') !== false) {
+ $label = $this->_tplFetch($this->_error);
+ } elseif (!empty($html) && strpos($this->_error, $this->_tpl->left_delimiter . '$html') !== false) {
+ $html = $this->_tplFetch($this->_error);
+ }
+ $this->_tpl->clear_assign(array('label', 'html', 'error'));
+ } // end func _renderError
+
+ /**
+ * Process an template sourced in a string with Smarty
+ *
+ * Smarty has no core function to render a template given as a string.
+ * So we use the smarty eval plugin function to do this.
+ *
+ * @param string The template source
+ * @access private
+ * @return void
+ */
+ function _tplFetch($tplSource)
+ {
+ if (!function_exists('smarty_function_eval')) {
+ require SMARTY_DIR . '/plugins/function.eval.php';
+ }
+ return smarty_function_eval(array('var' => $tplSource), $this->_tpl);
+ }// end func _tplFetch
+
+ /**
+ * Sets the way required elements are rendered
+ *
+ * You can use {$label} or {$html} placeholders to let the renderer know where
+ * where the element label or the element html are positionned according to the
+ * required tag. They will be replaced accordingly with the right value. You
+ * can use the full smarty syntax here, especially a custom modifier for I18N.
+ * For example:
+ * {if $required}<span style="color: red;">*</span>{/if}{$label|translate}
+ * will put a red star in front of the label if the element is required and
+ * translate the label.
+ *
+ *
+ * @param string The required element template
+ * @access public
+ * @return void
+ */
+ function setRequiredTemplate($template)
+ {
+ $this->_required = $template;
+ } // end func setRequiredTemplate
+
+ /**
+ * Sets the way elements with validation errors are rendered
+ *
+ * You can use {$label} or {$html} placeholders to let the renderer know where
+ * where the element label or the element html are positionned according to the
+ * error message. They will be replaced accordingly with the right value.
+ * The error message will replace the {$error} placeholder.
+ * For example:
+ * {if $error}<span style="color: red;">{$error}</span>{/if}<br />{$html}
+ * will put the error message in red on top of the element html.
+ *
+ * If you want all error messages to be output in the main error block, use
+ * the {$form.errors} part of the rendered array that collects all raw error
+ * messages.
+ *
+ * If you want to place all error messages manually, do not specify {$html}
+ * nor {$label}.
+ *
+ * Groups can have special layouts. With this kind of groups, you have to
+ * place the formated error message manually. In this case, use {$form.group.error}
+ * where you want the formated error message to appear in the form.
+ *
+ * @param string The element error template
+ * @access public
+ * @return void
+ */
+ function setErrorTemplate($template)
+ {
+ $this->_error = $template;
+ } // end func setErrorTemplate
+}
+?> \ No newline at end of file
diff --git a/pearlib/HTML/QuickForm/Renderer/Default.php b/pearlib/HTML/QuickForm/Renderer/Default.php
new file mode 100644
index 00000000..4f6b9990
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Renderer/Default.php
@@ -0,0 +1,471 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Alexey Borzov <[email protected]> |
+// | Adam Daniel <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id$
+
+require_once('HTML/QuickForm/Renderer.php');
+
+/**
+ * A concrete renderer for HTML_QuickForm,
+ * based on QuickForm 2.x built-in one
+ *
+ * @access public
+ */
+class HTML_QuickForm_Renderer_Default extends HTML_QuickForm_Renderer
+{
+ /**
+ * The HTML of the form
+ * @var string
+ * @access private
+ */
+ var $_html;
+
+ /**
+ * Header Template string
+ * @var string
+ * @access private
+ */
+ var $_headerTemplate =
+ "\n\t<tr>\n\t\t<td style=\"white-space: nowrap; background-color: #CCCCCC;\" align=\"left\" valign=\"top\" colspan=\"2\"><b>{header}</b></td>\n\t</tr>";
+
+ /**
+ * Element template string
+ * @var string
+ * @access private
+ */
+ var $_elementTemplate =
+ "\n\t<tr>\n\t\t<td align=\"right\" valign=\"top\"><!-- BEGIN required --><span style=\"color: #ff0000\">*</span><!-- END required --><b>{label}</b></td>\n\t\t<td valign=\"top\" align=\"left\"><!-- BEGIN error --><span style=\"color: #ff0000\">{error}</span><br /><!-- END error -->\t{element}</td>\n\t</tr>";
+
+ /**
+ * Form template string
+ * @var string
+ * @access private
+ */
+ var $_formTemplate =
+ "\n<form{attributes}>\n<div>\n{hidden}<table border=\"0\">\n{content}\n</table>\n</div>\n</form>";
+
+ /**
+ * Required Note template string
+ * @var string
+ * @access private
+ */
+ var $_requiredNoteTemplate =
+ "\n\t<tr>\n\t\t<td></td>\n\t<td align=\"left\" valign=\"top\">{requiredNote}</td>\n\t</tr>";
+
+ /**
+ * Array containing the templates for customised elements
+ * @var array
+ * @access private
+ */
+ var $_templates = array();
+
+ /**
+ * Array containing the templates for group wraps.
+ *
+ * These templates are wrapped around group elements and groups' own
+ * templates wrap around them. This is set by setGroupTemplate().
+ *
+ * @var array
+ * @access private
+ */
+ var $_groupWraps = array();
+
+ /**
+ * Array containing the templates for elements within groups
+ * @var array
+ * @access private
+ */
+ var $_groupTemplates = array();
+
+ /**
+ * True if we are inside a group
+ * @var bool
+ * @access private
+ */
+ var $_inGroup = false;
+
+ /**
+ * Array with HTML generated for group elements
+ * @var array
+ * @access private
+ */
+ var $_groupElements = array();
+
+ /**
+ * Template for an element inside a group
+ * @var string
+ * @access private
+ */
+ var $_groupElementTemplate = '';
+
+ /**
+ * HTML that wraps around the group elements
+ * @var string
+ * @access private
+ */
+ var $_groupWrap = '';
+
+ /**
+ * HTML for the current group
+ * @var string
+ * @access private
+ */
+ var $_groupTemplate = '';
+
+ /**
+ * Collected HTML of the hidden fields
+ * @var string
+ * @access private
+ */
+ var $_hiddenHtml = '';
+
+ /**
+ * Constructor
+ *
+ * @access public
+ */
+ function HTML_QuickForm_Renderer_Default()
+ {
+ $this->HTML_QuickForm_Renderer();
+ } // end constructor
+
+ /**
+ * returns the HTML generated for the form
+ *
+ * @access public
+ * @return string
+ */
+ function toHtml()
+ {
+ return $this->_html;
+ } // end func toHtml
+
+ /**
+ * Called when visiting a form, before processing any form elements
+ *
+ * @param object An HTML_QuickForm object being visited
+ * @access public
+ * @return void
+ */
+ function startForm(&$form)
+ {
+ $this->_html = '';
+ $this->_hiddenHtml = '';
+ } // end func startForm
+
+ /**
+ * Called when visiting a form, after processing all form elements
+ * Adds required note, form attributes, validation javascript and form content.
+ *
+ * @param object An HTML_QuickForm object being visited
+ * @access public
+ * @return void
+ */
+ function finishForm(&$form)
+ {
+ // add a required note, if one is needed
+ if (!empty($form->_required) && !$form->_freezeAll) {
+ $this->_html .= str_replace('{requiredNote}', $form->getRequiredNote(), $this->_requiredNoteTemplate);
+ }
+ // add form attributes and content
+ $html = str_replace('{attributes}', $form->getAttributes(true), $this->_formTemplate);
+ if (strpos($this->_formTemplate, '{hidden}')) {
+ $html = str_replace('{hidden}', $this->_hiddenHtml, $html);
+ } else {
+ $this->_html .= $this->_hiddenHtml;
+ }
+ $this->_html = str_replace('{content}', $this->_html, $html);
+ // add a validation script
+ if ('' != ($script = $form->getValidationScript())) {
+ $this->_html = $script . "\n" . $this->_html;
+ }
+ } // end func finishForm
+
+ /**
+ * Called when visiting a header element
+ *
+ * @param object An HTML_QuickForm_header element being visited
+ * @access public
+ * @return void
+ */
+ function renderHeader(&$header)
+ {
+ $name = $header->getName();
+ if (!empty($name) && isset($this->_templates[$name])) {
+ $this->_html .= str_replace('{header}', $header->toHtml(), $this->_templates[$name]);
+ } else {
+ $this->_html .= str_replace('{header}', $header->toHtml(), $this->_headerTemplate);
+ }
+ } // end func renderHeader
+
+ /**
+ * Helper method for renderElement
+ *
+ * @param string Element name
+ * @param mixed Element label (if using an array of labels, you should set the appropriate template)
+ * @param bool Whether an element is required
+ * @param string Error message associated with the element
+ * @access private
+ * @see renderElement()
+ * @return string Html for element
+ */
+ function _prepareTemplate($name, $label, $required, $error)
+ {
+ if (is_array($label)) {
+ $nameLabel = array_shift($label);
+ } else {
+ $nameLabel = $label;
+ }
+ if (isset($this->_templates[$name])) {
+ $html = str_replace('{label}', $nameLabel, $this->_templates[$name]);
+ } else {
+ $html = str_replace('{label}', $nameLabel, $this->_elementTemplate);
+ }
+ if ($required) {
+ $html = str_replace('<!-- BEGIN required -->', '', $html);
+ $html = str_replace('<!-- END required -->', '', $html);
+ } else {
+ $html = preg_replace("/([ \t\n\r]*)?<!-- BEGIN required -->(\s|\S)*<!-- END required -->([ \t\n\r]*)?/i", '', $html);
+ }
+ if (isset($error)) {
+ $html = str_replace('{error}', $error, $html);
+ $html = str_replace('<!-- BEGIN error -->', '', $html);
+ $html = str_replace('<!-- END error -->', '', $html);
+ } else {
+ $html = preg_replace("/([ \t\n\r]*)?<!-- BEGIN error -->(\s|\S)*<!-- END error -->([ \t\n\r]*)?/i", '', $html);
+ }
+ if (is_array($label)) {
+ foreach($label as $key => $text) {
+ $key = is_int($key)? $key + 2: $key;
+ $html = str_replace("{label_{$key}}", $text, $html);
+ $html = str_replace("<!-- BEGIN label_{$key} -->", '', $html);
+ $html = str_replace("<!-- END label_{$key} -->", '', $html);
+ }
+ }
+ if (strpos($html, '{label_')) {
+ $html = preg_replace('/\s*<!-- BEGIN label_(\S+) -->.*<!-- END label_\1 -->\s*/i', '', $html);
+ }
+ return $html;
+ } // end func _prepareTemplate
+
+ /**
+ * Renders an element Html
+ * Called when visiting an element
+ *
+ * @param object An HTML_QuickForm_element object being visited
+ * @param bool Whether an element is required
+ * @param string An error message associated with an element
+ * @access public
+ * @return void
+ */
+ function renderElement(&$element, $required, $error)
+ {
+ if (!$this->_inGroup) {
+ $html = $this->_prepareTemplate($element->getName(), $element->getLabel(), $required, $error);
+ $this->_html .= str_replace('{element}', $element->toHtml(), $html);
+
+ } elseif (!empty($this->_groupElementTemplate)) {
+ $html = str_replace('{label}', $element->getLabel(), $this->_groupElementTemplate);
+ if ($required) {
+ $html = str_replace('<!-- BEGIN required -->', '', $html);
+ $html = str_replace('<!-- END required -->', '', $html);
+ } else {
+ $html = preg_replace("/([ \t\n\r]*)?<!-- BEGIN required -->(\s|\S)*<!-- END required -->([ \t\n\r]*)?/i", '', $html);
+ }
+ $this->_groupElements[] = str_replace('{element}', $element->toHtml(), $html);
+
+ } else {
+ $this->_groupElements[] = $element->toHtml();
+ }
+ } // end func renderElement
+
+ /**
+ * Renders an hidden element
+ * Called when visiting a hidden element
+ *
+ * @param object An HTML_QuickForm_hidden object being visited
+ * @access public
+ * @return void
+ */
+ function renderHidden(&$element)
+ {
+ $this->_hiddenHtml .= $element->toHtml() . "\n";
+ } // end func renderHidden
+
+ /**
+ * Called when visiting a raw HTML/text pseudo-element
+ *
+ * @param object An HTML_QuickForm_html element being visited
+ * @access public
+ * @return void
+ */
+ function renderHtml(&$data)
+ {
+ $this->_html .= $data->toHtml();
+ } // end func renderHtml
+
+ /**
+ * Called when visiting a group, before processing any group elements
+ *
+ * @param object An HTML_QuickForm_group object being visited
+ * @param bool Whether a group is required
+ * @param string An error message associated with a group
+ * @access public
+ * @return void
+ */
+ function startGroup(&$group, $required, $error)
+ {
+ $name = $group->getName();
+ $this->_groupTemplate = $this->_prepareTemplate($name, $group->getLabel(), $required, $error);
+ $this->_groupElementTemplate = empty($this->_groupTemplates[$name])? '': $this->_groupTemplates[$name];
+ $this->_groupWrap = empty($this->_groupWraps[$name])? '': $this->_groupWraps[$name];
+ $this->_groupElements = array();
+ $this->_inGroup = true;
+ } // end func startGroup
+
+ /**
+ * Called when visiting a group, after processing all group elements
+ *
+ * @param object An HTML_QuickForm_group object being visited
+ * @access public
+ * @return void
+ */
+ function finishGroup(&$group)
+ {
+ $separator = $group->_separator;
+ if (is_array($separator)) {
+ $count = count($separator);
+ $html = '';
+ for ($i = 0; $i < count($this->_groupElements); $i++) {
+ $html .= (0 == $i? '': $separator[($i - 1) % $count]) . $this->_groupElements[$i];
+ }
+ } else {
+ if (is_null($separator)) {
+ $separator = '&nbsp;';
+ }
+ $html = implode((string)$separator, $this->_groupElements);
+ }
+ if (!empty($this->_groupWrap)) {
+ $html = str_replace('{content}', $html, $this->_groupWrap);
+ }
+ $this->_html .= str_replace('{element}', $html, $this->_groupTemplate);
+ $this->_inGroup = false;
+ } // end func finishGroup
+
+ /**
+ * Sets element template
+ *
+ * @param string The HTML surrounding an element
+ * @param string (optional) Name of the element to apply template for
+ * @access public
+ * @return void
+ */
+ function setElementTemplate($html, $element = null)
+ {
+ if (is_null($element)) {
+ $this->_elementTemplate = $html;
+ } else {
+ $this->_templates[$element] = $html;
+ }
+ } // end func setElementTemplate
+
+
+ /**
+ * Sets template for a group wrapper
+ *
+ * This template is contained within a group-as-element template
+ * set via setTemplate() and contains group's element templates, set
+ * via setGroupElementTemplate()
+ *
+ * @param string The HTML surrounding group elements
+ * @param string Name of the group to apply template for
+ * @access public
+ * @return void
+ */
+ function setGroupTemplate($html, $group)
+ {
+ $this->_groupWraps[$group] = $html;
+ } // end func setGroupTemplate
+
+ /**
+ * Sets element template for elements within a group
+ *
+ * @param string The HTML surrounding an element
+ * @param string Name of the group to apply template for
+ * @access public
+ * @return void
+ */
+ function setGroupElementTemplate($html, $group)
+ {
+ $this->_groupTemplates[$group] = $html;
+ } // end func setGroupElementTemplate
+
+ /**
+ * Sets header template
+ *
+ * @param string The HTML surrounding the header
+ * @access public
+ * @return void
+ */
+ function setHeaderTemplate($html)
+ {
+ $this->_headerTemplate = $html;
+ } // end func setHeaderTemplate
+
+ /**
+ * Sets form template
+ *
+ * @param string The HTML surrounding the form tags
+ * @access public
+ * @return void
+ */
+ function setFormTemplate($html)
+ {
+ $this->_formTemplate = $html;
+ } // end func setFormTemplate
+
+ /**
+ * Sets the note indicating required fields template
+ *
+ * @param string The HTML surrounding the required note
+ * @access public
+ * @return void
+ */
+ function setRequiredNoteTemplate($html)
+ {
+ $this->_requiredNoteTemplate = $html;
+ } // end func setRequiredNoteTemplate
+
+ /**
+ * Clears all the HTML out of the templates that surround notes, elements, etc.
+ * Useful when you want to use addData() to create a completely custom form look
+ *
+ * @access public
+ * @return void
+ */
+ function clearAllTemplates()
+ {
+ $this->setElementTemplate('{element}');
+ $this->setFormTemplate("\n\t<form{attributes}>{content}\n\t</form>\n");
+ $this->setRequiredNoteTemplate('');
+ $this->_templates = array();
+ } // end func clearAllTemplates
+} // end class HTML_QuickForm_Renderer_Default
+?>
diff --git a/pearlib/HTML/QuickForm/Renderer/ITDynamic.php b/pearlib/HTML/QuickForm/Renderer/ITDynamic.php
new file mode 100644
index 00000000..ff1d9d48
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Renderer/ITDynamic.php
@@ -0,0 +1,287 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Alexey Borzov <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: ITDynamic.php,v 1.5 2004/10/15 13:04:36 avb Exp $
+
+require_once 'HTML/QuickForm/Renderer.php';
+
+/**
+ * A concrete renderer for HTML_QuickForm, using Integrated Templates.
+ *
+ * This is a "dynamic" renderer, which means that concrete form look
+ * is defined at runtime. This also means that you can define
+ * <b>one</b> template file for <b>all</b> your forms. That template
+ * should contain a block for every element 'look' appearing in your
+ * forms and also some special blocks (consult the examples). If a
+ * special block is not set for an element, the renderer falls back to
+ * a default one.
+ *
+ * @author Alexey Borzov <[email protected]>
+ * @access public
+ */
+class HTML_QuickForm_Renderer_ITDynamic extends HTML_QuickForm_Renderer
+{
+ /**
+ * A template class (HTML_Template_ITX or HTML_Template_Sigma) instance
+ * @var object
+ */
+ var $_tpl = null;
+
+ /**
+ * The errors that were not shown near concrete fields go here
+ * @var array
+ */
+ var $_errors = array();
+
+ /**
+ * Show the block with required note?
+ * @var bool
+ */
+ var $_showRequired = false;
+
+ /**
+ * A separator for group elements
+ * @var mixed
+ */
+ var $_groupSeparator = null;
+
+ /**
+ * The current element index inside a group
+ * @var integer
+ */
+ var $_groupElementIdx = 0;
+
+ /**
+ * Blocks to use for different elements
+ * @var array
+ */
+ var $_elementBlocks = array();
+
+ /**
+ * Block to use for headers
+ * @var string
+ */
+ var $_headerBlock = null;
+
+
+ /**
+ * Constructor
+ *
+ * @param object An HTML_Template_ITX/HTML_Template_Sigma object to use
+ */
+ function HTML_QuickForm_Renderer_ITDynamic(&$tpl)
+ {
+ $this->HTML_QuickForm_Renderer();
+ $this->_tpl =& $tpl;
+ $this->_tpl->setCurrentBlock('qf_main_loop');
+ }
+
+
+ function finishForm(&$form)
+ {
+ // display errors above form
+ if (!empty($this->_errors) && $this->_tpl->blockExists('qf_error_loop')) {
+ foreach ($this->_errors as $error) {
+ $this->_tpl->setVariable('qf_error', $error);
+ $this->_tpl->parse('qf_error_loop');
+ }
+ }
+ // show required note
+ if ($this->_showRequired) {
+ $this->_tpl->setVariable('qf_required_note', $form->getRequiredNote());
+ }
+ // assign form attributes
+ $this->_tpl->setVariable('qf_attributes', $form->getAttributes(true));
+ // assign javascript validation rules
+ $this->_tpl->setVariable('qf_javascript', $form->getValidationScript());
+ }
+
+
+ function renderHeader(&$header)
+ {
+ $blockName = $this->_matchBlock($header);
+ if ('qf_header' == $blockName && isset($this->_headerBlock)) {
+ $blockName = $this->_headerBlock;
+ }
+ $this->_tpl->setVariable('qf_header', $header->toHtml());
+ $this->_tpl->parse($blockName);
+ $this->_tpl->parse('qf_main_loop');
+ }
+
+
+ function renderElement(&$element, $required, $error)
+ {
+ $blockName = $this->_matchBlock($element);
+ // are we inside a group?
+ if ('qf_main_loop' != $this->_tpl->currentBlock) {
+ if (0 != $this->_groupElementIdx && $this->_tpl->placeholderExists('qf_separator', $blockName)) {
+ if (is_array($this->_groupSeparator)) {
+ $this->_tpl->setVariable('qf_separator', $this->_groupSeparator[($this->_groupElementIdx - 1) % count($this->_groupSeparator)]);
+ } else {
+ $this->_tpl->setVariable('qf_separator', (string)$this->_groupSeparator);
+ }
+ }
+ $this->_groupElementIdx++;
+
+ } elseif(!empty($error)) {
+ // show the error message or keep it for later use
+ if ($this->_tpl->blockExists($blockName . '_error')) {
+ $this->_tpl->setVariable('qf_error', $error);
+ } else {
+ $this->_errors[] = $error;
+ }
+ }
+ // show an '*' near the required element
+ if ($required) {
+ $this->_showRequired = true;
+ if ($this->_tpl->blockExists($blockName . '_required')) {
+ $this->_tpl->touchBlock($blockName . '_required');
+ }
+ }
+ // Prepare multiple labels
+ $labels = $element->getLabel();
+ if (is_array($labels)) {
+ $mainLabel = array_shift($labels);
+ } else {
+ $mainLabel = $labels;
+ }
+ // render the element itself with its main label
+ $this->_tpl->setVariable('qf_element', $element->toHtml());
+ if ($this->_tpl->placeholderExists('qf_label', $blockName)) {
+ $this->_tpl->setVariable('qf_label', $mainLabel);
+ }
+ // render extra labels, if any
+ if (is_array($labels)) {
+ foreach($labels as $key => $label) {
+ $key = is_int($key)? $key + 2: $key;
+ if ($this->_tpl->blockExists($blockName . '_label_' . $key)) {
+ $this->_tpl->setVariable('qf_label_' . $key, $label);
+ }
+ }
+ }
+ $this->_tpl->parse($blockName);
+ $this->_tpl->parseCurrentBlock();
+ }
+
+
+ function renderHidden(&$element)
+ {
+ $this->_tpl->setVariable('qf_hidden', $element->toHtml());
+ $this->_tpl->parse('qf_hidden_loop');
+ }
+
+
+ function startGroup(&$group, $required, $error)
+ {
+ $blockName = $this->_matchBlock($group);
+ $this->_tpl->setCurrentBlock($blockName . '_loop');
+ $this->_groupElementIdx = 0;
+ $this->_groupSeparator = is_null($group->_separator)? '&nbsp;': $group->_separator;
+ // show an '*' near the required element
+ if ($required) {
+ $this->_showRequired = true;
+ if ($this->_tpl->blockExists($blockName . '_required')) {
+ $this->_tpl->touchBlock($blockName . '_required');
+ }
+ }
+ // show the error message or keep it for later use
+ if (!empty($error)) {
+ if ($this->_tpl->blockExists($blockName . '_error')) {
+ $this->_tpl->setVariable('qf_error', $error);
+ } else {
+ $this->_errors[] = $error;
+ }
+ }
+ $this->_tpl->setVariable('qf_group_label', $group->getLabel());
+ }
+
+
+ function finishGroup(&$group)
+ {
+ $this->_tpl->parse($this->_matchBlock($group));
+ $this->_tpl->setCurrentBlock('qf_main_loop');
+ $this->_tpl->parseCurrentBlock();
+ }
+
+
+ /**
+ * Returns the name of a block to use for element rendering
+ *
+ * If a name was not explicitly set via setElementBlock(), it tries
+ * the names '{prefix}_{element type}' and '{prefix}_{element}', where
+ * prefix is either 'qf' or the name of the current group's block
+ *
+ * @param object An HTML_QuickForm_element object
+ * @access private
+ * @return string block name
+ */
+ function _matchBlock(&$element)
+ {
+ $name = $element->getName();
+ $type = $element->getType();
+ if (isset($this->_elementBlocks[$name]) && $this->_tpl->blockExists($this->_elementBlocks[$name])) {
+ if (('group' == $type) || ($this->_elementBlocks[$name] . '_loop' != $this->_tpl->currentBlock)) {
+ return $this->_elementBlocks[$name];
+ }
+ }
+ if ('group' != $type && 'qf_main_loop' != $this->_tpl->currentBlock) {
+ $prefix = substr($this->_tpl->currentBlock, 0, -5); // omit '_loop' postfix
+ } else {
+ $prefix = 'qf';
+ }
+ if ($this->_tpl->blockExists($prefix . '_' . $type)) {
+ return $prefix . '_' . $type;
+ } elseif ($this->_tpl->blockExists($prefix . '_' . $name)) {
+ return $prefix . '_' . $name;
+ } else {
+ return $prefix . '_element';
+ }
+ }
+
+
+ /**
+ * Sets the block to use for element rendering
+ *
+ * @param mixed element name or array ('element name' => 'block name')
+ * @param string block name if $elementName is not an array
+ * @access public
+ * @return void
+ */
+ function setElementBlock($elementName, $blockName = null)
+ {
+ if (is_array($elementName)) {
+ $this->_elementBlocks = array_merge($this->_elementBlocks, $elementName);
+ } else {
+ $this->_elementBlocks[$elementName] = $blockName;
+ }
+ }
+
+
+ /**
+ * Sets the name of a block to use for header rendering
+ *
+ * @param string block name
+ * @access public
+ * @return void
+ */
+ function setHeaderBlock($blockName)
+ {
+ $this->_headerBlock = $blockName;
+ }
+}
+?>
diff --git a/pearlib/HTML/QuickForm/Renderer/ITStatic.php b/pearlib/HTML/QuickForm/Renderer/ITStatic.php
new file mode 100644
index 00000000..ecb6ba54
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Renderer/ITStatic.php
@@ -0,0 +1,490 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: ITStatic.php,v 1.7 2004/06/28 14:20:22 avb Exp $
+
+require_once('HTML/QuickForm/Renderer.php');
+
+/**
+ * A static renderer for HTML_QuickForm compatible
+ * with HTML_Template_IT and HTML_Template_Sigma.
+ *
+ * As opposed to the dynamic renderer, this renderer needs
+ * every elements and labels in the form to be specified by
+ * placeholders at the position you want them to be displayed.
+ *
+ * @author Bertrand Mansion <[email protected]>
+ * @access public
+ */
+class HTML_QuickForm_Renderer_ITStatic extends HTML_QuickForm_Renderer
+{
+ /**
+ * An HTML_Template_IT or some other API compatible Template instance
+ * @var object
+ */
+ var $_tpl = null;
+
+ /**
+ * Rendered form name
+ * @var string
+ */
+ var $_formName = 'form';
+
+ /**
+ * The errors that were not shown near concrete fields go here
+ * @var array
+ */
+ var $_errors = array();
+
+ /**
+ * Show the block with required note?
+ * @var bool
+ */
+ var $_showRequired = false;
+
+ /**
+ * Which group are we currently parsing ?
+ * @var string
+ */
+ var $_inGroup;
+
+ /**
+ * Index of the element in its group
+ * @var int
+ */
+ var $_elementIndex = 0;
+
+ /**
+ * If elements have been added with the same name
+ * @var array
+ */
+ var $_duplicateElements = array();
+
+ /**
+ * How to handle the required tag for required fields
+ * @var string
+ */
+ var $_required = '{label}<font size="1" color="red">*</font>';
+
+ /**
+ * How to handle error messages in form validation
+ * @var string
+ */
+ var $_error = '<font color="red">{error}</font><br />{html}';
+
+ /**
+ * Collected HTML for hidden elements, if needed
+ * @var string
+ */
+ var $_hidden = '';
+
+ /**
+ * Constructor
+ *
+ * @param object An HTML_Template_IT or other compatible Template object to use
+ */
+ function HTML_QuickForm_Renderer_ITStatic(&$tpl)
+ {
+ $this->HTML_QuickForm_Renderer();
+ $this->_tpl =& $tpl;
+ } // end constructor
+
+ /**
+ * Called when visiting a form, before processing any form elements
+ *
+ * @param object An HTML_QuickForm object being visited
+ * @access public
+ * @return void
+ */
+ function startForm(&$form)
+ {
+ $this->_formName = $form->getAttribute('id');
+
+ if (count($form->_duplicateIndex) > 0) {
+ // Take care of duplicate elements
+ foreach ($form->_duplicateIndex as $elementName => $indexes) {
+ $this->_duplicateElements[$elementName] = 0;
+ }
+ }
+ } // end func startForm
+
+ /**
+ * Called when visiting a form, after processing all form elements
+ *
+ * @param object An HTML_QuickForm object being visited
+ * @access public
+ * @return void
+ */
+ function finishForm(&$form)
+ {
+ // display errors above form
+ if (!empty($this->_errors) && $this->_tpl->blockExists($this->_formName.'_error_loop')) {
+ foreach ($this->_errors as $error) {
+ $this->_tpl->setVariable($this->_formName.'_error', $error);
+ $this->_tpl->parse($this->_formName.'_error_loop');
+ }
+ }
+ // show required note
+ if ($this->_showRequired) {
+ $this->_tpl->setVariable($this->_formName.'_required_note', $form->getRequiredNote());
+ }
+ // add hidden elements, if collected
+ if (!empty($this->_hidden)) {
+ $this->_tpl->setVariable($this->_formName . '_hidden', $this->_hidden);
+ }
+ // assign form attributes
+ $this->_tpl->setVariable($this->_formName.'_attributes', $form->getAttributes(true));
+ // assign javascript validation rules
+ $this->_tpl->setVariable($this->_formName.'_javascript', $form->getValidationScript());
+ } // end func finishForm
+
+ /**
+ * Called when visiting a header element
+ *
+ * @param object An HTML_QuickForm_header element being visited
+ * @access public
+ * @return void
+ */
+ function renderHeader(&$header)
+ {
+ $name = $header->getName();
+ $varName = $this->_formName.'_header';
+
+ // Find placeHolder
+ if (!empty($name) && $this->_tpl->placeHolderExists($this->_formName.'_header_'.$name)) {
+ $varName = $this->_formName.'_header_'.$name;
+ }
+ $this->_tpl->setVariable($varName, $header->toHtml());
+ } // end func renderHeader
+
+ /**
+ * Called when visiting an element
+ *
+ * @param object An HTML_QuickForm_element object being visited
+ * @param bool Whether an element is required
+ * @param string An error message associated with an element
+ * @access public
+ * @return void
+ */
+ function renderElement(&$element, $required, $error)
+ {
+ $name = $element->getName();
+
+ // are we inside a group?
+ if (!empty($this->_inGroup)) {
+ $varName = $this->_formName.'_'.str_replace(array('[', ']'), '_', $name);
+ if (substr($varName, -2) == '__') {
+ // element name is of type : group[]
+ $varName = $this->_inGroup.'_'.$this->_elementIndex.'_';
+ $this->_elementIndex++;
+ }
+ if ($varName != $this->_inGroup) {
+ $varName .= '_' == substr($varName, -1)? '': '_';
+ // element name is of type : group[name]
+ $label = $element->getLabel();
+ $html = $element->toHtml();
+
+ if ($required && !$element->isFrozen()) {
+ $this->_renderRequired($label, $html);
+ $this->_showRequired = true;
+ }
+ if (!empty($label)) {
+ if (is_array($label)) {
+ foreach ($label as $key => $value) {
+ $this->_tpl->setVariable($varName.'label_'.$key, $value);
+ }
+ } else {
+ $this->_tpl->setVariable($varName.'label', $label);
+ }
+ }
+ $this->_tpl->setVariable($varName.'html', $html);
+ }
+
+ } else {
+
+ $name = str_replace(array('[', ']'), array('_', ''), $name);
+
+ if (isset($this->_duplicateElements[$name])) {
+ // Element is a duplicate
+ $varName = $this->_formName.'_'.$name.'_'.$this->_duplicateElements[$name];
+ $this->_duplicateElements[$name]++;
+ } else {
+ $varName = $this->_formName.'_'.$name;
+ }
+
+ $label = $element->getLabel();
+ $html = $element->toHtml();
+
+ if ($required) {
+ $this->_showRequired = true;
+ $this->_renderRequired($label, $html);
+ }
+ if (!empty($error)) {
+ $this->_renderError($label, $html, $error);
+ }
+ if (is_array($label)) {
+ foreach ($label as $key => $value) {
+ $this->_tpl->setVariable($varName.'_label_'.$key, $value);
+ }
+ } else {
+ $this->_tpl->setVariable($varName.'_label', $label);
+ }
+ $this->_tpl->setVariable($varName.'_html', $html);
+ }
+ } // end func renderElement
+
+ /**
+ * Called when visiting a hidden element
+ *
+ * @param object An HTML_QuickForm_hidden object being visited
+ * @access public
+ * @return void
+ */
+ function renderHidden(&$element)
+ {
+ if ($this->_tpl->placeholderExists($this->_formName . '_hidden')) {
+ $this->_hidden .= $element->toHtml();
+ } else {
+ $name = $element->getName();
+ $name = str_replace(array('[', ']'), array('_', ''), $name);
+ $this->_tpl->setVariable($this->_formName.'_'.$name.'_html', $element->toHtml());
+ }
+ } // end func renderHidden
+
+ /**
+ * Called when visiting a group, before processing any group elements
+ *
+ * @param object An HTML_QuickForm_group object being visited
+ * @param bool Whether a group is required
+ * @param string An error message associated with a group
+ * @access public
+ * @return void
+ */
+ function startGroup(&$group, $required, $error)
+ {
+ $name = $group->getName();
+ $varName = $this->_formName.'_'.$name;
+
+ $this->_elementIndex = 0;
+
+ $html = $this->_tpl->placeholderExists($varName.'_html') ? $group->toHtml() : '';
+ $label = $group->getLabel();
+
+ if ($required) {
+ $this->_renderRequired($label, $html);
+ }
+ if (!empty($error)) {
+ $this->_renderError($label, $html, $error);
+ }
+ if (!empty($html)) {
+ $this->_tpl->setVariable($varName.'_html', $html);
+ } else {
+ // Uses error blocks to set the special groups layout error
+ // <!-- BEGIN form_group_error -->{form_group_error}<!-- END form_group_error -->
+ if (!empty($error)) {
+ if ($this->_tpl->placeholderExists($varName.'_error')) {
+ if ($this->_tpl->blockExists($this->_formName . '_error_block')) {
+ $this->_tpl->setVariable($this->_formName . '_error', $error);
+ $error = $this->_getTplBlock($this->_formName . '_error_block');
+ } elseif (strpos($this->_error, '{html}') !== false || strpos($this->_error, '{label}') !== false) {
+ $error = str_replace('{error}', $error, $this->_error);
+ }
+ }
+ $this->_tpl->setVariable($varName . '_error', $error);
+ array_pop($this->_errors);
+ }
+ }
+ if (is_array($label)) {
+ foreach ($label as $key => $value) {
+ $this->_tpl->setVariable($varName.'_label_'.$key, $value);
+ }
+ } else {
+ $this->_tpl->setVariable($varName.'_label', $label);
+ }
+ $this->_inGroup = $varName;
+ } // end func startGroup
+
+ /**
+ * Called when visiting a group, after processing all group elements
+ *
+ * @param object An HTML_QuickForm_group object being visited
+ * @access public
+ * @return void
+ */
+ function finishGroup(&$group)
+ {
+ $this->_inGroup = '';
+ } // end func finishGroup
+
+ /**
+ * Sets the way required elements are rendered
+ *
+ * You can use {label} or {html} placeholders to let the renderer know where
+ * where the element label or the element html are positionned according to the
+ * required tag. They will be replaced accordingly with the right value.
+ * For example:
+ * <font color="red">*</font>{label}
+ * will put a red star in front of the label if the element is required.
+ *
+ * @param string The required element template
+ * @access public
+ * @return void
+ */
+ function setRequiredTemplate($template)
+ {
+ $this->_required = $template;
+ } // end func setRequiredTemplate
+
+ /**
+ * Sets the way elements with validation errors are rendered
+ *
+ * You can use {label} or {html} placeholders to let the renderer know where
+ * where the element label or the element html are positionned according to the
+ * error message. They will be replaced accordingly with the right value.
+ * The error message will replace the {error} place holder.
+ * For example:
+ * <font color="red">{error}</font><br />{html}
+ * will put the error message in red on top of the element html.
+ *
+ * If you want all error messages to be output in the main error block, do not specify
+ * {html} nor {label}.
+ *
+ * Groups can have special layouts. With this kind of groups, the renderer will need
+ * to know where to place the error message. In this case, use error blocks like:
+ * <!-- BEGIN form_group_error -->{form_group_error}<!-- END form_group_error -->
+ * where you want the error message to appear in the form.
+ *
+ * @param string The element error template
+ * @access public
+ * @return void
+ */
+ function setErrorTemplate($template)
+ {
+ $this->_error = $template;
+ } // end func setErrorTemplate
+
+ /**
+ * Called when an element is required
+ *
+ * This method will add the required tag to the element label and/or the element html
+ * such as defined with the method setRequiredTemplate
+ *
+ * @param string The element label
+ * @param string The element html rendering
+ * @see setRequiredTemplate()
+ * @access private
+ * @return void
+ */
+ function _renderRequired(&$label, &$html)
+ {
+ if ($this->_tpl->blockExists($tplBlock = $this->_formName . '_required_block')) {
+ if (!empty($label) && $this->_tpl->placeholderExists($this->_formName . '_label', $tplBlock)) {
+ $this->_tpl->setVariable($this->_formName . '_label', is_array($label)? $label[0]: $label);
+ if (is_array($label)) {
+ $label[0] = $this->_getTplBlock($tplBlock);
+ } else {
+ $label = $this->_getTplBlock($tplBlock);
+ }
+ }
+ if (!empty($html) && $this->_tpl->placeholderExists($this->_formName . '_html', $tplBlock)) {
+ $this->_tpl->setVariable($this->_formName . '_html', $html);
+ $html = $this->_getTplBlock($tplBlock);
+ }
+ } else {
+ if (!empty($label) && strpos($this->_required, '{label}') !== false) {
+ if (is_array($label)) {
+ $label[0] = str_replace('{label}', $label[0], $this->_required);
+ } else {
+ $label = str_replace('{label}', $label, $this->_required);
+ }
+ }
+ if (!empty($html) && strpos($this->_required, '{html}') !== false) {
+ $html = str_replace('{html}', $html, $this->_required);
+ }
+ }
+ } // end func _renderRequired
+
+ /**
+ * Called when an element has a validation error
+ *
+ * This method will add the error message to the element label or the element html
+ * such as defined with the method setErrorTemplate. If the error placeholder is not found
+ * in the template, the error will be displayed in the form error block.
+ *
+ * @param string The element label
+ * @param string The element html rendering
+ * @param string The element error
+ * @see setErrorTemplate()
+ * @access private
+ * @return void
+ */
+ function _renderError(&$label, &$html, $error)
+ {
+ if ($this->_tpl->blockExists($tplBlock = $this->_formName . '_error_block')) {
+ $this->_tpl->setVariable($this->_formName . '_error', $error);
+ if (!empty($label) && $this->_tpl->placeholderExists($this->_formName . '_label', $tplBlock)) {
+ $this->_tpl->setVariable($this->_formName . '_label', is_array($label)? $label[0]: $label);
+ if (is_array($label)) {
+ $label[0] = $this->_getTplBlock($tplBlock);
+ } else {
+ $label = $this->_getTplBlock($tplBlock);
+ }
+ } elseif (!empty($html) && $this->_tpl->placeholderExists($this->_formName . '_html', $tplBlock)) {
+ $this->_tpl->setVariable($this->_formName . '_html', $html);
+ $html = $this->_getTplBlock($tplBlock);
+ }
+ // clean up after ourselves
+ $this->_tpl->setVariable($this->_formName . '_error', null);
+ } elseif (!empty($label) && strpos($this->_error, '{label}') !== false) {
+ if (is_array($label)) {
+ $label[0] = str_replace(array('{label}', '{error}'), array($label[0], $error), $this->_error);
+ } else {
+ $label = str_replace(array('{label}', '{error}'), array($label, $error), $this->_error);
+ }
+ } elseif (!empty($html) && strpos($this->_error, '{html}') !== false) {
+ $html = str_replace(array('{html}', '{error}'), array($html, $error), $this->_error);
+ } else {
+ $this->_errors[] = $error;
+ }
+ }// end func _renderError
+
+
+ /**
+ * Returns the block's contents
+ *
+ * The method is needed because ITX and Sigma implement clearing
+ * the block contents on get() a bit differently
+ *
+ * @param string Block name
+ * @return string Block contents
+ */
+ function _getTplBlock($block)
+ {
+ $this->_tpl->parse($block);
+ if (is_a($this->_tpl, 'html_template_sigma')) {
+ $ret = $this->_tpl->get($block, true);
+ } else {
+ $oldClear = $this->_tpl->clearCache;
+ $this->_tpl->clearCache = true;
+ $ret = $this->_tpl->get($block);
+ $this->_tpl->clearCache = $oldClear;
+ }
+ return $ret;
+ }
+} // end class HTML_QuickForm_Renderer_ITStatic
+?> \ No newline at end of file
diff --git a/pearlib/HTML/QuickForm/Renderer/Object.php b/pearlib/HTML/QuickForm/Renderer/Object.php
new file mode 100644
index 00000000..a8fe26a6
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Renderer/Object.php
@@ -0,0 +1,438 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Ron McClain <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Object.php,v 1.2 2003/11/03 12:53:01 avb Exp $
+
+require_once('HTML/QuickForm/Renderer.php');
+
+/**
+ * A concrete renderer for HTML_QuickForm, makes an object from form contents
+ *
+ * Based on HTML_Quickform_Renderer_Array code
+ *
+ * @public
+ */
+class HTML_QuickForm_Renderer_Object extends HTML_QuickForm_Renderer {
+ /**
+ * The object being generated
+ * @var object $_obj
+ */
+ var $_obj= null;
+
+ /**
+ * Number of sections in the form (i.e. number of headers in it)
+ * @var integer $_sectionCount
+ */
+ var $_sectionCount;
+
+ /**
+ * Current section number
+ * @var integer $_currentSection
+ */
+ var $_currentSection;
+
+ /**
+ * Object representing current group
+ * @var object $_currentGroup
+ */
+ var $_currentGroup = null;
+
+ /**
+ * Class of Element Objects
+ * @var object $_elementType
+ */
+ var $_elementType = 'QuickFormElement';
+
+ /**
+ * Additional style information for different elements
+ * @var array $_elementStyles
+ */
+ var $_elementStyles = array();
+
+ /**
+ * true: collect all hidden elements into string; false: process them as usual form elements
+ * @var bool $_collectHidden
+ */
+ var $_collectHidden = false;
+
+
+ /**
+ * Constructor
+ *
+ * @param collecthidden bool true: collect all hidden elements
+ * @public
+ */
+ function HTML_QuickForm_Renderer_Object($collecthidden = false)
+ {
+ $this->HTML_QuickForm_Renderer();
+ $this->_collectHidden = $collecthidden;
+ $this->_obj = new QuickformForm;
+ }
+
+ /**
+ * Return the rendered Object
+ * @public
+ */
+ function toObject()
+ {
+ return $this->_obj;
+ }
+
+ /**
+ * Set the class of the form elements. Defaults to StdClass.
+ * @param type string Name of element class
+ * @public
+ */
+ function setElementType($type) {
+ $this->_elementType = $type;
+ }
+
+ function startForm(&$form)
+ {
+ $this->_obj->frozen = $form->isFrozen();
+ $this->_obj->javascript = $form->getValidationScript();
+ $this->_obj->attributes = $form->getAttributes(true);
+ $this->_obj->requirednote = $form->getRequiredNote();
+ $this->_obj->errors = new StdClass;
+
+ if($this->_collectHidden) {
+ $this->_obj->hidden = '';
+ }
+ $this->_elementIdx = 1;
+ $this->_currentSection = null;
+ $this->_sectionCount = 0;
+ } // end func startForm
+
+ function renderHeader(&$header)
+ {
+ $hobj = new StdClass;
+ $hobj->header = $header->toHtml();
+ $this->_obj->sections[$this->_sectionCount] = $hobj;
+ $this->_currentSection = $this->_sectionCount++;
+ }
+ function renderElement(&$element, $required, $error)
+ {
+ $elObj = $this->_elementToObject($element, $required, $error);
+ if(!empty($error)) {
+ $name = $elObj->name;
+ $this->_obj->errors->$name = $error;
+ }
+ $this->_storeObject($elObj);
+ } // end func renderElement
+
+ function renderHidden(&$element) {
+ if($this->_collectHidden) {
+ $this->_obj->hidden .= $element->toHtml() . "\n";
+ } else {
+ $this->renderElement($element, false, null);
+ }
+ } //end func renderHidden
+
+ function startGroup(&$group, $required, $error)
+ {
+ $this->_currentGroup = $this->_elementToObject($group, $required, $error);
+ if(!empty($error)) {
+ $name = $this->_currentGroup->name;
+ $this->_view->errors->$name = $error;
+ }
+ } // end func startGroup
+
+ function finishGroup(&$group)
+ {
+ $this->_storeObject($this->_currentGroup);
+ $this->_currentGroup = null;
+ } // end func finishGroup
+
+ /**
+ * Creates an object representing an element
+ *
+ * @private
+ * @param element object An HTML_QuickForm_element object
+ * @param required bool Whether an element is required
+ * @param error string Error associated with the element
+ * @return object
+ */
+ function _elementToObject(&$element, $required, $error)
+ {
+ if($this->_elementType) {
+ $ret = new $this->_elementType;
+ }
+ $ret->name = $element->getName();
+ $ret->value = $element->getValue();
+ $ret->type = $element->getType();
+ $ret->frozen = $element->isFrozen();
+ $labels = $element->getLabel();
+ if (is_array($labels)) {
+ $ret->label = array_shift($labels);
+ foreach ($labels as $key => $label) {
+ $key = is_int($key)? $key + 2: $key;
+ $ret->{'label_' . $key} = $label;
+ }
+ } else {
+ $ret->label = $labels;
+ }
+ $ret->required = $required;
+ $ret->error = $error;
+
+ if(isset($this->_elementStyles[$ret->name])) {
+ $ret->style = $this->_elementStyles[$ret->name];
+ $ret->styleTemplate = "styles/". $ret->style .".html";
+ }
+ if($ret->type == 'group') {
+ $ret->separator = $element->_separator;
+ $ret->elements = array();
+ } else {
+ $ret->html = $element->toHtml();
+ }
+ return $ret;
+ }
+
+ /**
+ * Stores an object representation of an element in the form array
+ *
+ * @private
+ * @param elObj object Object representation of an element
+ * @return void
+ */
+ function _storeObject($elObj)
+ {
+ $name = $elObj->name;
+ if(is_object($this->_currentGroup) && $elObj->type != 'group') {
+ $this->_currentGroup->elements[] = $elObj;
+ } elseif (isset($this->_currentSection)) {
+ $this->_obj->sections[$this->_currentSection]->elements[] = $elObj;
+ } else {
+ $this->_obj->elements[] = $elObj;
+ }
+ }
+
+ function setElementStyle($elementName, $styleName = null)
+ {
+ if(is_array($elementName)) {
+ $this->_elementStyles = array_merge($this->_elementStyles, $elementName);
+ } else {
+ $this->_elementStyles[$elementName] = $styleName;
+ }
+ }
+
+} // end class HTML_QuickForm_Renderer_Object
+
+
+
+/**
+ * @abstract Long Description
+ * This class represents the object passed to outputObject()
+ *
+ * Eg.
+ * {form.outputJavaScript():h}
+ * {form.outputHeader():h}
+ * <table>
+ * <tr>
+ * <td>{form.name.label:h}</td><td>{form.name.html:h}</td>
+ * </tr>
+ * </table>
+ * </form>
+ *
+ * @public
+ */
+class QuickformForm {
+ /**
+ * Whether the form has been frozen
+ * @var boolean $frozen
+ */
+ var $frozen;
+
+ /**
+ * Javascript for client-side validation
+ * @var string $javascript
+ */
+ var $javascript;
+
+ /**
+ * Attributes for form tag
+ * @var string $attributes
+ */
+ var $attributes;
+
+ /**
+ * Note about required elements
+ * @var string $requirednote
+ */
+ var $requirednote;
+
+ /**
+ * Collected html of all hidden variables
+ * @var string $hidden
+ */
+ var $hidden;
+
+ /**
+ * Set if there were validation errors.
+ * StdClass object with element names for keys and their
+ * error messages as values
+ * @var object $errors
+ */
+ var $errors;
+
+ /**
+ * Array of QuickformElementObject elements. If there are headers in the form
+ * this will be empty and the elements will be in the
+ * separate sections
+ * @var array $elements
+ */
+ var $elements;
+
+ /**
+ * Array of sections contained in the document
+ * @var array $sections
+ */
+ var $sections;
+
+ /**
+ * Output &lt;form&gt; header
+ * {form.outputHeader():h}
+ * @return string &lt;form attributes&gt;
+ */
+ function outputHeader() {
+ $hdr = "<form " . $this->attributes . ">\n";
+ return $hdr;
+ }
+ /**
+ * Output form javascript
+ * {form.outputJavaScript():h}
+ * @return string Javascript
+ */
+ function outputJavaScript() {
+ return $this->javascript;
+ }
+} // end class QuickformForm
+
+
+/**
+ * Convenience class describing a form element.
+ * The properties defined here will be available from
+ * your flexy templates by referencing
+ * {form.zip.label:h}, {form.zip.html:h}, etc.
+ */
+class QuickformElement {
+
+ /**
+ * Element name
+ * @var string $name
+ */
+ var $name;
+
+ /**
+ * Element value
+ * @var mixed $value
+ */
+ var $value;
+
+ /**
+ * Type of element
+ * @var string $type
+ */
+ var $type;
+
+ /**
+ * Whether the element is frozen
+ * @var boolean $frozen
+ */
+ var $frozen;
+
+ /**
+ * Label for the element
+ * @var string $label
+ */
+ var $label;
+
+ /**
+ * Whether element is required
+ * @var boolean $required
+ */
+ var $required;
+
+ /**
+ * Error associated with the element
+ * @var string $error
+ */
+ var $error;
+
+ /**
+ * Some information about element style
+ * @var string $style
+ */
+ var $style;
+
+ /**
+ * HTML for the element
+ * @var string $html
+ */
+ var $html;
+
+ /**
+ * If element is a group, the group separator
+ * @var mixed $separator
+ */
+ var $separator;
+
+ /**
+ * If element is a group, an array of subelements
+ * @var array $elements
+ */
+ var $elements;
+
+ function isType($type)
+ {
+ if($this->type == $type) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ function notFrozen()
+ {
+ if(!$this->frozen) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ function isButton()
+ {
+ if($this->type == "submit" || $this->type == "reset") {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ function outputStyle()
+ {
+ $filename = "styles/".$this->style.".html";
+ ob_start();
+ HTML_Template_Flexy::staticQuickTemplate($filename, $this);
+ $ret = ob_get_contents();
+ ob_end_clean();
+ return $ret;
+ }
+
+} // end class QuickformElement
+?> \ No newline at end of file
diff --git a/pearlib/HTML/QuickForm/Renderer/ObjectFlexy.php b/pearlib/HTML/QuickForm/Renderer/ObjectFlexy.php
new file mode 100644
index 00000000..b3a74486
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Renderer/ObjectFlexy.php
@@ -0,0 +1,413 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Ron McClain <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: ObjectFlexy.php,v 1.6 2004/06/25 17:20:32 ths Exp $
+
+require_once("HTML/QuickForm/Renderer/Object.php");
+
+/**
+ * @abstract Long Description
+ * A static renderer for HTML_Quickform. Makes a QuickFormFlexyObject
+ * from the form content suitable for use with a Flexy template
+ *
+ * Usage:
+ * $form =& new HTML_QuickForm('form', 'POST');
+ * $template =& new HTML_Template_Flexy();
+ * $renderer =& new HTML_QuickForm_Renderer_ObjectFlexy(&$template);
+ * $renderer->setHtmlTemplate("html.html");
+ * $renderer->setLabelTemplate("label.html");
+ * $form->accept($renderer);
+ * $view = new StdClass;
+ * $view->form = $renderer->toObject();
+ * $template->compile("mytemplate.html");
+ *
+ * @see QuickFormFlexyObject
+ *
+ * Based on the code for HTML_QuickForm_Renderer_ArraySmarty
+ *
+ * @public
+ */
+class HTML_QuickForm_Renderer_ObjectFlexy extends HTML_QuickForm_Renderer_Object {
+ /**
+ * HTML_Template_Flexy instance
+ * @var object $_flexy
+ */
+ var $_flexy;
+
+ /**
+ * Current element index
+ * @var integer $_elementIdx
+ */
+ var $_elementIdx;
+
+ /**
+ * The current element index inside a group
+ * @var integer $_groupElementIdx
+ */
+ var $_groupElementIdx = 0;
+
+ /**
+ * Name of template file for form html
+ * @var string $_html
+ * @see setRequiredTemplate()
+ */
+ var $_html = '';
+
+ /**
+ * Name of template file for form labels
+ * @var string $label
+ * @see setErrorTemplate()
+ */
+ var $label = '';
+
+ /**
+ * Class of the element objects, so you can add your own
+ * element methods
+ * @var string $_elementType
+ */
+ var $_elementType = 'QuickformFlexyElement';
+
+ /**
+ * Constructor
+ *
+ * @param $flexy object HTML_Template_Flexy instance
+ * @public
+ */
+ function HTML_QuickForm_Renderer_ObjectFlexy(&$flexy)
+ {
+ $this->HTML_QuickForm_Renderer_Object(true);
+ $this->_obj = new QuickformFlexyForm();
+ $this->_flexy =& $flexy;
+ } // end constructor
+
+ function renderHeader(&$header)
+ {
+ if($name = $header->getName()) {
+ $this->_obj->header->$name = $header->toHtml();
+ } else {
+ $this->_obj->header[$this->_sectionCount] = $header->toHtml();
+ }
+ $this->_currentSection = $this->_sectionCount++;
+ } // end func renderHeader
+
+ function startGroup(&$group, $required, $error)
+ {
+ parent::startGroup($group, $required, $error);
+ $this->_groupElementIdx = 1;
+ } //end func startGroup
+
+ /**
+ * Creates an object representing an element containing
+ * the key for storing this
+ *
+ * @private
+ * @param element object An HTML_QuickForm_element object
+ * @param required bool Whether an element is required
+ * @param error string Error associated with the element
+ * @return object
+ */
+ function _elementToObject(&$element, $required, $error)
+ {
+ $ret = parent::_elementToObject($element, $required, $error);
+ if($ret->type == 'group') {
+ $ret->html = $element->toHtml();
+ unset($ret->elements);
+ }
+ if(!empty($this->_label)) {
+ $this->_renderLabel($ret);
+ }
+
+ if(!empty($this->_html)) {
+ $this->_renderHtml($ret);
+ $ret->error = $error;
+ }
+
+ // Create an element key from the name
+ if (false !== ($pos = strpos($ret->name, '[')) || is_object($this->_currentGroup)) {
+ if (!$pos) {
+ $keys = '->{\'' . $ret->name . '\'}';
+ } else {
+ $keys = '->{\'' . str_replace(array('[', ']'), array('\'}->{\'', ''), $ret->name) . '\'}';
+ }
+ // special handling for elements in native groups
+ if (is_object($this->_currentGroup)) {
+ // skip unnamed group items unless radios: no name -> no static access
+ // identification: have the same key string as the parent group
+ if ($this->_currentGroup->keys == $keys && 'radio' != $ret->type) {
+ return false;
+ }
+ // reduce string of keys by remove leading group keys
+ if (0 === strpos($keys, $this->_currentGroup->keys)) {
+ $keys = substr_replace($keys, '', 0, strlen($this->_currentGroup->keys));
+ }
+ }
+ } elseif (0 == strlen($ret->name)) {
+ $keys = '->{\'element_' . $this->_elementIdx . '\'}';
+ } else {
+ $keys = '->{\'' . $ret->name . '\'}';
+ }
+ // for radios: add extra key from value
+ if ('radio' == $ret->type && '[]' != substr($keys, -2)) {
+ $keys .= '->{\'' . $ret->value . '\'}';
+ }
+ $ret->keys = $keys;
+ $this->_elementIdx++;
+ return $ret;
+ }
+
+ /**
+ * Stores an object representation of an element in the
+ * QuickformFormObject instance
+ *
+ * @private
+ * @param elObj object Object representation of an element
+ * @return void
+ */
+ function _storeObject($elObj)
+ {
+ if ($elObj) {
+ $keys = $elObj->keys;
+ unset($elObj->keys);
+ if(is_object($this->_currentGroup) && ('group' != $elObj->type)) {
+ $code = '$this->_currentGroup' . $keys . ' = $elObj;';
+ } else {
+ $code = '$this->_obj' . $keys . ' = $elObj;';
+ }
+ eval($code);
+ }
+ }
+
+ /**
+ * Set the filename of the template to render html elements.
+ * In your template, {html} is replaced by the unmodified html.
+ * If the element is required, {required} will be true.
+ * Eg.
+ * {if:error}
+ * <font color="red" size="1">{error:h}</font><br />
+ * {end:}
+ * {html:h}
+ *
+ * @public
+ * @param template string Filename of template
+ * @return void
+ */
+ function setHtmlTemplate($template)
+ {
+ $this->_html = $template;
+ }
+
+ /**
+ * Set the filename of the template to render form labels
+ * In your template, {label} is replaced by the unmodified label.
+ * {error} will be set to the error, if any. {required} will
+ * be true if this is a required field
+ * Eg.
+ * {if:required}
+ * <font color="orange" size="1">*</font>
+ * {end:}
+ * {label:h}
+ *
+ * @public
+ * @param template string Filename of template
+ * @return void
+ */
+ function setLabelTemplate($template)
+ {
+ $this->_label = $template;
+ }
+
+ function _renderLabel(&$ret)
+ {
+ $this->_flexy->compile($this->_label);
+ $ret->label = $this->_flexy->bufferedOutputObject($ret);
+ }
+
+ function _renderHtml(&$ret)
+ {
+ $this->_flexy->compile($this->_html);
+ $ret->html = $this->_flexy->bufferedOutputObject($ret);
+ }
+
+} // end class HTML_QuickForm_Renderer_ObjectFlexy
+
+/**
+ * @abstract Long Description
+ * This class represents the object passed to outputObject()
+ *
+ * Eg.
+ * {form.outputJavaScript():h}
+ * {form.outputHeader():h}
+ * <table>
+ * <tr>
+ * <td>{form.name.label:h}</td><td>{form.name.html:h}</td>
+ * </tr>
+ * </table>
+ * </form>
+ *
+ * @public
+ */
+class QuickformFlexyForm {
+ /**
+ * Whether the form has been frozen
+ * @var boolean $frozen
+ */
+ var $frozen;
+
+ /**
+ * Javascript for client-side validation
+ * @var string $javascript
+ */
+ var $javascript;
+
+ /**
+ * Attributes for form tag
+ * @var string $attributes
+ */
+ var $attributes;
+
+ /**
+ * Note about required elements
+ * @var string $requirednote
+ */
+ var $requirednote;
+
+ /**
+ * Collected html of all hidden variables
+ * @var string $hidden
+ */
+ var $hidden;
+
+ /**
+ * Set if there were validation errors.
+ * StdClass object with element names for keys and their
+ * error messages as values
+ * @var object $errors
+ */
+ var $errors;
+
+ /**
+ * Array of QuickformElementObject elements. If there are headers in the form
+ * this will be empty and the elements will be in the
+ * separate sections
+ * @var array $elements
+ */
+ var $elements;
+
+ /**
+ * Array of sections contained in the document
+ * @var array $sections
+ */
+ var $sections;
+
+ /**
+ * Output &lt;form&gt; header
+ * {form.outputHeader():h}
+ * @return string &lt;form attributes&gt;
+ */
+ function outputHeader()
+ {
+ $hdr = "<form " . $this->attributes . ">\n";
+ return $hdr;
+ }
+
+ /**
+ * Output form javascript
+ * {form.outputJavaScript():h}
+ * @return string Javascript
+ */
+ function outputJavaScript()
+ {
+ return $this->javascript;
+ }
+} // end class QuickformFlexyForm
+
+/**
+ * Convenience class describing a form element.
+ * The properties defined here will be available from
+ * your flexy templates by referencing
+ * {form.zip.label:h}, {form.zip.html:h}, etc.
+ */
+class QuickformFlexyElement {
+
+ /**
+ * Element name
+ * @var string $name
+ */
+ var $name;
+
+ /**
+ * Element value
+ * @var mixed $value
+ */
+ var $value;
+
+ /**
+ * Type of element
+ * @var string $type
+ */
+ var $type;
+
+ /**
+ * Whether the element is frozen
+ * @var boolean $frozen
+ */
+ var $frozen;
+
+ /**
+ * Label for the element
+ * @var string $label
+ */
+ var $label;
+
+ /**
+ * Whether element is required
+ * @var boolean $required
+ */
+ var $required;
+
+ /**
+ * Error associated with the element
+ * @var string $error
+ */
+ var $error;
+
+ /**
+ * Some information about element style
+ * @var string $style
+ */
+ var $style;
+
+ /**
+ * HTML for the element
+ * @var string $html
+ */
+ var $html;
+
+ /**
+ * If element is a group, the group separator
+ * @var mixed $separator
+ */
+ var $separator;
+
+ /**
+ * If element is a group, an array of subelements
+ * @var array $elements
+ */
+ var $elements;
+} // end class QuickformFlexyElement
+?> \ No newline at end of file
diff --git a/pearlib/HTML/QuickForm/Renderer/QuickHtml.php b/pearlib/HTML/QuickForm/Renderer/QuickHtml.php
new file mode 100644
index 00000000..d4792352
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Renderer/QuickHtml.php
@@ -0,0 +1,203 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Jason Rust <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: QuickHtml.php,v 1.1 2003/08/25 16:41:02 jrust Exp $
+
+require_once('HTML/QuickForm/Renderer/Default.php');
+
+/**
+ * A renderer that makes it quick and easy to create customized forms.
+ *
+ * This renderer has three main distinctives: an easy way to create
+ * custom-looking forms, the ability to separate the creation of form
+ * elements from their display, and being able to use QuickForm in
+ * widget-based template systems. See the online docs for more info.
+ * For a usage example see: docs/renderers/QuickHtml_example.php
+ *
+ * @access public
+ * @package QuickForm
+ */
+class HTML_QuickForm_Renderer_QuickHtml extends HTML_QuickForm_Renderer_Default {
+ // {{{ properties
+
+ /**
+ * The array of rendered elements
+ * @var array
+ */
+ var $renderedElements = array();
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * Constructor
+ *
+ * @access public
+ * @return void
+ */
+ function HTML_QuickForm_Renderer_QuickHtml()
+ {
+ $this->HTML_QuickForm_Renderer_Default();
+ // The default templates aren't used for this renderer
+ $this->clearAllTemplates();
+ } // end constructor
+
+ // }}}
+ // {{{ toHtml()
+
+ /**
+ * returns the HTML generated for the form
+ *
+ * @param string $data (optional) Any extra data to put before the end of the form
+ *
+ * @access public
+ * @return string
+ */
+ function toHtml($data = '')
+ {
+ // Render any elements that haven't been rendered explicitly by elementToHtml()
+ foreach (array_keys($this->renderedElements) as $key) {
+ if (!$this->renderedElements[$key]['rendered']) {
+ $this->renderedElements[$key]['rendered'] = true;
+ $data .= $this->renderedElements[$key]['html'] . "\n";
+ }
+ }
+
+ // Insert the extra data and form elements at the end of the form
+ $this->_html = str_replace('</form>', $data . "\n</form>", $this->_html);
+ return $this->_html;
+ } // end func toHtml
+
+ // }}}
+ // {{{ elementToHtml()
+
+ /**
+ * Gets the html for an element and marks it as rendered.
+ *
+ * @param string $elementName The element name
+ * @param string $elementValue (optional) The value of the element. This is only useful
+ * for elements that have the same name (i.e. radio and checkbox), but
+ * different values
+ *
+ * @access public
+ * @return string The html for the QuickForm element
+ */
+ function elementToHtml($elementName, $elementValue = null)
+ {
+ $elementKey = null;
+ // Find the key for the element
+ foreach ($this->renderedElements as $key => $data) {
+ if ($data['name'] == $elementName &&
+ // See if the value must match as well
+ (is_null($elementValue) ||
+ $data['value'] == $elementValue)) {
+ $elementKey = $key;
+ break;
+ }
+ }
+
+ if (is_null($elementKey)) {
+ $msg = is_null($elementValue) ? "Element $elementName does not exist." :
+ "Element $elementName with value of $elementValue does not exist.";
+ return PEAR::raiseError(null, QUICKFORM_UNREGISTERED_ELEMENT, null, E_USER_WARNING, $msg, 'HTML_QuickForm_Error', true);
+ } else {
+ if ($this->renderedElements[$elementKey]['rendered']) {
+ $msg = is_null($elementValue) ? "Element $elementName has already been rendered." :
+ "Element $elementName with value of $elementValue has already been rendered.";
+ return PEAR::raiseError(null, QUICKFORM_ERROR, null, E_USER_WARNING, $msg, 'HTML_QuickForm_Error', true);
+ } else {
+ $this->renderedElements[$elementKey]['rendered'] = true;
+ return $this->renderedElements[$elementKey]['html'];
+ }
+ }
+ } // end func elementToHtml
+
+ // }}}
+ // {{{ renderElement()
+
+ /**
+ * Gets the html for an element and adds it to the array by calling
+ * parent::renderElement()
+ *
+ * @param object An HTML_QuickForm_element object
+ * @param bool Whether an element is required
+ * @param string An error message associated with an element
+ *
+ * @access public
+ * @return mixed HTML string of element if $immediateRender is set, else we just add the
+ * html to the global _html string
+ */
+ function renderElement(&$element, $required, $error)
+ {
+ $this->_html = '';
+ parent::renderElement($element, $required, $error);
+ if (!$this->_inGroup) {
+ $this->renderedElements[] = array(
+ 'name' => $element->getName(),
+ 'value' => $element->getValue(),
+ 'html' => $this->_html,
+ 'rendered' => false);
+ }
+ $this->_html = '';
+ } // end func renderElement
+
+ // }}}
+ // {{{ renderHidden()
+
+ /**
+ * Gets the html for a hidden element and adds it to the array.
+ *
+ * @param object An HTML_QuickForm_hidden object being visited
+ * @access public
+ * @return void
+ */
+ function renderHidden(&$element)
+ {
+ $this->renderedElements[] = array(
+ 'name' => $element->getName(),
+ 'value' => $element->getValue(),
+ 'html' => $element->toHtml(),
+ 'rendered' => false);
+ } // end func renderHidden
+
+ // }}}
+ // {{{ finishGroup()
+
+ /**
+ * Gets the html for the group element and adds it to the array by calling
+ * parent::finishGroup()
+ *
+ * @param object An HTML_QuickForm_group object being visited
+ * @access public
+ * @return void
+ */
+ function finishGroup(&$group)
+ {
+ $this->_html = '';
+ parent::finishGroup($group);
+ $this->renderedElements[] = array(
+ 'name' => $group->getName(),
+ 'value' => $group->getValue(),
+ 'html' => $this->_html,
+ 'rendered' => false);
+ $this->_html = '';
+ } // end func finishGroup
+
+ // }}}
+} // end class HTML_QuickForm_Renderer_QuickHtml
+?>
diff --git a/pearlib/HTML/QuickForm/Rule.php b/pearlib/HTML/QuickForm/Rule.php
new file mode 100644
index 00000000..a301bc0e
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Rule.php
@@ -0,0 +1,67 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Rule.php,v 1.2 2003/11/03 16:08:22 avb Exp $
+
+class HTML_QuickForm_Rule
+{
+ /**
+ * Name of the rule to use in validate method
+ *
+ * This property is used in more global rules like Callback and Regex
+ * to determine which callback and which regex is to be used for validation
+ *
+ * @var string
+ * @access public
+ */
+ var $name;
+
+ /**
+ * Validates a value
+ *
+ * @access public
+ * @abstract
+ */
+ function validate($value)
+ {
+ return true;
+ }
+
+ /**
+ * Sets the rule name
+ *
+ * @access public
+ */
+ function setName($ruleName)
+ {
+ $this->name = $ruleName;
+ }
+
+ /**
+ * Returns the javascript test (the test should return true if the value is INVALID)
+ *
+ * @param mixed Options for the rule
+ * @access public
+ * @return array first element is code to setup validation, second is the check itself
+ */
+ function getValidationScript($options = null)
+ {
+ return array('', '');
+ }
+}
+?> \ No newline at end of file
diff --git a/pearlib/HTML/QuickForm/Rule/Callback.php b/pearlib/HTML/QuickForm/Rule/Callback.php
new file mode 100644
index 00000000..43f54a0a
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Rule/Callback.php
@@ -0,0 +1,113 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Callback.php,v 1.7 2003/11/11 12:41:13 avb Exp $
+
+require_once('HTML/QuickForm/Rule.php');
+
+/**
+* Validates values using callback functions or methods
+* @version 1.0
+*/
+class HTML_QuickForm_Rule_Callback extends HTML_QuickForm_Rule
+{
+ /**
+ * Array of callbacks
+ *
+ * Array is in the format:
+ * $_data['rulename'] = array('functionname', 'classname');
+ * If the callback is not a method, then the class name is not set.
+ *
+ * @var array
+ * @access private
+ */
+ var $_data = array();
+
+ /**
+ * Whether to use BC mode for specific rules
+ *
+ * Previous versions of QF passed element's name as a first parameter
+ * to validation functions, but not to validation methods. This behaviour
+ * is emulated if you are using 'function' as rule type when registering.
+ *
+ * @var array
+ * @access private
+ */
+ var $_BCMode = array();
+
+ /**
+ * Validates a value using a callback
+ *
+ * @param string $value Value to be checked
+ * @param mixed $options Options for callback
+ * @access public
+ * @return boolean true if value is valid
+ */
+ function validate($value, $options = null)
+ {
+ if (isset($this->_data[$this->name])) {
+ $callback = $this->_data[$this->name];
+ if (isset($callback[1])) {
+ return call_user_func(array($callback[1], $callback[0]), $value, $options);
+ } elseif ($this->_BCMode[$this->name]) {
+ return $callback[0]('', $value, $options);
+ } else {
+ return $callback[0]($value, $options);
+ }
+ } elseif (is_callable($options)) {
+ return call_user_func($options, $value);
+ } else {
+ return true;
+ }
+ } // end func validate
+
+ /**
+ * Adds new callbacks to the callbacks list
+ *
+ * @param string $name Name of rule
+ * @param string $callback Name of function or method
+ * @param string $class Name of class containing the method
+ * @param bool $BCMode Backwards compatibility mode
+ * @access public
+ */
+ function addData($name, $callback, $class = null, $BCMode = false)
+ {
+ if (!empty($class)) {
+ $this->_data[$name] = array($callback, $class);
+ } else {
+ $this->_data[$name] = array($callback);
+ }
+ $this->_BCMode[$name] = $BCMode;
+ } // end func addData
+
+
+ function getValidationScript($options = null)
+ {
+ if (isset($this->_data[$this->name])) {
+ $callback = $this->_data[$this->name][0];
+ $params = ($this->_BCMode[$this->name]? "'', {jsVar}": '{jsVar}') .
+ (isset($options)? ", '{$options}'": '');
+ } else {
+ $callback = is_array($options)? $options[1]: $options;
+ $params = '{jsVar}';
+ }
+ return array('', "{jsVar} != '' && !{$callback}({$params})");
+ } // end func getValidationScript
+
+} // end class HTML_QuickForm_Rule_Callback
+?> \ No newline at end of file
diff --git a/pearlib/HTML/QuickForm/Rule/Compare.php b/pearlib/HTML/QuickForm/Rule/Compare.php
new file mode 100644
index 00000000..a53e4dc1
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Rule/Compare.php
@@ -0,0 +1,95 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Alexey Borzov <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Compare.php,v 1.3 2003/11/03 16:08:24 avb Exp $
+
+require_once 'HTML/QuickForm/Rule.php';
+
+/**
+ * Rule to compare two form fields
+ *
+ * The most common usage for this is to ensure that the password
+ * confirmation field matches the password field
+ *
+ * @access public
+ * @package HTML_QuickForm
+ * @version $Revision: 1.3 $
+ */
+class HTML_QuickForm_Rule_Compare extends HTML_QuickForm_Rule
+{
+ /**
+ * Possible operators to use
+ * @var array
+ * @access private
+ */
+ var $_operators = array(
+ 'eq' => '==',
+ 'neq' => '!=',
+ 'gt' => '>',
+ 'gte' => '>=',
+ 'lt' => '<',
+ 'lte' => '<='
+ );
+
+
+ /**
+ * Returns the operator to use for comparing the values
+ *
+ * @access private
+ * @param string operator name
+ * @return string operator to use for validation
+ */
+ function _findOperator($name)
+ {
+ if (empty($name)) {
+ return '==';
+ } elseif (isset($this->_operators[$name])) {
+ return $this->_operators[$name];
+ } elseif (in_array($name, $this->_operators)) {
+ return $name;
+ } else {
+ return '==';
+ }
+ }
+
+
+ function validate($values, $operator = null)
+ {
+ $operator = $this->_findOperator($operator);
+ if ('==' != $operator && '!=' != $operator) {
+ $compareFn = create_function('$a, $b', 'return floatval($a) ' . $operator . ' floatval($b);');
+ } else {
+ $compareFn = create_function('$a, $b', 'return $a ' . $operator . ' $b;');
+ }
+
+ return $compareFn($values[0], $values[1]);
+ }
+
+
+ function getValidationScript($operator = null)
+ {
+ $operator = $this->_findOperator($operator);
+ if ('==' != $operator && '!=' != $operator) {
+ $check = "!(Number({jsVar}[0]) {$operator} Number({jsVar}[1]))";
+ } else {
+ $check = "!({jsVar}[0] {$operator} {jsVar}[1])";
+ }
+ return array('', "'' != {jsVar}[0] && {$check}");
+ }
+}
+?>
diff --git a/pearlib/HTML/QuickForm/Rule/Email.php b/pearlib/HTML/QuickForm/Rule/Email.php
new file mode 100644
index 00000000..d9cc4b1b
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Rule/Email.php
@@ -0,0 +1,61 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Email.php,v 1.4 2003/12/18 14:21:57 mansion Exp $
+
+require_once('HTML/QuickForm/Rule.php');
+
+/**
+* Email validation rule
+* @version 1.0
+*/
+class HTML_QuickForm_Rule_Email extends HTML_QuickForm_Rule
+{
+ var $regex = '/^((\"[^\"\f\n\r\t\v\b]+\")|([\w\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+(\.[\w\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+)*))@((\[(((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9])))\])|(((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9])))|((([A-Za-z0-9\-])+\.)+[A-Za-z\-]+))$/';
+
+ /**
+ * Validates an email address
+ *
+ * @param string $email Email address
+ * @param boolean $checkDomain True if dns check should be performed
+ * @access public
+ * @return boolean true if email is valid
+ */
+ function validate($email, $checkDomain = false)
+ {
+ if (preg_match($this->regex, $email)) {
+ if ($checkDomain && function_exists('checkdnsrr')) {
+ $tokens = explode('@', $email);
+ if (checkdnsrr($tokens[1], 'MX') || checkdnsrr($tokens[1], 'A')) {
+ return true;
+ }
+ return false;
+ }
+ return true;
+ }
+ return false;
+ } // end func validate
+
+
+ function getValidationScript($options = null)
+ {
+ return array(" var regex = " . $this->regex . ";\n", "{jsVar} != '' && !regex.test({jsVar})");
+ } // end func getValidationScript
+
+} // end class HTML_QuickForm_Rule_Email
+?> \ No newline at end of file
diff --git a/pearlib/HTML/QuickForm/Rule/Range.php b/pearlib/HTML/QuickForm/Rule/Range.php
new file mode 100644
index 00000000..af6ecbe4
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Rule/Range.php
@@ -0,0 +1,64 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Range.php,v 1.6 2003/11/03 16:08:24 avb Exp $
+
+require_once('HTML/QuickForm/Rule.php');
+
+/**
+* Validates values using range comparison
+* @version 1.0
+*/
+class HTML_QuickForm_Rule_Range extends HTML_QuickForm_Rule
+{
+ /**
+ * Validates a value using a range comparison
+ *
+ * @param string $value Value to be checked
+ * @param mixed $options Int for length, array for range
+ * @access public
+ * @return boolean true if value is valid
+ */
+ function validate($value, $options)
+ {
+ $length = strlen($value);
+ switch ($this->name) {
+ case 'minlength': return ($length >= $options);
+ case 'maxlength': return ($length <= $options);
+ default: return ($length >= $options[0] && $length <= $options[1]);
+ }
+ } // end func validate
+
+
+ function getValidationScript($options = null)
+ {
+ switch ($this->name) {
+ case 'minlength':
+ $test = '{jsVar}.length < '.$options;
+ break;
+ case 'maxlength':
+ $test = '{jsVar}.length > '.$options;
+ break;
+ default:
+ $test = '({jsVar}.length < '.$options[0].' || {jsVar}.length > '.$options[1].')';
+ }
+ return array('', "{jsVar} != '' && {$test}");
+ } // end func getValidationScript
+
+} // end class HTML_QuickForm_Rule_Range
+?> \ No newline at end of file
diff --git a/pearlib/HTML/QuickForm/Rule/Regex.php b/pearlib/HTML/QuickForm/Rule/Regex.php
new file mode 100644
index 00000000..106084dc
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Rule/Regex.php
@@ -0,0 +1,89 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Regex.php,v 1.3 2003/11/03 16:08:24 avb Exp $
+
+require_once('HTML/QuickForm/Rule.php');
+
+/**
+* Validates values using regular expressions
+* @version 1.0
+*/
+class HTML_QuickForm_Rule_Regex extends HTML_QuickForm_Rule
+{
+ /**
+ * Array of regular expressions
+ *
+ * Array is in the format:
+ * $_data['rulename'] = 'pattern';
+ *
+ * @var array
+ * @access private
+ */
+ var $_data = array(
+ 'lettersonly' => '/^[a-zA-Z]+$/',
+ 'alphanumeric' => '/^[a-zA-Z0-9]+$/',
+ 'numeric' => '/(^-?\d\d*\.\d*$)|(^-?\d\d*$)|(^-?\.\d\d*$)/',
+ 'nopunctuation' => '/^[^().\/\*\^\?#!@$%+=,\"\'><~\[\]{}]+$/',
+ 'nonzero' => '/^-?[1-9][0-9]*/'
+ );
+
+ /**
+ * Validates a value using a regular expression
+ *
+ * @param string $value Value to be checked
+ * @param string $regex Regular expression
+ * @access public
+ * @return boolean true if value is valid
+ */
+ function validate($value, $regex = null)
+ {
+ if (isset($this->_data[$this->name])) {
+ if (!preg_match($this->_data[$this->name], $value)) {
+ return false;
+ }
+ } else {
+ if (!preg_match($regex, $value)) {
+ return false;
+ }
+ }
+ return true;
+ } // end func validate
+
+ /**
+ * Adds new regular expressions to the list
+ *
+ * @param string $name Name of rule
+ * @param string $pattern Regular expression pattern
+ * @access public
+ */
+ function addData($name, $pattern)
+ {
+ $this->_data[$name] = $pattern;
+ } // end func addData
+
+
+ function getValidationScript($options = null)
+ {
+ $regex = isset($this->_data[$this->name]) ? $this->_data[$this->name] : $options;
+
+ return array(" var regex = " . $regex . ";\n", "{jsVar} != '' && !regex.test({jsVar})");
+ } // end func getValidationScript
+
+} // end class HTML_QuickForm_Rule_Regex
+?> \ No newline at end of file
diff --git a/pearlib/HTML/QuickForm/Rule/Required.php b/pearlib/HTML/QuickForm/Rule/Required.php
new file mode 100644
index 00000000..b1aa0013
--- /dev/null
+++ b/pearlib/HTML/QuickForm/Rule/Required.php
@@ -0,0 +1,52 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Required.php,v 1.3 2003/11/03 16:08:24 avb Exp $
+
+require_once('HTML/QuickForm/Rule.php');
+
+/**
+* Required elements validation
+* @version 1.0
+*/
+class HTML_QuickForm_Rule_Required extends HTML_QuickForm_Rule
+{
+ /**
+ * Checks if an element is empty
+ *
+ * @param string $value Value to check
+ * @param mixed $options Not used yet
+ * @access public
+ * @return boolean true if value is not empty
+ */
+ function validate($value, $options = null)
+ {
+ if ($value == '') {
+ return false;
+ }
+ return true;
+ } // end func validate
+
+
+ function getValidationScript($options = null)
+ {
+ return array('', "{jsVar} == ''");
+ } // end func getValidationScript
+
+} // end class HTML_QuickForm_Rule_Required
+?>
diff --git a/pearlib/HTML/QuickForm/RuleRegistry.php b/pearlib/HTML/QuickForm/RuleRegistry.php
new file mode 100644
index 00000000..7c791309
--- /dev/null
+++ b/pearlib/HTML/QuickForm/RuleRegistry.php
@@ -0,0 +1,317 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Adam Daniel <[email protected]> |
+// | Alexey Borzov <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: RuleRegistry.php,v 1.12 2004/02/28 15:47:22 avb Exp $
+
+/**
+* Registers rule objects and uses them for validation
+*
+*/
+class HTML_QuickForm_RuleRegistry
+{
+ /**
+ * Array containing references to used rules
+ * @var array
+ * @access private
+ */
+ var $_rules = array();
+
+
+ /**
+ * Returns a singleton of HTML_QuickForm_RuleRegistry
+ *
+ * Usually, only one RuleRegistry object is needed, this is the reason
+ * why it is recommended to use this method to get the validation object.
+ *
+ * @access public
+ * @static
+ * @return object Reference to the HTML_QuickForm_RuleRegistry singleton
+ */
+ function &singleton()
+ {
+ static $obj;
+ if (!isset($obj)) {
+ $obj = new HTML_QuickForm_RuleRegistry();
+ }
+ return $obj;
+ } // end func singleton
+
+ /**
+ * Registers a new validation rule
+ *
+ * In order to use a custom rule in your form, you need to register it
+ * first. For regular expressions, one can directly use the 'regex' type
+ * rule in addRule(), this is faster than registering the rule.
+ *
+ * Functions and methods can be registered. Use the 'function' type.
+ * When registering a method, specify the class name as second parameter.
+ *
+ * You can also register an HTML_QuickForm_Rule subclass with its own
+ * validate() method.
+ *
+ * @param string $ruleName Name of validation rule
+ * @param string $type Either: 'regex', 'function' or null
+ * @param string $data1 Name of function, regular expression or
+ * HTML_QuickForm_Rule object class name
+ * @param string $data2 Object parent of above function or HTML_QuickForm_Rule file path
+ * @access public
+ * @return void
+ */
+ function registerRule($ruleName, $type, $data1, $data2 = null)
+ {
+ $type = strtolower($type);
+ if ($type == 'regex') {
+ // Regular expression
+ $rule =& $this->getRule('regex');
+ $rule->addData($ruleName, $data1);
+ $GLOBALS['_HTML_QuickForm_registered_rules'][$ruleName] = $GLOBALS['_HTML_QuickForm_registered_rules']['regex'];
+
+ } elseif ($type == 'function' || $type == 'callback') {
+ // Callback function
+ $rule =& $this->getRule('callback');
+ $rule->addData($ruleName, $data1, $data2, 'function' == $type);
+ $GLOBALS['_HTML_QuickForm_registered_rules'][$ruleName] = $GLOBALS['_HTML_QuickForm_registered_rules']['callback'];
+
+ } elseif (is_object($data1)) {
+ // An instance of HTML_QuickForm_Rule
+ $this->_rules[strtolower(get_class($data1))] = $data1;
+ $GLOBALS['_HTML_QuickForm_registered_rules'][$ruleName] = array(strtolower(get_class($data1)), null);
+
+ } else {
+ // Rule class name
+ $GLOBALS['_HTML_QuickForm_registered_rules'][$ruleName] = array(strtolower($data1), $data2);
+ }
+ } // end func registerRule
+
+ /**
+ * Returns a reference to the requested rule object
+ *
+ * @param string $ruleName Name of the requested rule
+ * @access public
+ * @return object
+ */
+ function &getRule($ruleName)
+ {
+ list($class, $path) = $GLOBALS['_HTML_QuickForm_registered_rules'][$ruleName];
+
+ if (!isset($this->_rules[$class])) {
+ if (!empty($path)) {
+ include_once($path);
+ }
+ $this->_rules[$class] =& new $class();
+ }
+ $this->_rules[$class]->setName($ruleName);
+ return $this->_rules[$class];
+ } // end func getRule
+
+ /**
+ * Performs validation on the given values
+ *
+ * @param string $ruleName Name of the rule to be used
+ * @param mixed $values Can be a scalar or an array of values
+ * to be validated
+ * @param mixed $options Options used by the rule
+ * @param mixed $multiple Whether to validate an array of values altogether
+ * @access public
+ * @return mixed true if no error found, int of valid values (when an array of values is given) or false if error
+ */
+ function validate($ruleName, $values, $options = null, $multiple = false)
+ {
+ $rule =& $this->getRule($ruleName);
+
+ if (is_array($values) && !$multiple) {
+ $result = 0;
+ foreach ($values as $value) {
+ if ($rule->validate($value, $options) === true) {
+ $result++;
+ }
+ }
+ return ($result == 0) ? false : $result;
+ } else {
+ return $rule->validate($values, $options);
+ }
+ } // end func validate
+
+ /**
+ * Returns the validation test in javascript code
+ *
+ * @param mixed Element(s) the rule applies to
+ * @param string Element name, in case $element is not array
+ * @param array Rule data
+ * @access public
+ * @return string JavaScript for the rule
+ */
+ function getValidationScript(&$element, $elementName, $ruleData)
+ {
+ $reset = (isset($ruleData['reset'])) ? $ruleData['reset'] : false;
+ $rule =& $this->getRule($ruleData['type']);
+ if (!is_array($element)) {
+ list($jsValue, $jsReset) = $this->_getJsValue($element, $elementName, $reset, null);
+ } else {
+ $jsValue = " value = new Array();\n";
+ $jsReset = '';
+ for ($i = 0; $i < count($element); $i++) {
+ list($tmp_value, $tmp_reset) = $this->_getJsValue($element[$i], $element[$i]->getName(), $reset, $i);
+ $jsValue .= "\n" . $tmp_value;
+ $jsReset .= $tmp_reset;
+ }
+ }
+ $jsField = isset($ruleData['group'])? $ruleData['group']: $elementName;
+ list ($jsPrefix, $jsCheck) = $rule->getValidationScript($ruleData['format']);
+ if (!isset($ruleData['howmany'])) {
+ $js = $jsValue . "\n" . $jsPrefix .
+ " if (" . str_replace('{jsVar}', 'value', $jsCheck) . " && !errFlag['{$jsField}']) {\n" .
+ " errFlag['{$jsField}'] = true;\n" .
+ " _qfMsg = _qfMsg + '\\n - {$ruleData['message']}';\n" .
+ $jsReset .
+ " }\n";
+ } else {
+ $js = $jsValue . "\n" . $jsPrefix .
+ " var res = 0;\n" .
+ " for (var i = 0; i < value.length; i++) {\n" .
+ " if (!(" . str_replace('{jsVar}', 'value[i]', $jsCheck) . ")) {\n" .
+ " res++;\n" .
+ " }\n" .
+ " }\n" .
+ " if (res < {$ruleData['howmany']} && !errFlag['{$jsField}']) {\n" .
+ " errFlag['{$jsField}'] = true;\n" .
+ " _qfMsg = _qfMsg + '\\n - {$ruleData['message']}';\n" .
+ $jsReset .
+ " }\n";
+ }
+ return $js;
+ } // end func getValidationScript
+
+
+ /**
+ * Returns JavaScript to get and to reset the element's value
+ *
+ * @access private
+ * @param object HTML_QuickForm_element element being processed
+ * @param string element's name
+ * @param bool whether to generate JavaScript to reset the value
+ * @param integer value's index in the array (only used for multielement rules)
+ * @return array first item is value javascript, second is reset
+ */
+ function _getJsValue(&$element, $elementName, $reset = false, $index = null)
+ {
+ $jsIndex = isset($index)? '[' . $index . ']': '';
+ $tmp_reset = $reset? " var field = frm.elements['$elementName'];\n": '';
+ if (is_a($element, 'html_quickform_group')) {
+ $value = " var {$elementName}Elements = '::";
+ for ($i = 0, $count = count($element->_elements); $i < $count; $i++) {
+ $value .= $element->getElementName($i) . '::';
+ }
+ $value .=
+ "';\n" .
+ " value{$jsIndex} = new Array();\n" .
+ " var valueIdx = 0;\n" .
+ " for (var i = 0; i < frm.elements.length; i++) {\n" .
+ " var _element = frm.elements[i];\n" .
+ " if ({$elementName}Elements.indexOf('::' + _element.name + '::') >= 0) {\n" .
+ " switch (_element.type) {\n" .
+ " case 'checkbox':\n" .
+ " case 'radio':\n" .
+ " if (_element.checked) {\n" .
+ " value{$jsIndex}[valueIdx++] = _element.value;\n" .
+ " }\n" .
+ " break;\n" .
+ " case 'select':\n" .
+ " if (-1 != _element.selectedIndex) {\n" .
+ " value{$jsIndex}[valueIdx++] = _element.options[_element.selectedIndex].value;\n" .
+ " }\n" .
+ " break;\n" .
+ " default:\n" .
+ " value{$jsIndex}[valueIdx++] = _element.value;\n" .
+ " }\n" .
+ " }\n" .
+ " }\n";
+ if ($reset) {
+ $tmp_reset =
+ " for (var i = 0; i < frm.elements.length; i++) {\n" .
+ " var _element = frm.elements[i];\n" .
+ " if ({$elementName}Elements.indexOf('::' + _element.name + '::') >= 0) {\n" .
+ " switch (_element.type) {\n" .
+ " case 'checkbox':\n" .
+ " case 'radio':\n" .
+ " _element.checked = _element.defaultChecked;\n" .
+ " break;\n" .
+ " case 'select':\n" .
+ " for (var j = 0; j < _element.options.length; j++) {\n" .
+ " _element.options[j].selected = _element.options[j].defaultSelected;\n" .
+ " }\n" .
+ " break;\n" .
+ " default:\n" .
+ " _element.value = _element.defaultValue;\n" .
+ " }\n" .
+ " }\n" .
+ " }\n";
+ }
+
+ } elseif ($element->getType() == 'select') {
+ if ($element->getMultiple()) {
+ $elementName .= '[]';
+ $value =
+ " value{$jsIndex} = new Array();\n" .
+ " var valueIdx = 0;\n" .
+ " for (var i = 0; i < frm.elements['{$elementName}'].options.length; i++) {\n" .
+ " if (frm.elements['{$elementName}'].options[i].selected) {\n" .
+ " value{$jsIndex}[valueIdx++] = frm.elements['{$elementName}'].options[i].value;\n" .
+ " }\n" .
+ " }\n";
+ } else {
+ $value = " value{$jsIndex} = frm.elements['{$elementName}'].options[frm.elements['{$elementName}'].selectedIndex].value;\n";
+ }
+ if ($reset) {
+ $tmp_reset .=
+ " for (var i = 0; i < field.options.length; i++) {\n" .
+ " field.options[i].selected = field.options[i].defaultSelected;\n" .
+ " }\n";
+ }
+
+ } elseif ($element->getType() == 'checkbox') {
+ $value = " if (frm.elements['$elementName'].checked) {\n" .
+ " value{$jsIndex} = '1';\n" .
+ " } else {\n" .
+ " value{$jsIndex} = '';\n" .
+ " }";
+ $tmp_reset .= ($reset) ? " field.checked = field.defaultChecked;\n" : '';
+
+ } elseif ($element->getType() == 'radio') {
+ $value = " value{$jsIndex} = '';\n" .
+ " for (var i = 0; i < frm.elements['$elementName'].length; i++) {\n" .
+ " if (frm.elements['$elementName'][i].checked) {\n" .
+ " value{$jsIndex} = frm.elements['$elementName'][i].value;\n" .
+ " }\n" .
+ " }";
+ if ($reset) {
+ $tmp_reset .= " for (var i = 0; i < field.length; i++) {\n" .
+ " field[i].checked = field[i].defaultChecked;\n" .
+ " }";
+ }
+
+ } else {
+ $value = " value{$jsIndex} = frm.elements['$elementName'].value;";
+ $tmp_reset .= ($reset) ? " field.value = field.defaultValue;\n" : '';
+ }
+ return array($value, $tmp_reset);
+ }
+} // end class HTML_QuickForm_RuleRegistry
+?>
diff --git a/pearlib/HTML/QuickForm/advcheckbox.php b/pearlib/HTML/QuickForm/advcheckbox.php
new file mode 100644
index 00000000..2fb427bc
--- /dev/null
+++ b/pearlib/HTML/QuickForm/advcheckbox.php
@@ -0,0 +1,279 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Adam Daniel <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: advcheckbox.php,v 1.14 2004/04/19 11:40:01 avb Exp $
+
+require_once('HTML/QuickForm/checkbox.php');
+
+/**
+ * HTML class for an advanced checkbox type field
+ *
+ * Basically this fixes a problem that HTML has had
+ * where checkboxes can only pass a single value (the
+ * value of the checkbox when checked). A value for when
+ * the checkbox is not checked cannot be passed, and
+ * furthermore the checkbox variable doesn't even exist if
+ * the checkbox was submitted unchecked.
+ *
+ * It works by creating a hidden field with the passed-in name
+ * and creating the checkbox with no name, but with a javascript
+ * onclick which sets the value of the hidden field.
+ *
+ * @author Jason Rust <[email protected]>
+ * @since 2.0
+ * @access public
+ */
+class HTML_QuickForm_advcheckbox extends HTML_QuickForm_checkbox
+{
+ // {{{ properties
+
+ /**
+ * The values passed by the hidden elment
+ *
+ * @var array
+ * @access private
+ */
+ var $_values = null;
+
+ /**
+ * The default value
+ *
+ * @var boolean
+ * @access private
+ */
+ var $_currentValue = null;
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ *
+ * @param string $elementName (optional)Input field name attribute
+ * @param string $elementLabel (optional)Input field label
+ * @param string $text (optional)Text to put after the checkbox
+ * @param mixed $attributes (optional)Either a typical HTML attribute string
+ * or an associative array
+ * @param mixed $values (optional)Values to pass if checked or not checked
+ *
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function HTML_QuickForm_advcheckbox($elementName=null, $elementLabel=null, $text=null, $attributes=null, $values=null)
+ {
+ $this->HTML_QuickForm_checkbox($elementName, $elementLabel, $text, $attributes);
+ $this->setValues($values);
+ } //end constructor
+
+ // }}}
+ // {{{ getPrivateName()
+
+ /**
+ * Gets the pribate name for the element
+ *
+ * @param string $elementName The element name to make private
+ *
+ * @access public
+ * @return string
+ */
+ function getPrivateName($elementName)
+ {
+ return '__'.$elementName;
+ }
+
+ // }}}
+ // {{{ getOnclickJs()
+
+ /**
+ * Create the javascript for the onclick event which will
+ * set the value of the hidden field
+ *
+ * @param string $elementName The element name
+ *
+ * @access public
+ * @return string
+ */
+ function getOnclickJs($elementName)
+ {
+ $onclickJs = 'if (this.checked) { this.form[\''.$elementName.'\'].value=\''.addcslashes($this->_values[1], '\'').'\'; }';
+ $onclickJs .= 'else { this.form[\''.$elementName.'\'].value=\''.addcslashes($this->_values[0], '\'').'\'; }';
+ return $onclickJs;
+ }
+
+ // }}}
+ // {{{ setValues()
+
+ /**
+ * Sets the values used by the hidden element
+ *
+ * @param mixed $values The values, either a string or an array
+ *
+ * @access public
+ * @return void
+ */
+ function setValues($values)
+ {
+ if (empty($values)) {
+ // give it default checkbox behavior
+ $this->_values = array('', 1);
+ } elseif (is_scalar($values)) {
+ // if it's string, then assume the value to
+ // be passed is for when the element is checked
+ $this->_values = array('', $values);
+ } else {
+ $this->_values = $values;
+ }
+ $this->setChecked($this->_currentValue == $this->_values[1]);
+ }
+
+ // }}}
+ // {{{ setValue()
+
+ /**
+ * Sets the element's value
+ *
+ * @param mixed Element's value
+ * @access public
+ */
+ function setValue($value)
+ {
+ $this->setChecked(isset($this->_values[1]) && $value == $this->_values[1]);
+ $this->_currentValue = $value;
+ }
+
+ // }}}
+ // {{{ getValue()
+
+ /**
+ * Returns the element's value
+ *
+ * @access public
+ * @return mixed
+ */
+ function getValue()
+ {
+ if (is_array($this->_values)) {
+ return $this->_values[$this->getChecked()? 1: 0];
+ } else {
+ return null;
+ }
+ }
+
+ // }}}
+ // {{{ toHtml()
+
+ /**
+ * Returns the checkbox element in HTML
+ * and the additional hidden element in HTML
+ *
+ * @access public
+ * @return string
+ */
+ function toHtml()
+ {
+ if ($this->_flagFrozen) {
+ return parent::toHtml();
+ } else {
+ $oldName = $this->getName();
+ $oldJs = $this->getAttribute('onclick');
+ $this->updateAttributes(array(
+ 'name' => $this->getPrivateName($oldName),
+ 'onclick' => $this->getOnclickJs($oldName) . ' ' . $oldJs
+ ));
+ $html = parent::toHtml() . '<input type="hidden" name="' . $oldName .
+ '" value="' . $this->getValue() . '" />';
+ // revert the name and JS, in case this method will be called once more
+ $this->updateAttributes(array(
+ 'name' => $oldName,
+ 'onclick' => $oldJs
+ ));
+ return $html;
+ }
+ } //end func toHtml
+
+ // }}}
+ // {{{ getFrozenHtml()
+
+ /**
+ * Unlike checkbox, this has to append a hidden input in both
+ * checked and non-checked states
+ */
+ function getFrozenHtml()
+ {
+ return ($this->getChecked()? '<tt>[x]</tt>': '<tt>[ ]</tt>') .
+ $this->_getPersistantData();
+ }
+
+ // }}}
+ // {{{ onQuickFormEvent()
+
+ /**
+ * Called by HTML_QuickForm whenever form event is made on this element
+ *
+ * @param string $event Name of event
+ * @param mixed $arg event arguments
+ * @param object $caller calling object
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function onQuickFormEvent($event, $arg, &$caller)
+ {
+ switch ($event) {
+ case 'updateValue':
+ // constant values override both default and submitted ones
+ // default values are overriden by submitted
+ $value = $this->_findValue($caller->_constantValues);
+ if (null === $value) {
+ $value = $this->_findValue($caller->_submitValues);
+ if (null === $value) {
+ $value = $this->_findValue($caller->_defaultValues);
+ }
+ }
+ if (null !== $value) {
+ $this->setValue($value);
+ }
+ break;
+ default:
+ parent::onQuickFormEvent($event, $arg, $caller);
+ }
+ return true;
+ } // end func onQuickFormLoad
+
+ // }}}
+ // {{{ exportValue()
+
+ /**
+ * This element has a value even if it is not checked, thus we override
+ * checkbox's behaviour here
+ */
+ function exportValue(&$submitValues, $assoc)
+ {
+ $value = $this->_findValue($submitValues);
+ if (null === $value) {
+ $value = $this->getValue();
+ } elseif (is_array($this->_values) && ($value != $this->_values[0]) && ($value != $this->_values[1])) {
+ $value = null;
+ }
+ return $this->_prepareValue($value, $assoc);
+ }
+ // }}}
+} //end class HTML_QuickForm_advcheckbox
+?>
diff --git a/pearlib/HTML/QuickForm/autocomplete.php b/pearlib/HTML/QuickForm/autocomplete.php
new file mode 100644
index 00000000..f732be31
--- /dev/null
+++ b/pearlib/HTML/QuickForm/autocomplete.php
@@ -0,0 +1,240 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Matteo Di Giovinazzo <[email protected]> |
+// | |
+// | For the JavaScript code thanks to Martin Honnen and |
+// | Nicholas C. Zakas |
+// | See: |
+// | http://www.faqts.com/knowledge_base/view.phtml/aid/13562 |
+// | and |
+// | http://www.sitepoint.com/article/1220 |
+// +----------------------------------------------------------------------+
+//
+// $Id: autocomplete.php,v 1.5 2004/10/09 19:24:32 avb Exp $
+
+
+require_once("HTML/QuickForm/text.php");
+
+
+/**
+ * Class to dynamically create an HTML input text element that
+ * at every keypressed javascript event, check in an array of options
+ * if there's a match and autocomplete the text in case of match.
+ *
+ * Ex:
+ * $autocomplete =& $form->addElement('autocomplete', 'fruit', 'Favourite fruit:');
+ * $options = array("Apple", "Orange", "Pear", "Strawberry");
+ * $autocomplete->setOptions($options);
+ *
+ * @author Matteo Di Giovinazzo <[email protected]>
+ */
+class HTML_QuickForm_autocomplete extends HTML_QuickForm_text
+{
+ // {{{ properties
+
+ /**
+ * Options for the autocomplete input text element
+ *
+ * @var array
+ * @access private
+ */
+ var $_options = array();
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ *
+ * @param string $elementName (optional)Input field name attribute
+ * @param string $elementLabel (optional)Input field label in form
+ * @param array $options (optional)Autocomplete options
+ * @param mixed $attributes (optional)Either a typical HTML attribute string
+ * or an associative array. Date format is passed along the attributes.
+ * @access public
+ * @return void
+ */
+ function HTML_QuickForm_autocomplete($elementName = null, $elementLabel = null, $options = null, $attributes = null)
+ {
+ $this->HTML_QuickForm_text($elementName, $elementLabel, $attributes);
+ $this->_persistantFreeze = true;
+ $this->_type = 'autocomplete';
+ if (isset($options)) {
+ $this->setOptions($options);
+ }
+ } //end constructor
+
+ // }}}
+ // {{{ setOptions()
+
+ /**
+ * Sets the options for the autocomplete input text element
+ *
+ * @param array $options Array of options for the autocomplete input text element
+ * @access public
+ * @return void
+ */
+ function setOptions($options)
+ {
+ $this->_options = array_values($options);
+ } // end func setOptions
+
+ // }}}
+ // {{{ toHtml()
+
+ /**
+ * Returns Html for the autocomplete input text element
+ *
+ * @access public
+ * @return string
+ */
+ function toHtml()
+ {
+ // prevent problems with grouped elements
+ $arrayName = str_replace(array('[', ']'), array('__', ''), $this->getName()) . '_values';
+
+ $this->updateAttributes(array(
+ 'onkeypress' => 'return autocomplete(this, event, ' . $arrayName . ');'
+ ));
+ if ($this->_flagFrozen) {
+ $js = '';
+ } else {
+ $js = "<script type=\"text/javascript\">\n//<![CDATA[\n";
+ if (!defined('HTML_QUICKFORM_AUTOCOMPLETE_EXISTS')) {
+ $js .= <<<EOS
+
+/* begin javascript for autocomplete */
+function setSelectionRange(input, selectionStart, selectionEnd) {
+ if (input.setSelectionRange) {
+ input.setSelectionRange(selectionStart, selectionEnd);
+ }
+ else if (input.createTextRange) {
+ var range = input.createTextRange();
+ range.collapse(true);
+ range.moveEnd("character", selectionEnd);
+ range.moveStart("character", selectionStart);
+ range.select();
+ }
+ input.focus();
+}
+
+function setCaretToPosition(input, position) {
+ setSelectionRange(input, position, position);
+}
+
+function replaceSelection (input, replaceString) {
+ var len = replaceString.length;
+ if (input.setSelectionRange) {
+ var selectionStart = input.selectionStart;
+ var selectionEnd = input.selectionEnd;
+
+ input.value = input.value.substring(0, selectionStart) + replaceString + input.value.substring(selectionEnd);
+ input.selectionStart = selectionStart + len;
+ input.selectionEnd = selectionStart + len;
+ }
+ else if (document.selection) {
+ var range = document.selection.createRange();
+ var saved_range = range.duplicate();
+
+ if (range.parentElement() == input) {
+ range.text = replaceString;
+ range.moveEnd("character", saved_range.selectionStart + len);
+ range.moveStart("character", saved_range.selectionStart + len);
+ range.select();
+ }
+ }
+ input.focus();
+}
+
+
+function autocompleteMatch (text, values) {
+ for (var i = 0; i < values.length; i++) {
+ if (values[i].toUpperCase().indexOf(text.toUpperCase()) == 0) {
+ return values[i];
+ }
+ }
+
+ return null;
+}
+
+function autocomplete(textbox, event, values) {
+ if (textbox.setSelectionRange || textbox.createTextRange) {
+ switch (event.keyCode) {
+ case 38: // up arrow
+ case 40: // down arrow
+ case 37: // left arrow
+ case 39: // right arrow
+ case 33: // page up
+ case 34: // page down
+ case 36: // home
+ case 35: // end
+ case 13: // enter
+ case 9: // tab
+ case 27: // esc
+ case 16: // shift
+ case 17: // ctrl
+ case 18: // alt
+ case 20: // caps lock
+ case 8: // backspace
+ case 46: // delete
+ return true;
+ break;
+
+ default:
+ var c = String.fromCharCode(
+ (event.charCode == undefined) ? event.keyCode : event.charCode
+ );
+ replaceSelection(textbox, c);
+ sMatch = autocompleteMatch(textbox.value, values);
+ var len = textbox.value.length;
+
+ if (sMatch != null) {
+ textbox.value = sMatch;
+ setSelectionRange(textbox, len, textbox.value.length);
+ }
+ return false;
+ }
+ }
+ else {
+ return true;
+ }
+}
+/* end javascript for autocomplete */
+
+EOS;
+ define('HTML_QUICKFORM_AUTOCOMPLETE_EXISTS', true);
+ }
+ $jsEscape = array(
+ "\r" => '\r',
+ "\n" => '\n',
+ "\t" => '\t',
+ "'" => "\\'",
+ '"' => '\"',
+ '\\' => '\\\\'
+ );
+
+ $js .= 'var ' . $arrayName . " = new Array();\n";
+ for ($i = 0; $i < count($this->_options); $i++) {
+ $js .= $arrayName . '[' . $i . "] = '" . strtr($this->_options[$i], $jsEscape) . "';\n";
+ }
+ $js .= "//]]>\n</script>";
+ }
+ return $js . parent::toHtml();
+ }// end func toHtml
+
+ // }}}
+} // end class HTML_QuickForm_autocomplete
+?>
diff --git a/pearlib/HTML/QuickForm/button.php b/pearlib/HTML/QuickForm/button.php
new file mode 100644
index 00000000..16460087
--- /dev/null
+++ b/pearlib/HTML/QuickForm/button.php
@@ -0,0 +1,73 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Adam Daniel <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: button.php,v 1.4 2003/06/18 19:36:20 avb Exp $
+
+require_once("HTML/QuickForm/input.php");
+
+/**
+ * HTML class for a button type element
+ *
+ * @author Adam Daniel <[email protected]>
+ * @author Bertrand Mansion <[email protected]>
+ * @version 1.1
+ * @since PHP4.04pl1
+ * @access public
+ */
+class HTML_QuickForm_button extends HTML_QuickForm_input
+{
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ *
+ * @param string $elementName (optional)Input field name attribute
+ * @param string $value (optional)Input field value
+ * @param mixed $attributes (optional)Either a typical HTML attribute string
+ * or an associative array
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function HTML_QuickForm_button($elementName=null, $value=null, $attributes=null)
+ {
+ HTML_QuickForm_input::HTML_QuickForm_input($elementName, null, $attributes);
+ $this->_persistantFreeze = false;
+ $this->setValue($value);
+ $this->setType('button');
+ } //end constructor
+
+ // }}}
+ // {{{ freeze()
+
+ /**
+ * Freeze the element so that only its value is returned
+ *
+ * @access public
+ * @return void
+ */
+ function freeze()
+ {
+ return false;
+ } //end func freeze
+
+ // }}}
+
+} //end class HTML_QuickForm_button
+?>
diff --git a/pearlib/HTML/QuickForm/checkbox.php b/pearlib/HTML/QuickForm/checkbox.php
new file mode 100644
index 00000000..fa230279
--- /dev/null
+++ b/pearlib/HTML/QuickForm/checkbox.php
@@ -0,0 +1,268 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Adam Daniel <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: checkbox.php,v 1.19 2004/02/28 22:10:16 avb Exp $
+
+require_once("HTML/QuickForm/input.php");
+
+/**
+ * HTML class for a checkbox type field
+ *
+ * @author Adam Daniel <[email protected]>
+ * @author Bertrand Mansion <[email protected]>
+ * @version 1.1
+ * @since PHP4.04pl1
+ * @access public
+ */
+class HTML_QuickForm_checkbox extends HTML_QuickForm_input
+{
+ // {{{ properties
+
+ /**
+ * Checkbox display text
+ * @var string
+ * @since 1.1
+ * @access private
+ */
+ var $_text = '';
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ *
+ * @param string $elementName (optional)Input field name attribute
+ * @param string $elementLabel (optional)Input field value
+ * @param string $text (optional)Checkbox display text
+ * @param mixed $attributes (optional)Either a typical HTML attribute string
+ * or an associative array
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function HTML_QuickForm_checkbox($elementName=null, $elementLabel=null, $text='', $attributes=null)
+ {
+ HTML_QuickForm_input::HTML_QuickForm_input($elementName, $elementLabel, $attributes);
+ $this->_persistantFreeze = true;
+ $this->_text = $text;
+ $this->setType('checkbox');
+ $this->updateAttributes(array('value'=>1));
+ $this->_generateId();
+ } //end constructor
+
+ // }}}
+ // {{{ setChecked()
+
+ /**
+ * Sets whether a checkbox is checked
+ *
+ * @param bool $checked Whether the field is checked or not
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setChecked($checked)
+ {
+ if (!$checked) {
+ $this->removeAttribute('checked');
+ } else {
+ $this->updateAttributes(array('checked'=>'checked'));
+ }
+ } //end func setChecked
+
+ // }}}
+ // {{{ getChecked()
+
+ /**
+ * Returns whether a checkbox is checked
+ *
+ * @since 1.0
+ * @access public
+ * @return bool
+ */
+ function getChecked()
+ {
+ return (bool)$this->getAttribute('checked');
+ } //end func getChecked
+
+ // }}}
+ // {{{ toHtml()
+
+ /**
+ * Returns the checkbox element in HTML
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ */
+ function toHtml()
+ {
+ if (0 == strlen($this->_text)) {
+ $label = '';
+ } elseif ($this->_flagFrozen) {
+ $label = $this->_text;
+ } else {
+ $label = '<label for="' . $this->getAttribute('id') . '">' . $this->_text . '</label>';
+ }
+ return HTML_QuickForm_input::toHtml() . $label;
+ } //end func toHtml
+
+ // }}}
+ // {{{ getFrozenHtml()
+
+ /**
+ * Returns the value of field without HTML tags
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ */
+ function getFrozenHtml()
+ {
+ if ($this->getChecked()) {
+ return '<tt>[x]</tt>' .
+ $this->_getPersistantData();
+ } else {
+ return '<tt>[ ]</tt>';
+ }
+ } //end func getFrozenHtml
+
+ // }}}
+ // {{{ setText()
+
+ /**
+ * Sets the checkbox text
+ *
+ * @param string $text
+ * @since 1.1
+ * @access public
+ * @return void
+ */
+ function setText($text)
+ {
+ $this->_text = $text;
+ } //end func setText
+
+ // }}}
+ // {{{ getText()
+
+ /**
+ * Returns the checkbox text
+ *
+ * @since 1.1
+ * @access public
+ * @return string
+ */
+ function getText()
+ {
+ return $this->_text;
+ } //end func getText
+
+ // }}}
+ // {{{ setValue()
+
+ /**
+ * Sets the value of the form element
+ *
+ * @param string $value Default value of the form element
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setValue($value)
+ {
+ return $this->setChecked($value);
+ } // end func setValue
+
+ // }}}
+ // {{{ getValue()
+
+ /**
+ * Returns the value of the form element
+ *
+ * @since 1.0
+ * @access public
+ * @return bool
+ */
+ function getValue()
+ {
+ return $this->getChecked();
+ } // end func getValue
+
+ // }}}
+ // {{{ onQuickFormEvent()
+
+ /**
+ * Called by HTML_QuickForm whenever form event is made on this element
+ *
+ * @param string $event Name of event
+ * @param mixed $arg event arguments
+ * @param object $caller calling object
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function onQuickFormEvent($event, $arg, &$caller)
+ {
+ switch ($event) {
+ case 'updateValue':
+ // constant values override both default and submitted ones
+ // default values are overriden by submitted
+ $value = $this->_findValue($caller->_constantValues);
+ if (null === $value) {
+ // if no boxes were checked, then there is no value in the array
+ // yet we don't want to display default value in this case
+ if (isset($caller->_submitValues) && 0 < count($caller->_submitValues)) {
+ $value = $this->_findValue($caller->_submitValues);
+ } else {
+ $value = $this->_findValue($caller->_defaultValues);
+ }
+ }
+ if (null !== $value) {
+ $this->setChecked($value);
+ }
+ break;
+ case 'setGroupValue':
+ $this->setChecked($arg);
+ break;
+ default:
+ parent::onQuickFormEvent($event, $arg, $caller);
+ }
+ return true;
+ } // end func onQuickFormEvent
+
+ // }}}
+ // {{{ exportValue()
+
+ /**
+ * Return true if the checkbox is checked, null if it is not checked (getValue() returns false)
+ */
+ function exportValue(&$submitValues, $assoc = false)
+ {
+ $value = $this->_findValue($submitValues);
+ if (null === $value) {
+ $value = $this->getChecked()? true: null;
+ }
+ return $this->_prepareValue($value, $assoc);
+ }
+
+ // }}}
+} //end class HTML_QuickForm_checkbox
+?>
diff --git a/pearlib/HTML/QuickForm/date.php b/pearlib/HTML/QuickForm/date.php
new file mode 100644
index 00000000..cb24a198
--- /dev/null
+++ b/pearlib/HTML/QuickForm/date.php
@@ -0,0 +1,451 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Alexey Borzov <[email protected]> |
+// | Adam Daniel <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: date.php,v 1.47 2004/10/14 19:55:41 avb Exp $
+
+require_once 'HTML/QuickForm/group.php';
+require_once 'HTML/QuickForm/select.php';
+
+/**
+ * Class for a group of elements used to input dates (and times).
+ *
+ * Inspired by original 'date' element but reimplemented as a subclass
+ * of HTML_QuickForm_group
+ *
+ * @author Alexey Borzov <[email protected]>
+ * @access public
+ */
+class HTML_QuickForm_date extends HTML_QuickForm_group
+{
+ // {{{ properties
+
+ /**
+ * Various options to control the element's display.
+ *
+ * Currently known options are
+ * 'language': date language
+ * 'format': Format of the date, based on PHP's date() function.
+ * The following characters are recognised in format string:
+ * D => Short names of days
+ * l => Long names of days
+ * d => Day numbers
+ * M => Short names of months
+ * F => Long names of months
+ * m => Month numbers
+ * Y => Four digit year
+ * y => Two digit year
+ * h => 12 hour format
+ * H => 23 hour format
+ * i => Minutes
+ * s => Seconds
+ * a => am/pm
+ * A => AM/PM
+ * 'minYear': Minimum year in year select
+ * 'maxYear': Maximum year in year select
+ * 'addEmptyOption': Should an empty option be added to the top of
+ * each select box?
+ * 'emptyOptionValue': The value passed by the empty option.
+ * 'emptyOptionText': The text displayed for the empty option.
+ * 'optionIncrement': Step to increase the option values by (works for 'i' and 's')
+ *
+ * @access private
+ * @var array
+ */
+ var $_options = array(
+ 'language' => 'en',
+ 'format' => 'dMY',
+ 'minYear' => 2001,
+ 'maxYear' => 2010,
+ 'addEmptyOption' => false,
+ 'emptyOptionValue' => '',
+ 'emptyOptionText' => '&nbsp;',
+ 'optionIncrement' => array('i' => 1, 's' => 1)
+ );
+
+ /**
+ * These complement separators, they are appended to the resultant HTML
+ * @access private
+ * @var array
+ */
+ var $_wrap = array('', '');
+
+ /**
+ * Options in different languages
+ *
+ * Note to potential translators: to avoid encoding problems please send
+ * your translations with "weird" letters encoded as HTML Unicode entities
+ *
+ * @access private
+ * @var array
+ */
+ var $_locale = array(
+ 'en' => array (
+ 'weekdays_short'=> array ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'),
+ 'weekdays_long' => array ('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'),
+ 'months_short' => array ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'),
+ 'months_long' => array ('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December')
+ ),
+ 'de' => array (
+ 'weekdays_short'=> array ('So', 'Mon', 'Di', 'Mi', 'Do', 'Fr', 'Sa'),
+ 'weekdays_long' => array ('Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'),
+ 'months_short' => array ('Jan', 'Feb', 'M&#xe4;rz', 'April', 'Mai', 'Juni', 'Juli', 'Aug', 'Sept', 'Okt', 'Nov', 'Dez'),
+ 'months_long' => array ('Januar', 'Februar', 'M&#xe4;rz', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember')
+ ),
+ 'fr' => array (
+ 'weekdays_short'=> array ('Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'),
+ 'weekdays_long' => array ('Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'),
+ 'months_short' => array ('Jan', 'Fev', 'Mar', 'Avr', 'Mai', 'Jun', 'Jul', 'Aou', 'Sep', 'Oct', 'Nov', 'Dec'),
+ 'months_long' => array ('Janvier', 'Fevrier', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Aout', 'Septembre', 'Octobre', 'Novembre', 'Decembre')
+ ),
+ 'fr' => array (
+ 'weekdays_short'=> array ('Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'),
+ 'weekdays_long' => array ('Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'),
+ 'months_short' => array ('Jan', 'Fev', 'Mar', 'Avr', 'Mai', 'Jun', 'Jul', 'Aou', 'Sep', 'Oct', 'Nov', 'Dec'),
+ 'months_long' => array ('Janvier', 'F&#xe9;vrier', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Ao&#xfb;t', 'Septembre', 'Octobre', 'Novembre', 'D&#xe9;cembre')
+ ),
+ 'hu' => array (
+ 'weekdays_short'=> array ('V', 'H', 'K', 'Sze', 'Cs', 'P', 'Szo'),
+ 'weekdays_long' => array ('vas&#xe1;rnap', 'h&#xe9;tf&#x151;', 'kedd', 'szerda', 'cs&#xfc;t&#xf6;rt&#xf6;k', 'p&#xe9;ntek', 'szombat'),
+ 'months_short' => array ('jan', 'feb', 'm&#xe1;rc', '&#xe1;pr', 'm&#xe1;j', 'j&#xfa;n', 'j&#xfa;l', 'aug', 'szept', 'okt', 'nov', 'dec'),
+ 'months_long' => array ('janu&#xe1;r', 'febru&#xe1;r', 'm&#xe1;rcius', '&#xe1;prilis', 'm&#xe1;jus', 'j&#xfa;nius', 'j&#xfa;lius', 'augusztus', 'szeptember', 'okt&#xf3;ber', 'november', 'december')
+ ),
+ 'pl' => array (
+ 'weekdays_short'=> array ('Nie', 'Pn', 'Wt', '&#x15a;r', 'Czw', 'Pt', 'Sob'),
+ 'weekdays_long' => array ('Niedziela', 'Poniedzia&#x142;ek', 'Wtorek', '&#x15a;roda', 'Czwartek', 'Pi&#x105;tek', 'Sobota'),
+ 'months_short' => array ('Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze', 'Lip', 'Sie', 'Wrz', 'Pa&#x17a;', 'Lis', 'Gru'),
+ 'months_long' => array ('Stycze&#x144;', 'Luty', 'Marzec', 'Kwiecie&#x144;', 'Maj', 'Czerwiec', 'Lipiec', 'Sierpie&#x144;', 'Wrzesie&#x144;', 'Pa&#x17a;dziernik', 'Listopad', 'Grudzie&#x144;')
+ ),
+ 'sl' => array (
+ 'weekdays_short'=> array ('Ned', 'Pon', 'Tor', 'Sre', 'Cet', 'Pet', 'Sob'),
+ 'weekdays_long' => array ('Nedelja', 'Ponedeljek', 'Torek', 'Sreda', 'Cetrtek', 'Petek', 'Sobota'),
+ 'months_short' => array ('Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Avg', 'Sep', 'Okt', 'Nov', 'Dec'),
+ 'months_long' => array ('Januar', 'Februar', 'Marec', 'April', 'Maj', 'Junij', 'Julij', 'Avgust', 'September', 'Oktober', 'November', 'December')
+ ),
+ 'ru' => array (
+ 'weekdays_short'=> array ('&#x412;&#x441;', '&#x41f;&#x43d;', '&#x412;&#x442;', '&#x421;&#x440;', '&#x427;&#x442;', '&#x41f;&#x442;', '&#x421;&#x431;'),
+ 'weekdays_long' => array ('&#x412;&#x43e;&#x441;&#x43a;&#x440;&#x435;&#x441;&#x435;&#x43d;&#x44c;&#x435;', '&#x41f;&#x43e;&#x43d;&#x435;&#x434;&#x435;&#x43b;&#x44c;&#x43d;&#x438;&#x43a;', '&#x412;&#x442;&#x43e;&#x440;&#x43d;&#x438;&#x43a;', '&#x421;&#x440;&#x435;&#x434;&#x430;', '&#x427;&#x435;&#x442;&#x432;&#x435;&#x440;&#x433;', '&#x41f;&#x44f;&#x442;&#x43d;&#x438;&#x446;&#x430;', '&#x421;&#x443;&#x431;&#x431;&#x43e;&#x442;&#x430;'),
+ 'months_short' => array ('&#x42f;&#x43d;&#x432;', '&#x424;&#x435;&#x432;', '&#x41c;&#x430;&#x440;', '&#x410;&#x43f;&#x440;', '&#x41c;&#x430;&#x439;', '&#x418;&#x44e;&#x43d;', '&#x418;&#x44e;&#x43b;', '&#x410;&#x432;&#x433;', '&#x421;&#x435;&#x43d;', '&#x41e;&#x43a;&#x442;', '&#x41d;&#x43e;&#x44f;', '&#x414;&#x435;&#x43a;'),
+ 'months_long' => array ('&#x42f;&#x43d;&#x432;&#x430;&#x440;&#x44c;', '&#x424;&#x435;&#x432;&#x440;&#x430;&#x43b;&#x44c;', '&#x41c;&#x430;&#x440;&#x442;', '&#x410;&#x43f;&#x440;&#x435;&#x43b;&#x44c;', '&#x41c;&#x430;&#x439;', '&#x418;&#x44e;&#x43d;&#x44c;', '&#x418;&#x44e;&#x43b;&#x44c;', '&#x410;&#x432;&#x433;&#x443;&#x441;&#x442;', '&#x421;&#x435;&#x43d;&#x442;&#x44f;&#x431;&#x440;&#x44c;', '&#x41e;&#x43a;&#x442;&#x44f;&#x431;&#x440;&#x44c;', '&#x41d;&#x43e;&#x44f;&#x431;&#x440;&#x44c;', '&#x414;&#x435;&#x43a;&#x430;&#x431;&#x440;&#x44c;')
+ ),
+ 'es' => array (
+ 'weekdays_short'=> array ('Dom', 'Lun', 'Mar', 'Mi&#xe9;', 'Jue', 'Vie', 'S&#xe1;b'),
+ 'weekdays_long' => array ('Domingo', 'Lunes', 'Martes', 'Mi&#xe9;rcoles', 'Jueves', 'Viernes', 'S&#xe1;bado'),
+ 'months_short' => array ('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'),
+ 'months_long' => array ('Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septimbre', 'Octubre', 'Noviembre', 'Diciembre')
+ ),
+ 'da' => array (
+ 'weekdays_short'=> array ('S&#xf8;n', 'Man', 'Tir', 'Ons', 'Tor', 'Fre', 'L&#xf8;r'),
+ 'weekdays_long' => array ('S&#xf8;ndag', 'Mandag', 'Tirsdag', 'Onsdag', 'Torsdag', 'Fredag', 'L&#xf8;rdag'),
+ 'months_short' => array ('Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'),
+ 'months_long' => array ('Januar', 'Februar', 'Marts', 'April', 'Maj', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'December')
+ ),
+ 'is' => array (
+ 'weekdays_short'=> array ('Sun', 'M&#xe1;n', '&#xde;ri', 'Mi&#xf0;', 'Fim', 'F&#xf6;s', 'Lau'),
+ 'weekdays_long' => array ('Sunnudagur', 'M&#xe1;nudagur', '&#xde;ri&#xf0;judagur', 'Mi&#xf0;vikudagur', 'Fimmtudagur', 'F&#xf6;studagur', 'Laugardagur'),
+ 'months_short' => array ('Jan', 'Feb', 'Mar', 'Apr', 'Ma&#xed;', 'J&#xfa;n', 'J&#xfa;l', '&#xc1;g&#xfa;', 'Sep', 'Okt', 'N&#xf3;v', 'Des'),
+ 'months_long' => array ('Jan&#xfa;ar', 'Febr&#xfa;ar', 'Mars', 'Apr&#xed;l', 'Ma&#xed;', 'J&#xfa;n&#xed;', 'J&#xfa;l&#xed;', '&#xc1;g&#xfa;st', 'September', 'Okt&#xf3;ber', 'N&#xf3;vember', 'Desember')
+ ),
+ 'it' => array (
+ 'weekdays_short'=> array ('Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab'),
+ 'weekdays_long' => array ('Domenica', 'Luned&#xec;', 'Marted&#xec;', 'Mercoled&#xec;', 'Gioved&#xec;', 'Venerd&#xec;', 'Sabato'),
+ 'months_short' => array ('Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic'),
+ 'months_long' => array ('Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre')
+ ),
+ 'sk' => array (
+ 'weekdays_short'=> array ('Ned', 'Pon', 'Uto', 'Str', '&#x8a;tv', 'Pia', 'Sob'),
+ 'weekdays_long' => array ('Nede&#x17e;a', 'Pondelok', 'Utorok', 'Streda', '&#x8a;tvrtok', 'Piatok', 'Sobota'),
+ 'months_short' => array ('Jan', 'Feb', 'Mar', 'Apr', 'M&#xe1;j', 'J&#xfa;n', 'J&#xfa;l', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'),
+ 'months_long' => array ('Janu&#xe1;r', 'Febru&#xe1;r', 'Marec', 'Apr&#xed;l', 'M&#xe1;j', 'J&#xfa;n', 'J&#xfa;l', 'August', 'September', 'Okt&#xf3;ber', 'November', 'December')
+ ),
+ 'cs' => array (
+ 'weekdays_short'=> array ('Ne', 'Po', '&#xda;t', 'St', '&#x10c;t', 'P&#xe1;', 'So'),
+ 'weekdays_long' => array ('Ned&#x11b;le', 'Pond&#x11b;l&#xed;', '&#xda;ter&#xfd;', 'St&#x159;eda', '&#x10c;tvrtek', 'P&#xe1;tek', 'Sobota'),
+ 'months_short' => array ('Led', '&#xda;no', 'B&#x159;e', 'Dub', 'Kv&#x11b;', '&#x10c;en', '&#x10c;ec', 'Srp', 'Z&#xe1;&#x159;', '&#x158;&#xed;j', 'Lis', 'Pro'),
+ 'months_long' => array ('Leden', '&#xda;nor', 'B&#x159;ezen', 'Duben', 'Kv&#x11b;ten', '&#x10c;erven', '&#x10c;ervenec', 'Srpen', 'Z&#xe1;&#x159;&#xed;', '&#x158;&#xed;jen', 'Listopad', 'Prosinec')
+ ),
+ 'hy' => array (
+ 'weekdays_short'=> array ('&#x53f;&#x580;&#x56f;', '&#x535;&#x580;&#x56f;', '&#x535;&#x580;&#x584;', '&#x549;&#x580;&#x584;', '&#x540;&#x576;&#x563;', '&#x548;&#x582;&#x580;', '&#x547;&#x562;&#x569;'),
+ 'weekdays_long' => array ('&#x53f;&#x56b;&#x580;&#x561;&#x56f;&#x56b;', '&#x535;&#x580;&#x56f;&#x578;&#x582;&#x577;&#x561;&#x562;&#x569;&#x56b;', '&#x535;&#x580;&#x565;&#x584;&#x577;&#x561;&#x562;&#x569;&#x56b;', '&#x549;&#x578;&#x580;&#x565;&#x584;&#x577;&#x561;&#x562;&#x569;&#x56b;', '&#x540;&#x56b;&#x576;&#x563;&#x577;&#x561;&#x562;&#x569;&#x56b;', '&#x548;&#x582;&#x580;&#x562;&#x561;&#x569;', '&#x547;&#x561;&#x562;&#x561;&#x569;'),
+ 'months_short' => array ('&#x540;&#x576;&#x57e;', '&#x553;&#x57f;&#x580;', '&#x544;&#x580;&#x57f;', '&#x531;&#x57a;&#x580;', '&#x544;&#x575;&#x57d;', '&#x540;&#x576;&#x57d;', '&#x540;&#x56c;&#x57d;', '&#x555;&#x563;&#x57d;', '&#x54d;&#x57a;&#x57f;', '&#x540;&#x56f;&#x57f;', '&#x546;&#x575;&#x574;', '&#x534;&#x56f;&#x57f;'),
+ 'months_long' => array ('&#x540;&#x578;&#x582;&#x576;&#x57e;&#x561;&#x580;', '&#x553;&#x565;&#x57f;&#x580;&#x57e;&#x561;&#x580;', '&#x544;&#x561;&#x580;&#x57f;', '&#x531;&#x57a;&#x580;&#x56b;&#x56c;', '&#x544;&#x561;&#x575;&#x56b;&#x57d;', '&#x540;&#x578;&#x582;&#x576;&#x56b;&#x57d;', '&#x540;&#x578;&#x582;&#x56c;&#x56b;&#x57d;', '&#x555;&#x563;&#x578;&#x57d;&#x57f;&#x578;&#x57d;', '&#x54d;&#x565;&#x57a;&#x57f;&#x565;&#x574;&#x562;&#x565;&#x580;', '&#x540;&#x578;&#x56f;&#x57f;&#x565;&#x574;&#x562;&#x565;&#x580;', '&#x546;&#x578;&#x575;&#x565;&#x574;&#x562;&#x565;&#x580;', '&#x534;&#x565;&#x56f;&#x57f;&#x565;&#x574;&#x562;&#x565;&#x580;')
+ ),
+ 'nl' => array (
+ 'weekdays_short'=> array ('Zo', 'Ma', 'Di', 'Wo', 'Do', 'Vr', 'Za'),
+ 'weekdays_long' => array ('Zondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag', 'Vrijdag', 'Zaterdag'),
+ 'months_short' => array ('Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'),
+ 'months_long' => array ('Januari', 'Februari', 'Maart', 'April', 'Mei', 'Juni', 'Juli', 'Augustus', 'September', 'Oktober', 'November', 'December')
+ ),
+ 'et' => array (
+ 'weekdays_short'=> array ('P', 'E', 'T', 'K', 'N', 'R', 'L'),
+ 'weekdays_long' => array ('P&#xfc;hap&#xe4;ev', 'Esmasp&#xe4;ev', 'Teisip&#xe4;ev', 'Kolmap&#xe4;ev', 'Neljap&#xe4;ev', 'Reede', 'Laup&#xe4;ev'),
+ 'months_short' => array ('Jaan', 'Veebr', 'M&#xe4;rts', 'Aprill', 'Mai', 'Juuni', 'Juuli', 'Aug', 'Sept', 'Okt', 'Nov', 'Dets'),
+ 'months_long' => array ('Jaanuar', 'Veebruar', 'M&#xe4;rts', 'Aprill', 'Mai', 'Juuni', 'Juuli', 'August', 'September', 'Oktoober', 'November', 'Detsember')
+ ),
+ 'tr' => array (
+ 'weekdays_short'=> array ('Paz', 'Pzt', 'Sal', '&#xc7;ar', 'Per', 'Cum', 'Cts'),
+ 'weekdays_long' => array ('Pazar', 'Pazartesi', 'Sal&#x131;', '&#xc7;ar&#x15f;amba', 'Per&#x15f;embe', 'Cuma', 'Cumartesi'),
+ 'months_short' => array ('Ock', '&#x15e;bt', 'Mrt', 'Nsn', 'Mys', 'Hzrn', 'Tmmz', 'A&#x11f;st', 'Eyl', 'Ekm', 'Ksm', 'Arlk'),
+ 'months_long' => array ('Ocak', '&#x15e;ubat', 'Mart', 'Nisan', 'May&#x131;s', 'Haziran', 'Temmuz', 'A&#x11f;ustos', 'Eyl&#xfc;l', 'Ekim', 'Kas&#x131;m', 'Aral&#x131;k')
+ ),
+ 'no' => array (
+ 'weekdays_short'=> array ('S&#xf8;n', 'Man', 'Tir', 'Ons', 'Tor', 'Fre', 'L&#xf8;r'),
+ 'weekdays_long' => array ('S&#xf8;ndag', 'Mandag', 'Tirsdag', 'Onsdag', 'Torsdag', 'Fredag', 'L&#xf8;rdag'),
+ 'months_short' => array ('Jan', 'Feb', 'Mar', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Des'),
+ 'months_long' => array ('Januar', 'Februar', 'Mars', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Desember')
+ ),
+ 'eo' => array (
+ 'weekdays_short'=> array ('Dim', 'Lun', 'Mar', 'Mer', '&#x134;a&#x16D;', 'Ven', 'Sab'),
+ 'weekdays_long' => array ('Diman&#x109;o', 'Lundo', 'Mardo', 'Merkredo', '&#x134;a&#x16D;do', 'Vendredo', 'Sabato'),
+ 'months_short' => array ('Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'A&#x16D;g', 'Sep', 'Okt', 'Nov', 'Dec'),
+ 'months_long' => array ('Januaro', 'Februaro', 'Marto', 'Aprilo', 'Majo', 'Junio', 'Julio', 'A&#x16D;gusto', 'Septembro', 'Oktobro', 'Novembro', 'Decembro')
+ ),
+ 'ua' => array (
+ 'weekdays_short'=> array('&#x41d;&#x434;&#x43b;', '&#x41f;&#x43d;&#x434;', '&#x412;&#x442;&#x440;', '&#x421;&#x440;&#x434;', '&#x427;&#x442;&#x432;', '&#x41f;&#x442;&#x43d;', '&#x421;&#x431;&#x442;'),
+ 'weekdays_long' => array('&#x41d;&#x435;&#x434;&#x456;&#x43b;&#x44f;', '&#x41f;&#x43e;&#x43d;&#x435;&#x434;&#x456;&#x43b;&#x43e;&#x43a;', '&#x412;&#x456;&#x432;&#x442;&#x43e;&#x440;&#x43e;&#x43a;', '&#x421;&#x435;&#x440;&#x435;&#x434;&#x430;', '&#x427;&#x435;&#x442;&#x432;&#x435;&#x440;', '&#x41f;\'&#x44f;&#x442;&#x43d;&#x438;&#x446;&#x44f;', '&#x421;&#x443;&#x431;&#x43e;&#x442;&#x430;'),
+ 'months_short' => array('&#x421;&#x456;&#x447;', '&#x41b;&#x44e;&#x442;', '&#x411;&#x435;&#x440;', '&#x41a;&#x432;&#x456;', '&#x422;&#x440;&#x430;', '&#x427;&#x435;&#x440;', '&#x41b;&#x438;&#x43f;', '&#x421;&#x435;&#x440;', '&#x412;&#x435;&#x440;', '&#x416;&#x43e;&#x432;', '&#x41b;&#x438;&#x441;', '&#x413;&#x440;&#x443;'),
+ 'months_long' => array('&#x421;&#x456;&#x447;&#x435;&#x43d;&#x44c;', '&#x41b;&#x44e;&#x442;&#x438;&#x439;', '&#x411;&#x435;&#x440;&#x435;&#x437;&#x435;&#x43d;&#x44c;', '&#x41a;&#x432;&#x456;&#x442;&#x435;&#x43d;&#x44c;', '&#x422;&#x440;&#x430;&#x432;&#x435;&#x43d;&#x44c;', '&#x427;&#x435;&#x440;&#x432;&#x435;&#x43d;&#x44c;', '&#x41b;&#x438;&#x43f;&#x435;&#x43d;&#x44c;', '&#x421;&#x435;&#x440;&#x43f;&#x435;&#x43d;&#x44c;', '&#x412;&#x435;&#x440;&#x435;&#x441;&#x435;&#x43d;&#x44c;', '&#x416;&#x43e;&#x432;&#x442;&#x435;&#x43d;&#x44c;', '&#x41b;&#x438;&#x441;&#x442;&#x43e;&#x43f;&#x430;&#x434;', '&#x413;&#x440;&#x443;&#x434;&#x435;&#x43d;&#x44c;')
+ )
+ );
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ *
+ * @access public
+ * @param string Element's name
+ * @param mixed Label(s) for an element
+ * @param array Options to control the element's display
+ * @param mixed Either a typical HTML attribute string or an associative array
+ */
+ function HTML_QuickForm_date($elementName = null, $elementLabel = null, $options = array(), $attributes = null)
+ {
+ $this->HTML_QuickForm_element($elementName, $elementLabel, $attributes);
+ $this->_persistantFreeze = true;
+ $this->_appendName = true;
+ $this->_type = 'date';
+ // set the options, do not bother setting bogus ones
+ if (is_array($options)) {
+ foreach ($options as $name => $value) {
+ if ('language' == $name) {
+ $this->_options['language'] = isset($this->_locale[$value])? $value: 'en';
+ } elseif (isset($this->_options[$name])) {
+ if (is_array($value)) {
+ $this->_options[$name] = @array_merge($this->_options[$name], $value);
+ } else {
+ $this->_options[$name] = $value;
+ }
+ }
+ }
+ }
+ }
+
+ // }}}
+ // {{{ _createElements()
+
+ function _createElements()
+ {
+ $this->_separator = $this->_elements = array();
+ $separator = '';
+ $locale =& $this->_locale[$this->_options['language']];
+ $backslash = false;
+ for ($i = 0, $length = strlen($this->_options['format']); $i < $length; $i++) {
+ $sign = $this->_options['format']{$i};
+ if ($backslash) {
+ $backslash = false;
+ $separator .= $sign;
+ } else {
+ $loadSelect = true;
+ switch ($sign) {
+ case 'D':
+ // Sunday is 0 like with 'w' in date()
+ $options = $locale['weekdays_short'];
+ break;
+ case 'l':
+ $options = $locale['weekdays_long'];
+ break;
+ case 'd':
+ $options = $this->_createOptionList(1, 31);
+ break;
+ case 'M':
+ $options = $locale['months_short'];
+ array_unshift($options , '');
+ unset($options[0]);
+ break;
+ case 'm':
+ $options = $this->_createOptionList(1, 12);
+ break;
+ case 'F':
+ $options = $locale['months_long'];
+ array_unshift($options , '');
+ unset($options[0]);
+ break;
+ case 'Y':
+ $options = $this->_createOptionList(
+ $this->_options['minYear'],
+ $this->_options['maxYear'],
+ $this->_options['minYear'] > $this->_options['maxYear']? -1: 1
+ );
+ break;
+ case 'y':
+ $options = $this->_createOptionList(
+ $this->_options['minYear'],
+ $this->_options['maxYear'],
+ $this->_options['minYear'] > $this->_options['maxYear']? -1: 1
+ );
+ array_walk($options, create_function('&$v,$k','$v = substr($v,-2);'));
+ break;
+ case 'h':
+ $options = $this->_createOptionList(1, 12);
+ break;
+ case 'H':
+ $options = $this->_createOptionList(0, 23);
+ break;
+ case 'i':
+ $options = $this->_createOptionList(0, 59, $this->_options['optionIncrement']['i']);
+ break;
+ case 's':
+ $options = $this->_createOptionList(0, 59, $this->_options['optionIncrement']['s']);
+ break;
+ case 'a':
+ $options = array('am' => 'am', 'pm' => 'pm');
+ break;
+ case 'A':
+ $options = array('AM' => 'AM', 'PM' => 'PM');
+ break;
+ case '\\':
+ $backslash = true;
+ $loadSelect = false;
+ break;
+ default:
+ $separator .= (' ' == $sign? '&nbsp;': $sign);
+ $loadSelect = false;
+ }
+
+ if ($loadSelect) {
+ if (0 < count($this->_elements)) {
+ $this->_separator[] = $separator;
+ } else {
+ $this->_wrap[0] = $separator;
+ }
+ $separator = '';
+ // Should we add an empty option to the top of the select?
+ if ($this->_options['addEmptyOption']) {
+ // Preserve the keys
+ $options = array($this->_options['emptyOptionValue'] => $this->_options['emptyOptionText']) + $options;
+ }
+ $this->_elements[] =& new HTML_QuickForm_select($sign, null, $options, $this->getAttributes());
+ }
+ }
+ }
+ $this->_wrap[1] = $separator . ($backslash? '\\': '');
+ }
+
+ // }}}
+ // {{{ _createOptionList()
+
+ /**
+ * Creates an option list containing the numbers from the start number to the end, inclusive
+ *
+ * @param int The start number
+ * @param int The end number
+ * @param int Increment by this value
+ * @access private
+ * @return array An array of numeric options.
+ */
+ function _createOptionList($start, $end, $step = 1)
+ {
+ for ($i = $start, $options = array(); $start > $end? $i >= $end: $i <= $end; $i += $step) {
+ $options[$i] = sprintf('%02d', $i);
+ }
+ return $options;
+ }
+
+ // }}}
+ // {{{ setValue()
+
+ function setValue($value)
+ {
+ if (empty($value)) {
+ $value = array();
+ } elseif (is_scalar($value)) {
+ if (!is_numeric($value)) {
+ $value = strtotime($value);
+ }
+ // might be a unix epoch, then we fill all possible values
+ $arr = explode('-', date('w-d-n-Y-h-H-i-s-a-A', (int)$value));
+ $value = array(
+ 'D' => $arr[0],
+ 'l' => $arr[0],
+ 'd' => $arr[1],
+ 'M' => $arr[2],
+ 'm' => $arr[2],
+ 'F' => $arr[2],
+ 'Y' => $arr[3],
+ 'y' => $arr[3],
+ 'h' => $arr[4],
+ 'H' => $arr[5],
+ 'i' => $arr[6],
+ 's' => $arr[7],
+ 'a' => $arr[8],
+ 'A' => $arr[9]
+ );
+ }
+ parent::setValue($value);
+ }
+
+ // }}}
+ // {{{ toHtml()
+
+ function toHtml()
+ {
+ include_once('HTML/QuickForm/Renderer/Default.php');
+ $renderer =& new HTML_QuickForm_Renderer_Default();
+ $renderer->setElementTemplate($this->_wrap[0] . '{element}' . $this->_wrap[1]);
+ parent::accept($renderer);
+ return $renderer->toHtml();
+ }
+
+ // }}}
+ // {{{ accept()
+
+ function accept(&$renderer, $required = false, $error = null)
+ {
+ $renderer->renderElement($this, $required, $error);
+ }
+
+ // }}}
+ // {{{ onQuickFormEvent()
+
+ function onQuickFormEvent($event, $arg, &$caller)
+ {
+ if ('updateValue' == $event) {
+ // we need to call setValue(), 'cause the default/constant value
+ // may be in fact a timestamp, not an array
+ return HTML_QuickForm_element::onQuickFormEvent($event, $arg, $caller);
+ } else {
+ return parent::onQuickFormEvent($event, $arg, $caller);
+ }
+ }
+
+ // }}}
+}
+?> \ No newline at end of file
diff --git a/pearlib/HTML/QuickForm/element.php b/pearlib/HTML/QuickForm/element.php
new file mode 100644
index 00000000..a826c855
--- /dev/null
+++ b/pearlib/HTML/QuickForm/element.php
@@ -0,0 +1,478 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Adam Daniel <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: element.php,v 1.32 2004/10/14 20:00:49 avb Exp $
+
+require_once('HTML/Common.php');
+
+/**
+ * Base class for form elements
+ *
+ * @author Adam Daniel <[email protected]>
+ * @author Bertrand Mansion <[email protected]>
+ * @version 1.3
+ * @since PHP4.04pl1
+ * @access public
+ * @abstract
+ */
+class HTML_QuickForm_element extends HTML_Common
+{
+ // {{{ properties
+
+ /**
+ * Label of the field
+ * @var string
+ * @since 1.3
+ * @access private
+ */
+ var $_label = '';
+
+ /**
+ * Form element type
+ * @var string
+ * @since 1.0
+ * @access private
+ */
+ var $_type = '';
+
+ /**
+ * Flag to tell if element is frozen
+ * @var boolean
+ * @since 1.0
+ * @access private
+ */
+ var $_flagFrozen = false;
+
+ /**
+ * Does the element support persistant data when frozen
+ * @var boolean
+ * @since 1.3
+ * @access private
+ */
+ var $_persistantFreeze = false;
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ *
+ * @param string Name of the element
+ * @param mixed Label(s) for the element
+ * @param mixed Associative array of tag attributes or HTML attributes name="value" pairs
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function HTML_QuickForm_element($elementName=null, $elementLabel=null, $attributes=null)
+ {
+ HTML_Common::HTML_Common($attributes);
+ if (isset($elementName)) {
+ $this->setName($elementName);
+ }
+ if (isset($elementLabel)) {
+ $this->setLabel($elementLabel);
+ }
+ } //end constructor
+
+ // }}}
+ // {{{ apiVersion()
+
+ /**
+ * Returns the current API version
+ *
+ * @since 1.0
+ * @access public
+ * @return float
+ */
+ function apiVersion()
+ {
+ return 2.0;
+ } // end func apiVersion
+
+ // }}}
+ // {{{ getType()
+
+ /**
+ * Returns element type
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ */
+ function getType()
+ {
+ return $this->_type;
+ } // end func getType
+
+ // }}}
+ // {{{ setName()
+
+ /**
+ * Sets the input field name
+ *
+ * @param string $name Input field name attribute
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setName($name)
+ {
+ // interface method
+ } //end func setName
+
+ // }}}
+ // {{{ getName()
+
+ /**
+ * Returns the element name
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ */
+ function getName()
+ {
+ // interface method
+ } //end func getName
+
+ // }}}
+ // {{{ setValue()
+
+ /**
+ * Sets the value of the form element
+ *
+ * @param string $value Default value of the form element
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setValue($value)
+ {
+ // interface
+ } // end func setValue
+
+ // }}}
+ // {{{ getValue()
+
+ /**
+ * Returns the value of the form element
+ *
+ * @since 1.0
+ * @access public
+ * @return mixed
+ */
+ function getValue()
+ {
+ // interface
+ return null;
+ } // end func getValue
+
+ // }}}
+ // {{{ freeze()
+
+ /**
+ * Freeze the element so that only its value is returned
+ *
+ * @access public
+ * @return void
+ */
+ function freeze()
+ {
+ $this->_flagFrozen = true;
+ } //end func freeze
+
+ // }}}
+ // {{{ unfreeze()
+
+ /**
+ * Unfreezes the element so that it becomes editable
+ *
+ * @access public
+ * @return void
+ * @since 3.2.4
+ */
+ function unfreeze()
+ {
+ $this->_flagFrozen = false;
+ }
+
+ // }}}
+ // {{{ getFrozenHtml()
+
+ /**
+ * Returns the value of field without HTML tags
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ */
+ function getFrozenHtml()
+ {
+ $value = $this->getValue();
+ return ('' != $value? htmlspecialchars($value): '&nbsp;') .
+ $this->_getPersistantData();
+ } //end func getFrozenHtml
+
+ // }}}
+ // {{{ _getPersistantData()
+
+ /**
+ * Used by getFrozenHtml() to pass the element's value if _persistantFreeze is on
+ *
+ * @access private
+ * @return string
+ */
+ function _getPersistantData()
+ {
+ if (!$this->_persistantFreeze) {
+ return '';
+ } else {
+ $id = $this->getAttribute('id');
+ return '<input type="hidden"' .
+ (isset($id)? ' id="' . $id . '"': '') .
+ ' name="' . $this->getName() . '"' .
+ ' value="' . htmlspecialchars($this->getValue()) . '" />';
+ }
+ }
+
+ // }}}
+ // {{{ isFrozen()
+
+ /**
+ * Returns whether or not the element is frozen
+ *
+ * @since 1.3
+ * @access public
+ * @return bool
+ */
+ function isFrozen()
+ {
+ return $this->_flagFrozen;
+ } // end func isFrozen
+
+ // }}}
+ // {{{ setPersistantFreeze()
+
+ /**
+ * Sets wether an element value should be kept in an hidden field
+ * when the element is frozen or not
+ *
+ * @param bool $persistant True if persistant value
+ * @since 2.0
+ * @access public
+ * @return void
+ */
+ function setPersistantFreeze($persistant=false)
+ {
+ $this->_persistantFreeze = $persistant;
+ } //end func setPersistantFreeze
+
+ // }}}
+ // {{{ setLabel()
+
+ /**
+ * Sets display text for the element
+ *
+ * @param string $label Display text for the element
+ * @since 1.3
+ * @access public
+ * @return void
+ */
+ function setLabel($label)
+ {
+ $this->_label = $label;
+ } //end func setLabel
+
+ // }}}
+ // {{{ getLabel()
+
+ /**
+ * Returns display text for the element
+ *
+ * @since 1.3
+ * @access public
+ * @return string
+ */
+ function getLabel()
+ {
+ return $this->_label;
+ } //end func getLabel
+
+ // }}}
+ // {{{ _findValue()
+
+ /**
+ * Tries to find the element value from the values array
+ *
+ * @since 2.7
+ * @access private
+ * @return mixed
+ */
+ function _findValue(&$values)
+ {
+ if (empty($values)) {
+ return null;
+ }
+ $elementName = $this->getName();
+ if (isset($values[$elementName])) {
+ return $values[$elementName];
+ } elseif (strpos($elementName, '[')) {
+ $myVar = "['" . str_replace(array(']', '['), array('', "']['"), $elementName) . "']";
+ return eval("return (isset(\$values$myVar)) ? \$values$myVar : null;");
+ } else {
+ return null;
+ }
+ } //end func _findValue
+
+ // }}}
+ // {{{ onQuickFormEvent()
+
+ /**
+ * Called by HTML_QuickForm whenever form event is made on this element
+ *
+ * @param string $event Name of event
+ * @param mixed $arg event arguments
+ * @param object $caller calling object
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function onQuickFormEvent($event, $arg, &$caller)
+ {
+ switch ($event) {
+ case 'createElement':
+ $className = get_class($this);
+ $this->$className($arg[0], $arg[1], $arg[2], $arg[3], $arg[4]);
+ break;
+ case 'addElement':
+ $this->onQuickFormEvent('createElement', $arg, $caller);
+ $this->onQuickFormEvent('updateValue', null, $caller);
+ break;
+ case 'updateValue':
+ // constant values override both default and submitted ones
+ // default values are overriden by submitted
+ $value = $this->_findValue($caller->_constantValues);
+ if (null === $value) {
+ $value = $this->_findValue($caller->_submitValues);
+ if (null === $value) {
+ $value = $this->_findValue($caller->_defaultValues);
+ }
+ }
+ if (null !== $value) {
+ $this->setValue($value);
+ }
+ break;
+ case 'setGroupValue':
+ $this->setValue($arg);
+ }
+ return true;
+ } // end func onQuickFormEvent
+
+ // }}}
+ // {{{ accept()
+
+ /**
+ * Accepts a renderer
+ *
+ * @param object An HTML_QuickForm_Renderer object
+ * @param bool Whether an element is required
+ * @param string An error message associated with an element
+ * @access public
+ * @return void
+ */
+ function accept(&$renderer, $required=false, $error=null)
+ {
+ $renderer->renderElement($this, $required, $error);
+ } // end func accept
+
+ // }}}
+ // {{{ _generateId()
+
+ /**
+ * Automatically generates and assigns an 'id' attribute for the element.
+ *
+ * Currently used to ensure that labels work on radio buttons and
+ * checkboxes. Per idea of Alexander Radivanovich.
+ *
+ * @access private
+ * @return void
+ */
+ function _generateId()
+ {
+ static $idx = 1;
+
+ if (!$this->getAttribute('id')) {
+ $this->updateAttributes(array('id' => 'qf_' . substr(md5(microtime() . $idx++), 0, 6)));
+ }
+ } // end func _generateId
+
+ // }}}
+ // {{{ exportValue()
+
+ /**
+ * Returns a 'safe' element's value
+ *
+ * @param array array of submitted values to search
+ * @param bool whether to return the value as associative array
+ * @access public
+ * @return mixed
+ */
+ function exportValue(&$submitValues, $assoc = false)
+ {
+ $value = $this->_findValue($submitValues);
+ if (null === $value) {
+ $value = $this->getValue();
+ }
+ return $this->_prepareValue($value, $assoc);
+ }
+
+ // }}}
+ // {{{ _prepareValue()
+
+ /**
+ * Used by exportValue() to prepare the value for returning
+ *
+ * @param mixed the value found in exportValue()
+ * @param bool whether to return the value as associative array
+ * @access private
+ * @return mixed
+ */
+ function _prepareValue($value, $assoc)
+ {
+ if (null === $value) {
+ return null;
+ } elseif (!$assoc) {
+ return $value;
+ } else {
+ $name = $this->getName();
+ if (!strpos($name, '[')) {
+ return array($name => $value);
+ } else {
+ $valueAry = array();
+ $myIndex = "['" . str_replace(array(']', '['), array('', "']['"), $name) . "']";
+ eval("\$valueAry$myIndex = \$value;");
+ return $valueAry;
+ }
+ }
+ }
+
+ // }}}
+} // end class HTML_QuickForm_element
+?> \ No newline at end of file
diff --git a/pearlib/HTML/QuickForm/file.php b/pearlib/HTML/QuickForm/file.php
new file mode 100644
index 00000000..3ae5d2a4
--- /dev/null
+++ b/pearlib/HTML/QuickForm/file.php
@@ -0,0 +1,346 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Adam Daniel <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: file.php,v 1.19 2004/10/09 19:26:00 avb Exp $
+
+require_once("HTML/QuickForm/input.php");
+
+// register file-related rules
+if (class_exists('HTML_QuickForm')) {
+ HTML_QuickForm::registerRule('uploadedfile', 'callback', '_ruleIsUploadedFile', 'HTML_QuickForm_file');
+ HTML_QuickForm::registerRule('maxfilesize', 'callback', '_ruleCheckMaxFileSize', 'HTML_QuickForm_file');
+ HTML_QuickForm::registerRule('mimetype', 'callback', '_ruleCheckMimeType', 'HTML_QuickForm_file');
+ HTML_QuickForm::registerRule('filename', 'callback', '_ruleCheckFileName', 'HTML_QuickForm_file');
+}
+
+/**
+ * HTML class for a file type element
+ *
+ * @author Adam Daniel <[email protected]>
+ * @author Bertrand Mansion <[email protected]>
+ * @version 1.0
+ * @since PHP4.04pl1
+ * @access public
+ */
+class HTML_QuickForm_file extends HTML_QuickForm_input
+{
+ // {{{ properties
+
+ /**
+ * Uploaded file data, from $_FILES
+ * @var array
+ */
+ var $_value = null;
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ *
+ * @param string Input field name attribute
+ * @param string Input field label
+ * @param mixed (optional)Either a typical HTML attribute string
+ * or an associative array
+ * @since 1.0
+ * @access public
+ */
+ function HTML_QuickForm_file($elementName=null, $elementLabel=null, $attributes=null)
+ {
+ HTML_QuickForm_input::HTML_QuickForm_input($elementName, $elementLabel, $attributes);
+ $this->setType('file');
+ } //end constructor
+
+ // }}}
+ // {{{ setSize()
+
+ /**
+ * Sets size of file element
+ *
+ * @param int Size of file element
+ * @since 1.0
+ * @access public
+ */
+ function setSize($size)
+ {
+ $this->updateAttributes(array('size' => $size));
+ } //end func setSize
+
+ // }}}
+ // {{{ getSize()
+
+ /**
+ * Returns size of file element
+ *
+ * @since 1.0
+ * @access public
+ * @return int
+ */
+ function getSize()
+ {
+ return $this->getAttribute('size');
+ } //end func getSize
+
+ // }}}
+ // {{{ freeze()
+
+ /**
+ * Freeze the element so that only its value is returned
+ *
+ * @access public
+ * @return bool
+ */
+ function freeze()
+ {
+ return false;
+ } //end func freeze
+
+ // }}}
+ // {{{ setValue()
+
+ /**
+ * Sets value for file element.
+ *
+ * Actually this does nothing. The function is defined here to override
+ * HTML_Quickform_input's behaviour of setting the 'value' attribute. As
+ * no sane user-agent uses <input type="file">'s value for anything
+ * (because of security implications) we implement file's value as a
+ * read-only property with a special meaning.
+ *
+ * @param mixed Value for file element
+ * @since 3.0
+ * @access public
+ */
+ function setValue($value)
+ {
+ return null;
+ } //end func setValue
+
+ // }}}
+ // {{{ getValue()
+
+ /**
+ * Returns information about the uploaded file
+ *
+ * @since 3.0
+ * @access public
+ * @return array
+ */
+ function getValue()
+ {
+ return $this->_value;
+ } // end func getValue
+
+ // }}}
+ // {{{ onQuickFormEvent()
+
+ /**
+ * Called by HTML_QuickForm whenever form event is made on this element
+ *
+ * @param string Name of event
+ * @param mixed event arguments
+ * @param object calling object
+ * @since 1.0
+ * @access public
+ * @return bool
+ */
+ function onQuickFormEvent($event, $arg, &$caller)
+ {
+ switch ($event) {
+ case 'updateValue':
+ if ($caller->getAttribute('method') == 'get') {
+ return PEAR::raiseError('Cannot add a file upload field to a GET method form');
+ }
+ $this->_value = $this->_findValue();
+ $caller->updateAttributes(array('enctype' => 'multipart/form-data'));
+ $caller->setMaxFileSize();
+ break;
+ case 'addElement':
+ $this->onQuickFormEvent('createElement', $arg, $caller);
+ return $this->onQuickFormEvent('updateValue', null, $caller);
+ break;
+ case 'createElement':
+ $className = get_class($this);
+ $this->$className($arg[0], $arg[1], $arg[2]);
+ break;
+ }
+ return true;
+ } // end func onQuickFormEvent
+
+ // }}}
+ // {{{ moveUploadedFile()
+
+ /**
+ * Moves an uploaded file into the destination
+ *
+ * @param string Destination directory path
+ * @param string New file name
+ * @access public
+ */
+ function moveUploadedFile($dest, $fileName = '')
+ {
+ if ($dest != '' && substr($dest, -1) != '/') {
+ $dest .= '/';
+ }
+ $fileName = ($fileName != '') ? $fileName : basename($this->_value['name']);
+ if (move_uploaded_file($this->_value['tmp_name'], $dest . $fileName)) {
+ return true;
+ } else {
+ return false;
+ }
+ } // end func moveUploadedFile
+
+ // }}}
+ // {{{ isUploadedFile()
+
+ /**
+ * Checks if the element contains an uploaded file
+ *
+ * @access public
+ * @return bool true if file has been uploaded, false otherwise
+ */
+ function isUploadedFile()
+ {
+ return $this->_ruleIsUploadedFile($this->_value);
+ } // end func isUploadedFile
+
+ // }}}
+ // {{{ _ruleIsUploadedFile()
+
+ /**
+ * Checks if the given element contains an uploaded file
+ *
+ * @param array Uploaded file info (from $_FILES)
+ * @access private
+ * @return bool true if file has been uploaded, false otherwise
+ */
+ function _ruleIsUploadedFile($elementValue)
+ {
+ if ((isset($elementValue['error']) && $elementValue['error'] == 0) ||
+ (!empty($elementValue['tmp_name']) && $elementValue['tmp_name'] != 'none')) {
+ return is_uploaded_file($elementValue['tmp_name']);
+ } else {
+ return false;
+ }
+ } // end func _ruleIsUploadedFile
+
+ // }}}
+ // {{{ _ruleCheckMaxFileSize()
+
+ /**
+ * Checks that the file does not exceed the max file size
+ *
+ * @param array Uploaded file info (from $_FILES)
+ * @param int Max file size
+ * @access private
+ * @return bool true if filesize is lower than maxsize, false otherwise
+ */
+ function _ruleCheckMaxFileSize($elementValue, $maxSize)
+ {
+ if (!empty($elementValue['error']) &&
+ (UPLOAD_ERR_FORM_SIZE == $elementValue['error'] || UPLOAD_ERR_INI_SIZE == $elementValue['error'])) {
+ return false;
+ }
+ if (!HTML_QuickForm_file::_ruleIsUploadedFile($elementValue)) {
+ return true;
+ }
+ return ($maxSize >= @filesize($elementValue['tmp_name']));
+ } // end func _ruleCheckMaxFileSize
+
+ // }}}
+ // {{{ _ruleCheckMimeType()
+
+ /**
+ * Checks if the given element contains an uploaded file of the right mime type
+ *
+ * @param array Uploaded file info (from $_FILES)
+ * @param mixed Mime Type (can be an array of allowed types)
+ * @access private
+ * @return bool true if mimetype is correct, false otherwise
+ */
+ function _ruleCheckMimeType($elementValue, $mimeType)
+ {
+ if (!HTML_QuickForm_file::_ruleIsUploadedFile($elementValue)) {
+ return true;
+ }
+ if (is_array($mimeType)) {
+ return in_array($elementValue['type'], $mimeType);
+ }
+ return $elementValue['type'] == $mimeType;
+ } // end func _ruleCheckMimeType
+
+ // }}}
+ // {{{ _ruleCheckFileName()
+
+ /**
+ * Checks if the given element contains an uploaded file of the filename regex
+ *
+ * @param array Uploaded file info (from $_FILES)
+ * @param string Regular expression
+ * @access private
+ * @return bool true if name matches regex, false otherwise
+ */
+ function _ruleCheckFileName($elementValue, $regex)
+ {
+ if (!HTML_QuickForm_file::_ruleIsUploadedFile($elementValue)) {
+ return true;
+ }
+ return preg_match($regex, $elementValue['name']);
+ } // end func _ruleCheckFileName
+
+ // }}}
+ // {{{ _findValue()
+
+ /**
+ * Tries to find the element value from the values array
+ *
+ * Needs to be redefined here as $_FILES is populated differently from
+ * other arrays when element name is of the form foo[bar]
+ *
+ * @access private
+ * @return mixed
+ */
+ function _findValue()
+ {
+ if (empty($_FILES)) {
+ return null;
+ }
+ $elementName = $this->getName();
+ if (isset($_FILES[$elementName])) {
+ return $_FILES[$elementName];
+ } elseif (false !== ($pos = strpos($elementName, '['))) {
+ $base = substr($elementName, 0, $pos);
+ $idx = "['" . str_replace(array(']', '['), array('', "']['"), substr($elementName, $pos + 1, -1)) . "']";
+ $props = array('name', 'type', 'size', 'tmp_name', 'error');
+ $code = "if (!isset(\$_FILES['{$base}']['name']{$idx})) {\n" .
+ " return null;\n" .
+ "} else {\n" .
+ " \$value = array();\n";
+ foreach ($props as $prop) {
+ $code .= " \$value['{$prop}'] = \$_FILES['{$base}']['{$prop}']{$idx};\n";
+ }
+ return eval($code . " return \$value;\n}\n");
+ } else {
+ return null;
+ }
+ }
+
+ // }}}
+} // end class HTML_QuickForm_file
+?>
diff --git a/pearlib/HTML/QuickForm/group.php b/pearlib/HTML/QuickForm/group.php
new file mode 100644
index 00000000..7dca2ce2
--- /dev/null
+++ b/pearlib/HTML/QuickForm/group.php
@@ -0,0 +1,568 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Adam Daniel <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: group.php,v 1.34 2004/10/14 20:00:49 avb Exp $
+
+require_once("HTML/QuickForm/element.php");
+
+/**
+ * HTML class for a form element group
+ *
+ * @author Adam Daniel <[email protected]>
+ * @author Bertrand Mansion <[email protected]>
+ * @version 1.0
+ * @since PHP4.04pl1
+ * @access public
+ */
+class HTML_QuickForm_group extends HTML_QuickForm_element
+{
+ // {{{ properties
+
+ /**
+ * Name of the element
+ * @var string
+ * @since 1.0
+ * @access private
+ */
+ var $_name = '';
+
+ /**
+ * Array of grouped elements
+ * @var array
+ * @since 1.0
+ * @access private
+ */
+ var $_elements = array();
+
+ /**
+ * String to separate elements
+ * @var mixed
+ * @since 2.5
+ * @access private
+ */
+ var $_separator = null;
+
+ /**
+ * Required elements in this group
+ * @var array
+ * @since 2.5
+ * @access private
+ */
+ var $_required = array();
+
+ /**
+ * Whether to change elements' names to $groupName[$elementName] or leave them as is
+ * @var bool
+ * @since 3.0
+ * @access private
+ */
+ var $_appendName = true;
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ *
+ * @param string $elementName (optional)Group name
+ * @param array $elementLabel (optional)Group label
+ * @param array $elements (optional)Group elements
+ * @param mixed $separator (optional)Use a string for one separator,
+ * use an array to alternate the separators.
+ * @param bool $appendName (optional)whether to change elements' names to
+ * the form $groupName[$elementName] or leave
+ * them as is.
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function HTML_QuickForm_group($elementName=null, $elementLabel=null, $elements=null, $separator=null, $appendName = true)
+ {
+ $this->HTML_QuickForm_element($elementName, $elementLabel);
+ $this->_type = 'group';
+ if (isset($elements) && is_array($elements)) {
+ $this->setElements($elements);
+ }
+ if (isset($separator)) {
+ $this->_separator = $separator;
+ }
+ if (isset($appendName)) {
+ $this->_appendName = $appendName;
+ }
+ } //end constructor
+
+ // }}}
+ // {{{ setName()
+
+ /**
+ * Sets the group name
+ *
+ * @param string $name Group name
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setName($name)
+ {
+ $this->_name = $name;
+ } //end func setName
+
+ // }}}
+ // {{{ getName()
+
+ /**
+ * Returns the group name
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ */
+ function getName()
+ {
+ return $this->_name;
+ } //end func getName
+
+ // }}}
+ // {{{ setValue()
+
+ /**
+ * Sets values for group's elements
+ *
+ * @param mixed Values for group's elements
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setValue($value)
+ {
+ if (empty($this->_elements)) {
+ $this->_createElements();
+ }
+ foreach (array_keys($this->_elements) as $key) {
+ if (!$this->_appendName) {
+ $v = $this->_elements[$key]->_findValue($value);
+ if (null !== $v) {
+ $this->_elements[$key]->onQuickFormEvent('setGroupValue', $v, $this);
+ }
+
+ } else {
+ $elementName = $this->_elements[$key]->getName();
+ $index = (!empty($elementName)) ? $elementName : $key;
+ if (is_array($value)) {
+ if (isset($value[$index])) {
+ $this->_elements[$key]->onQuickFormEvent('setGroupValue', $value[$index], $this);
+ }
+ } elseif (isset($value)) {
+ $this->_elements[$key]->onQuickFormEvent('setGroupValue', $value, $this);
+ }
+ }
+ }
+ } //end func setValue
+
+ // }}}
+ // {{{ getValue()
+
+ /**
+ * Returns the value of the group
+ *
+ * @since 1.0
+ * @access public
+ * @return mixed
+ */
+ function getValue()
+ {
+ $value = null;
+ foreach (array_keys($this->_elements) as $key) {
+ $element =& $this->_elements[$key];
+ switch ($element->getType()) {
+ case 'radio':
+ $v = $element->getChecked()? $element->getValue(): null;
+ break;
+ case 'checkbox':
+ $v = $element->getChecked()? true: null;
+ break;
+ default:
+ $v = $element->getValue();
+ }
+ if (null !== $v) {
+ $elementName = $element->getName();
+ if (is_null($elementName)) {
+ $value = $v;
+ } else {
+ if (!is_array($value)) {
+ $value = is_null($value)? array(): array($value);
+ }
+ if ('' == $elementName) {
+ $value[] = $v;
+ } else {
+ $value[$elementName] = $v;
+ }
+ }
+ }
+ }
+ return $value;
+ } // end func getValue
+
+ // }}}
+ // {{{ setElements()
+
+ /**
+ * Sets the grouped elements
+ *
+ * @param array $elements Array of elements
+ * @since 1.1
+ * @access public
+ * @return void
+ */
+ function setElements($elements)
+ {
+ $this->_elements = array_values($elements);
+ if ($this->_flagFrozen) {
+ $this->freeze();
+ }
+ } // end func setElements
+
+ // }}}
+ // {{{ getElements()
+
+ /**
+ * Gets the grouped elements
+ *
+ * @since 2.4
+ * @access public
+ * @return array
+ */
+ function &getElements()
+ {
+ return $this->_elements;
+ } // end func getElements
+
+ // }}}
+ // {{{ getGroupType()
+
+ /**
+ * Gets the group type based on its elements
+ * Will return 'mixed' if elements contained in the group
+ * are of different types.
+ *
+ * @access public
+ * @return string group elements type
+ */
+ function getGroupType()
+ {
+ if (empty($this->_elements)) {
+ $this->_createElements();
+ }
+ $prevType = '';
+ foreach (array_keys($this->_elements) as $key) {
+ $type = $this->_elements[$key]->getType();
+ if ($type != $prevType && $prevType != '') {
+ return 'mixed';
+ }
+ $prevType = $type;
+ }
+ return $type;
+ } // end func getGroupType
+
+ // }}}
+ // {{{ toHtml()
+
+ /**
+ * Returns Html for the group
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ */
+ function toHtml()
+ {
+ include_once('HTML/QuickForm/Renderer/Default.php');
+ $renderer =& new HTML_QuickForm_Renderer_Default();
+ $renderer->setElementTemplate('{element}');
+ $this->accept($renderer);
+ return $renderer->toHtml();
+ } //end func toHtml
+
+ // }}}
+ // {{{ getElementName()
+
+ /**
+ * Returns the element name inside the group such as found in the html form
+ *
+ * @param mixed $index Element name or element index in the group
+ * @since 3.0
+ * @access public
+ * @return mixed string with element name, false if not found
+ */
+ function getElementName($index)
+ {
+ if (empty($this->_elements)) {
+ $this->_createElements();
+ }
+ $elementName = false;
+ if (is_int($index) && isset($this->_elements[$index])) {
+ $elementName = $this->_elements[$index]->getName();
+ if (isset($elementName) && $elementName == '') {
+ $elementName = $index;
+ }
+ if ($this->_appendName) {
+ if (is_null($elementName)) {
+ $elementName = $this->getName();
+ } else {
+ $elementName = $this->getName().'['.$elementName.']';
+ }
+ }
+
+ } elseif (is_string($index)) {
+ foreach (array_keys($this->_elements) as $key) {
+ $elementName = $this->_elements[$key]->getName();
+ if ($index == $elementName) {
+ if ($this->_appendName) {
+ $elementName = $this->getName().'['.$elementName.']';
+ }
+ break;
+ } elseif ($this->_appendName && $this->getName().'['.$elementName.']' == $index) {
+ break;
+ }
+ }
+ }
+ return $elementName;
+ } //end func getElementName
+
+ // }}}
+ // {{{ getFrozenHtml()
+
+ /**
+ * Returns the value of field without HTML tags
+ *
+ * @since 1.3
+ * @access public
+ * @return string
+ */
+ function getFrozenHtml()
+ {
+ $flags = array();
+ if (empty($this->_elements)) {
+ $this->_createElements();
+ }
+ foreach (array_keys($this->_elements) as $key) {
+ if (false === ($flags[$key] = $this->_elements[$key]->isFrozen())) {
+ $this->_elements[$key]->freeze();
+ }
+ }
+ $html = $this->toHtml();
+ foreach (array_keys($this->_elements) as $key) {
+ if (!$flags[$key]) {
+ $this->_elements[$key]->unfreeze();
+ }
+ }
+ return $html;
+ } //end func getFrozenHtml
+
+ // }}}
+ // {{{ onQuickFormEvent()
+
+ /**
+ * Called by HTML_QuickForm whenever form event is made on this element
+ *
+ * @param string $event Name of event
+ * @param mixed $arg event arguments
+ * @param object $caller calling object
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function onQuickFormEvent($event, $arg, &$caller)
+ {
+ switch ($event) {
+ case 'updateValue':
+ if (empty($this->_elements)) {
+ $this->_createElements();
+ }
+ foreach (array_keys($this->_elements) as $key) {
+ if ($this->_appendName) {
+ $elementName = $this->_elements[$key]->getName();
+ if (is_null($elementName)) {
+ $this->_elements[$key]->setName($this->getName());
+ } elseif ('' == $elementName) {
+ $this->_elements[$key]->setName($this->getName() . '[' . $key . ']');
+ } else {
+ $this->_elements[$key]->setName($this->getName() . '[' . $elementName . ']');
+ }
+ }
+ $this->_elements[$key]->onQuickFormEvent('updateValue', $arg, $caller);
+ if ($this->_appendName) {
+ $this->_elements[$key]->setName($elementName);
+ }
+ }
+ break;
+
+ default:
+ parent::onQuickFormEvent($event, $arg, $caller);
+ }
+ return true;
+ } // end func onQuickFormEvent
+
+ // }}}
+ // {{{ accept()
+
+ /**
+ * Accepts a renderer
+ *
+ * @param object An HTML_QuickForm_Renderer object
+ * @param bool Whether a group is required
+ * @param string An error message associated with a group
+ * @access public
+ * @return void
+ */
+ function accept(&$renderer, $required = false, $error = null)
+ {
+ if (empty($this->_elements)) {
+ $this->_createElements();
+ }
+ $renderer->startGroup($this, $required, $error);
+ $name = $this->getName();
+ foreach (array_keys($this->_elements) as $key) {
+ $element =& $this->_elements[$key];
+
+ if ($this->_appendName) {
+ $elementName = $element->getName();
+ if (isset($elementName)) {
+ $element->setName($name . '['. (strlen($elementName)? $elementName: $key) .']');
+ } else {
+ $element->setName($name);
+ }
+ }
+
+ $required = !$element->isFrozen() && in_array($element->getName(), $this->_required);
+
+ $element->accept($renderer, $required);
+
+ // restore the element's name
+ if ($this->_appendName) {
+ $element->setName($elementName);
+ }
+ }
+ $renderer->finishGroup($this);
+ } // end func accept
+
+ // }}}
+ // {{{ exportValue()
+
+ /**
+ * As usual, to get the group's value we access its elements and call
+ * their exportValue() methods
+ */
+ function exportValue(&$submitValues, $assoc = false)
+ {
+ $value = null;
+ foreach (array_keys($this->_elements) as $key) {
+ $elementName = $this->_elements[$key]->getName();
+ if ($this->_appendName) {
+ if (is_null($elementName)) {
+ $this->_elements[$key]->setName($this->getName());
+ } elseif ('' == $elementName) {
+ $this->_elements[$key]->setName($this->getName() . '[' . $key . ']');
+ } else {
+ $this->_elements[$key]->setName($this->getName() . '[' . $elementName . ']');
+ }
+ }
+ $v = $this->_elements[$key]->exportValue($submitValues, $assoc);
+ if ($this->_appendName) {
+ $this->_elements[$key]->setName($elementName);
+ }
+ if (null !== $v) {
+ // Make $value an array, we will use it like one
+ if (null === $value) {
+ $value = array();
+ }
+ if ($assoc) {
+ // just like HTML_QuickForm::exportValues()
+ $value = HTML_QuickForm::arrayMerge($value, $v);
+ } else {
+ // just like getValue(), but should work OK every time here
+ if (is_null($elementName)) {
+ $value = $v;
+ } elseif ('' == $elementName) {
+ $value[] = $v;
+ } else {
+ $value[$elementName] = $v;
+ }
+ }
+ }
+ }
+ // do not pass the value through _prepareValue, we took care of this already
+ return $value;
+ }
+
+ // }}}
+ // {{{ _createElements()
+
+ /**
+ * Creates the group's elements.
+ *
+ * This should be overriden by child classes that need to create their
+ * elements. The method will be called automatically when needed, calling
+ * it from the constructor is discouraged as the constructor is usually
+ * called _twice_ on element creation, first time with _no_ parameters.
+ *
+ * @access private
+ * @abstract
+ */
+ function _createElements()
+ {
+ // abstract
+ }
+
+ // }}}
+ // {{{ freeze()
+
+ function freeze()
+ {
+ parent::freeze();
+ foreach (array_keys($this->_elements) as $key) {
+ $this->_elements[$key]->freeze();
+ }
+ }
+
+ // }}}
+ // {{{ unfreeze()
+
+ function unfreeze()
+ {
+ parent::unfreeze();
+ foreach (array_keys($this->_elements) as $key) {
+ $this->_elements[$key]->unfreeze();
+ }
+ }
+
+ // }}}
+ // {{{ setPersistantFreeze()
+
+ function setPersistantFreeze($persistant = false)
+ {
+ parent::setPersistantFreeze($persistant);
+ foreach (array_keys($this->_elements) as $key) {
+ $this->_elements[$key]->setPersistantFreeze($persistant);
+ }
+ }
+
+ // }}}
+} //end class HTML_QuickForm_group
+?> \ No newline at end of file
diff --git a/pearlib/HTML/QuickForm/header.php b/pearlib/HTML/QuickForm/header.php
new file mode 100644
index 00000000..12e0232d
--- /dev/null
+++ b/pearlib/HTML/QuickForm/header.php
@@ -0,0 +1,65 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Alexey Borzov <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: header.php,v 1.1 2003/03/12 11:16:33 avb Exp $
+
+require_once 'HTML/QuickForm/static.php';
+
+/**
+ * A pseudo-element used for adding headers to form
+ *
+ * @author Alexey Borzov <[email protected]>
+ * @access public
+ */
+class HTML_QuickForm_header extends HTML_QuickForm_static
+{
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ *
+ * @param string $elementName Header name
+ * @param string $text Header text
+ * @access public
+ * @return void
+ */
+ function HTML_QuickForm_header($elementName = null, $text = null)
+ {
+ $this->HTML_QuickForm_static($elementName, null, $text);
+ $this->_type = 'header';
+ }
+
+ // }}}
+ // {{{ accept()
+
+ /**
+ * Accepts a renderer
+ *
+ * @param object An HTML_QuickForm_Renderer object
+ * @access public
+ * @return void
+ */
+ function accept(&$renderer)
+ {
+ $renderer->renderHeader($this);
+ } // end func accept
+
+ // }}}
+
+} //end class HTML_QuickForm_header
+?>
diff --git a/pearlib/HTML/QuickForm/hidden.php b/pearlib/HTML/QuickForm/hidden.php
new file mode 100644
index 00000000..99b10586
--- /dev/null
+++ b/pearlib/HTML/QuickForm/hidden.php
@@ -0,0 +1,87 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Adam Daniel <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: hidden.php,v 1.10 2003/06/18 19:36:20 avb Exp $
+
+require_once("HTML/QuickForm/input.php");
+
+/**
+ * HTML class for a hidden type element
+ *
+ * @author Adam Daniel <[email protected]>
+ * @author Bertrand Mansion <[email protected]>
+ * @version 1.0
+ * @since PHP4.04pl1
+ * @access public
+ */
+class HTML_QuickForm_hidden extends HTML_QuickForm_input
+{
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ *
+ * @param string $elementName (optional)Input field name attribute
+ * @param string $value (optional)Input field value
+ * @param mixed $attributes (optional)Either a typical HTML attribute string
+ * or an associative array
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function HTML_QuickForm_hidden($elementName=null, $value='', $attributes=null)
+ {
+ HTML_QuickForm_input::HTML_QuickForm_input($elementName, null, $attributes);
+ $this->setType('hidden');
+ $this->setValue($value);
+ } //end constructor
+
+ // }}}
+ // {{{ freeze()
+
+ /**
+ * Freeze the element so that only its value is returned
+ *
+ * @access public
+ * @return void
+ */
+ function freeze()
+ {
+ return false;
+ } //end func freeze
+
+ // }}}
+ // {{{ accept()
+
+ /**
+ * Accepts a renderer
+ *
+ * @param object An HTML_QuickForm_Renderer object
+ * @access public
+ * @return void
+ */
+ function accept(&$renderer)
+ {
+ $renderer->renderHidden($this);
+ } // end func accept
+
+ // }}}
+
+} //end class HTML_QuickForm_hidden
+?>
diff --git a/pearlib/HTML/QuickForm/hiddenselect.php b/pearlib/HTML/QuickForm/hiddenselect.php
new file mode 100644
index 00000000..6df76f20
--- /dev/null
+++ b/pearlib/HTML/QuickForm/hiddenselect.php
@@ -0,0 +1,103 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Adam Daniel <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: hiddenselect.php,v 1.3 2003/06/18 19:36:20 avb Exp $
+
+require_once('HTML/QuickForm/select.php');
+
+/**
+ * This class takes the same arguments as a select element, but instead
+ * of creating a select ring it creates hidden elements for all values
+ * already selected with setDefault or setConstant. This is useful if
+ * you have a select ring that you don't want visible, but you need all
+ * selected values to be passed.
+ *
+ * @author Isaac Shepard <[email protected]>
+ *
+ * @version 1.0
+ * @since 2.1
+ * @access public
+ */
+class HTML_QuickForm_hiddenselect extends HTML_QuickForm_select
+{
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ *
+ * @param string Select name attribute
+ * @param mixed Label(s) for the select (not used)
+ * @param mixed Data to be used to populate options
+ * @param mixed Either a typical HTML attribute string or an associative array (not used)
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function HTML_QuickForm_hiddenselect($elementName=null, $elementLabel=null, $options=null, $attributes=null)
+ {
+ HTML_QuickForm_element::HTML_QuickForm_element($elementName, $elementLabel, $attributes);
+ $this->_persistantFreeze = true;
+ $this->_type = 'hiddenselect';
+ if (isset($options)) {
+ $this->load($options);
+ }
+ } //end constructor
+
+ // }}}
+ // {{{ toHtml()
+
+ /**
+ * Returns the SELECT in HTML
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ * @throws
+ */
+ function toHtml()
+ {
+ $tabs = $this->_getTabs();
+ $name = $this->getPrivateName();
+ $strHtml = '';
+
+ foreach ($this->_values as $key => $val) {
+ for ($i = 0, $optCount = count($this->_options); $i < $optCount; $i++) {
+ if ($val == $this->_options[$i]['attr']['value']) {
+ $strHtml .= $tabs . '<input type="hidden" name="' . $name . '" value="' . $val . '" />' . "\n";
+ }
+ }
+ }
+
+ return $strHtml;
+ } //end func toHtml
+
+ // }}}
+ // {{{ accept()
+
+ /**
+ * This is essentially a hidden element and should be rendered as one
+ */
+ function accept(&$renderer)
+ {
+ $renderer->renderHidden($this);
+ }
+
+ // }}}
+} //end class HTML_QuickForm_hiddenselect
+?>
diff --git a/pearlib/HTML/QuickForm/hierselect.php b/pearlib/HTML/QuickForm/hierselect.php
new file mode 100644
index 00000000..d851ae70
--- /dev/null
+++ b/pearlib/HTML/QuickForm/hierselect.php
@@ -0,0 +1,444 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Herim Vasquez <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: hierselect.php,v 1.12 2004/10/20 10:03:49 avb Exp $
+
+require_once('HTML/QuickForm/group.php');
+require_once('HTML/QuickForm/select.php');
+
+/**
+ * Class to dynamically create two or more HTML Select elements
+ * The first select changes the content of the second select and so on.
+ * This element is considered as a group. Selects will be named
+ * groupName[0], groupName[1], groupName[2]...
+ *
+ * @author Herim Vasquez <[email protected]>
+ * @author Bertrand Mansion <[email protected]>
+ * @version 1.0
+ * @since PHP4.04pl1
+ * @access public
+ */
+class HTML_QuickForm_hierselect extends HTML_QuickForm_group
+{
+ // {{{ properties
+
+ /**
+ * Options for all the select elements
+ *
+ * Format is a bit more complex as we need to know which options
+ * are related to the ones in the previous select:
+ *
+ * Ex:
+ * // first select
+ * $select1[0] = 'Pop';
+ * $select1[1] = 'Classical';
+ * $select1[2] = 'Funeral doom';
+ *
+ * // second select
+ * $select2[0][0] = 'Red Hot Chil Peppers';
+ * $select2[0][1] = 'The Pixies';
+ * $select2[1][0] = 'Wagner';
+ * $select2[1][1] = 'Strauss';
+ * $select2[2][0] = 'Pantheist';
+ * $select2[2][1] = 'Skepticism';
+ *
+ * // If only need two selects
+ * // - and using the depracated functions
+ * $sel =& $form->addElement('hierselect', 'cds', 'Choose CD:');
+ * $sel->setMainOptions($select1);
+ * $sel->setSecOptions($select2);
+ *
+ * // - and using the new setOptions function
+ * $sel =& $form->addElement('hierselect', 'cds', 'Choose CD:');
+ * $sel->setOptions(array($select1, $select2));
+ *
+ * // If you have a third select with prices for the cds
+ * $select3[0][0][0] = '15.00$';
+ * $select3[0][0][1] = '17.00$';
+ * etc
+ *
+ * // You can now use
+ * $sel =& $form->addElement('hierselect', 'cds', 'Choose CD:');
+ * $sel->setOptions(array($select1, $select2, $select3));
+ *
+ * @var array
+ * @access private
+ */
+ var $_options = array();
+
+ /**
+ * Number of select elements on this group
+ *
+ * @var int
+ * @access private
+ */
+ var $_nbElements = 0;
+
+ /**
+ * The javascript used to set and change the options
+ *
+ * @var string
+ * @access private
+ */
+ var $_js = '';
+
+ /**
+ * The javascript array name
+ */
+ var $_jsArrayName = '';
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ *
+ * @param string $elementName (optional)Input field name attribute
+ * @param string $elementLabel (optional)Input field label in form
+ * @param mixed $attributes (optional)Either a typical HTML attribute string
+ * or an associative array. Date format is passed along the attributes.
+ * @param mixed $separator (optional)Use a string for one separator,
+ * use an array to alternate the separators.
+ * @access public
+ * @return void
+ */
+ function HTML_QuickForm_hierselect($elementName=null, $elementLabel=null, $attributes=null, $separator=null)
+ {
+ $this->HTML_QuickForm_element($elementName, $elementLabel, $attributes);
+ $this->_persistantFreeze = true;
+ if (isset($separator)) {
+ $this->_separator = $separator;
+ }
+ $this->_type = 'hierselect';
+ $this->_appendName = true;
+ } //end constructor
+
+ // }}}
+ // {{{ setOptions()
+
+ /**
+ * Initialize the array structure containing the options for each select element.
+ * Call the functions that actually do the magic.
+ *
+ * @param array $options Array of options defining each element
+ *
+ * @access public
+ * @return void
+ */
+ function setOptions($options)
+ {
+ $this->_options = $options;
+
+ if (empty($this->_elements)) {
+ $this->_nbElements = count($this->_options);
+ $this->_createElements();
+ } else {
+ // setDefaults has probably been called before this function
+ // check if all elements have been created
+ $totalNbElements = count($this->_options);
+ for ($i = $this->_nbElements; $i < $totalNbElements; $i ++) {
+ $this->_elements[] =& new HTML_QuickForm_select($i, null, array(), $this->getAttributes());
+ $this->_nbElements++;
+ }
+ }
+
+ $this->_setOptions();
+ $this->_setJS();
+ } // end func setMainOptions
+
+ // }}}
+ // {{{ setMainOptions()
+
+ /**
+ * Sets the options for the first select element. Deprecated. setOptions() should be used.
+ *
+ * @param array $array Options for the first select element
+ *
+ * @access public
+ * @return void
+ */
+ function setMainOptions($array)
+ {
+ $this->_options[0] = $array;
+
+ if (empty($this->_elements)) {
+ $this->_nbElements = 2;
+ $this->_createElements();
+ }
+ } // end func setMainOptions
+
+ // }}}
+ // {{{ setSecOptions()
+
+ /**
+ * Sets the options for the second select element. Deprecated. setOptions() should be used.
+ * The main _options array is initialized and the _setOptions function is called.
+ *
+ * @param array $array Options for the second select element
+ *
+ * @access public
+ * @return void
+ */
+ function setSecOptions($array)
+ {
+ $this->_options[1] = $array;
+
+ if (empty($this->_elements)) {
+ $this->_nbElements = 2;
+ $this->_createElements();
+ } else {
+ // setDefaults has probably been called before this function
+ // check if all elements have been created
+ $totalNbElements = 2;
+ for ($i = $this->_nbElements; $i < $totalNbElements; $i ++) {
+ $this->_elements[] =& new HTML_QuickForm_select($i, null, array(), $this->getAttributes());
+ $this->_nbElements++;
+ }
+ }
+
+ $this->_setOptions();
+ $this->_setJS();
+ } // end func setSecOptions
+
+ // }}}
+ // {{{ _setOptions()
+
+ /**
+ * Sets the options for each select element
+ *
+ * @access private
+ * @return void
+ */
+ function _setOptions()
+ {
+ $toLoad = '';
+ foreach (array_keys($this->_elements) AS $key) {
+ if (eval("return isset(\$this->_options[{$key}]{$toLoad});") ) {
+ $array = eval("return \$this->_options[{$key}]{$toLoad};");
+ if (is_array($array)) {
+ $select =& $this->_elements[$key];
+ $select->_options = array();
+ $select->loadArray($array);
+
+ $value = is_array($v = $select->getValue()) ? $v[0] : key($array);
+ $toLoad .= '[\''.$value.'\']';
+ }
+ }
+ }
+ } // end func _setOptions
+
+ // }}}
+ // {{{ setValue()
+
+ /**
+ * Sets values for group's elements
+ *
+ * @param array $value An array of 2 or more values, for the first,
+ * the second, the third etc. select
+ *
+ * @access public
+ * @return void
+ */
+ function setValue($value)
+ {
+ $this->_nbElements = count($value);
+ parent::setValue($value);
+ $this->_setOptions();
+ } // end func setValue
+
+ // }}}
+ // {{{ _createElements()
+
+ /**
+ * Creates all the elements for the group
+ *
+ * @access private
+ * @return void
+ */
+ function _createElements()
+ {
+ for ($i = 0; $i < $this->_nbElements; $i++) {
+ $this->_elements[] =& new HTML_QuickForm_select($i, null, array(), $this->getAttributes());
+ }
+ } // end func _createElements
+
+ // }}}
+ // {{{ _setJS()
+
+ /**
+ * Set the JavaScript for each select element (excluding de main one).
+ *
+ * @access private
+ * @return void
+ */
+ function _setJS()
+ {
+ $this->_js = $js = '';
+ $this->_jsArrayName = 'hs_' . $this->getName();
+ for ($i = 1; $i < $this->_nbElements; $i++) {
+ $this->_setJSArray($this->_jsArrayName, $this->_options[$i], $js);
+ }
+ } // end func _setJS
+
+ // }}}
+ // {{{ _setJSArray()
+
+ /**
+ * Recursively builds the JavaScript array defining the options that a select
+ * element can have.
+ *
+ * @param string $grpName Group Name attribute
+ * @param array $options Select element options
+ * @param string $js JavaScript definition is build using this variable
+ * @param string $optValue The value for the current JavaScript option
+ *
+ * @access private
+ * @return void
+ */
+ function _setJSArray($grpName, $options, &$js, $optValue = '')
+ {
+ if (is_array($options)) {
+ $js = '';
+ // For a hierselect containing 3 elements:
+ // if option 1 has been selected for the 1st element
+ // and option 3 has been selected for the 2nd element,
+ // then the javascript array containing the values to load
+ // on the 3rd element will have the following name: grpName_1_3
+ $name = ($optValue === '') ? $grpName : $grpName.'_'.$optValue;
+ foreach($options AS $k => $v) {
+ $this->_setJSArray($name, $v, $js, $k);
+ }
+
+ // if $js !== '' add it to the JavaScript
+ $this->_js .= ($js !== '') ? $name." = {\n".$js."\n}\n" : '';
+ $js = '';
+ } else {
+ // $js empty means that we are adding the first element to the JavaScript.
+ if ($js != '') {
+ $js .= ",\n";
+ }
+ $js .= '"'.$optValue.'":"'.$options.'"';
+ }
+ }
+
+ // }}}
+ // {{{ toHtml()
+
+ /**
+ * Returns Html for the group
+ *
+ * @access public
+ * @return string
+ */
+ function toHtml()
+ {
+ if ($this->_flagFrozen) {
+ $this->_js = '';
+ } else {
+ // set the onchange attribute for each element
+ $keys = array_keys($this->_elements);
+ $nbElements = count($keys);
+ $nbElementsUsingFnc = $nbElements - 1; // last element doesn't need it
+ for ($i = 0; $i < $nbElementsUsingFnc; $i++) {
+ $select =& $this->_elements[$keys[$i]];
+ $select->updateAttributes(
+ array('onChange' => 'swapOptions(this, \''.$this->getName().'\', '.$keys[$i].', '.$nbElements.', \''.$this->_jsArrayName.'\');')
+ );
+ }
+
+ // create the js function to call
+ if (!defined('HTML_QUICKFORM_HIERSELECT_EXISTS')) {
+ $this->_js .= "function swapOptions(frm, grpName, eleIndex, nbElements, arName)\n"
+ ."{\n"
+ ." var n = \"\";\n"
+ ." var ctl;\n\n"
+ ." for (var i = 0; i < nbElements; i++) {\n"
+ ." ctl = frm.form[grpName+'['+i+']'];\n"
+ ." if (!ctl) {\n"
+ ." ctl = frm.form[grpName+'['+i+'][]'];\n"
+ ." }\n"
+ ." if (i <= eleIndex) {\n"
+ ." n += \"_\"+ctl.value;\n"
+ ." } else {\n"
+ ." ctl.length = 0;\n"
+ ." }\n"
+ ." }\n\n"
+ ." var t = eval(\"typeof(\"+arName + n +\")\");\n"
+ ." if (t != 'undefined') {\n"
+ ." var the_array = eval(arName+n);\n"
+ ." var j = 0;\n"
+ ." n = eleIndex + 1;\n"
+ ." ctl = frm.form[grpName+'['+ n +']'];\n"
+ ." if (!ctl) {\n"
+ ." ctl = frm.form[grpName+'['+ n +'][]'];\n"
+ ." }\n"
+ ." for (var i in the_array) {\n"
+ ." opt = new Option(the_array[i], i, false, false);\n"
+ ." ctl.options[j++] = opt;\n"
+ ." }\n"
+ ." }\n"
+ ." if (eleIndex+1 < nbElements) {\n"
+ ." swapOptions(frm, grpName, eleIndex+1, nbElements, arName);\n"
+ ." }\n"
+ ."}\n";
+ define('HTML_QUICKFORM_HIERSELECT_EXISTS', true);
+ }
+ }
+ include_once('HTML/QuickForm/Renderer/Default.php');
+ $renderer =& new HTML_QuickForm_Renderer_Default();
+ $renderer->setElementTemplate('{element}');
+ parent::accept($renderer);
+ return "<script type=\"text/javascript\">\n//<![CDATA[\n" . $this->_js . "//]]>\n</script>" .
+ $renderer->toHtml();
+ } // end func toHtml
+
+ // }}}
+ // {{{ accept()
+
+ /**
+ * Accepts a renderer
+ *
+ * @param object An HTML_QuickForm_Renderer object
+ * @param bool Whether a group is required
+ * @param string An error message associated with a group
+ * @access public
+ * @return void
+ */
+ function accept(&$renderer, $required = false, $error = null)
+ {
+ $renderer->renderElement($this, $required, $error);
+ } // end func accept
+
+ // }}}
+ // {{{ onQuickFormEvent()
+
+ function onQuickFormEvent($event, $arg, &$caller)
+ {
+ if ('updateValue' == $event) {
+ // we need to call setValue() so that the secondary option
+ // matches the main option
+ return HTML_QuickForm_element::onQuickFormEvent($event, $arg, $caller);
+ } else {
+ return parent::onQuickFormEvent($event, $arg, $caller);
+ }
+ } // end func onQuickFormEvent
+
+ // }}}
+} // end class HTML_QuickForm_hierselect
+?> \ No newline at end of file
diff --git a/pearlib/HTML/QuickForm/html.php b/pearlib/HTML/QuickForm/html.php
new file mode 100644
index 00000000..e0e6a6a8
--- /dev/null
+++ b/pearlib/HTML/QuickForm/html.php
@@ -0,0 +1,67 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Alexey Borzov <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: html.php,v 1.1 2003/03/12 11:16:33 avb Exp $
+
+require_once 'HTML/QuickForm/static.php';
+
+/**
+ * A pseudo-element used for adding raw HTML to form
+ *
+ * Intended for use with the default renderer only, template-based
+ * ones may (and probably will) completely ignore this
+ *
+ * @author Alexey Borzov <[email protected]>
+ * @access public
+ */
+class HTML_QuickForm_html extends HTML_QuickForm_static
+{
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ *
+ * @param string $text raw HTML to add
+ * @access public
+ * @return void
+ */
+ function HTML_QuickForm_html($text = null)
+ {
+ $this->HTML_QuickForm_static(null, null, $text);
+ $this->_type = 'html';
+ }
+
+ // }}}
+ // {{{ accept()
+
+ /**
+ * Accepts a renderer
+ *
+ * @param object An HTML_QuickForm_Renderer object
+ * @access public
+ * @return void
+ */
+ function accept(&$renderer)
+ {
+ $renderer->renderHtml($this);
+ } // end func accept
+
+ // }}}
+
+} //end class HTML_QuickForm_header
+?>
diff --git a/pearlib/HTML/QuickForm/image.php b/pearlib/HTML/QuickForm/image.php
new file mode 100644
index 00000000..c06dbfe7
--- /dev/null
+++ b/pearlib/HTML/QuickForm/image.php
@@ -0,0 +1,119 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Adam Daniel <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: image.php,v 1.4 2003/06/18 19:36:20 avb Exp $
+require_once("HTML/QuickForm/input.php");
+
+/**
+ * HTML class for a image type element
+ *
+ * @author Adam Daniel <[email protected]>
+ * @author Bertrand Mansion <[email protected]>
+ * @version 1.0
+ * @since PHP4.04pl1
+ * @access public
+ */
+class HTML_QuickForm_image extends HTML_QuickForm_input
+{
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ *
+ * @param string $elementName (optional)Element name attribute
+ * @param string $src (optional)Image source
+ * @param mixed $attributes (optional)Either a typical HTML attribute string
+ * or an associative array
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function HTML_QuickForm_image($elementName=null, $src='', $attributes=null)
+ {
+ HTML_QuickForm_input::HTML_QuickForm_input($elementName, null, $attributes);
+ $this->setType('image');
+ $this->setSource($src);
+ } // end class constructor
+
+ // }}}
+ // {{{ setSource()
+
+ /**
+ * Sets source for image element
+ *
+ * @param string $src source for image element
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setSource($src)
+ {
+ $this->updateAttributes(array('src' => $src));
+ } // end func setSource
+
+ // }}}
+ // {{{ setBorder()
+
+ /**
+ * Sets border size for image element
+ *
+ * @param string $border border for image element
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setBorder($border)
+ {
+ $this->updateAttributes(array('border' => $border));
+ } // end func setBorder
+
+ // }}}
+ // {{{ setAlign()
+
+ /**
+ * Sets alignment for image element
+ *
+ * @param string $align alignment for image element
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setAlign($align)
+ {
+ $this->updateAttributes(array('align' => $align));
+ } // end func setAlign
+
+ // }}}
+ // {{{ freeze()
+
+ /**
+ * Freeze the element so that only its value is returned
+ *
+ * @access public
+ * @return void
+ */
+ function freeze()
+ {
+ return false;
+ } //end func freeze
+
+ // }}}
+
+} // end class HTML_QuickForm_image
+?>
diff --git a/pearlib/HTML/QuickForm/input.php b/pearlib/HTML/QuickForm/input.php
new file mode 100644
index 00000000..238ad0df
--- /dev/null
+++ b/pearlib/HTML/QuickForm/input.php
@@ -0,0 +1,202 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Adam Daniel <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: input.php,v 1.8 2003/06/18 19:36:20 avb Exp $
+
+require_once("HTML/QuickForm/element.php");
+
+/**
+ * Base class for input form elements
+ *
+ * @author Adam Daniel <[email protected]>
+ * @author Bertrand Mansion <[email protected]>
+ * @version 1.0
+ * @since PHP4.04pl1
+ * @access public
+ * @abstract
+ */
+class HTML_QuickForm_input extends HTML_QuickForm_element
+{
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ *
+ * @param string Input field name attribute
+ * @param mixed Label(s) for the input field
+ * @param mixed Either a typical HTML attribute string or an associative array
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function HTML_QuickForm_input($elementName=null, $elementLabel=null, $attributes=null)
+ {
+ $this->HTML_QuickForm_element($elementName, $elementLabel, $attributes);
+ } //end constructor
+
+ // }}}
+ // {{{ setType()
+
+ /**
+ * Sets the element type
+ *
+ * @param string $type Element type
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setType($type)
+ {
+ $this->_type = $type;
+ $this->updateAttributes(array('type'=>$type));
+ } // end func setType
+
+ // }}}
+ // {{{ setName()
+
+ /**
+ * Sets the input field name
+ *
+ * @param string $name Input field name attribute
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setName($name)
+ {
+ $this->updateAttributes(array('name'=>$name));
+ } //end func setName
+
+ // }}}
+ // {{{ getName()
+
+ /**
+ * Returns the element name
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ */
+ function getName()
+ {
+ return $this->getAttribute('name');
+ } //end func getName
+
+ // }}}
+ // {{{ setValue()
+
+ /**
+ * Sets the value of the form element
+ *
+ * @param string $value Default value of the form element
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setValue($value)
+ {
+ $this->updateAttributes(array('value'=>$value));
+ } // end func setValue
+
+ // }}}
+ // {{{ getValue()
+
+ /**
+ * Returns the value of the form element
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ */
+ function getValue()
+ {
+ return $this->getAttribute('value');
+ } // end func getValue
+
+ // }}}
+ // {{{ toHtml()
+
+ /**
+ * Returns the input field in HTML
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ */
+ function toHtml()
+ {
+ if ($this->_flagFrozen) {
+ return $this->getFrozenHtml();
+ } else {
+ return $this->_getTabs() . '<input' . $this->_getAttrString($this->_attributes) . ' />';
+ }
+ } //end func toHtml
+
+ // }}}
+ // {{{ onQuickFormEvent()
+
+ /**
+ * Called by HTML_QuickForm whenever form event is made on this element
+ *
+ * @param string $event Name of event
+ * @param mixed $arg event arguments
+ * @param object $caller calling object
+ * @since 1.0
+ * @access public
+ * @return void
+ * @throws
+ */
+ function onQuickFormEvent($event, $arg, &$caller)
+ {
+ // do not use submit values for button-type elements
+ $type = $this->getType();
+ if (('updateValue' != $event) ||
+ ('submit' != $type && 'reset' != $type && 'image' != $type && 'button' != $type)) {
+ parent::onQuickFormEvent($event, $arg, $caller);
+ } else {
+ $value = $this->_findValue($caller->_constantValues);
+ if (null === $value) {
+ $value = $this->_findValue($caller->_defaultValues);
+ }
+ if (null !== $value) {
+ $this->setValue($value);
+ }
+ }
+ return true;
+ } // end func onQuickFormEvent
+
+ // }}}
+ // {{{ exportValue()
+
+ /**
+ * We don't need values from button-type elements (except submit) and files
+ */
+ function exportValue(&$submitValues, $assoc = false)
+ {
+ $type = $this->getType();
+ if ('reset' == $type || 'image' == $type || 'button' == $type || 'file' == $type) {
+ return null;
+ } else {
+ return parent::exportValue($submitValues, $assoc);
+ }
+ }
+
+ // }}}
+} // end class HTML_QuickForm_element
+?>
diff --git a/pearlib/HTML/QuickForm/link.php b/pearlib/HTML/QuickForm/link.php
new file mode 100644
index 00000000..947f76c3
--- /dev/null
+++ b/pearlib/HTML/QuickForm/link.php
@@ -0,0 +1,192 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Adam Daniel <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+
+require_once 'HTML/QuickForm/static.php';
+
+/**
+ * HTML class for a link type field
+ *
+ * @author Adam Daniel <[email protected]>
+ * @author Bertrand Mansion <[email protected]>
+ * @version 1.0
+ * @since PHP4.04pl1
+ * @access public
+ */
+class HTML_QuickForm_link extends HTML_QuickForm_static
+{
+ // {{{ properties
+
+ /**
+ * Link display text
+ * @var string
+ * @since 1.0
+ * @access private
+ */
+ var $_text = "";
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ *
+ * @param string $elementLabel (optional)Link label
+ * @param string $href (optional)Link href
+ * @param string $text (optional)Link display text
+ * @param mixed $attributes (optional)Either a typical HTML attribute string
+ * or an associative array
+ * @since 1.0
+ * @access public
+ * @return void
+ * @throws
+ */
+ function HTML_QuickForm_link($elementName=null, $elementLabel=null, $href=null, $text=null, $attributes=null)
+ {
+ HTML_QuickForm_element::HTML_QuickForm_element($elementName, $elementLabel, $attributes);
+ $this->_persistantFreeze = false;
+ $this->_type = 'link';
+ $this->setHref($href);
+ $this->_text = $text;
+ } //end constructor
+
+ // }}}
+ // {{{ setName()
+
+ /**
+ * Sets the input field name
+ *
+ * @param string $name Input field name attribute
+ * @since 1.0
+ * @access public
+ * @return void
+ * @throws
+ */
+ function setName($name)
+ {
+ $this->updateAttributes(array('name'=>$name));
+ } //end func setName
+
+ // }}}
+ // {{{ getName()
+
+ /**
+ * Returns the element name
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ * @throws
+ */
+ function getName()
+ {
+ return $this->getAttribute('name');
+ } //end func getName
+
+ // }}}
+ // {{{ setValue()
+
+ /**
+ * Sets value for textarea element
+ *
+ * @param string $value Value for password element
+ * @since 1.0
+ * @access public
+ * @return void
+ * @throws
+ */
+ function setValue($value)
+ {
+ return;
+ } //end func setValue
+
+ // }}}
+ // {{{ getValue()
+
+ /**
+ * Returns the value of the form element
+ *
+ * @since 1.0
+ * @access public
+ * @return void
+ * @throws
+ */
+ function getValue()
+ {
+ return;
+ } // end func getValue
+
+
+ // }}}
+ // {{{ setHref()
+
+ /**
+ * Sets the links href
+ *
+ * @param string $href
+ * @since 1.0
+ * @access public
+ * @return void
+ * @throws
+ */
+ function setHref($href)
+ {
+ $this->updateAttributes(array('href'=>$href));
+ } // end func setHref
+
+ // }}}
+ // {{{ toHtml()
+
+ /**
+ * Returns the textarea element in HTML
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ * @throws
+ */
+ function toHtml()
+ {
+ $tabs = $this->_getTabs();
+ $html = "$tabs<a".$this->_getAttrString($this->_attributes).">";
+ $html .= $this->_text;
+ $html .= "</a>";
+ return $html;
+ } //end func toHtml
+
+ // }}}
+ // {{{ getFrozenHtml()
+
+ /**
+ * Returns the value of field without HTML tags (in this case, value is changed to a mask)
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ * @throws
+ */
+ function getFrozenHtml()
+ {
+ return;
+ } //end func getFrozenHtml
+
+ // }}}
+
+} //end class HTML_QuickForm_textarea
+?>
diff --git a/pearlib/HTML/QuickForm/password.php b/pearlib/HTML/QuickForm/password.php
new file mode 100644
index 00000000..7f5d0321
--- /dev/null
+++ b/pearlib/HTML/QuickForm/password.php
@@ -0,0 +1,108 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Adam Daniel <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: password.php,v 1.6 2004/02/28 22:10:16 avb Exp $
+
+require_once("HTML/QuickForm/input.php");
+
+/**
+ * HTML class for a password type field
+ *
+ * @author Adam Daniel <[email protected]>
+ * @author Bertrand Mansion <[email protected]>
+ * @version 1.1
+ * @since PHP4.04pl1
+ * @access public
+ */
+class HTML_QuickForm_password extends HTML_QuickForm_input
+{
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ *
+ * @param string $elementName (optional)Input field name attribute
+ * @param string $elementLabel (optional)Input field label
+ * @param mixed $attributes (optional)Either a typical HTML attribute string
+ * or an associative array
+ * @since 1.0
+ * @access public
+ * @return void
+ * @throws
+ */
+ function HTML_QuickForm_password($elementName=null, $elementLabel=null, $attributes=null)
+ {
+ HTML_QuickForm_input::HTML_QuickForm_input($elementName, $elementLabel, $attributes);
+ $this->setType('password');
+ } //end constructor
+
+ // }}}
+ // {{{ setSize()
+
+ /**
+ * Sets size of password element
+ *
+ * @param string $size Size of password field
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setSize($size)
+ {
+ $this->updateAttributes(array('size'=>$size));
+ } //end func setSize
+
+ // }}}
+ // {{{ setMaxlength()
+
+ /**
+ * Sets maxlength of password element
+ *
+ * @param string $maxlength Maximum length of password field
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setMaxlength($maxlength)
+ {
+ $this->updateAttributes(array('maxlength'=>$maxlength));
+ } //end func setMaxlength
+
+ // }}}
+ // {{{ getFrozenHtml()
+
+ /**
+ * Returns the value of field without HTML tags (in this case, value is changed to a mask)
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ * @throws
+ */
+ function getFrozenHtml()
+ {
+ $value = $this->getValue();
+ return ('' != $value? '**********': '&nbsp;') .
+ $this->_getPersistantData();
+ } //end func getFrozenHtml
+
+ // }}}
+
+} //end class HTML_QuickForm_password
+?>
diff --git a/pearlib/HTML/QuickForm/radio.php b/pearlib/HTML/QuickForm/radio.php
new file mode 100644
index 00000000..c8d3c131
--- /dev/null
+++ b/pearlib/HTML/QuickForm/radio.php
@@ -0,0 +1,244 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Adam Daniel <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: radio.php,v 1.17 2004/02/28 22:10:16 avb Exp $
+
+require_once('HTML/QuickForm/input.php');
+
+/**
+ * HTML class for a radio type element
+ *
+ * @author Adam Daniel <[email protected]>
+ * @author Bertrand Mansion <[email protected]>
+ * @version 1.1
+ * @since PHP4.04pl1
+ * @access public
+ */
+class HTML_QuickForm_radio extends HTML_QuickForm_input
+{
+ // {{{ properties
+
+ /**
+ * Radio display text
+ * @var string
+ * @since 1.1
+ * @access private
+ */
+ var $_text = '';
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ *
+ * @param string Input field name attribute
+ * @param mixed Label(s) for a field
+ * @param string Text to display near the radio
+ * @param string Input field value
+ * @param mixed Either a typical HTML attribute string or an associative array
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function HTML_QuickForm_radio($elementName=null, $elementLabel=null, $text=null, $value=null, $attributes=null)
+ {
+ $this->HTML_QuickForm_element($elementName, $elementLabel, $attributes);
+ if (isset($value)) {
+ $this->setValue($value);
+ }
+ $this->_persistantFreeze = true;
+ $this->setType('radio');
+ $this->_text = $text;
+ $this->_generateId();
+ } //end constructor
+
+ // }}}
+ // {{{ setChecked()
+
+ /**
+ * Sets whether radio button is checked
+ *
+ * @param bool $checked Whether the field is checked or not
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setChecked($checked)
+ {
+ if (!$checked) {
+ $this->removeAttribute('checked');
+ } else {
+ $this->updateAttributes(array('checked'=>'checked'));
+ }
+ } //end func setChecked
+
+ // }}}
+ // {{{ getChecked()
+
+ /**
+ * Returns whether radio button is checked
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ */
+ function getChecked()
+ {
+ return $this->getAttribute('checked');
+ } //end func getChecked
+
+ // }}}
+ // {{{ toHtml()
+
+ /**
+ * Returns the radio element in HTML
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ */
+ function toHtml()
+ {
+ if (0 == strlen($this->_text)) {
+ $label = '';
+ } elseif ($this->_flagFrozen) {
+ $label = $this->_text;
+ } else {
+ $label = '<label for="' . $this->getAttribute('id') . '">' . $this->_text . '</label>';
+ }
+ return HTML_QuickForm_input::toHtml() . $label;
+ } //end func toHtml
+
+ // }}}
+ // {{{ getFrozenHtml()
+
+ /**
+ * Returns the value of field without HTML tags
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ */
+ function getFrozenHtml()
+ {
+ if ($this->getChecked()) {
+ return '<tt>(x)</tt>' .
+ $this->_getPersistantData();
+ } else {
+ return '<tt>( )</tt>';
+ }
+ } //end func getFrozenHtml
+
+ // }}}
+ // {{{ setText()
+
+ /**
+ * Sets the radio text
+ *
+ * @param string $text Text to display near the radio button
+ * @since 1.1
+ * @access public
+ * @return void
+ */
+ function setText($text)
+ {
+ $this->_text = $text;
+ } //end func setText
+
+ // }}}
+ // {{{ getText()
+
+ /**
+ * Returns the radio text
+ *
+ * @since 1.1
+ * @access public
+ * @return string
+ */
+ function getText()
+ {
+ return $this->_text;
+ } //end func getText
+
+ // }}}
+ // {{{ onQuickFormEvent()
+
+ /**
+ * Called by HTML_QuickForm whenever form event is made on this element
+ *
+ * @param string $event Name of event
+ * @param mixed $arg event arguments
+ * @param object $caller calling object
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function onQuickFormEvent($event, $arg, &$caller)
+ {
+ switch ($event) {
+ case 'updateValue':
+ // constant values override both default and submitted ones
+ // default values are overriden by submitted
+ $value = $this->_findValue($caller->_constantValues);
+ if (null === $value) {
+ $value = $this->_findValue($caller->_submitValues);
+ if (null === $value) {
+ $value = $this->_findValue($caller->_defaultValues);
+ }
+ }
+ if ($value == $this->getValue()) {
+ $this->setChecked(true);
+ } else {
+ $this->setChecked(false);
+ }
+ break;
+ case 'setGroupValue':
+ if ($arg == $this->getValue()) {
+ $this->setChecked(true);
+ } else {
+ $this->setChecked(false);
+ }
+ break;
+ default:
+ parent::onQuickFormEvent($event, $arg, $caller);
+ }
+ return true;
+ } // end func onQuickFormLoad
+
+ // }}}
+ // {{{ exportValue()
+
+ /**
+ * Returns the value attribute if the radio is checked, null if it is not
+ */
+ function exportValue(&$submitValues, $assoc = false)
+ {
+ $value = $this->_findValue($submitValues);
+ if (null === $value) {
+ $value = $this->getChecked()? $this->getValue(): null;
+ } elseif ($value != $this->getValue()) {
+ $value = null;
+ }
+ return $this->_prepareValue($value, $assoc);
+ }
+
+ // }}}
+} //end class HTML_QuickForm_radio
+?>
diff --git a/pearlib/HTML/QuickForm/reset.php b/pearlib/HTML/QuickForm/reset.php
new file mode 100644
index 00000000..ff0c54c5
--- /dev/null
+++ b/pearlib/HTML/QuickForm/reset.php
@@ -0,0 +1,72 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Adam Daniel <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: reset.php,v 1.4 2003/06/18 19:36:20 avb Exp $
+
+require_once("HTML/QuickForm/input.php");
+
+/**
+ * HTML class for a reset type element
+ *
+ * @author Adam Daniel <[email protected]>
+ * @author Bertrand Mansion <[email protected]>
+ * @version 1.1
+ * @since PHP4.04pl1
+ * @access public
+ */
+class HTML_QuickForm_reset extends HTML_QuickForm_input
+{
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ *
+ * @param string $elementName (optional)Input field name attribute
+ * @param string $value (optional)Input field value
+ * @param mixed $attributes (optional)Either a typical HTML attribute string
+ * or an associative array
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function HTML_QuickForm_reset($elementName=null, $value=null, $attributes=null)
+ {
+ HTML_QuickForm_input::HTML_QuickForm_input($elementName, null, $attributes);
+ $this->setValue($value);
+ $this->setType('reset');
+ } //end constructor
+
+ // }}}
+ // {{{ freeze()
+
+ /**
+ * Freeze the element so that only its value is returned
+ *
+ * @access public
+ * @return void
+ */
+ function freeze()
+ {
+ return false;
+ } //end func freeze
+
+ // }}}
+
+} //end class HTML_QuickForm_reset
+?>
diff --git a/pearlib/HTML/QuickForm/select.php b/pearlib/HTML/QuickForm/select.php
new file mode 100644
index 00000000..99d862de
--- /dev/null
+++ b/pearlib/HTML/QuickForm/select.php
@@ -0,0 +1,577 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Adam Daniel <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: select.php,v 1.26 2004/02/28 22:10:16 avb Exp $
+
+require_once('HTML/QuickForm/element.php');
+
+/**
+ * Class to dynamically create an HTML SELECT
+ *
+ * @author Adam Daniel <[email protected]>
+ * @author Bertrand Mansion <[email protected]>
+ * @version 1.0
+ * @since PHP4.04pl1
+ * @access public
+ */
+class HTML_QuickForm_select extends HTML_QuickForm_element {
+
+ // {{{ properties
+
+ /**
+ * Contains the select options
+ *
+ * @var array
+ * @since 1.0
+ * @access private
+ */
+ var $_options = array();
+
+ /**
+ * Default values of the SELECT
+ *
+ * @var string
+ * @since 1.0
+ * @access private
+ */
+ var $_values = null;
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ *
+ * @param string Select name attribute
+ * @param mixed Label(s) for the select
+ * @param mixed Data to be used to populate options
+ * @param mixed Either a typical HTML attribute string or an associative array
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function HTML_QuickForm_select($elementName=null, $elementLabel=null, $options=null, $attributes=null)
+ {
+ HTML_QuickForm_element::HTML_QuickForm_element($elementName, $elementLabel, $attributes);
+ $this->_persistantFreeze = true;
+ $this->_type = 'select';
+ if (isset($options)) {
+ $this->load($options);
+ }
+ } //end constructor
+
+ // }}}
+ // {{{ apiVersion()
+
+ /**
+ * Returns the current API version
+ *
+ * @since 1.0
+ * @access public
+ * @return double
+ */
+ function apiVersion()
+ {
+ return 2.3;
+ } //end func apiVersion
+
+ // }}}
+ // {{{ setSelected()
+
+ /**
+ * Sets the default values of the select box
+ *
+ * @param mixed $values Array or comma delimited string of selected values
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setSelected($values)
+ {
+ if (is_string($values) && $this->getMultiple()) {
+ $values = split("[ ]?,[ ]?", $values);
+ }
+ if (is_array($values)) {
+ $this->_values = array_values($values);
+ } else {
+ $this->_values = array($values);
+ }
+ } //end func setSelected
+
+ // }}}
+ // {{{ getSelected()
+
+ /**
+ * Returns an array of the selected values
+ *
+ * @since 1.0
+ * @access public
+ * @return array of selected values
+ */
+ function getSelected()
+ {
+ return $this->_values;
+ } // end func getSelected
+
+ // }}}
+ // {{{ setName()
+
+ /**
+ * Sets the input field name
+ *
+ * @param string $name Input field name attribute
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setName($name)
+ {
+ $this->updateAttributes(array('name' => $name));
+ } //end func setName
+
+ // }}}
+ // {{{ getName()
+
+ /**
+ * Returns the element name
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ */
+ function getName()
+ {
+ return $this->getAttribute('name');
+ } //end func getName
+
+ // }}}
+ // {{{ getPrivateName()
+
+ /**
+ * Returns the element name (possibly with brackets appended)
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ */
+ function getPrivateName()
+ {
+ if ($this->getAttribute('multiple')) {
+ return $this->getName() . '[]';
+ } else {
+ return $this->getName();
+ }
+ } //end func getPrivateName
+
+ // }}}
+ // {{{ setValue()
+
+ /**
+ * Sets the value of the form element
+ *
+ * @param mixed $values Array or comma delimited string of selected values
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setValue($value)
+ {
+ $this->setSelected($value);
+ } // end func setValue
+
+ // }}}
+ // {{{ getValue()
+
+ /**
+ * Returns an array of the selected values
+ *
+ * @since 1.0
+ * @access public
+ * @return array of selected values
+ */
+ function getValue()
+ {
+ return $this->_values;
+ } // end func getValue
+
+ // }}}
+ // {{{ setSize()
+
+ /**
+ * Sets the select field size, only applies to 'multiple' selects
+ *
+ * @param int $size Size of select field
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setSize($size)
+ {
+ $this->updateAttributes(array('size' => $size));
+ } //end func setSize
+
+ // }}}
+ // {{{ getSize()
+
+ /**
+ * Returns the select field size
+ *
+ * @since 1.0
+ * @access public
+ * @return int
+ */
+ function getSize()
+ {
+ return $this->getAttribute('size');
+ } //end func getSize
+
+ // }}}
+ // {{{ setMultiple()
+
+ /**
+ * Sets the select mutiple attribute
+ *
+ * @param bool $multiple Whether the select supports multi-selections
+ * @since 1.2
+ * @access public
+ * @return void
+ */
+ function setMultiple($multiple)
+ {
+ if ($multiple) {
+ $this->updateAttributes(array('multiple' => 'multiple'));
+ } else {
+ $this->removeAttribute('multiple');
+ }
+ } //end func setMultiple
+
+ // }}}
+ // {{{ getMultiple()
+
+ /**
+ * Returns the select mutiple attribute
+ *
+ * @since 1.2
+ * @access public
+ * @return bool true if multiple select, false otherwise
+ */
+ function getMultiple()
+ {
+ return (bool)$this->getAttribute('multiple');
+ } //end func getMultiple
+
+ // }}}
+ // {{{ addOption()
+
+ /**
+ * Adds a new OPTION to the SELECT
+ *
+ * @param string $text Display text for the OPTION
+ * @param string $value Value for the OPTION
+ * @param mixed $attributes Either a typical HTML attribute string
+ * or an associative array
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function addOption($text, $value, $attributes=null)
+ {
+ if (null === $attributes) {
+ $attributes = array('value' => $value);
+ } else {
+ $attributes = $this->_parseAttributes($attributes);
+ if (isset($attributes['selected'])) {
+ // the 'selected' attribute will be set in toHtml()
+ $this->_removeAttr('selected', $attributes);
+ if (is_null($this->_values)) {
+ $this->_values = array($value);
+ } elseif (!in_array($value, $this->_values)) {
+ $this->_values[] = $value;
+ }
+ }
+ $this->_updateAttrArray($attributes, array('value' => $value));
+ }
+ $this->_options[] = array('text' => $text, 'attr' => $attributes);
+ } // end func addOption
+
+ // }}}
+ // {{{ loadArray()
+
+ /**
+ * Loads the options from an associative array
+ *
+ * @param array $arr Associative array of options
+ * @param mixed $values (optional) Array or comma delimited string of selected values
+ * @since 1.0
+ * @access public
+ * @return PEAR_Error on error or true
+ * @throws PEAR_Error
+ */
+ function loadArray($arr, $values=null)
+ {
+ if (!is_array($arr)) {
+ return PEAR::raiseError('Argument 1 of HTML_Select::loadArray is not a valid array');
+ }
+ if (isset($values)) {
+ $this->setSelected($values);
+ }
+ foreach ($arr as $key => $val) {
+ // Warning: new API since release 2.3
+ $this->addOption($val, $key);
+ }
+ return true;
+ } // end func loadArray
+
+ // }}}
+ // {{{ loadDbResult()
+
+ /**
+ * Loads the options from DB_result object
+ *
+ * If no column names are specified the first two columns of the result are
+ * used as the text and value columns respectively
+ * @param object $result DB_result object
+ * @param string $textCol (optional) Name of column to display as the OPTION text
+ * @param string $valueCol (optional) Name of column to use as the OPTION value
+ * @param mixed $values (optional) Array or comma delimited string of selected values
+ * @since 1.0
+ * @access public
+ * @return PEAR_Error on error or true
+ * @throws PEAR_Error
+ */
+ function loadDbResult(&$result, $textCol=null, $valueCol=null, $values=null)
+ {
+ if (!is_object($result) || !is_a($result, 'db_result')) {
+ return PEAR::raiseError('Argument 1 of HTML_Select::loadDbResult is not a valid DB_result');
+ }
+ if (isset($values)) {
+ $this->setValue($values);
+ }
+ $fetchMode = ($textCol && $valueCol) ? DB_FETCHMODE_ASSOC : DB_FETCHMODE_DEFAULT;
+ while (is_array($row = $result->fetchRow($fetchMode)) ) {
+ if ($fetchMode == DB_FETCHMODE_ASSOC) {
+ $this->addOption($row[$textCol], $row[$valueCol]);
+ } else {
+ $this->addOption($row[0], $row[1]);
+ }
+ }
+ return true;
+ } // end func loadDbResult
+
+ // }}}
+ // {{{ loadQuery()
+
+ /**
+ * Queries a database and loads the options from the results
+ *
+ * @param mixed $conn Either an existing DB connection or a valid dsn
+ * @param string $sql SQL query string
+ * @param string $textCol (optional) Name of column to display as the OPTION text
+ * @param string $valueCol (optional) Name of column to use as the OPTION value
+ * @param mixed $values (optional) Array or comma delimited string of selected values
+ * @since 1.1
+ * @access public
+ * @return void
+ * @throws PEAR_Error
+ */
+ function loadQuery(&$conn, $sql, $textCol=null, $valueCol=null, $values=null)
+ {
+ if (is_string($conn)) {
+ require_once('DB.php');
+ $dbConn = &DB::connect($conn, true);
+ if (DB::isError($dbConn)) {
+ return $dbConn;
+ }
+ } elseif (is_subclass_of($conn, "db_common")) {
+ $dbConn = &$conn;
+ } else {
+ return PEAR::raiseError('Argument 1 of HTML_Select::loadQuery is not a valid type');
+ }
+ $result = $dbConn->query($sql);
+ if (DB::isError($result)) {
+ return $result;
+ }
+ $this->loadDbResult($result, $textCol, $valueCol, $values);
+ $result->free();
+ if (is_string($conn)) {
+ $dbConn->disconnect();
+ }
+ return true;
+ } // end func loadQuery
+
+ // }}}
+ // {{{ load()
+
+ /**
+ * Loads options from different types of data sources
+ *
+ * This method is a simulated overloaded method. The arguments, other than the
+ * first are optional and only mean something depending on the type of the first argument.
+ * If the first argument is an array then all arguments are passed in order to loadArray.
+ * If the first argument is a db_result then all arguments are passed in order to loadDbResult.
+ * If the first argument is a string or a DB connection then all arguments are
+ * passed in order to loadQuery.
+ * @param mixed $options Options source currently supports assoc array or DB_result
+ * @param mixed $param1 (optional) See function detail
+ * @param mixed $param2 (optional) See function detail
+ * @param mixed $param3 (optional) See function detail
+ * @param mixed $param4 (optional) See function detail
+ * @since 1.1
+ * @access public
+ * @return PEAR_Error on error or true
+ * @throws PEAR_Error
+ */
+ function load(&$options, $param1=null, $param2=null, $param3=null, $param4=null)
+ {
+ switch (true) {
+ case is_array($options):
+ return $this->loadArray($options, $param1);
+ break;
+ case (is_a($options, 'db_result')):
+ return $this->loadDbResult($options, $param1, $param2, $param3);
+ break;
+ case (is_string($options) && !empty($options) || is_subclass_of($options, "db_common")):
+ return $this->loadQuery($options, $param1, $param2, $param3, $param4);
+ break;
+ }
+ } // end func load
+
+ // }}}
+ // {{{ toHtml()
+
+ /**
+ * Returns the SELECT in HTML
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ */
+ function toHtml()
+ {
+ if ($this->_flagFrozen) {
+ return $this->getFrozenHtml();
+ } else {
+ $tabs = $this->_getTabs();
+ $strHtml = '';
+
+ if ($this->getComment() != '') {
+ $strHtml .= $tabs . '<!-- ' . $this->getComment() . " //-->\n";
+ }
+
+ if (!$this->getMultiple()) {
+ $attrString = $this->_getAttrString($this->_attributes);
+ } else {
+ $myName = $this->getName();
+ $this->setName($myName . '[]');
+ $attrString = $this->_getAttrString($this->_attributes);
+ $this->setName($myName);
+ }
+ $strHtml .= $tabs . '<select' . $attrString . ">\n";
+
+ foreach ($this->_options as $option) {
+ if (is_array($this->_values) && in_array((string)$option['attr']['value'], $this->_values)) {
+ $this->_updateAttrArray($option['attr'], array('selected' => 'selected'));
+ }
+ $strHtml .= $tabs . "\t<option" . $this->_getAttrString($option['attr']) . '>' .
+ $option['text'] . "</option>\n";
+ }
+
+ return $strHtml . $tabs . '</select>';
+ }
+ } //end func toHtml
+
+ // }}}
+ // {{{ getFrozenHtml()
+
+ /**
+ * Returns the value of field without HTML tags
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ */
+ function getFrozenHtml()
+ {
+ $value = array();
+ if (is_array($this->_values)) {
+ foreach ($this->_values as $key => $val) {
+ for ($i = 0, $optCount = count($this->_options); $i < $optCount; $i++) {
+ if ($val == $this->_options[$i]['attr']['value']) {
+ $value[$key] = $this->_options[$i]['text'];
+ break;
+ }
+ }
+ }
+ }
+ $html = empty($value)? '&nbsp;': join('<br />', $value);
+ if ($this->_persistantFreeze) {
+ $name = $this->getPrivateName();
+ // Only use id attribute if doing single hidden input
+ if (1 == count($value)) {
+ $id = $this->getAttribute('id');
+ $idAttr = isset($id)? ' id="' . $id . '"': '';
+ } else {
+ $idAttr = '';
+ }
+ foreach ($value as $key => $item) {
+ $html .= '<input type="hidden"' . $idAttr . ' name="' .
+ $name . '" value="' . $this->_values[$key] . '" />';
+ }
+ }
+ return $html;
+ } //end func getFrozenHtml
+
+ // }}}
+ // {{{ exportValue()
+
+ /**
+ * We check the options and return only the values that _could_ have been
+ * selected. We also return a scalar value if select is not "multiple"
+ */
+ function exportValue(&$submitValues, $assoc = false)
+ {
+ $value = $this->_findValue($submitValues);
+ if (is_null($value)) {
+ $value = $this->getValue();
+ } elseif(!is_array($value)) {
+ $value = array($value);
+ }
+ if (is_array($value) && !empty($this->_options)) {
+ $cleanValue = null;
+ foreach ($value as $v) {
+ for ($i = 0, $optCount = count($this->_options); $i < $optCount; $i++) {
+ if ($v == $this->_options[$i]['attr']['value']) {
+ $cleanValue[] = $v;
+ break;
+ }
+ }
+ }
+ } else {
+ $cleanValue = $value;
+ }
+ if (is_array($cleanValue) && !$this->getMultiple()) {
+ return $this->_prepareValue($cleanValue[0], $assoc);
+ } else {
+ return $this->_prepareValue($cleanValue, $assoc);
+ }
+ }
+
+ // }}}
+} //end class HTML_QuickForm_select
+?>
diff --git a/pearlib/HTML/QuickForm/static.php b/pearlib/HTML/QuickForm/static.php
new file mode 100644
index 00000000..82f9b52b
--- /dev/null
+++ b/pearlib/HTML/QuickForm/static.php
@@ -0,0 +1,193 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Adam Daniel <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: static.php,v 1.6 2003/06/16 13:06:26 avb Exp $
+
+require_once("HTML/QuickForm/element.php");
+
+/**
+ * HTML class for static data
+ *
+ * @author Wojciech Gdela <[email protected]>
+ * @access public
+ */
+class HTML_QuickForm_static extends HTML_QuickForm_element {
+
+ // {{{ properties
+
+ /**
+ * Display text
+ * @var string
+ * @access private
+ */
+ var $_text = null;
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ *
+ * @param string $elementLabel (optional)Label
+ * @param string $text (optional)Display text
+ * @access public
+ * @return void
+ */
+ function HTML_QuickForm_static($elementName=null, $elementLabel=null, $text=null)
+ {
+ HTML_QuickForm_element::HTML_QuickForm_element($elementName, $elementLabel);
+ $this->_persistantFreeze = false;
+ $this->_type = 'static';
+ $this->_text = $text;
+ } //end constructor
+
+ // }}}
+ // {{{ setName()
+
+ /**
+ * Sets the element name
+ *
+ * @param string $name Element name
+ * @access public
+ * @return void
+ */
+ function setName($name)
+ {
+ $this->updateAttributes(array('name'=>$name));
+ } //end func setName
+
+ // }}}
+ // {{{ getName()
+
+ /**
+ * Returns the element name
+ *
+ * @access public
+ * @return string
+ */
+ function getName()
+ {
+ return $this->getAttribute('name');
+ } //end func getName
+
+ // }}}
+ // {{{ setText()
+
+ /**
+ * Sets the text
+ *
+ * @param string $text
+ * @access public
+ * @return void
+ */
+ function setText($text)
+ {
+ $this->_text = $text;
+ } // end func setText
+
+ // }}}
+ // {{{ setValue()
+
+ /**
+ * Sets the text (uses the standard setValue call to emulate a form element.
+ *
+ * @param string $text
+ * @access public
+ * @return void
+ */
+ function setValue($text)
+ {
+ $this->setText($text);
+ } // end func setValue
+
+ // }}}
+ // {{{ toHtml()
+
+ /**
+ * Returns the static text element in HTML
+ *
+ * @access public
+ * @return string
+ */
+ function toHtml()
+ {
+ return $this->_getTabs() . $this->_text;
+ } //end func toHtml
+
+ // }}}
+ // {{{ getFrozenHtml()
+
+ /**
+ * Returns the value of field without HTML tags
+ *
+ * @access public
+ * @return string
+ */
+ function getFrozenHtml()
+ {
+ return $this->toHtml();
+ } //end func getFrozenHtml
+
+ // }}}
+ // {{{ onQuickFormEvent()
+
+ /**
+ * Called by HTML_QuickForm whenever form event is made on this element
+ *
+ * @param string $event Name of event
+ * @param mixed $arg event arguments
+ * @param object $caller calling object
+ * @since 1.0
+ * @access public
+ * @return void
+ * @throws
+ */
+ function onQuickFormEvent($event, $arg, &$caller)
+ {
+ switch ($event) {
+ case 'updateValue':
+ // do NOT use submitted values for static elements
+ $value = $this->_findValue($caller->_constantValues);
+ if (null === $value) {
+ $value = $this->_findValue($caller->_defaultValues);
+ }
+ if (null !== $value) {
+ $this->setValue($value);
+ }
+ break;
+ default:
+ parent::onQuickFormEvent($event, $arg, $caller);
+ }
+ return true;
+ } // end func onQuickFormEvent
+
+ // }}}
+ // {{{ exportValue()
+
+ /**
+ * We override this here because we don't want any values from static elements
+ */
+ function exportValue(&$submitValues, $assoc = false)
+ {
+ return null;
+ }
+
+ // }}}
+} //end class HTML_QuickForm_static
+?>
diff --git a/pearlib/HTML/QuickForm/submit.php b/pearlib/HTML/QuickForm/submit.php
new file mode 100644
index 00000000..3959f4bc
--- /dev/null
+++ b/pearlib/HTML/QuickForm/submit.php
@@ -0,0 +1,82 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Adam Daniel <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: submit.php,v 1.4 2003/06/18 19:36:20 avb Exp $
+
+require_once("HTML/QuickForm/input.php");
+
+/**
+ * HTML class for a submit type element
+ *
+ * @author Adam Daniel <[email protected]>
+ * @author Bertrand Mansion <[email protected]>
+ * @version 1.0
+ * @since PHP4.04pl1
+ * @access public
+ */
+class HTML_QuickForm_submit extends HTML_QuickForm_input
+{
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ *
+ * @param string Input field name attribute
+ * @param string Input field value
+ * @param mixed Either a typical HTML attribute string or an associative array
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function HTML_QuickForm_submit($elementName=null, $value=null, $attributes=null)
+ {
+ HTML_QuickForm_input::HTML_QuickForm_input($elementName, null, $attributes);
+ $this->setValue($value);
+ $this->setType('submit');
+ } //end constructor
+
+ // }}}
+ // {{{ freeze()
+
+ /**
+ * Freeze the element so that only its value is returned
+ *
+ * @access public
+ * @return void
+ */
+ function freeze()
+ {
+ return false;
+ } //end func freeze
+
+ // }}}
+ // {{{ exportValue()
+
+ /**
+ * Only return the value if it is found within $submitValues (i.e. if
+ * this particular submit button was clicked)
+ */
+ function exportValue(&$submitValues, $assoc = false)
+ {
+ return $this->_prepareValue($this->_findValue($submitValues), $assoc);
+ }
+
+ // }}}
+} //end class HTML_QuickForm_submit
+?>
diff --git a/pearlib/HTML/QuickForm/text.php b/pearlib/HTML/QuickForm/text.php
new file mode 100644
index 00000000..652e7348
--- /dev/null
+++ b/pearlib/HTML/QuickForm/text.php
@@ -0,0 +1,91 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Adam Daniel <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: text.php,v 1.5 2003/06/18 19:36:20 avb Exp $
+
+require_once("HTML/QuickForm/input.php");
+
+/**
+ * HTML class for a text field
+ *
+ * @author Adam Daniel <[email protected]>
+ * @author Bertrand Mansion <[email protected]>
+ * @version 1.0
+ * @since PHP4.04pl1
+ * @access public
+ */
+class HTML_QuickForm_text extends HTML_QuickForm_input
+{
+
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ *
+ * @param string $elementName (optional)Input field name attribute
+ * @param string $elementLabel (optional)Input field label
+ * @param mixed $attributes (optional)Either a typical HTML attribute string
+ * or an associative array
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function HTML_QuickForm_text($elementName=null, $elementLabel=null, $attributes=null)
+ {
+ HTML_QuickForm_input::HTML_QuickForm_input($elementName, $elementLabel, $attributes);
+ $this->_persistantFreeze = true;
+ $this->setType('text');
+ } //end constructor
+
+ // }}}
+ // {{{ setSize()
+
+ /**
+ * Sets size of text field
+ *
+ * @param string $size Size of text field
+ * @since 1.3
+ * @access public
+ * @return void
+ */
+ function setSize($size)
+ {
+ $this->updateAttributes(array('size'=>$size));
+ } //end func setSize
+
+ // }}}
+ // {{{ setMaxlength()
+
+ /**
+ * Sets maxlength of text field
+ *
+ * @param string $maxlength Maximum length of text field
+ * @since 1.3
+ * @access public
+ * @return void
+ */
+ function setMaxlength($maxlength)
+ {
+ $this->updateAttributes(array('maxlength'=>$maxlength));
+ } //end func setMaxlength
+
+ // }}}
+
+} //end class HTML_QuickForm_text
+?>
diff --git a/pearlib/HTML/QuickForm/textarea.php b/pearlib/HTML/QuickForm/textarea.php
new file mode 100644
index 00000000..1ab99dd7
--- /dev/null
+++ b/pearlib/HTML/QuickForm/textarea.php
@@ -0,0 +1,222 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Adam Daniel <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: textarea.php,v 1.11 2004/02/28 22:10:16 avb Exp $
+
+require_once("HTML/QuickForm/element.php");
+
+/**
+ * HTML class for a textarea type field
+ *
+ * @author Adam Daniel <[email protected]>
+ * @author Bertrand Mansion <[email protected]>
+ * @version 1.0
+ * @since PHP4.04pl1
+ * @access public
+ */
+class HTML_QuickForm_textarea extends HTML_QuickForm_element
+{
+ // {{{ properties
+
+ /**
+ * Field value
+ * @var string
+ * @since 1.0
+ * @access private
+ */
+ var $_value = null;
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * Class constructor
+ *
+ * @param string Input field name attribute
+ * @param mixed Label(s) for a field
+ * @param mixed Either a typical HTML attribute string or an associative array
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function HTML_QuickForm_textarea($elementName=null, $elementLabel=null, $attributes=null)
+ {
+ HTML_QuickForm_element::HTML_QuickForm_element($elementName, $elementLabel, $attributes);
+ $this->_persistantFreeze = true;
+ $this->_type = 'textarea';
+ } //end constructor
+
+ // }}}
+ // {{{ setName()
+
+ /**
+ * Sets the input field name
+ *
+ * @param string $name Input field name attribute
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setName($name)
+ {
+ $this->updateAttributes(array('name'=>$name));
+ } //end func setName
+
+ // }}}
+ // {{{ getName()
+
+ /**
+ * Returns the element name
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ */
+ function getName()
+ {
+ return $this->getAttribute('name');
+ } //end func getName
+
+ // }}}
+ // {{{ setValue()
+
+ /**
+ * Sets value for textarea element
+ *
+ * @param string $value Value for textarea element
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setValue($value)
+ {
+ $this->_value = $value;
+ } //end func setValue
+
+ // }}}
+ // {{{ getValue()
+
+ /**
+ * Returns the value of the form element
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ */
+ function getValue()
+ {
+ return $this->_value;
+ } // end func getValue
+
+ // }}}
+ // {{{ setWrap()
+
+ /**
+ * Sets wrap type for textarea element
+ *
+ * @param string $wrap Wrap type
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setWrap($wrap)
+ {
+ $this->updateAttributes(array('wrap' => $wrap));
+ } //end func setWrap
+
+ // }}}
+ // {{{ setRows()
+
+ /**
+ * Sets height in rows for textarea element
+ *
+ * @param string $rows Height expressed in rows
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setRows($rows)
+ {
+ $this->updateAttributes(array('rows' => $rows));
+ } //end func setRows
+
+ // }}}
+ // {{{ setCols()
+
+ /**
+ * Sets width in cols for textarea element
+ *
+ * @param string $cols Width expressed in cols
+ * @since 1.0
+ * @access public
+ * @return void
+ */
+ function setCols($cols)
+ {
+ $this->updateAttributes(array('cols' => $cols));
+ } //end func setCols
+
+ // }}}
+ // {{{ toHtml()
+
+ /**
+ * Returns the textarea element in HTML
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ */
+ function toHtml()
+ {
+ if ($this->_flagFrozen) {
+ return $this->getFrozenHtml();
+ } else {
+ return $this->_getTabs() .
+ '<textarea' . $this->_getAttrString($this->_attributes) . '>' .
+ // because we wrap the form later we don't want the text indented
+ preg_replace("/(\r\n|\n|\r)/", '&#010;', htmlspecialchars($this->_value)) .
+ '</textarea>';
+ }
+ } //end func toHtml
+
+ // }}}
+ // {{{ getFrozenHtml()
+
+ /**
+ * Returns the value of field without HTML tags (in this case, value is changed to a mask)
+ *
+ * @since 1.0
+ * @access public
+ * @return string
+ */
+ function getFrozenHtml()
+ {
+ $value = htmlspecialchars($this->getValue());
+ if ($this->getAttribute('wrap') == 'off') {
+ $html = $this->_getTabs() . '<pre>' . $value."</pre>\n";
+ } else {
+ $html = nl2br($value)."\n";
+ }
+ return $html . $this->_getPersistantData();
+ } //end func getFrozenHtml
+
+ // }}}
+
+} //end class HTML_QuickForm_textarea
+?>
diff --git a/pearlib/HTML/QuickForm/xbutton.php b/pearlib/HTML/QuickForm/xbutton.php
new file mode 100644
index 00000000..bdc0cbf8
--- /dev/null
+++ b/pearlib/HTML/QuickForm/xbutton.php
@@ -0,0 +1,145 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Alexey Borzov <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: xbutton.php,v 1.1 2004/05/18 09:46:46 avb Exp $
+
+require_once 'HTML/QuickForm/element.php';
+
+/**
+ * Class for HTML 4.0 <button> element
+ *
+ * @author Alexey Borzov <[email protected]>
+ * @since 3.2.3
+ * @access public
+ */
+class HTML_QuickForm_xbutton extends HTML_QuickForm_element
+{
+ /**
+ * Contents of the <button> tag
+ * @var string
+ * @access private
+ */
+ var $_content;
+
+ /**
+ * Class constructor
+ *
+ * @param string Button name
+ * @param string Button content (HTML to add between <button></button> tags)
+ * @param mixed Either a typical HTML attribute string or an associative array
+ * @access public
+ */
+ function HTML_QuickForm_xbutton($elementName = null, $elementContent = null, $attributes = null)
+ {
+ $this->HTML_QuickForm_element($elementName, null, $attributes);
+ $this->setContent($elementContent);
+ $this->setPersistantFreeze(false);
+ $this->_type = 'xbutton';
+ }
+
+
+ function toHtml()
+ {
+ return '<button' . $this->getAttributes(true) . '>' . $this->_content . '</button>';
+ }
+
+
+ function getFrozenHtml()
+ {
+ return $this->toHtml();
+ }
+
+
+ function freeze()
+ {
+ return false;
+ }
+
+
+ function setName($name)
+ {
+ $this->updateAttributes(array(
+ 'name' => $name
+ ));
+ }
+
+
+ function getName()
+ {
+ return $this->getAttribute('name');
+ }
+
+
+ function setValue($value)
+ {
+ $this->updateAttributes(array(
+ 'value' => $value
+ ));
+ }
+
+
+ function getValue()
+ {
+ return $this->getAttribute('value');
+ }
+
+
+ /**
+ * Sets the contents of the button element
+ *
+ * @param string Button content (HTML to add between <button></button> tags)
+ */
+ function setContent($content)
+ {
+ $this->_content = $content;
+ }
+
+
+ function onQuickFormEvent($event, $arg, &$caller)
+ {
+ if ('updateValue' != $event) {
+ return parent::onQuickFormEvent($event, $arg, $caller);
+ } else {
+ $value = $this->_findValue($caller->_constantValues);
+ if (null === $value) {
+ $value = $this->_findValue($caller->_defaultValues);
+ }
+ if (null !== $value) {
+ $this->setValue($value);
+ }
+ }
+ return true;
+ }
+
+
+ /**
+ * Returns a 'safe' element's value
+ *
+ * The value is only returned if the button's type is "submit" and if this
+ * particlular button was clicked
+ */
+ function exportValue(&$submitValues, $assoc = false)
+ {
+ if ('submit' == $this->getAttribute('type')) {
+ return $this->_prepareValue($this->_findValue($submitValues), $assoc);
+ } else {
+ return null;
+ }
+ }
+}
+?>
diff --git a/pearlib/HTML/Template/Sigma.php b/pearlib/HTML/Template/Sigma.php
new file mode 100644
index 00000000..c10e5aa5
--- /dev/null
+++ b/pearlib/HTML/Template/Sigma.php
@@ -0,0 +1,1778 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.02 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Ulf Wendel <[email protected]> |
+// | Alexey Borzov <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Sigma.php,v 1.12 2004/10/20 10:52:14 avb Exp $
+//
+
+require_once 'PEAR.php';
+
+define('SIGMA_OK', 1);
+define('SIGMA_ERROR', -1);
+define('SIGMA_TPL_NOT_FOUND', -2);
+define('SIGMA_BLOCK_NOT_FOUND', -3);
+define('SIGMA_BLOCK_DUPLICATE', -4);
+define('SIGMA_CACHE_ERROR', -5);
+define('SIGMA_UNKNOWN_OPTION', -6);
+define('SIGMA_PLACEHOLDER_NOT_FOUND', -10);
+define('SIGMA_PLACEHOLDER_DUPLICATE', -11);
+define('SIGMA_BLOCK_EXISTS', -12);
+define('SIGMA_INVALID_CALLBACK', -13);
+define('SIGMA_CALLBACK_SYNTAX_ERROR', -14);
+
+/**
+* HTML_Template_Sigma: implementation of Integrated Templates API with
+* template 'compilation' added.
+*
+* The main new feature in Sigma is the template 'compilation'. Consider the
+* following: when loading a template file the engine has to parse it using
+* regular expressions to find all the blocks and variable placeholders. This
+* is a very "expensive" operation and is definitely an overkill to do on
+* every page request: templates seldom change on production websites. This is
+* where the cache kicks in: it saves an internal representation of the
+* template structure into a file and this file gets loaded instead of the
+* source one on subsequent requests (unless the source changes, of course).
+*
+* While HTML_Template_Sigma inherits PHPLib Template's template syntax, it has
+* an API which is easier to understand. When using HTML_Template_PHPLIB, you
+* have to explicitly name a source and a target the block gets parsed into.
+* This gives maximum flexibility but requires full knowledge of template
+* structure from the programmer.
+*
+* Integrated Template on the other hands manages block nesting and parsing
+* itself. The engine knows that inner1 is a child of block2, there's
+* no need to tell it about this:
+*
+* + __global__ (hidden and automatically added)
+* + block1
+* + block2
+* + inner1
+* + inner2
+*
+* To add content to block1 you simply type:
+* <code>$tpl->setCurrentBlock("block1");</code>
+* and repeat this as often as needed:
+* <code>
+* $tpl->setVariable(...);
+* $tpl->parseCurrentBlock();
+* </code>
+*
+* To add content to block2 you would type something like:
+* <code>
+* $tpl->setCurrentBlock("inner1");
+* $tpl->setVariable(...);
+* $tpl->parseCurrentBlock();
+*
+* $tpl->setVariable(...);
+* $tpl->parseCurrentBlock();
+*
+* $tpl->parse("block2");
+* </code>
+*
+* This will result in one repetition of block2 which contains two repetitions
+* of inner1. inner2 will be removed if $removeEmptyBlock is set to true (which
+* is the default).
+*
+* Usage:
+* <code>
+* $tpl = new HTML_Template_Sigma( [string filerootdir], [string cacherootdir] );
+*
+* // load a template or set it with setTemplate()
+* $tpl->loadTemplatefile( string filename [, boolean removeUnknownVariables, boolean removeEmptyBlocks] )
+*
+* // set "global" Variables meaning variables not beeing within a (inner) block
+* $tpl->setVariable( string variablename, mixed value );
+*
+* // like with the HTML_Template_PHPLIB there's a second way to use setVariable()
+* $tpl->setVariable( array ( string varname => mixed value ) );
+*
+* // Let's use any block, even a deeply nested one
+* $tpl->setCurrentBlock( string blockname );
+*
+* // repeat this as often as you need it.
+* $tpl->setVariable( array ( string varname => mixed value ) );
+* $tpl->parseCurrentBlock();
+*
+* // get the parsed template or print it: $tpl->show()
+* $html = $tpl->get();
+* </code>
+*
+* @author Ulf Wendel <[email protected]>
+* @author Alexey Borzov <[email protected]>
+* @version $Revision: 1.12 $
+* @access public
+* @package HTML_Template_Sigma
+*/
+class HTML_Template_Sigma extends PEAR
+{
+ /**
+ * First character of a variable placeholder ( _{_VARIABLE} ).
+ * @var string
+ * @access public
+ * @see $closingDelimiter, $blocknameRegExp, $variablenameRegExp
+ */
+ var $openingDelimiter = '{';
+
+ /**
+ * Last character of a variable placeholder ( {VARIABLE_}_ )
+ * @var string
+ * @access public
+ * @see $openingDelimiter, $blocknameRegExp, $variablenameRegExp
+ */
+ var $closingDelimiter = '}';
+
+ /**
+ * RegExp for matching the block names in the template.
+ * Per default "sm" is used as the regexp modifier, "i" is missing.
+ * That means a case sensitive search is done.
+ * @var string
+ * @access public
+ * @see $variablenameRegExp, $openingDelimiter, $closingDelimiter
+ */
+ var $blocknameRegExp = '[0-9A-Za-z_-]+';
+
+ /**
+ * RegExp matching a variable placeholder in the template.
+ * Per default "sm" is used as the regexp modifier, "i" is missing.
+ * That means a case sensitive search is done.
+ * @var string
+ * @access public
+ * @see $blocknameRegExp, $openingDelimiter, $closingDelimiter
+ */
+ var $variablenameRegExp = '[0-9A-Za-z_-]+';
+
+ /**
+ * RegExp used to find variable placeholder, filled by the constructor
+ * @var string Looks somewhat like @(delimiter varname delimiter)@
+ * @see HTML_Template_Sigma()
+ */
+ var $variablesRegExp = '';
+
+ /**
+ * RegExp used to strip unused variable placeholders
+ * @see $variablesRegExp, HTML_Template_Sigma()
+ */
+ var $removeVariablesRegExp = '';
+
+ /**
+ * RegExp used to find blocks and their content, filled by the constructor
+ * @var string
+ * @see HTML_Template_Sigma()
+ */
+ var $blockRegExp = '';
+
+ /**
+ * Controls the handling of unknown variables, default is remove
+ * @var boolean
+ * @access public
+ */
+ var $removeUnknownVariables = true;
+
+ /**
+ * Controls the handling of empty blocks, default is remove
+ * @var boolean
+ * @access public
+ */
+ var $removeEmptyBlocks = true;
+
+ /**
+ * Name of the current block
+ * @var string
+ */
+ var $currentBlock = '__global__';
+
+ /**
+ * Template blocks and their content
+ * @var array
+ * @see _buildBlocks()
+ * @access private
+ */
+ var $_blocks = array();
+
+ /**
+ * Content of parsed blocks
+ * @var array
+ * @see get(), parse()
+ * @access private
+ */
+ var $_parsedBlocks = array();
+
+ /**
+ * Variable names that appear in the block
+ * @var array
+ * @see _buildBlockVariables()
+ * @access private
+ */
+ var $_blockVariables = array();
+
+ /**
+ * Inner blocks inside the block
+ * @var array
+ * @see _buildBlocks()
+ * @access private
+ */
+ var $_children = array();
+
+ /**
+ * List of blocks to preserve even if they are "empty"
+ * @var array
+ * @see touchBlock(), $removeEmptyBlocks
+ * @access private
+ */
+ var $_touchedBlocks = array();
+
+ /**
+ * List of blocks which should not be shown even if not "empty"
+ * @var array
+ * @see hideBlock(), $removeEmptyBlocks
+ * @access private
+ */
+ var $_hiddenBlocks = array();
+
+ /**
+ * Variables for substitution.
+ *
+ * Variables are kept in this array before the replacements are done.
+ * This allows automatic removal of empty blocks.
+ *
+ * @var array
+ * @see setVariable()
+ * @access private
+ */
+ var $_variables = array();
+
+ /**
+ * Global variables for substitution
+ *
+ * These are substituted into all blocks, are not cleared on
+ * block parsing and do not trigger "non-empty" logic. I.e. if
+ * only global variables are substituted into the block, it is
+ * still considered "empty".
+ *
+ * @var array
+ * @see setVariable(), setGlobalVariable()
+ * @access private
+ */
+ var $_globalVariables = array();
+
+ /**
+ * Root directory for "source" templates
+ * @var string
+ * @see HTML_Template_Sigma(), setRoot()
+ */
+ var $fileRoot = '';
+
+ /**
+ * Directory to store the "prepared" templates in
+ * @var string
+ * @see HTML_Template_Sigma(), setCacheRoot()
+ * @access private
+ */
+ var $_cacheRoot = null;
+
+ /**
+ * Flag indicating that the global block was parsed
+ * @var boolean
+ */
+ var $flagGlobalParsed = false;
+
+ /**
+ * Options to control some finer aspects of Sigma's work.
+ *
+ * $_options['preserve_data'] If false, then substitute variables and remove empty
+ * placeholders in data passed through setVariable (see also bugs #20199, #21951)
+ * $_options['trim_on_save'] Whether to trim extra whitespace from template on cache save.
+ * Generally safe to have this on, unless you have <pre></pre> in templates or want to
+ * preserve HTML indentantion
+ */
+ var $_options = array(
+ 'preserve_data' => false,
+ 'trim_on_save' => true
+ );
+
+ /**
+ * Function name prefix used when searching for function calls in the template
+ * @var string
+ */
+ var $functionPrefix = 'func_';
+
+ /**
+ * Function name RegExp
+ * @var string
+ */
+ var $functionnameRegExp = '[_a-zA-Z]+[A-Za-z_0-9]*';
+
+ /**
+ * RegExp used to grep function calls in the template (set by the constructor)
+ * @var string
+ * @see _buildFunctionlist(), HTML_Template_Sigma()
+ */
+ var $functionRegExp = '';
+
+ /**
+ * List of functions found in the template.
+ * @var array
+ * @access private
+ */
+ var $_functions = array();
+
+ /**
+ * List of callback functions specified by the user
+ * @var array
+ * @access private
+ */
+ var $_callback = array();
+
+ /**
+ * RegExp used to find file inclusion calls in the template (should have 'e' modifier)
+ * @var string
+ */
+ var $includeRegExp = '#<!--\s+INCLUDE\s+(\S+)\s+-->#ime';
+
+ /**
+ * Files queued for inclusion
+ * @var array
+ * @access private
+ */
+ var $_triggers = array();
+
+
+ /**
+ * Constructor: builds some complex regular expressions and optionally
+ * sets the root directories.
+ *
+ * Make sure that you call this constructor if you derive your template
+ * class from this one.
+ *
+ * @param string root directory for templates
+ * @param string directory to cache "prepared" templates in
+ * @see setRoot(), setCacheRoot()
+ */
+ function HTML_Template_Sigma($root = '', $cacheRoot = '')
+ {
+ // the class is inherited from PEAR to be able to use $this->setErrorHandling()
+ $this->PEAR();
+ $this->variablesRegExp = '@' . $this->openingDelimiter . '(' . $this->variablenameRegExp . ')' .
+ '(:(' . $this->functionnameRegExp . '))?' . $this->closingDelimiter . '@sm';
+ $this->removeVariablesRegExp = '@'.$this->openingDelimiter.'\s*('.$this->variablenameRegExp.')\s*'.$this->closingDelimiter.'@sm';
+ $this->blockRegExp = '@<!--\s+BEGIN\s+('.$this->blocknameRegExp.')\s+-->(.*)<!--\s+END\s+\1\s+-->@sm';
+ $this->functionRegExp = '@' . $this->functionPrefix . '(' . $this->functionnameRegExp . ')\s*\(@sm';
+ $this->setRoot($root);
+ $this->setCacheRoot($cacheRoot);
+
+ $this->setCallbackFunction('h', 'htmlspecialchars');
+ $this->setCallbackFunction('u', 'urlencode');
+ $this->setCallbackFunction('j', array(&$this, '_jsEscape'));
+ }
+
+
+ /**
+ * Sets the file root for templates. The file root gets prefixed to all
+ * filenames passed to the object.
+ *
+ * @param string directory name
+ * @see HTML_Template_Sigma()
+ * @access public
+ */
+ function setRoot($root)
+ {
+ if (('' != $root) && ('/' != substr($root, -1))) {
+ $root .= '/';
+ }
+ $this->fileRoot = $root;
+ }
+
+
+ /**
+ * Sets the directory to cache "prepared" templates in, the directory should be writable for PHP.
+ *
+ * The "prepared" template contains an internal representation of template
+ * structure: essentially a serialized array of $_blocks, $_blockVariables,
+ * $_children and $_functions, may also contain $_triggers. This allows
+ * to bypass expensive calls to _buildBlockVariables() and especially
+ * _buildBlocks() when reading the "prepared" template instead of
+ * the "source" one.
+ *
+ * The files in this cache do not have any TTL and are regenerated when the
+ * source templates change.
+ *
+ * @param string directory name
+ * @see HTML_Template_Sigma(), _getCached(), _writeCache()
+ * @access public
+ */
+ function setCacheRoot($root)
+ {
+ if (empty($root)) {
+ return true;
+ } elseif (('' != $root) && ('/' != substr($root, -1))) {
+ $root .= '/';
+ }
+ $this->_cacheRoot = $root;
+ }
+
+
+ /**
+ * Sets the option for the template class
+ *
+ * @access public
+ * @param string option name
+ * @param mixed option value
+ * @return mixed SIGMA_OK on success, error object on failure
+ */
+ function setOption($option, $value)
+ {
+ if (isset($this->_options[$option])) {
+ $this->_options[$option] = $value;
+ return SIGMA_OK;
+ }
+ return $this->raiseError($this->errorMessage(SIGMA_UNKNOWN_OPTION, $option), SIGMA_UNKNOWN_OPTION);
+ }
+
+
+ /**
+ * Returns a textual error message for an error code
+ *
+ * @access public
+ * @param integer error code
+ * @param string additional data to insert into message
+ * @return string error message
+ */
+ function errorMessage($code, $data = null)
+ {
+ static $errorMessages;
+ if (!isset($errorMessages)) {
+ $errorMessages = array(
+ SIGMA_ERROR => 'unknown error',
+ SIGMA_OK => '',
+ SIGMA_TPL_NOT_FOUND => 'Cannot read the template file \'%s\'',
+ SIGMA_BLOCK_NOT_FOUND => 'Cannot find block \'%s\'',
+ SIGMA_BLOCK_DUPLICATE => 'The name of a block must be unique within a template. Block \'%s\' found twice.',
+ SIGMA_CACHE_ERROR => 'Cannot save template file \'%s\'',
+ SIGMA_UNKNOWN_OPTION => 'Unknown option \'%s\'',
+ SIGMA_PLACEHOLDER_NOT_FOUND => 'Variable placeholder \'%s\' not found',
+ SIGMA_PLACEHOLDER_DUPLICATE => 'Placeholder \'%s\' should be unique, found in multiple blocks',
+ SIGMA_BLOCK_EXISTS => 'Block \'%s\' already exists',
+ SIGMA_INVALID_CALLBACK => 'Callback does not exist',
+ SIGMA_CALLBACK_SYNTAX_ERROR => 'Cannot parse template function: %s'
+ );
+ }
+
+ if (PEAR::isError($code)) {
+ $code = $code->getCode();
+ }
+ if (!isset($errorMessages[$code])) {
+ return $errorMessages[SIGMA_ERROR];
+ } else {
+ return (null === $data)? $errorMessages[$code]: sprintf($errorMessages[$code], $data);
+ }
+ }
+
+
+ /**
+ * Prints a block with all replacements done.
+ *
+ * @access public
+ * @param string block name
+ * @see get()
+ */
+ function show($block = '__global__')
+ {
+ print $this->get($block);
+ }
+
+
+ /**
+ * Returns a block with all replacements done.
+ *
+ * @param string block name
+ * @param bool whether to clear parsed block contents
+ * @return string block with all replacements done
+ * @throws PEAR_Error
+ * @access public
+ * @see show()
+ */
+ function get($block = '__global__', $clear = false)
+ {
+ if (!isset($this->_blocks[$block])) {
+ return $this->raiseError($this->errorMessage(SIGMA_BLOCK_NOT_FOUND, $block), SIGMA_BLOCK_NOT_FOUND);
+ }
+ if ('__global__' == $block && !$this->flagGlobalParsed) {
+ $this->parse('__global__');
+ }
+ // return the parsed block, removing the unknown placeholders if needed
+ if (!isset($this->_parsedBlocks[$block])) {
+ return '';
+
+ } else {
+ $ret = $this->_parsedBlocks[$block];
+ if ($clear) {
+ unset($this->_parsedBlocks[$block]);
+ }
+ if ($this->removeUnknownVariables) {
+ $ret = preg_replace($this->removeVariablesRegExp, '', $ret);
+ }
+ if ($this->_options['preserve_data']) {
+ $ret = str_replace($this->openingDelimiter . '%preserved%' . $this->closingDelimiter, $this->openingDelimiter, $ret);
+ }
+ return $ret;
+ }
+ }
+
+
+ /**
+ * Parses the given block.
+ *
+ * @param string block name
+ * @param boolean true if the function is called recursively (do not set this to true yourself!)
+ * @param boolean true if parsing a "hidden" block (do not set this to true yourself!)
+ * @access public
+ * @see parseCurrentBlock()
+ * @throws PEAR_Error
+ */
+ function parse($block = '__global__', $flagRecursion = false, $fakeParse = false)
+ {
+ static $vars;
+
+ if (!isset($this->_blocks[$block])) {
+ return $this->raiseError($this->errorMessage(SIGMA_BLOCK_NOT_FOUND, $block), SIGMA_BLOCK_NOT_FOUND);
+ }
+ if ('__global__' == $block) {
+ $this->flagGlobalParsed = true;
+ }
+ if (!isset($this->_parsedBlocks[$block])) {
+ $this->_parsedBlocks[$block] = '';
+ }
+ $outer = $this->_blocks[$block];
+
+ if (!$flagRecursion) {
+ $vars = array();
+ }
+ // block is not empty if its local var is substituted
+ $empty = true;
+ foreach ($this->_blockVariables[$block] as $allowedvar => $v) {
+ if (isset($this->_variables[$allowedvar])) {
+ $vars[$this->openingDelimiter . $allowedvar . $this->closingDelimiter] = $this->_variables[$allowedvar];
+ $empty = false;
+ // vital for checking "empty/nonempty" status
+ unset($this->_variables[$allowedvar]);
+ }
+ }
+
+ // processing of the inner blocks
+ if (isset($this->_children[$block])) {
+ foreach ($this->_children[$block] as $innerblock => $v) {
+ $placeholder = $this->openingDelimiter.'__'.$innerblock.'__'.$this->closingDelimiter;
+
+ if (isset($this->_hiddenBlocks[$innerblock])) {
+ // don't bother actually parsing this inner block; but we _have_
+ // to go through its local vars to prevent problems on next iteration
+ $this->parse($innerblock, true, true);
+ unset($this->_hiddenBlocks[$innerblock]);
+ $outer = str_replace($placeholder, '', $outer);
+
+ } else {
+ $this->parse($innerblock, true, $fakeParse);
+ // block is not empty if its inner block is not empty
+ if ('' != $this->_parsedBlocks[$innerblock]) {
+ $empty = false;
+ }
+
+ $outer = str_replace($placeholder, $this->_parsedBlocks[$innerblock], $outer);
+ $this->_parsedBlocks[$innerblock] = '';
+ }
+ }
+ }
+
+ // add "global" variables to the static array
+ foreach ($this->_globalVariables as $allowedvar => $value) {
+ if (isset($this->_blockVariables[$block][$allowedvar])) {
+ $vars[$this->openingDelimiter . $allowedvar . $this->closingDelimiter] = $value;
+ }
+ }
+ // if we are inside a hidden block, don't bother
+ if (!$fakeParse) {
+ if (0 != count($vars) && (!$flagRecursion || !empty($this->_functions[$block]))) {
+ $varKeys = array_keys($vars);
+ $varValues = $this->_options['preserve_data']? array_map(array(&$this, '_preserveOpeningDelimiter'), array_values($vars)): array_values($vars);
+ }
+
+ // check whether the block is considered "empty" and append parsed content if not
+ if (!$empty || ('__global__' == $block) || !$this->removeEmptyBlocks || isset($this->_touchedBlocks[$block])) {
+ // perform callbacks
+ if (!empty($this->_functions[$block])) {
+ foreach ($this->_functions[$block] as $id => $data) {
+ $placeholder = $this->openingDelimiter . '__function_' . $id . '__' . $this->closingDelimiter;
+ // do not waste time calling function more than once
+ if (!isset($vars[$placeholder])) {
+ $args = array();
+ $preserveArgs = isset($this->_callback[$data['name']]['preserveArgs']) && $this->_callback[$data['name']]['preserveArgs'];
+ foreach ($data['args'] as $arg) {
+ $args[] = (empty($varKeys) || $preserveArgs)? $arg: str_replace($varKeys, $varValues, $arg);
+ }
+ if (isset($this->_callback[$data['name']]['data'])) {
+ $res = call_user_func_array($this->_callback[$data['name']]['data'], $args);
+ } else {
+ $res = isset($args[0])? $args[0]: '';
+ }
+ $outer = str_replace($placeholder, $res, $outer);
+ // save the result to variable cache, it can be requested somewhere else
+ $vars[$placeholder] = $res;
+ }
+ }
+ }
+ // substitute variables only on non-recursive call, thus all
+ // variables from all inner blocks get substituted
+ if (!$flagRecursion && !empty($varKeys)) {
+ $outer = str_replace($varKeys, $varValues, $outer);
+ }
+
+ $this->_parsedBlocks[$block] .= $outer;
+ if (isset($this->_touchedBlocks[$block])) {
+ unset($this->_touchedBlocks[$block]);
+ }
+ }
+ }
+ return $empty;
+ }
+
+
+ /**
+ * Sets a variable value.
+ *
+ * The function can be used either like setVariable("varname", "value")
+ * or with one array $variables["varname"] = "value" given setVariable($variables)
+ *
+ * @access public
+ * @param mixed variable name or array ('varname'=>'value')
+ * @param string variable value if $variable is not an array
+ */
+ function setVariable($variable, $value = '')
+ {
+ if (is_array($variable)) {
+ $this->_variables = array_merge($this->_variables, $variable);
+ } else {
+ $this->_variables[$variable] = $value;
+ }
+ }
+
+
+ /**
+ * Sets a global variable value.
+ *
+ * @access public
+ * @param mixed variable name or array ('varname'=>'value')
+ * @param string variable value if $variable is not an array
+ * @see setVariable()
+ */
+ function setGlobalVariable($variable, $value = '')
+ {
+ if (is_array($variable)) {
+ $this->_globalVariables = array_merge($this->_globalVariables, $variable);
+ } else {
+ $this->_globalVariables[$variable] = $value;
+ }
+ }
+
+
+ /**
+ * Sets the name of the current block: the block where variables are added
+ *
+ * @param string block name
+ * @return mixed SIGMA_OK on success, error object on failure
+ * @throws PEAR_Error
+ * @access public
+ */
+ function setCurrentBlock($block = '__global__')
+ {
+ if (!isset($this->_blocks[$block])) {
+ return $this->raiseError($this->errorMessage(SIGMA_BLOCK_NOT_FOUND, $block), SIGMA_BLOCK_NOT_FOUND);
+ }
+ $this->currentBlock = $block;
+ return SIGMA_OK;
+ }
+
+
+ /**
+ * Parses the current block
+ *
+ * @see parse(), setCurrentBlock()
+ * @access public
+ */
+ function parseCurrentBlock()
+ {
+ return $this->parse($this->currentBlock);
+ }
+
+
+ /**
+ * Returns the current block name
+ *
+ * @return string block name
+ * @access public
+ */
+ function getCurrentBlock()
+ {
+ return $this->currentBlock;
+ }
+
+
+ /**
+ * Preserves the block even if empty blocks should be removed.
+ *
+ * Sometimes you have blocks that should be preserved although they are
+ * empty (no placeholder replaced). Think of a shopping basket. If it's
+ * empty you have to show a message to the user. If it's filled you have
+ * to show the contents of the shopping basket. Now where to place the
+ * message that the basket is empty? It's not a good idea to place it
+ * in you application as customers tend to like unecessary minor text
+ * changes. Having another template file for an empty basket means that
+ * one fine day the filled and empty basket templates will have different
+ * layouts.
+ *
+ * So blocks that do not contain any placeholders but only messages like
+ * "Your shopping basked is empty" are intoduced. Now if there is no
+ * replacement done in such a block the block will be recognized as "empty"
+ * and by default ($removeEmptyBlocks = true) be stripped off. To avoid this
+ * you can call touchBlock()
+ *
+ * @param string block name
+ * @return mixed SIGMA_OK on success, error object on failure
+ * @throws PEAR_Error
+ * @access public
+ * @see $removeEmptyBlocks, $_touchedBlocks
+ */
+ function touchBlock($block)
+ {
+ if (!isset($this->_blocks[$block])) {
+ return $this->raiseError($this->errorMessage(SIGMA_BLOCK_NOT_FOUND, $block), SIGMA_BLOCK_NOT_FOUND);
+ }
+ if (isset($this->_hiddenBlocks[$block])) {
+ unset($this->_hiddenBlocks[$block]);
+ }
+ $this->_touchedBlocks[$block] = true;
+ return SIGMA_OK;
+ }
+
+
+ /**
+ * Hides the block even if it is not "empty".
+ *
+ * Is somewhat an opposite to touchBlock().
+ *
+ * Consider a block (a 'edit' link for example) that should be visible to
+ * registered/"special" users only, but its visibility is triggered by
+ * some little 'id' field passed in a large array into setVariable(). You
+ * can either carefully juggle your variables to prevent the block from
+ * appearing (a fragile solution) or simply call hideBlock()
+ *
+ * @param string block name
+ * @return mixed SIGMA_OK on success, error object on failure
+ * @throws PEAR_Error
+ * @access public
+ */
+ function hideBlock($block)
+ {
+ if (!isset($this->_blocks[$block])) {
+ return $this->raiseError($this->errorMessage(SIGMA_BLOCK_NOT_FOUND, $block), SIGMA_BLOCK_NOT_FOUND);
+ }
+ if (isset($this->_touchedBlocks[$block])) {
+ unset($this->_touchedBlocks[$block]);
+ }
+ $this->_hiddenBlocks[$block] = true;
+ return SIGMA_OK;
+ }
+
+
+ /**
+ * Sets the template.
+ *
+ * You can either load a template file from disk with LoadTemplatefile() or set the
+ * template manually using this function.
+ *
+ * @access public
+ * @param string template content
+ * @param boolean remove unknown/unused variables?
+ * @param boolean remove empty blocks?
+ * @return mixed SIGMA_OK on success, error object on failure
+ * @see loadTemplatefile()
+ */
+ function setTemplate($template, $removeUnknownVariables = true, $removeEmptyBlocks = true)
+ {
+ $this->_resetTemplate($removeUnknownVariables, $removeEmptyBlocks);
+ $list = $this->_buildBlocks('<!-- BEGIN __global__ -->'.$template.'<!-- END __global__ -->');
+ if (PEAR::isError($list)) {
+ return $list;
+ }
+ return $this->_buildBlockVariables();
+ }
+
+
+ /**
+ * Loads a template file.
+ *
+ * If caching is on, then it checks whether a "prepared" template exists.
+ * If it does, it gets loaded instead of the original, if it does not, then
+ * the original gets loaded and prepared and then the prepared version is saved.
+ * addBlockfile() and replaceBlockfile() implement quite the same logic.
+ *
+ * @param string filename
+ * @param boolean remove unknown/unused variables?
+ * @param boolean remove empty blocks?
+ * @access public
+ * @return mixed SIGMA_OK on success, error object on failure
+ * @see setTemplate(), $removeUnknownVariables, $removeEmptyBlocks
+ */
+ function loadTemplateFile($filename, $removeUnknownVariables = true, $removeEmptyBlocks = true)
+ {
+ if ($this->_isCached($filename)) {
+ $this->_resetTemplate($removeUnknownVariables, $removeEmptyBlocks);
+ return $this->_getCached($filename);
+ }
+ $template = $this->_getFile($this->_sourceName($filename));
+ if (PEAR::isError($template)) {
+ return $template;
+ }
+ $this->_triggers = array();
+ $template = preg_replace($this->includeRegExp, "\$this->_makeTrigger('\\1', '__global__')", $template);
+ if (SIGMA_OK !== ($res = $this->setTemplate($template, $removeUnknownVariables, $removeEmptyBlocks))) {
+ return $res;
+ } else {
+ return $this->_writeCache($filename, '__global__');
+ }
+ }
+
+
+ /**
+ * Adds a block to the template changing a variable placeholder to a block placeholder.
+ *
+ * This means that a new block will be integrated into the template in
+ * place of a variable placeholder. The variable placeholder will be
+ * removed and the new block will behave in the same way as if it was
+ * inside the original template.
+ *
+ * The block content must not start with <!-- BEGIN blockname --> and end with
+ * <!-- END blockname -->, if it does the error will be thrown.
+ *
+ * @param string name of the variable placeholder, the name must be unique within the template.
+ * @param string name of the block to be added
+ * @param string content of the block
+ * @return mixed SIGMA_OK on success, error object on failure
+ * @throws PEAR_Error
+ * @see addBlockfile()
+ * @access public
+ */
+ function addBlock($placeholder, $block, $template)
+ {
+ if (isset($this->_blocks[$block])) {
+ return $this->raiseError($this->errorMessage(SIGMA_BLOCK_EXISTS, $block), SIGMA_BLOCK_EXISTS);
+ }
+ $parents = $this->_findParentBlocks($placeholder);
+ if (0 == count($parents)) {
+ return $this->raiseError($this->errorMessage(SIGMA_PLACEHOLDER_NOT_FOUND, $placeholder), SIGMA_PLACEHOLDER_NOT_FOUND);
+ } elseif (count($parents) > 1) {
+ return $this->raiseError($this->errorMessage(SIGMA_PLACEHOLDER_DUPLICATE, $placeholder), SIGMA_PLACEHOLDER_DUPLICATE);
+ }
+
+ $template = "<!-- BEGIN $block -->" . $template . "<!-- END $block -->";
+ $list = $this->_buildBlocks($template);
+ if (PEAR::isError($list)) {
+ return $list;
+ }
+ $this->_replacePlaceholder($parents[0], $placeholder, $block);
+ return $this->_buildBlockVariables($block);
+ }
+
+
+ /**
+ * Adds a block taken from a file to the template, changing a variable placeholder
+ * to a block placeholder.
+ *
+ * @param string name of the variable placeholder
+ * @param string name of the block to be added
+ * @param string template file that contains the block
+ * @return mixed SIGMA_OK on success, error object on failure
+ * @throws PEAR_Error
+ * @see addBlock()
+ * @access public
+ */
+ function addBlockfile($placeholder, $block, $filename)
+ {
+ if ($this->_isCached($filename)) {
+ return $this->_getCached($filename, $block, $placeholder);
+ }
+ $template = $this->_getFile($this->_sourceName($filename));
+ if (PEAR::isError($template)) {
+ return $template;
+ }
+ $template = preg_replace($this->includeRegExp, "\$this->_makeTrigger('\\1', '{$block}')", $template);
+ if (SIGMA_OK !== ($res = $this->addBlock($placeholder, $block, $template))) {
+ return $res;
+ } else {
+ return $this->_writeCache($filename, $block);
+ }
+ }
+
+
+ /**
+ * Replaces an existing block with new content.
+ *
+ * This function will replace a block of the template and all blocks
+ * contained in it and add a new block instead. This means you can
+ * dynamically change your template.
+ *
+ * Sigma analyses the way you've nested blocks and knows which block
+ * belongs into another block. This nesting information helps to make the
+ * API short and simple. Replacing blocks does not only mean that Sigma
+ * has to update the nesting information (relatively time consuming task)
+ * but you have to make sure that you do not get confused due to the
+ * template change yourself.
+ *
+ * @param string name of a block to replace
+ * @param string new content
+ * @param boolean true if the parsed contents of the block should be kept
+ * @access public
+ * @see replaceBlockfile(), addBlock()
+ * @return mixed SIGMA_OK on success, error object on failure
+ * @throws PEAR_Error
+ */
+ function replaceBlock($block, $template, $keepContent = false)
+ {
+ if (!isset($this->_blocks[$block])) {
+ return $this->raiseError($this->errorMessage(SIGMA_BLOCK_NOT_FOUND, $block), SIGMA_BLOCK_NOT_FOUND);
+ }
+ // should not throw a error as we already checked for block existance
+ $this->_removeBlockData($block, $keepContent);
+ $template = "<!-- BEGIN $block -->" . $template . "<!-- END $block -->";
+
+ $list = $this->_buildBlocks($template);
+ if (PEAR::isError($list)) {
+ return $list;
+ }
+ // renew the variables list
+ return $this->_buildBlockVariables($block);
+ }
+
+
+ /**
+ * Replaces an existing block with new content from a file.
+ *
+ * @access public
+ * @param string name of a block to replace
+ * @param string template file that contains the block
+ * @param boolean true if the parsed contents of the block should be kept
+ * @return mixed SIGMA_OK on success, error object on failure
+ * @throws PEAR_Error
+ * @see replaceBlock(), addBlockfile()
+ */
+ function replaceBlockfile($block, $filename, $keepContent = false)
+ {
+ if ($this->_isCached($filename)) {
+ if (PEAR::isError($res = $this->_removeBlockData($block, $keepContent))) {
+ return $res;
+ } else {
+ return $this->_getCached($filename, $block);
+ }
+ }
+ $template = $this->_getFile($this->_sourceName($filename));
+ if (PEAR::isError($template)) {
+ return $template;
+ }
+ $template = preg_replace($this->includeRegExp, "\$this->_makeTrigger('\\1', '{$block}')", $template);
+ if (SIGMA_OK !== ($res = $this->replaceBlock($block, $template, $keepContent))) {
+ return $res;
+ } else {
+ return $this->_writeCache($filename, $block);
+ }
+ }
+
+
+ /**
+ * Checks if the block exists in the template
+ *
+ * @param string block name
+ * @return bool
+ * @access public
+ */
+ function blockExists($block)
+ {
+ return isset($this->_blocks[$block]);
+ }
+
+
+ /**
+ * Returns the name of the (first) block that contains the specified placeholder.
+ *
+ * @param string Name of the placeholder you're searching
+ * @param string Name of the block to scan. If left out (default) all blocks are scanned.
+ * @return string Name of the (first) block that contains the specified placeholder.
+ * If the placeholder was not found an empty string is returned.
+ * @access public
+ * @throws PEAR_Error
+ */
+ function placeholderExists($placeholder, $block = '')
+ {
+ if ('' != $block && !isset($this->_blocks[$block])) {
+ return $this->raiseError($this->errorMessage(SIGMA_BLOCK_NOT_FOUND, $block), SIGMA_BLOCK_NOT_FOUND);
+ }
+ if ('' != $block) {
+ // if we search in the specific block, we should just check the array
+ return isset($this->_blockVariables[$block][$placeholder])? $block: '';
+ } else {
+ // _findParentBlocks returns an array, we need only the first element
+ $parents = $this->_findParentBlocks($placeholder);
+ return empty($parents)? '': $parents[0];
+ }
+ } // end func placeholderExists
+
+
+ /**
+ * Sets a callback function.
+ *
+ * Sigma templates can contain simple function calls. This means that the
+ * author of the template can add a special placeholder to it:
+ * func_h1("embedded in h1")
+ * Sigma will parse the template for these placeholders and will allow
+ * you to define a callback function for them. Callback will be called
+ * automatically when the block containing such function call is parse()'d.
+ *
+ * Please note that arguments to these template functions can contain
+ * variable placeholders: func_translate('Hello, {username}'), but not
+ * blocks or other function calls.
+ *
+ * This should NOT be used to add logic (except some presentation one) to
+ * the template. If you use a lot of such callbacks and implement business
+ * logic through them, then you're reinventing the wheel. Consider using
+ * XML/XSLT, native PHP or some other template engine.
+ *
+ * <?php
+ * function h_one($arg) {
+ * return '<h1>' . $arg . '</h1>';
+ * }
+ * ...
+ * $tpl = new HTML_Template_Sigma( ... );
+ * ...
+ * $tpl->setCallbackFunction('h1', 'h_one');
+ * ?>
+ *
+ * template:
+ * func_h1('H1 Headline');
+ *
+ * @param string Function name in the template
+ * @param mixed A callback: anything that can be passed to call_user_func_array()
+ * @param bool If true, then no variable substitution in arguments will take place before function call
+ * @return mixed SIGMA_OK on success, error object on failure
+ * @throws PEAR_Error
+ * @access public
+ */
+ function setCallbackFunction($tplFunction, $callback, $preserveArgs = false)
+ {
+ if (!is_callable($callback)) {
+ return $this->raiseError($this->errorMessage(SIGMA_INVALID_CALLBACK), SIGMA_INVALID_CALLBACK);
+ }
+ $this->_callback[$tplFunction] = array(
+ 'data' => $callback,
+ 'preserveArgs' => $preserveArgs
+ );
+ return SIGMA_OK;
+ } // end func setCallbackFunction
+
+
+ /**
+ * Returns a list of blocks within a template.
+ *
+ * If $recursive is false, it returns just a 'flat' array of $parent's
+ * direct subblocks. If $recursive is true, it builds a tree of template
+ * blocks using $parent as root. Tree structure is compatible with
+ * PEAR::Tree's Memory_Array driver.
+ *
+ * @param string parent block name
+ * @param bool whether to return a tree of child blocks (true) or a 'flat' array (false)
+ * @access public
+ * @return array a list of child blocks
+ * @throws PEAR_Error
+ */
+ function getBlockList($parent = '__global__', $recursive = false)
+ {
+ if (!isset($this->_blocks[$parent])) {
+ return $this->raiseError($this->errorMessage(SIGMA_BLOCK_NOT_FOUND, $parent), SIGMA_BLOCK_NOT_FOUND);
+ }
+ if (!$recursive) {
+ return isset($this->_children[$parent])? array_keys($this->_children[$parent]): array();
+ } else {
+ $ret = array('name' => $parent);
+ if (!empty($this->_children[$parent])) {
+ $ret['children'] = array();
+ foreach (array_keys($this->_children[$parent]) as $child) {
+ $ret['children'][] = $this->getBlockList($child, true);
+ }
+ }
+ return $ret;
+ }
+ }
+
+
+ /**
+ * Returns a list of placeholders within a block.
+ *
+ * Only 'normal' placeholders are returned, not auto-created ones.
+ *
+ * @param string block name
+ * @access public
+ * @return array a list of placeholders
+ * @throws PEAR_Error
+ */
+ function getPlaceholderList($block = '__global__')
+ {
+ if (!isset($this->_blocks[$block])) {
+ return $this->raiseError($this->errorMessage(SIGMA_BLOCK_NOT_FOUND, $block), SIGMA_BLOCK_NOT_FOUND);
+ }
+ $ret = array();
+ foreach ($this->_blockVariables[$block] as $var => $v) {
+ if ('__' != substr($var, 0, 2) || '__' != substr($var, -2)) {
+ $ret[] = $var;
+ }
+ }
+ return $ret;
+ }
+
+
+ /**
+ * Clears the variables
+ *
+ * Global variables are not affected. The method is useful when you add
+ * a lot of variables via setVariable() and are not sure whether all of
+ * them appear in the block you parse(). If you clear the variables after
+ * parse(), you don't risk them suddenly showing up in other blocks.
+ *
+ * @access public
+ * @see setVariable()
+ */
+ function clearVariables()
+ {
+ $this->_variables = array();
+ }
+
+
+ //------------------------------------------------------------
+ //
+ // Private methods follow
+ //
+ //------------------------------------------------------------
+
+
+ /**
+ * Reads the file and returns its content
+ *
+ * @param string filename
+ * @return string file content (or error object)
+ * @access private
+ */
+ function _getFile($filename)
+ {
+ if (!($fh = @fopen($filename, 'r'))) {
+ return $this->raiseError($this->errorMessage(SIGMA_TPL_NOT_FOUND, $filename), SIGMA_TPL_NOT_FOUND);
+ }
+ $content = fread($fh, filesize($filename));
+ fclose($fh);
+ return $content;
+ }
+
+
+ /**
+ * Recursively builds a list of all variables within a block.
+ *
+ * Also calls _buildFunctionlist() for each block it visits
+ *
+ * @param string block name
+ * @see _buildFunctionlist()
+ * @access private
+ */
+ function _buildBlockVariables($block = '__global__')
+ {
+ $this->_blockVariables[$block] = array();
+ $this->_functions[$block] = array();
+ preg_match_all($this->variablesRegExp, $this->_blocks[$block], $regs, PREG_SET_ORDER);
+ foreach ($regs as $match) {
+ $this->_blockVariables[$block][$match[1]] = true;
+ if (!empty($match[3])) {
+ $funcData = array(
+ 'name' => $match[3],
+ 'args' => array($this->openingDelimiter . $match[1] . $this->closingDelimiter)
+ );
+ $funcId = substr(md5(serialize($funcData)), 0, 10);
+
+ // update block info
+ $this->_blocks[$block] = str_replace($match[0], '{__function_' . $funcId . '__}', $this->_blocks[$block]);
+ $this->_blockVariables[$block]['__function_' . $funcId . '__'] = true;
+ $this->_functions[$block][$funcId] = $funcData;
+ }
+ }
+ if (SIGMA_OK != ($res = $this->_buildFunctionlist($block))) {
+ return $res;
+ }
+ if (isset($this->_children[$block]) && is_array($this->_children[$block])) {
+ foreach ($this->_children[$block] as $child => $v) {
+ if (SIGMA_OK != ($res = $this->_buildBlockVariables($child))) {
+ return $res;
+ }
+ }
+ }
+ return SIGMA_OK;
+ }
+
+
+ /**
+ * Recusively builds a list of all blocks within the template.
+ *
+ * @param string template to be scanned
+ * @see $_blocks
+ * @throws PEAR_Error
+ * @return mixed array of block names on success or error object on failure
+ * @access private
+ */
+ function _buildBlocks($string)
+ {
+ $blocks = array();
+ if (preg_match_all($this->blockRegExp, $string, $regs, PREG_SET_ORDER)) {
+ foreach ($regs as $k => $match) {
+ $blockname = $match[1];
+ $blockcontent = $match[2];
+ if (isset($this->_blocks[$blockname]) || isset($blocks[$blockname])) {
+ return $this->raiseError($this->errorMessage(SIGMA_BLOCK_DUPLICATE, $blockname), SIGMA_BLOCK_DUPLICATE);
+ }
+ $this->_blocks[$blockname] = $blockcontent;
+ $blocks[$blockname] = true;
+ $inner = $this->_buildBlocks($blockcontent);
+ if (PEAR::isError($inner)) {
+ return $inner;
+ }
+ foreach ($inner as $name => $v) {
+ $pattern = sprintf('@<!--\s+BEGIN\s+%s\s+-->(.*)<!--\s+END\s+%s\s+-->@sm', $name, $name);
+ $replacement = $this->openingDelimiter.'__'.$name.'__'.$this->closingDelimiter;
+ $this->_blocks[$blockname] = preg_replace($pattern, $replacement, $this->_blocks[$blockname]);
+ $this->_children[$blockname][$name] = true;
+ }
+ }
+ }
+ return $blocks;
+ }
+
+
+ /**
+ * Resets the object's properties, used before processing a new template
+ *
+ * @access private
+ * @param boolean remove unknown/unused variables?
+ * @param boolean remove empty blocks?
+ * @see setTemplate(), loadTemplateFile()
+ * @access private
+ */
+ function _resetTemplate($removeUnknownVariables = true, $removeEmptyBlocks = true)
+ {
+ $this->removeUnknownVariables = $removeUnknownVariables;
+ $this->removeEmptyBlocks = $removeEmptyBlocks;
+ $this->currentBlock = '__global__';
+ $this->_variables = array();
+ $this->_blocks = array();
+ $this->_children = array();
+ $this->_parsedBlocks = array();
+ $this->_touchedBlocks = array();
+ $this->_functions = array();
+ $this->flagGlobalParsed = false;
+ } // _resetTemplate
+
+
+ /**
+ * Checks whether we have a "prepared" template cached.
+ *
+ * If we do not do caching, always returns false
+ *
+ * @access private
+ * @param string source filename
+ * @return bool yes/no
+ * @see loadTemplatefile(), addBlockfile(), replaceBlockfile()
+ */
+ function _isCached($filename)
+ {
+ if (null === $this->_cacheRoot) {
+ return false;
+ }
+ $cachedName = $this->_cachedName($filename);
+ $sourceName = $this->_sourceName($filename);
+ // if $sourceName does not exist, error will be thrown later
+ $sourceTime = @filemtime($sourceName);
+ if ((false !== $sourceTime) && @file_exists($cachedName) && (filemtime($cachedName) > $sourceTime)) {
+ return true;
+ } else {
+ return false;
+ }
+ } // _isCached
+
+
+ /**
+ * Loads a "prepared" template file
+ *
+ * @access private
+ * @param string filename
+ * @param string block name
+ * @param string variable placeholder to replace by a block
+ * @return mixed SIGMA_OK on success, error object on failure
+ * @see loadTemplatefile(), addBlockfile(), replaceBlockfile()
+ */
+ function _getCached($filename, $block = '__global__', $placeholder = '')
+ {
+ // the same checks are done in addBlock()
+ if (!empty($placeholder)) {
+ if (isset($this->_blocks[$block])) {
+ return $this->raiseError($this->errorMessage(SIGMA_BLOCK_EXISTS, $block), SIGMA_BLOCK_EXISTS);
+ }
+ $parents = $this->_findParentBlocks($placeholder);
+ if (0 == count($parents)) {
+ return $this->raiseError($this->errorMessage(SIGMA_PLACEHOLDER_NOT_FOUND, $placeholder), SIGMA_PLACEHOLDER_NOT_FOUND);
+ } elseif (count($parents) > 1) {
+ return $this->raiseError($this->errorMessage(SIGMA_PLACEHOLDER_DUPLICATE, $placeholder), SIGMA_PLACEHOLDER_DUPLICATE);
+ }
+ }
+ $content = $this->_getFile($this->_cachedName($filename));
+ if (PEAR::isError($content)) {
+ return $content;
+ }
+ $cache = unserialize($content);
+ if ('__global__' != $block) {
+ $this->_blocks[$block] = $cache['blocks']['__global__'];
+ $this->_blockVariables[$block] = $cache['variables']['__global__'];
+ $this->_children[$block] = $cache['children']['__global__'];
+ $this->_functions[$block] = $cache['functions']['__global__'];
+ unset($cache['blocks']['__global__'], $cache['variables']['__global__'], $cache['children']['__global__'], $cache['functions']['__global__']);
+ }
+ $this->_blocks = array_merge($this->_blocks, $cache['blocks']);
+ $this->_blockVariables = array_merge($this->_blockVariables, $cache['variables']);
+ $this->_children = array_merge($this->_children, $cache['children']);
+ $this->_functions = array_merge($this->_functions, $cache['functions']);
+
+ // the same thing gets done in addBlockfile()
+ if (!empty($placeholder)) {
+ $this->_replacePlaceholder($parents[0], $placeholder, $block);
+ }
+ // pull the triggers, if any
+ if (isset($cache['triggers'])) {
+ return $this->_pullTriggers($cache['triggers']);
+ }
+ return SIGMA_OK;
+ } // _getCached
+
+
+ /**
+ * Returns a full name of a "prepared" template file
+ *
+ * @access private
+ * @param string source filename, relative to root directory
+ * @return string filename
+ */
+ function _cachedName($filename)
+ {
+ if ('/' == $filename{0} && '/' == substr($this->_cacheRoot, -1)) {
+ $filename = substr($filename, 1);
+ }
+ $filename = str_replace('/', '__', $filename);
+ return $this->_cacheRoot. $filename. '.it';
+ } // _cachedName
+
+
+ /**
+ * Returns a full name of a "source" template file
+ *
+ * @param string source filename, relative to root directory
+ * @access private
+ * @return string
+ */
+ function _sourceName($filename)
+ {
+ if ('/' == $filename{0} && '/' == substr($this->fileRoot, -1)) {
+ $filename = substr($filename, 1);
+ }
+ return $this->fileRoot . $filename;
+ } // _sourceName
+
+
+ /**
+ * Writes a prepared template file.
+ *
+ * Even if NO caching is going on, this method has a side effect: it calls
+ * the _pullTriggers() method and thus loads all files added via <!-- INCLUDE -->
+ *
+ * @access private
+ * @param string source filename, relative to root directory
+ * @param string name of the block to save into file
+ * @return mixed SIGMA_OK on success, error object on failure
+ */
+ function _writeCache($filename, $block)
+ {
+ // do not save anything if no cache dir, but do pull triggers
+ if (null !== $this->_cacheRoot) {
+ $cache = array(
+ 'blocks' => array(),
+ 'variables' => array(),
+ 'children' => array(),
+ 'functions' => array()
+ );
+ $cachedName = $this->_cachedName($filename);
+ $this->_buildCache($cache, $block);
+ if ('__global__' != $block) {
+ foreach (array_keys($cache) as $k) {
+ $cache[$k]['__global__'] = $cache[$k][$block];
+ unset($cache[$k][$block]);
+ }
+ }
+ if (isset($this->_triggers[$block])) {
+ $cache['triggers'] = $this->_triggers[$block];
+ }
+ if (!($fh = @fopen($cachedName, 'w'))) {
+ return $this->raiseError($this->errorMessage(SIGMA_CACHE_ERROR, $cachedName), SIGMA_CACHE_ERROR);
+ }
+ fwrite($fh, serialize($cache));
+ fclose($fh);
+ }
+ // now pull triggers
+ if (isset($this->_triggers[$block])) {
+ if (SIGMA_OK !== ($res = $this->_pullTriggers($this->_triggers[$block]))) {
+ return $res;
+ }
+ unset($this->_triggers[$block]);
+ }
+ return SIGMA_OK;
+ } // _writeCache
+
+
+ /**
+ * Builds an array of template data to be saved in prepared template file
+ *
+ * @access private
+ * @param array template data
+ * @param string block to add to the array
+ */
+ function _buildCache(&$cache, $block)
+ {
+ if (!$this->_options['trim_on_save']) {
+ $cache['blocks'][$block] = $this->_blocks[$block];
+ } else {
+ $cache['blocks'][$block] = preg_replace(
+ array('/^\\s+/m', '/\\s+$/m', '/(\\r?\\n)+/'),
+ array('', '', "\n"),
+ $this->_blocks[$block]
+ );
+ }
+ $cache['variables'][$block] = $this->_blockVariables[$block];
+ $cache['functions'][$block] = isset($this->_functions[$block])? $this->_functions[$block]: array();
+ if (!isset($this->_children[$block])) {
+ $cache['children'][$block] = array();
+ } else {
+ $cache['children'][$block] = $this->_children[$block];
+ foreach (array_keys($this->_children[$block]) as $child) {
+ $this->_buildCache($cache, $child);
+ }
+ }
+ }
+
+
+ /**
+ * Recursively removes all data belonging to a block
+ *
+ * @param string block name
+ * @param boolean true if the parsed contents of the block should be kept
+ * @return mixed SIGMA_OK on success, error object on failure
+ * @see replaceBlock(), replaceBlockfile()
+ * @access private
+ */
+ function _removeBlockData($block, $keepContent = false)
+ {
+ if (!isset($this->_blocks[$block])) {
+ return $this->raiseError($this->errorMessage(SIGMA_BLOCK_NOT_FOUND, $block), SIGMA_BLOCK_NOT_FOUND);
+ }
+ if (!empty($this->_children[$block])) {
+ foreach (array_keys($this->_children[$block]) as $child) {
+ $this->_removeBlockData($child, false);
+ }
+ unset($this->_children[$block]);
+ }
+ unset($this->_blocks[$block]);
+ unset($this->_blockVariables[$block]);
+ unset($this->_hiddenBlocks[$block]);
+ unset($this->_touchedBlocks[$block]);
+ unset($this->_functions[$block]);
+ if (!$keepContent) {
+ unset($this->_parsedBlocks[$block]);
+ }
+ return SIGMA_OK;
+ }
+
+
+ /**
+ * Returns the names of the blocks where the variable placeholder appears
+ *
+ * @param string variable name
+ * @return array block names
+ * @see addBlock(), addBlockfile(), placeholderExists()
+ * @access private
+ */
+ function _findParentBlocks($variable)
+ {
+ $parents = array();
+ foreach ($this->_blockVariables as $blockname => $varnames) {
+ if (!empty($varnames[$variable])) {
+ $parents[] = $blockname;
+ }
+ }
+ return $parents;
+ }
+
+
+ /**
+ * Replaces a variable placeholder by a block placeholder.
+ *
+ * Of course, it also updates the necessary arrays
+ *
+ * @param string name of the block containing the placeholder
+ * @param string variable name
+ * @param string block name
+ * @access private
+ */
+ function _replacePlaceholder($parent, $placeholder, $block)
+ {
+ $this->_children[$parent][$block] = true;
+ $this->_blockVariables[$parent]['__'.$block.'__'] = true;
+ $this->_blocks[$parent] = str_replace($this->openingDelimiter.$placeholder.$this->closingDelimiter,
+ $this->openingDelimiter.'__'.$block.'__'.$this->closingDelimiter,
+ $this->_blocks[$parent] );
+ unset($this->_blockVariables[$parent][$placeholder]);
+ }
+
+
+ /**
+ * Generates a placeholder to replace an <!-- INCLUDE filename --> statement
+ *
+ * @access private
+ * @param string filename
+ * @param string current block name
+ * @return string a placeholder
+ */
+ function _makeTrigger($filename, $block)
+ {
+ $name = 'trigger_' . substr(md5($filename . ' ' . uniqid($block)), 0, 10);
+ $this->_triggers[$block][$name] = $filename;
+ return $this->openingDelimiter . $name . $this->closingDelimiter;
+ }
+
+
+ /**
+ * Replaces the "trigger" placeholders by the matching file contents.
+ *
+ * @see _makeTrigger(), addBlockfile()
+ * @param array array ('trigger placeholder' => 'filename')
+ * @return mixed SIGMA_OK on success, error object on failure
+ * @access private
+ */
+ function _pullTriggers($triggers)
+ {
+ foreach ($triggers as $placeholder => $filename) {
+ if (SIGMA_OK !== ($res = $this->addBlockfile($placeholder, $placeholder, $filename))) {
+ return $res;
+ }
+ // we actually do not need the resultant block...
+ $parents = $this->_findParentBlocks('__' . $placeholder . '__');
+ // merge current block's children and variables with the parent's ones
+ if (isset($this->_children[$placeholder])) {
+ $this->_children[$parents[0]] = array_merge($this->_children[$parents[0]], $this->_children[$placeholder]);
+ }
+ $this->_blockVariables[$parents[0]] = array_merge($this->_blockVariables[$parents[0]], $this->_blockVariables[$placeholder]);
+ if (isset($this->_functions[$placeholder])) {
+ $this->_functions[$parents[0]] = array_merge($this->_functions[$parents[0]], $this->_functions[$placeholder]);
+ }
+ // substitute the block's contents into parent's
+ $this->_blocks[$parents[0]] = str_replace(
+ $this->openingDelimiter . '__' . $placeholder . '__' . $this->closingDelimiter,
+ $this->_blocks[$placeholder],
+ $this->_blocks[$parents[0]]
+ );
+ // remove the stuff that is no more needed
+ unset($this->_blocks[$placeholder], $this->_blockVariables[$placeholder], $this->_children[$placeholder], $this->_functions[$placeholder]);
+ unset($this->_children[$parents[0]][$placeholder], $this->_blockVariables[$parents[0]]['__' . $placeholder . '__']);
+ }
+ return SIGMA_OK;
+ }
+
+
+ /**
+ * Builds a list of functions in a block.
+ *
+ * @access private
+ * @param string Block name
+ * @see _buildBlockVariables()
+ */
+ function _buildFunctionlist($block)
+ {
+ $template = $this->_blocks[$block];
+ $this->_blocks[$block] = '';
+
+ while (preg_match($this->functionRegExp, $template, $regs)) {
+ $this->_blocks[$block] .= substr($template, 0, strpos($template, $regs[0]));
+ $template = substr($template, strpos($template, $regs[0]) + strlen($regs[0]));
+
+ $state = 1;
+ $funcData = array(
+ 'name' => $regs[1],
+ 'args' => array()
+ );
+ for ($i = 0, $len = strlen($template); $i < $len; $i++) {
+ $char = $template{$i};
+ switch ($state) {
+ case 0:
+ case -1:
+ break 2;
+
+ case 1:
+ $arg = '';
+ if (')' == $char) {
+ $state = 0;
+ } elseif (',' == $char) {
+ $error = 'Unexpected \',\'';
+ $state = -1;
+ } elseif ('\'' == $char || '"' == $char) {
+ $quote = $char;
+ $state = 5;
+ } elseif (!ctype_space($char)) {
+ $arg .= $char;
+ $state = 3;
+ }
+ break;
+
+ case 2:
+ $arg = '';
+ if (',' == $char || ')' == $char) {
+ $error = 'Unexpected \'' . $char . '\'';
+ $state = -1;
+ } elseif ('\'' == $char || '"' == $char) {
+ $quote = $char;
+ $state = 5;
+ } elseif (!ctype_space($char)) {
+ $arg .= $char;
+ $state = 3;
+ }
+ break;
+
+ case 3:
+ if (')' == $char) {
+ $funcData['args'][] = rtrim($arg);
+ $state = 0;
+ } elseif (',' == $char) {
+ $funcData['args'][] = rtrim($arg);
+ $state = 2;
+ } elseif ('\'' == $char || '"' == $char) {
+ $quote = $char;
+ $arg .= $char;
+ $state = 4;
+ } else {
+ $arg .= $char;
+ }
+ break;
+
+ case 4:
+ $arg .= $char;
+ if ($quote == $char) {
+ $state = 3;
+ }
+ break;
+
+ case 5:
+ if ('\\' == $char) {
+ $state = 6;
+ } elseif ($quote == $char) {
+ $state = 7;
+ } else {
+ $arg .= $char;
+ }
+ break;
+
+ case 6;
+ $arg .= $char;
+ $state = 5;
+ break;
+
+ case 7:
+ if (')' == $char) {
+ $funcData['args'][] = $arg;
+ $state = 0;
+ } elseif (',' == $char) {
+ $funcData['args'][] = $arg;
+ $state = 2;
+ } elseif (!ctype_space($char)) {
+ $error = 'Unexpected \'' . $char . '\' (expected: \')\' or \',\')';
+ $state = -1;
+ }
+ break;
+ } // switch
+ } // for
+ if (0 != $state) {
+ return $this->raiseError($this->errorMessage(SIGMA_CALLBACK_SYNTAX_ERROR, (empty($error)? 'Unexpected end of input': $error) . ' in ' . $regs[0] . substr($template, 0, $i)), SIGMA_CALLBACK_SYNTAX_ERROR);
+ } else {
+ $funcId = substr(md5(serialize($funcData)), 0, 10);
+ $template = substr($template, $i);
+
+ $this->_blocks[$block] .= '{__function_' . $funcId . '__}';
+ $this->_blockVariables[$block]['__function_' . $funcId . '__'] = true;
+ $this->_functions[$block][$funcId] = $funcData;
+ }
+ } // while
+ $this->_blocks[$block] .= $template;
+ return SIGMA_OK;
+ } // end func _buildFunctionlist
+
+
+ /**
+ * Replaces an opening delimiter by a special string.
+ *
+ * Used to implement $_options['preserve_data'] logic
+ *
+ * @access private
+ * @param string
+ * @return string
+ */
+ function _preserveOpeningDelimiter($str)
+ {
+ return (false === strpos($str, $this->openingDelimiter))?
+ $str:
+ str_replace($this->openingDelimiter, $this->openingDelimiter . '%preserved%' . $this->closingDelimiter, $str);
+ }
+
+
+ /**
+ * Quotes the string so that it can be used in Javascript string constants
+ *
+ * @access private
+ * @param string
+ * @return string
+ */
+ function _jsEscape($value)
+ {
+ return strtr($value, array(
+ "\r" => '\r', "'" => "\\'", "\n" => '\n',
+ '"' => '\"', "\t" => '\t', '\\' => '\\\\'
+ ));
+ }
+}
+?>
diff --git a/pearlib/HTTP/Client.php b/pearlib/HTTP/Client.php
new file mode 100644
index 00000000..5708cc3c
--- /dev/null
+++ b/pearlib/HTTP/Client.php
@@ -0,0 +1,461 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Alexey Borzov <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Client.php,v 1.4 2004/03/23 13:35:37 avb Exp $
+
+require_once 'HTTP/Request.php';
+require_once 'HTTP/Client/CookieManager.php';
+
+/**
+ * A simple HTTP client class.
+ *
+ * The class wraps around HTTP_Request providing a higher-level
+ * API for performing multiple HTTP requests
+ *
+ * @package HTTP_Client
+ * @author Alexey Borzov <[email protected]>
+ * @version $Revision: 1.4 $
+ */
+class HTTP_Client
+{
+ /**
+ * An HTTP_Client_CookieManager instance
+ * @var object
+ */
+ var $_cookieManager;
+
+ /**
+ * Received HTTP responses
+ * @var array
+ */
+ var $_responses;
+
+ /**
+ * Default headers to send on every request
+ * @var array
+ */
+ var $_defaultHeaders = array();
+
+ /**
+ * Default parameters for HTTP_Request's constructor
+ * @var array
+ */
+ var $_defaultRequestParams = array();
+
+ /**
+ * How many redirects were done
+ * @var integer
+ */
+ var $_redirectCount = 0;
+
+ /**
+ * Maximum allowed redirects
+ * @var integer
+ */
+ var $_maxRedirects = 5;
+
+ /**
+ * Listeners attached to the client
+ * @var array
+ */
+ var $_listeners = array();
+
+ /**
+ * Whether the listener should be propagated to Request objects
+ * @var array
+ */
+ var $_propagate = array();
+
+ /**
+ * Whether to keep all the responses or just the most recent one
+ * @var boolean
+ */
+ var $_isHistoryEnabled = true;
+
+ /**
+ * Constructor
+ *
+ * @access public
+ * @param array Parameters to pass to HTTP_Request's constructor
+ * @param array Default headers to send on every request
+ */
+ function HTTP_Client($defaultRequestParams = null, $defaultHeaders = null)
+ {
+ $this->_cookieManager =& new HTTP_Client_CookieManager();
+ if (isset($defaultHeaders)) {
+ $this->setDefaultHeader($defaultHeaders);
+ }
+ if (isset($defaultRequestParams)) {
+ $this->setRequestParameter($defaultRequestParams);
+ }
+ }
+
+
+ /**
+ * Sets the maximum redirects that will be processed.
+ *
+ * Setting this to 0 disables redirect processing. If not 0 and the
+ * number of redirects in a request is bigger than this number, then an
+ * error will be raised.
+ *
+ * @access public
+ * @param int Max number of redirects to process
+ */
+ function setMaxRedirects($value)
+ {
+ $this->_maxRedirects = $value;
+ }
+
+
+ /**
+ * Sets whether to keep all the responses or just the most recent one
+ *
+ * @access public
+ * @param bool Whether to enable history
+ */
+ function enableHistory($enable)
+ {
+ $this->_isHistoryEnabled = (bool)$enable;
+ }
+
+ /**
+ * Creates a HTTP_Request objects, applying all the necessary defaults
+ *
+ * @param string URL
+ * @param integer Method, constants are defined in HTTP_Request
+ * @access private
+ * @return object HTTP_Request object with all defaults applied
+ */
+ function &_createRequest($url, $method = HTTP_REQUEST_METHOD_GET)
+ {
+ $req =& new HTTP_Request($url, $this->_defaultRequestParams);
+ $req->setMethod($method);
+ foreach ($this->_defaultHeaders as $name => $value) {
+ $req->addHeader($name, $value);
+ }
+ $this->_cookieManager->passCookies($req);
+ foreach ($this->_propagate as $id => $propagate) {
+ if ($propagate) {
+ $req->attach($this->_listeners[$id]);
+ }
+ }
+ return $req;
+ }
+
+
+ /**
+ * Sends a 'HEAD' HTTP request
+ *
+ * @param string URL
+ * @access public
+ * @return integer HTTP response code
+ * @throws PEAR_Error
+ */
+ function head($url)
+ {
+ $request =& $this->_createRequest($url, HTTP_REQUEST_METHOD_HEAD);
+ return $this->_performRequest($request);
+ }
+
+
+ /**
+ * Sends a 'GET' HTTP request
+ *
+ * @param string URL
+ * @param mixed additional data to send
+ * @param boolean Whether the data is already urlencoded
+ * @access public
+ * @return integer HTTP response code
+ * @throws PEAR_Error
+ */
+ function get($url, $data = null, $preEncoded = false)
+ {
+ $request =& $this->_createRequest($url);
+ if (is_array($data)) {
+ foreach ($data as $name => $value) {
+ $request->addQueryString($name, $value, $preEncoded);
+ }
+ } elseif (isset($data)) {
+ $request->addRawQueryString($data, $preEncoded);
+ }
+ return $this->_performRequest($request);
+ }
+
+
+ /**
+ * Sends a 'POST' HTTP request
+ *
+ * @param string URL
+ * @param mixed Data to send
+ * @param boolean Whether the data is already urlencoded
+ * @param array Files to upload. Elements of the array should have the form:
+ * array(name, filename(s)[, content type]), see HTTP_Request::addFile()
+ * @access public
+ * @return integer HTTP response code
+ * @throws PEAR_Error
+ */
+ function post($url, $data, $preEncoded = false, $files = array())
+ {
+ $request =& $this->_createRequest($url, HTTP_REQUEST_METHOD_POST);
+ if (is_array($data)) {
+ foreach ($data as $name => $value) {
+ $request->addPostData($name, $value, $preEncoded);
+ }
+ } else {
+ $request->addRawPostData($data, $preEncoded);
+ }
+ foreach ($files as $fileData) {
+ $res = call_user_func_array(array(&$request, 'addFile'), $fileData);
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ }
+ return $this->_performRequest($request);
+ }
+
+
+ /**
+ * Sets default header(s) for HTTP requests
+ *
+ * @param mixed header name or array ('header name' => 'header value')
+ * @param string header value if $name is not an array
+ * @access public
+ */
+ function setDefaultHeader($name, $value = null)
+ {
+ if (is_array($name)) {
+ $this->_defaultHeaders = array_merge($this->_defaultHeaders, $name);
+ } else {
+ $this->_defaultHeaders[$name] = $value;
+ }
+ }
+
+
+ /**
+ * Sets parameter(s) for HTTP requests
+ *
+ * @param mixed parameter name or array ('parameter name' => 'parameter value')
+ * @param string parameter value if $name is not an array
+ * @access public
+ */
+ function setRequestParameter($name, $value = null)
+ {
+ if (is_array($name)) {
+ $this->_defaultRequestParams = array_merge($this->_defaultRequestParams, $name);
+ } else {
+ $this->_defaultRequestParams[$name] = $value;
+ }
+ }
+
+
+ /**
+ * Performs a request, processes redirects
+ *
+ * @param object HTTP_Request object
+ * @access private
+ * @return integer HTTP response code
+ * @throws PEAR_Error
+ */
+ function _performRequest(&$request)
+ {
+ // If this is not a redirect, notify the listeners of new request
+ if (0 == $this->_redirectCount) {
+ $this->_notify('request', $request->_url->getUrl());
+ }
+ if (PEAR::isError($err = $request->sendRequest())) {
+ return $err;
+ }
+ $this->_pushResponse($request);
+
+ $code = $request->getResponseCode();
+ if ($this->_maxRedirects > 0 && in_array($code, array(300, 301, 302, 303, 307))) {
+ if (++$this->_redirectCount > $this->_maxRedirects) {
+ return PEAR::raiseError('Too many redirects');
+ }
+ $location = $request->getResponseHeader('Location');
+ if ('' == $location) {
+ return PEAR::raiseError("No 'Location' field on redirect");
+ }
+ $url = $this->_redirectUrl($request->_url, $location);
+ // Notify of redirection
+ $this->_notify('httpRedirect', $url);
+ // we access the private properties directly, as there are no accessors for them
+ switch ($request->_method) {
+ case HTTP_REQUEST_METHOD_POST:
+ if (302 == $code || 303 == $code) {
+ return $this->get($url);
+ } else {
+ $postFiles = array();
+ foreach ($request->_postFiles as $name => $data) {
+ $postFiles[] = array($name, $data['name'], $data['type']);
+ }
+ return $this->post($url, $request->_postData, true, $postFiles);
+ }
+ case HTTP_REQUEST_METHOD_HEAD:
+ return (303 == $code? $this->get($url): $this->head($url));
+ case HTTP_REQUEST_METHOD_GET:
+ default:
+ return $this->get($url);
+ } // switch
+
+ } else {
+ $this->_redirectCount = 0;
+ if (400 >= $code) {
+ $this->_notify('httpSuccess');
+ $this->setDefaultHeader('Referer', $request->_url->getUrl());
+ // some result processing should go here
+ } else {
+ $this->_notify('httpError');
+ }
+ }
+ return $code;
+ }
+
+
+ /**
+ * Returns the most recent HTTP response
+ *
+ * @access public
+ * @return array
+ */
+ function &currentResponse()
+ {
+ return $this->_responses[count($this->_responses) - 1];
+ }
+
+
+ /**
+ * Saves the server's response to responses list
+ *
+ * @param object HTTP_Request object, with request already sent
+ * @access private
+ */
+ function _pushResponse(&$request)
+ {
+ $this->_cookieManager->updateCookies($request);
+ $idx = $this->_isHistoryEnabled? count($this->_responses): 0;
+ $this->_responses[$idx] = array(
+ 'code' => $request->getResponseCode(),
+ 'headers' => $request->getResponseHeader(),
+ 'body' => $request->getResponseBody()
+ );
+ }
+
+
+ /**
+ * Clears object's internal properties
+ *
+ * @access public
+ */
+ function reset()
+ {
+ $this->_cookieManager->reset();
+ $this->_responses = array();
+ $this->_defaultHeaders = array();
+ $this->_defaultRequestParams = array();
+ }
+
+
+ /**
+ * Adds a Listener to the list of listeners that are notified of
+ * the object's events
+ *
+ * @param object HTTP_Request_Listener instance to attach
+ * @param boolean Whether the listener should be attached to the
+ * created HTTP_Request objects
+ * @return boolean whether the listener was successfully attached
+ * @access public
+ */
+ function attach(&$listener, $propagate = false)
+ {
+ if (!is_a($listener, 'HTTP_Request_Listener')) {
+ return false;
+ }
+ $this->_listeners[$listener->getId()] =& $listener;
+ $this->_propagate[$listener->getId()] = $propagate;
+ return true;
+ }
+
+
+ /**
+ * Removes a Listener from the list of listeners
+ *
+ * @param object HTTP_Request_Listener instance to detach
+ * @return boolean whether the listener was successfully detached
+ * @access public
+ */
+ function detach(&$listener)
+ {
+ if (!is_a($listener, 'HTTP_Request_Listener') ||
+ !isset($this->_listeners[$listener->getId()])) {
+ return false;
+ }
+ unset($this->_listeners[$listener->getId()], $this->_propagate[$listener->getId()]);
+ return true;
+ }
+
+
+ /**
+ * Notifies all registered listeners of an event.
+ *
+ * Currently available events are:
+ * 'request': sent on HTTP request that is not a redirect
+ * 'httpSuccess': sent when we receive a successfull 2xx response
+ * 'httpRedirect': sent when we receive a redirection response
+ * 'httpError': sent on 4xx, 5xx response
+ *
+ * @param string Event name
+ * @param mixed Additional data
+ * @access private
+ */
+ function _notify($event, $data = null)
+ {
+ foreach (array_keys($this->_listeners) as $id) {
+ $this->_listeners[$id]->update($this, $event, $data);
+ }
+ }
+
+
+ /**
+ * Calculates the absolute URL of a redirect
+ *
+ * @param object Net_Url object containing the request URL
+ * @param string Value of the 'Location' response header
+ * @return string Absolute URL we are being redirected to
+ * @access private
+ */
+ function _redirectUrl($url, $location)
+ {
+ if (preg_match('!^https?://!i', $location)) {
+ return $location;
+ } else {
+ if ('/' == $location{0}) {
+ $url->path = Net_URL::resolvePath($location);
+ } elseif('/' == substr($url->path, -1)) {
+ $url->path = Net_URL::resolvePath($url->path . $location);
+ } else {
+ $dirname = (DIRECTORY_SEPARATOR == dirname($url->path)? '/': dirname($url->path));
+ $url->path = Net_URL::resolvePath($dirname . '/' . $location);
+ }
+ $url->querystring = array();
+ $url->anchor = '';
+ return $url->getUrl();
+ }
+ }
+}
+?>
diff --git a/pearlib/HTTP/Client/CookieManager.php b/pearlib/HTTP/Client/CookieManager.php
new file mode 100644
index 00000000..3b9abb20
--- /dev/null
+++ b/pearlib/HTTP/Client/CookieManager.php
@@ -0,0 +1,183 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Alexey Borzov <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: CookieManager.php,v 1.3 2004/04/10 10:04:52 avb Exp $
+
+/**
+ * This class is used to store cookies and pass them between HTTP requests.
+ *
+ * @package HTTP_Client
+ * @author Alexey Borzov <[email protected]>
+ * @version $Revision: 1.3 $
+ */
+class HTTP_Client_CookieManager
+{
+ /**
+ * An array containing cookie values
+ * @var array
+ */
+ var $_cookies = array();
+
+
+ /**
+ * Constructor
+ *
+ * @access public
+ */
+ function HTTP_Client_CookieManager()
+ {
+ // abstract
+ }
+
+
+ /**
+ * Adds cookies to the request
+ *
+ * @access public
+ * @param object An HTTP_Request object
+ */
+ function passCookies(&$request)
+ {
+ if (!empty($this->_cookies)) {
+ $url =& $request->_url;
+ // We do not check cookie's "expires" field, as we do not store deleted
+ // cookies in the array and our client does not work long enough for other
+ // cookies to expire. If some kind of persistence is added to this object,
+ // then expiration should be checked upon loading and session cookies should
+ // be cleared on saving.
+ $cookies = array();
+ foreach ($this->_cookies as $cookie) {
+ if ($this->_domainMatch($url->host, $cookie['domain']) && (0 === strpos($url->path, $cookie['path']))
+ && (empty($cookie['secure']) || $url->protocol == 'https')) {
+ $cookies[$cookie['name']][strlen($cookie['path'])] = $cookie['value'];
+ }
+ }
+ // cookies with longer paths go first
+ foreach ($cookies as $name => $values) {
+ krsort($values);
+ foreach ($values as $value) {
+ $request->addCookie($name, $value);
+ }
+ }
+ }
+ return true;
+ }
+
+
+ /**
+ * Explicitly adds cookie to the list
+ *
+ * @param array An array representing cookie, this function expects all of the array's
+ * fields to be set
+ * @access public
+ */
+ function addCookie($cookie)
+ {
+ $hash = $this->_makeHash($cookie['name'], $cookie['domain'], $cookie['path']);
+ $this->_cookies[$hash] = $cookie;
+ }
+
+
+ /**
+ * Updates cookie list from HTTP server response
+ *
+ * @access public
+ * @param object An HTTP_Request object with sendRequest() already done
+ */
+ function updateCookies(&$request)
+ {
+ if (false !== ($cookies = $request->getResponseCookies())) {
+ $url =& $request->_url;
+ foreach ($cookies as $cookie) {
+ // use the current domain by default
+ if (!isset($cookie['domain'])) {
+ $cookie['domain'] = $url->host;
+ }
+ // use the path to the current page by default
+ if (!isset($cookie['path'])) {
+ $cookie['path'] = DIRECTORY_SEPARATOR == dirname($url->path)? '/': dirname($url->path);
+ }
+ // check if the domains match
+ if ($this->_domainMatch($url->host, $cookie['domain'])) {
+ $hash = $this->_makeHash($cookie['name'], $cookie['domain'], $cookie['path']);
+ // if value is empty or the time is in the past the cookie is deleted, else added
+ if (strlen($cookie['value'])
+ && (!isset($cookie['expires']) || (strtotime($cookie['expires']) > time()))) {
+ $this->_cookies[$hash] = $cookie;
+ } elseif (isset($this->_cookies[$hash])) {
+ unset($this->_cookies[$hash]);
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Generates a key for the $_cookies array.
+ *
+ * The cookies is uniquely identified by its name, domain and path.
+ * Thus we cannot make f.e. an associative array with name as a key, we should
+ * generate a key from these 3 values.
+ *
+ * @access private
+ * @param string Cookie name
+ * @param string Cookie domain
+ * @param string Cookie path
+ * @return string a key
+ */
+ function _makeHash($name, $domain, $path)
+ {
+ return md5($name . "\r\n" . $domain . "\r\n" . $path);
+ }
+
+
+ /**
+ * Checks whether a cookie domain matches a request host.
+ *
+ * Cookie domain can begin with a dot, it also must contain at least
+ * two dots.
+ *
+ * @access private
+ * @param string request host
+ * @param string cookie domain
+ * @return bool match success
+ */
+ function _domainMatch($requestHost, $cookieDomain)
+ {
+ if ('.' != $cookieDomain{0}) {
+ return $requestHost == $cookieDomain;
+ } elseif (substr_count($cookieDomain, '.') < 2) {
+ return false;
+ } else {
+ return substr('.'. $requestHost, - strlen($cookieDomain)) == $cookieDomain;
+ }
+ }
+
+
+ /**
+ * Clears the $_cookies array
+ *
+ * @access public
+ */
+ function reset()
+ {
+ $this->_cookies = array();
+ }
+}
+?>
diff --git a/pearlib/HTTP/Request.php b/pearlib/HTTP/Request.php
new file mode 100644
index 00000000..5aac3ec5
--- /dev/null
+++ b/pearlib/HTTP/Request.php
@@ -0,0 +1,1132 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Copyright (c) 2002-2003, Richard Heyes |
+// | All rights reserved. |
+// | |
+// | Redistribution and use in source and binary forms, with or without |
+// | modification, are permitted provided that the following conditions |
+// | are met: |
+// | |
+// | o Redistributions of source code must retain the above copyright |
+// | notice, this list of conditions and the following disclaimer. |
+// | o Redistributions in binary form must reproduce the above copyright |
+// | notice, this list of conditions and the following disclaimer in the |
+// | documentation and/or other materials provided with the distribution.|
+// | o The names of the authors may not be used to endorse or promote |
+// | products derived from this software without specific prior written |
+// | permission. |
+// | |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+// | |
+// +-----------------------------------------------------------------------+
+// | Author: Richard Heyes <[email protected]> |
+// +-----------------------------------------------------------------------+
+//
+// $Id: Request.php,v 1.40 2004/10/01 15:48:52 avb Exp $
+//
+// HTTP_Request Class
+//
+// Simple example, (Fetches yahoo.com and displays it):
+//
+// $a = &new HTTP_Request('http://www.yahoo.com/');
+// $a->sendRequest();
+// echo $a->getResponseBody();
+//
+
+require_once 'PEAR.php';
+require_once 'Net/Socket.php';
+require_once 'Net/URL.php';
+
+define('HTTP_REQUEST_METHOD_GET', 'GET', true);
+define('HTTP_REQUEST_METHOD_HEAD', 'HEAD', true);
+define('HTTP_REQUEST_METHOD_POST', 'POST', true);
+define('HTTP_REQUEST_METHOD_PUT', 'PUT', true);
+define('HTTP_REQUEST_METHOD_DELETE', 'DELETE', true);
+define('HTTP_REQUEST_METHOD_OPTIONS', 'OPTIONS', true);
+define('HTTP_REQUEST_METHOD_TRACE', 'TRACE', true);
+
+define('HTTP_REQUEST_HTTP_VER_1_0', '1.0', true);
+define('HTTP_REQUEST_HTTP_VER_1_1', '1.1', true);
+
+class HTTP_Request {
+
+ /**
+ * Instance of Net_URL
+ * @var object
+ */
+ var $_url;
+
+ /**
+ * Type of request
+ * @var string
+ */
+ var $_method;
+
+ /**
+ * HTTP Version
+ * @var string
+ */
+ var $_http;
+
+ /**
+ * Request headers
+ * @var array
+ */
+ var $_requestHeaders;
+
+ /**
+ * Basic Auth Username
+ * @var string
+ */
+ var $_user;
+
+ /**
+ * Basic Auth Password
+ * @var string
+ */
+ var $_pass;
+
+ /**
+ * Socket object
+ * @var object
+ */
+ var $_sock;
+
+ /**
+ * Proxy server
+ * @var string
+ */
+ var $_proxy_host;
+
+ /**
+ * Proxy port
+ * @var integer
+ */
+ var $_proxy_port;
+
+ /**
+ * Proxy username
+ * @var string
+ */
+ var $_proxy_user;
+
+ /**
+ * Proxy password
+ * @var string
+ */
+ var $_proxy_pass;
+
+ /**
+ * Post data
+ * @var mixed
+ */
+ var $_postData;
+
+ /**
+ * Files to post
+ * @var array
+ */
+ var $_postFiles = array();
+
+ /**
+ * Connection timeout.
+ * @var integer
+ */
+ var $_timeout;
+
+ /**
+ * HTTP_Response object
+ * @var object
+ */
+ var $_response;
+
+ /**
+ * Whether to allow redirects
+ * @var boolean
+ */
+ var $_allowRedirects;
+
+ /**
+ * Maximum redirects allowed
+ * @var integer
+ */
+ var $_maxRedirects;
+
+ /**
+ * Current number of redirects
+ * @var integer
+ */
+ var $_redirects;
+
+ /**
+ * Whether to append brackets [] to array variables
+ * @var bool
+ */
+ var $_useBrackets = true;
+
+ /**
+ * Attached listeners
+ * @var array
+ */
+ var $_listeners = array();
+
+ /**
+ * Whether to save response body in response object property
+ * @var bool
+ */
+ var $_saveBody = true;
+
+ /**
+ * Timeout for reading from socket (array(seconds, microseconds))
+ * @var array
+ */
+ var $_readTimeout = null;
+
+ /**
+ * Options to pass to Net_Socket::connect. See stream_context_create
+ * @var array
+ */
+ var $_socketOptions = null;
+
+ /**
+ * Constructor
+ *
+ * Sets up the object
+ * @param $url The url to fetch/access
+ * @param $params Associative array of parameters which can be:
+ * method - Method to use, GET, POST etc
+ * http - HTTP Version to use, 1.0 or 1.1
+ * user - Basic Auth username
+ * pass - Basic Auth password
+ * proxy_host - Proxy server host
+ * proxy_port - Proxy server port
+ * proxy_user - Proxy auth username
+ * proxy_pass - Proxy auth password
+ * timeout - Connection timeout in seconds.
+ * allowRedirects - Whether to follow redirects or not
+ * maxRedirects - Max number of redirects to follow
+ * useBrackets - Whether to append [] to array variable names
+ * saveBody - Whether to save response body in response object property
+ * @access public
+ */
+ function HTTP_Request($url = '', $params = array())
+ {
+ $this->_sock = &new Net_Socket();
+ $this->_method = HTTP_REQUEST_METHOD_GET;
+ $this->_http = HTTP_REQUEST_HTTP_VER_1_1;
+ $this->_requestHeaders = array();
+ $this->_postData = null;
+
+ $this->_user = null;
+ $this->_pass = null;
+
+ $this->_proxy_host = null;
+ $this->_proxy_port = null;
+ $this->_proxy_user = null;
+ $this->_proxy_pass = null;
+
+ $this->_allowRedirects = false;
+ $this->_maxRedirects = 3;
+ $this->_redirects = 0;
+
+ $this->_timeout = null;
+ $this->_response = null;
+
+ foreach ($params as $key => $value) {
+ $this->{'_' . $key} = $value;
+ }
+
+ if (!empty($url)) {
+ $this->setURL($url);
+ }
+
+ // Default useragent
+ $this->addHeader('User-Agent', 'PEAR HTTP_Request class ( http://pear.php.net/ )');
+
+ // Make sure keepalives dont knobble us
+ $this->addHeader('Connection', 'close');
+
+ // Basic authentication
+ if (!empty($this->_user)) {
+ $this->_requestHeaders['Authorization'] = 'Basic ' . base64_encode($this->_user . ':' . $this->_pass);
+ }
+
+ // Use gzip encoding if possible
+ // Avoid gzip encoding if using multibyte functions (see #1781)
+ if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http && extension_loaded('zlib') &&
+ 0 == (2 & ini_get('mbstring.func_overload'))) {
+
+ $this->addHeader('Accept-Encoding', 'gzip');
+ }
+ }
+
+ /**
+ * Generates a Host header for HTTP/1.1 requests
+ *
+ * @access private
+ * @return string
+ */
+ function _generateHostHeader()
+ {
+ if ($this->_url->port != 80 AND strcasecmp($this->_url->protocol, 'http') == 0) {
+ $host = $this->_url->host . ':' . $this->_url->port;
+
+ } elseif ($this->_url->port != 443 AND strcasecmp($this->_url->protocol, 'https') == 0) {
+ $host = $this->_url->host . ':' . $this->_url->port;
+
+ } elseif ($this->_url->port == 443 AND strcasecmp($this->_url->protocol, 'https') == 0 AND strpos($this->_url->url, ':443') !== false) {
+ $host = $this->_url->host . ':' . $this->_url->port;
+
+ } else {
+ $host = $this->_url->host;
+ }
+
+ return $host;
+ }
+
+ /**
+ * Resets the object to its initial state (DEPRECATED).
+ * Takes the same parameters as the constructor.
+ *
+ * @param string $url The url to be requested
+ * @param array $params Associative array of parameters
+ * (see constructor for details)
+ * @access public
+ * @deprecated deprecated since 1.2, call the constructor if this is necessary
+ */
+ function reset($url, $params = array())
+ {
+ $this->HTTP_Request($url, $params);
+ }
+
+ /**
+ * Sets the URL to be requested
+ *
+ * @param string The url to be requested
+ * @access public
+ */
+ function setURL($url)
+ {
+ $this->_url = &new Net_URL($url, $this->_useBrackets);
+
+ if (!empty($this->_url->user) || !empty($this->_url->pass)) {
+ $this->setBasicAuth($this->_url->user, $this->_url->pass);
+ }
+
+ if (HTTP_REQUEST_HTTP_VER_1_1 == $this->_http) {
+ $this->addHeader('Host', $this->_generateHostHeader());
+ }
+ }
+
+ /**
+ * Sets a proxy to be used
+ *
+ * @param string Proxy host
+ * @param int Proxy port
+ * @param string Proxy username
+ * @param string Proxy password
+ * @access public
+ */
+ function setProxy($host, $port = 8080, $user = null, $pass = null)
+ {
+ $this->_proxy_host = $host;
+ $this->_proxy_port = $port;
+ $this->_proxy_user = $user;
+ $this->_proxy_pass = $pass;
+
+ if (!empty($user)) {
+ $this->addHeader('Proxy-Authorization', 'Basic ' . base64_encode($user . ':' . $pass));
+ }
+ }
+
+ /**
+ * Sets basic authentication parameters
+ *
+ * @param string Username
+ * @param string Password
+ */
+ function setBasicAuth($user, $pass)
+ {
+ $this->_user = $user;
+ $this->_pass = $pass;
+
+ $this->addHeader('Authorization', 'Basic ' . base64_encode($user . ':' . $pass));
+ }
+
+ /**
+ * Sets the method to be used, GET, POST etc.
+ *
+ * @param string Method to use. Use the defined constants for this
+ * @access public
+ */
+ function setMethod($method)
+ {
+ $this->_method = $method;
+ }
+
+ /**
+ * Sets the HTTP version to use, 1.0 or 1.1
+ *
+ * @param string Version to use. Use the defined constants for this
+ * @access public
+ */
+ function setHttpVer($http)
+ {
+ $this->_http = $http;
+ }
+
+ /**
+ * Adds a request header
+ *
+ * @param string Header name
+ * @param string Header value
+ * @access public
+ */
+ function addHeader($name, $value)
+ {
+ $this->_requestHeaders[$name] = $value;
+ }
+
+ /**
+ * Removes a request header
+ *
+ * @param string Header name to remove
+ * @access public
+ */
+ function removeHeader($name)
+ {
+ if (isset($this->_requestHeaders[$name])) {
+ unset($this->_requestHeaders[$name]);
+ }
+ }
+
+ /**
+ * Adds a querystring parameter
+ *
+ * @param string Querystring parameter name
+ * @param string Querystring parameter value
+ * @param bool Whether the value is already urlencoded or not, default = not
+ * @access public
+ */
+ function addQueryString($name, $value, $preencoded = false)
+ {
+ $this->_url->addQueryString($name, $value, $preencoded);
+ }
+
+ /**
+ * Sets the querystring to literally what you supply
+ *
+ * @param string The querystring data. Should be of the format foo=bar&x=y etc
+ * @param bool Whether data is already urlencoded or not, default = already encoded
+ * @access public
+ */
+ function addRawQueryString($querystring, $preencoded = true)
+ {
+ $this->_url->addRawQueryString($querystring, $preencoded);
+ }
+
+ /**
+ * Adds postdata items
+ *
+ * @param string Post data name
+ * @param string Post data value
+ * @param bool Whether data is already urlencoded or not, default = not
+ * @access public
+ */
+ function addPostData($name, $value, $preencoded = false)
+ {
+ if ($preencoded) {
+ $this->_postData[$name] = $value;
+ } else {
+ $this->_postData[$name] = $this->_arrayMapRecursive('urlencode', $value);
+ }
+ }
+
+ /**
+ * Recursively applies the callback function to the value
+ *
+ * @param mixed Callback function
+ * @param mixed Value to process
+ * @access private
+ * @return mixed Processed value
+ */
+ function _arrayMapRecursive($callback, $value)
+ {
+ if (!is_array($value)) {
+ return call_user_func($callback, $value);
+ } else {
+ $map = array();
+ foreach ($value as $k => $v) {
+ $map[$k] = $this->_arrayMapRecursive($callback, $v);
+ }
+ return $map;
+ }
+ }
+
+ /**
+ * Adds a file to upload
+ *
+ * This also changes content-type to 'multipart/form-data' for proper upload
+ *
+ * @access public
+ * @param string name of file-upload field
+ * @param mixed file name(s)
+ * @param mixed content-type(s) of file(s) being uploaded
+ * @return bool true on success
+ * @throws PEAR_Error
+ */
+ function addFile($inputName, $fileName, $contentType = 'application/octet-stream')
+ {
+ if (!is_array($fileName) && !is_readable($fileName)) {
+ return PEAR::raiseError("File '{$fileName}' is not readable");
+ } elseif (is_array($fileName)) {
+ foreach ($fileName as $name) {
+ if (!is_readable($name)) {
+ return PEAR::raiseError("File '{$name}' is not readable");
+ }
+ }
+ }
+ $this->addHeader('Content-Type', 'multipart/form-data');
+ $this->_postFiles[$inputName] = array(
+ 'name' => $fileName,
+ 'type' => $contentType
+ );
+ return true;
+ }
+
+ /**
+ * Adds raw postdata
+ *
+ * @param string The data
+ * @param bool Whether data is preencoded or not, default = already encoded
+ * @access public
+ */
+ function addRawPostData($postdata, $preencoded = true)
+ {
+ $this->_postData = $preencoded ? $postdata : urlencode($postdata);
+ }
+
+ /**
+ * Clears any postdata that has been added (DEPRECATED).
+ *
+ * Useful for multiple request scenarios.
+ *
+ * @access public
+ * @deprecated deprecated since 1.2
+ */
+ function clearPostData()
+ {
+ $this->_postData = null;
+ }
+
+ /**
+ * Appends a cookie to "Cookie:" header
+ *
+ * @param string $name cookie name
+ * @param string $value cookie value
+ * @access public
+ */
+ function addCookie($name, $value)
+ {
+ $cookies = isset($this->_requestHeaders['Cookie']) ? $this->_requestHeaders['Cookie']. '; ' : '';
+ $this->addHeader('Cookie', $cookies . $name . '=' . $value);
+ }
+
+ /**
+ * Clears any cookies that have been added (DEPRECATED).
+ *
+ * Useful for multiple request scenarios
+ *
+ * @access public
+ * @deprecated deprecated since 1.2
+ */
+ function clearCookies()
+ {
+ $this->removeHeader('Cookie');
+ }
+
+ /**
+ * Sends the request
+ *
+ * @access public
+ * @param bool Whether to store response body in Response object property,
+ * set this to false if downloading a LARGE file and using a Listener
+ * @return mixed PEAR error on error, true otherwise
+ */
+ function sendRequest($saveBody = true)
+ {
+ if (!is_a($this->_url, 'Net_URL')) {
+ return PEAR::raiseError('No URL given.');
+ }
+
+ $host = isset($this->_proxy_host) ? $this->_proxy_host : $this->_url->host;
+ $port = isset($this->_proxy_port) ? $this->_proxy_port : $this->_url->port;
+
+ // 4.3.0 supports SSL connections using OpenSSL. The function test determines
+ // we running on at least 4.3.0
+ if (strcasecmp($this->_url->protocol, 'https') == 0 AND function_exists('file_get_contents') AND extension_loaded('openssl')) {
+ if (isset($this->_proxy_host)) {
+ return PEAR::raiseError('HTTPS proxies are not supported.');
+ }
+ $host = 'ssl://' . $host;
+ }
+
+ // If this is a second request, we may get away without
+ // re-connecting if they're on the same server
+ if (PEAR::isError($err = $this->_sock->connect($host, $port, null, $this->_timeout, $this->_socketOptions)) ||
+ PEAR::isError($err = $this->_sock->write($this->_buildRequest()))) {
+
+ return $err;
+ }
+ if (!empty($this->_readTimeout)) {
+ $this->_sock->setTimeout($this->_readTimeout[0], $this->_readTimeout[1]);
+ }
+
+ $this->_notify('sentRequest');
+
+ // Read the response
+ $this->_response = &new HTTP_Response($this->_sock, $this->_listeners);
+ if (PEAR::isError($err = $this->_response->process($this->_saveBody && $saveBody)) ) {
+ return $err;
+ }
+
+ // Check for redirection
+ // Bugfix (PEAR) bug #18, 6 oct 2003 by Dave Mertens (headers are also stored lowercase, so we're gonna use them here)
+ // some non RFC2616 compliant servers (scripts) are returning lowercase headers ('location: xxx')
+ if ( $this->_allowRedirects
+ AND $this->_redirects <= $this->_maxRedirects
+ AND $this->getResponseCode() > 300
+ AND $this->getResponseCode() < 399
+ AND !empty($this->_response->_headers['location'])) {
+
+
+ $redirect = $this->_response->_headers['location'];
+
+ // Absolute URL
+ if (preg_match('/^https?:\/\//i', $redirect)) {
+ $this->_url = &new Net_URL($redirect);
+ $this->addHeader('Host', $this->_generateHostHeader());
+ // Absolute path
+ } elseif ($redirect{0} == '/') {
+ $this->_url->path = $redirect;
+
+ // Relative path
+ } elseif (substr($redirect, 0, 3) == '../' OR substr($redirect, 0, 2) == './') {
+ if (substr($this->_url->path, -1) == '/') {
+ $redirect = $this->_url->path . $redirect;
+ } else {
+ $redirect = dirname($this->_url->path) . '/' . $redirect;
+ }
+ $redirect = Net_URL::resolvePath($redirect);
+ $this->_url->path = $redirect;
+
+ // Filename, no path
+ } else {
+ if (substr($this->_url->path, -1) == '/') {
+ $redirect = $this->_url->path . $redirect;
+ } else {
+ $redirect = dirname($this->_url->path) . '/' . $redirect;
+ }
+ $this->_url->path = $redirect;
+ }
+
+ $this->_redirects++;
+ return $this->sendRequest($saveBody);
+
+ // Too many redirects
+ } elseif ($this->_allowRedirects AND $this->_redirects > $this->_maxRedirects) {
+ return PEAR::raiseError('Too many redirects');
+ }
+
+ $this->_sock->disconnect();
+
+ return true;
+ }
+
+ /**
+ * Returns the response code
+ *
+ * @access public
+ * @return mixed Response code, false if not set
+ */
+ function getResponseCode()
+ {
+ return isset($this->_response->_code) ? $this->_response->_code : false;
+ }
+
+ /**
+ * Returns either the named header or all if no name given
+ *
+ * @access public
+ * @param string The header name to return, do not set to get all headers
+ * @return mixed either the value of $headername (false if header is not present)
+ * or an array of all headers
+ */
+ function getResponseHeader($headername = null)
+ {
+ if (!isset($headername)) {
+ return isset($this->_response->_headers)? $this->_response->_headers: array();
+ } else {
+ return isset($this->_response->_headers[$headername]) ? $this->_response->_headers[$headername] : false;
+ }
+ }
+
+ /**
+ * Returns the body of the response
+ *
+ * @access public
+ * @return mixed response body, false if not set
+ */
+ function getResponseBody()
+ {
+ return isset($this->_response->_body) ? $this->_response->_body : false;
+ }
+
+ /**
+ * Returns cookies set in response
+ *
+ * @access public
+ * @return mixed array of response cookies, false if none are present
+ */
+ function getResponseCookies()
+ {
+ return isset($this->_response->_cookies) ? $this->_response->_cookies : false;
+ }
+
+ /**
+ * Builds the request string
+ *
+ * @access private
+ * @return string The request string
+ */
+ function _buildRequest()
+ {
+ $separator = ini_get('arg_separator.output');
+ ini_set('arg_separator.output', '&');
+ $querystring = ($querystring = $this->_url->getQueryString()) ? '?' . $querystring : '';
+ ini_set('arg_separator.output', $separator);
+
+ $host = isset($this->_proxy_host) ? $this->_url->protocol . '://' . $this->_url->host : '';
+ $port = (isset($this->_proxy_host) AND $this->_url->port != 80) ? ':' . $this->_url->port : '';
+ $path = (empty($this->_url->path)? '/': $this->_url->path) . $querystring;
+ $url = $host . $port . $path;
+
+ $request = $this->_method . ' ' . $url . ' HTTP/' . $this->_http . "\r\n";
+
+ if (HTTP_REQUEST_METHOD_POST != $this->_method && HTTP_REQUEST_METHOD_PUT != $this->_method) {
+ $this->removeHeader('Content-Type');
+ } else {
+ if (empty($this->_requestHeaders['Content-Type'])) {
+ // Add default content-type
+ $this->addHeader('Content-Type', 'application/x-www-form-urlencoded');
+ } elseif ('multipart/form-data' == $this->_requestHeaders['Content-Type']) {
+ $boundary = 'HTTP_Request_' . md5(uniqid('request') . microtime());
+ $this->addHeader('Content-Type', 'multipart/form-data; boundary=' . $boundary);
+ }
+ }
+
+ // Request Headers
+ if (!empty($this->_requestHeaders)) {
+ foreach ($this->_requestHeaders as $name => $value) {
+ $request .= $name . ': ' . $value . "\r\n";
+ }
+ }
+
+ // No post data or wrong method, so simply add a final CRLF
+ if ((HTTP_REQUEST_METHOD_POST != $this->_method && HTTP_REQUEST_METHOD_PUT != $this->_method) ||
+ (empty($this->_postData) && empty($this->_postFiles))) {
+
+ $request .= "\r\n";
+ // Post data if it's an array
+ } elseif ((!empty($this->_postData) && is_array($this->_postData)) || !empty($this->_postFiles)) {
+ // "normal" POST request
+ if (!isset($boundary)) {
+ $postdata = implode('&', array_map(
+ create_function('$a', 'return $a[0] . \'=\' . $a[1];'),
+ $this->_flattenArray('', $this->_postData)
+ ));
+
+ // multipart request, probably with file uploads
+ } else {
+ $postdata = '';
+ if (!empty($this->_postData)) {
+ $flatData = $this->_flattenArray('', $this->_postData);
+ foreach ($flatData as $item) {
+ $postdata .= '--' . $boundary . "\r\n";
+ $postdata .= 'Content-Disposition: form-data; name="' . $item[0] . '"';
+ $postdata .= "\r\n\r\n" . urldecode($item[1]) . "\r\n";
+ }
+ }
+ foreach ($this->_postFiles as $name => $value) {
+ if (is_array($value['name'])) {
+ $varname = $name . ($this->_useBrackets? '[]': '');
+ } else {
+ $varname = $name;
+ $value['name'] = array($value['name']);
+ }
+ foreach ($value['name'] as $key => $filename) {
+ $fp = fopen($filename, 'r');
+ $data = fread($fp, filesize($filename));
+ fclose($fp);
+ $basename = basename($filename);
+ $type = is_array($value['type'])? @$value['type'][$key]: $value['type'];
+
+ $postdata .= '--' . $boundary . "\r\n";
+ $postdata .= 'Content-Disposition: form-data; name="' . $varname . '"; filename="' . $basename . '"';
+ $postdata .= "\r\nContent-Type: " . $type;
+ $postdata .= "\r\n\r\n" . $data . "\r\n";
+ }
+ }
+ $postdata .= '--' . $boundary . "\r\n";
+ }
+ $request .= 'Content-Length: ' . strlen($postdata) . "\r\n\r\n";
+ $request .= $postdata;
+
+ // Post data if it's raw
+ } elseif(!empty($this->_postData)) {
+ $request .= 'Content-Length: ' . strlen($this->_postData) . "\r\n\r\n";
+ $request .= $this->_postData;
+ }
+
+ return $request;
+ }
+
+ /**
+ * Helper function to change the (probably multidimensional) associative array
+ * into the simple one.
+ *
+ * @param string name for item
+ * @param mixed item's values
+ * @return array array with the following items: array('item name', 'item value');
+ */
+ function _flattenArray($name, $values)
+ {
+ if (!is_array($values)) {
+ return array(array($name, $values));
+ } else {
+ $ret = array();
+ foreach ($values as $k => $v) {
+ if (empty($name)) {
+ $newName = $k;
+ } elseif ($this->_useBrackets) {
+ $newName = $name . '[' . $k . ']';
+ } else {
+ $newName = $name;
+ }
+ $ret = array_merge($ret, $this->_flattenArray($newName, $v));
+ }
+ return $ret;
+ }
+ }
+
+
+ /**
+ * Adds a Listener to the list of listeners that are notified of
+ * the object's events
+ *
+ * @param object HTTP_Request_Listener instance to attach
+ * @return boolean whether the listener was successfully attached
+ * @access public
+ */
+ function attach(&$listener)
+ {
+ if (!is_a($listener, 'HTTP_Request_Listener')) {
+ return false;
+ }
+ $this->_listeners[$listener->getId()] =& $listener;
+ return true;
+ }
+
+
+ /**
+ * Removes a Listener from the list of listeners
+ *
+ * @param object HTTP_Request_Listener instance to detach
+ * @return boolean whether the listener was successfully detached
+ * @access public
+ */
+ function detach(&$listener)
+ {
+ if (!is_a($listener, 'HTTP_Request_Listener') ||
+ !isset($this->_listeners[$listener->getId()])) {
+ return false;
+ }
+ unset($this->_listeners[$listener->getId()]);
+ return true;
+ }
+
+
+ /**
+ * Notifies all registered listeners of an event.
+ *
+ * Events sent by HTTP_Request object
+ * 'sentRequest': after the request was sent
+ * Events sent by HTTP_Response object
+ * 'gotHeaders': after receiving response headers (headers are passed in $data)
+ * 'tick': on receiving a part of response body (the part is passed in $data)
+ * 'gzTick': on receiving a gzip-encoded part of response body (ditto)
+ * 'gotBody': after receiving the response body (passes the decoded body in $data if it was gzipped)
+ *
+ * @param string Event name
+ * @param mixed Additional data
+ * @access private
+ */
+ function _notify($event, $data = null)
+ {
+ foreach (array_keys($this->_listeners) as $id) {
+ $this->_listeners[$id]->update($this, $event, $data);
+ }
+ }
+}
+
+
+/**
+* Response class to complement the Request class
+*/
+class HTTP_Response
+{
+ /**
+ * Socket object
+ * @var object
+ */
+ var $_sock;
+
+ /**
+ * Protocol
+ * @var string
+ */
+ var $_protocol;
+
+ /**
+ * Return code
+ * @var string
+ */
+ var $_code;
+
+ /**
+ * Response headers
+ * @var array
+ */
+ var $_headers;
+
+ /**
+ * Cookies set in response
+ * @var array
+ */
+ var $_cookies;
+
+ /**
+ * Response body
+ * @var string
+ */
+ var $_body = '';
+
+ /**
+ * Used by _readChunked(): remaining length of the current chunk
+ * @var string
+ */
+ var $_chunkLength = 0;
+
+ /**
+ * Attached listeners
+ * @var array
+ */
+ var $_listeners = array();
+
+ /**
+ * Constructor
+ *
+ * @param object Net_Socket socket to read the response from
+ * @param array listeners attached to request
+ * @return mixed PEAR Error on error, true otherwise
+ */
+ function HTTP_Response(&$sock, &$listeners)
+ {
+ $this->_sock =& $sock;
+ $this->_listeners =& $listeners;
+ }
+
+
+ /**
+ * Processes a HTTP response
+ *
+ * This extracts response code, headers, cookies and decodes body if it
+ * was encoded in some way
+ *
+ * @access public
+ * @param bool Whether to store response body in object property, set
+ * this to false if downloading a LARGE file and using a Listener.
+ * This is assumed to be true if body is gzip-encoded.
+ * @throws PEAR_Error
+ * @return mixed true on success, PEAR_Error in case of malformed response
+ */
+ function process($saveBody = true)
+ {
+ do {
+ $line = $this->_sock->readLine();
+ if (sscanf($line, 'HTTP/%s %s', $http_version, $returncode) != 2) {
+ return PEAR::raiseError('Malformed response.');
+ } else {
+ $this->_protocol = 'HTTP/' . $http_version;
+ $this->_code = intval($returncode);
+ }
+ while ('' !== ($header = $this->_sock->readLine())) {
+ $this->_processHeader($header);
+ }
+ } while (100 == $this->_code);
+
+ $this->_notify('gotHeaders', $this->_headers);
+
+ // If response body is present, read it and decode
+ $chunked = isset($this->_headers['transfer-encoding']) && ('chunked' == $this->_headers['transfer-encoding']);
+ $gzipped = isset($this->_headers['content-encoding']) && ('gzip' == $this->_headers['content-encoding']);
+ $hasBody = false;
+ while (!$this->_sock->eof()) {
+ if ($chunked) {
+ $data = $this->_readChunked();
+ } else {
+ $data = $this->_sock->read(4096);
+ }
+ if ('' != $data) {
+ $hasBody = true;
+ if ($saveBody || $gzipped) {
+ $this->_body .= $data;
+ }
+ $this->_notify($gzipped? 'gzTick': 'tick', $data);
+ }
+ }
+ if ($hasBody) {
+ // Uncompress the body if needed
+ if ($gzipped) {
+ $this->_body = gzinflate(substr($this->_body, 10));
+ $this->_notify('gotBody', $this->_body);
+ } else {
+ $this->_notify('gotBody');
+ }
+ }
+ return true;
+ }
+
+
+ /**
+ * Processes the response header
+ *
+ * @access private
+ * @param string HTTP header
+ */
+ function _processHeader($header)
+ {
+ list($headername, $headervalue) = explode(':', $header, 2);
+ $headername_i = strtolower($headername);
+ $headervalue = ltrim($headervalue);
+
+ if ('set-cookie' != $headername_i) {
+ $this->_headers[$headername] = $headervalue;
+ $this->_headers[$headername_i] = $headervalue;
+ } else {
+ $this->_parseCookie($headervalue);
+ }
+ }
+
+
+ /**
+ * Parse a Set-Cookie header to fill $_cookies array
+ *
+ * @access private
+ * @param string value of Set-Cookie header
+ */
+ function _parseCookie($headervalue)
+ {
+ $cookie = array(
+ 'expires' => null,
+ 'domain' => null,
+ 'path' => null,
+ 'secure' => false
+ );
+
+ // Only a name=value pair
+ if (!strpos($headervalue, ';')) {
+ $pos = strpos($headervalue, '=');
+ $cookie['name'] = trim(substr($headervalue, 0, $pos));
+ $cookie['value'] = trim(substr($headervalue, $pos + 1));
+
+ // Some optional parameters are supplied
+ } else {
+ $elements = explode(';', $headervalue);
+ $pos = strpos($elements[0], '=');
+ $cookie['name'] = trim(substr($elements[0], 0, $pos));
+ $cookie['value'] = trim(substr($elements[0], $pos + 1));
+
+ for ($i = 1; $i < count($elements); $i++) {
+ list ($elName, $elValue) = array_map('trim', explode('=', $elements[$i]));
+ $elName = strtolower($elName);
+ if ('secure' == $elName) {
+ $cookie['secure'] = true;
+ } elseif ('expires' == $elName) {
+ $cookie['expires'] = str_replace('"', '', $elValue);
+ } elseif ('path' == $elName || 'domain' == $elName) {
+ $cookie[$elName] = urldecode($elValue);
+ } else {
+ $cookie[$elName] = $elValue;
+ }
+ }
+ }
+ $this->_cookies[] = $cookie;
+ }
+
+
+ /**
+ * Read a part of response body encoded with chunked Transfer-Encoding
+ *
+ * @access private
+ * @return string
+ */
+ function _readChunked()
+ {
+ // at start of the next chunk?
+ if (0 == $this->_chunkLength) {
+ $line = $this->_sock->readLine();
+ if (preg_match('/^([0-9a-f]+)/i', $line, $matches)) {
+ $this->_chunkLength = hexdec($matches[1]);
+ // Chunk with zero length indicates the end
+ if (0 == $this->_chunkLength) {
+ $this->_sock->readAll(); // make this an eof()
+ return '';
+ }
+ }
+ }
+ $data = $this->_sock->read($this->_chunkLength);
+ $this->_chunkLength -= strlen($data);
+ if (0 == $this->_chunkLength) {
+ $this->_sock->readLine(); // Trailing CRLF
+ }
+ return $data;
+ }
+
+
+ /**
+ * Notifies all registered listeners of an event.
+ *
+ * @param string Event name
+ * @param mixed Additional data
+ * @access private
+ * @see HTTP_Request::_notify()
+ */
+ function _notify($event, $data = null)
+ {
+ foreach (array_keys($this->_listeners) as $id) {
+ $this->_listeners[$id]->update($this, $event, $data);
+ }
+ }
+} // End class HTTP_Response
+?>
diff --git a/pearlib/HTTP/Request/Listener.php b/pearlib/HTTP/Request/Listener.php
new file mode 100644
index 00000000..38b115a6
--- /dev/null
+++ b/pearlib/HTTP/Request/Listener.php
@@ -0,0 +1,96 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Copyright (c) 2002-2003, Richard Heyes |
+// | All rights reserved. |
+// | |
+// | Redistribution and use in source and binary forms, with or without |
+// | modification, are permitted provided that the following conditions |
+// | are met: |
+// | |
+// | o Redistributions of source code must retain the above copyright |
+// | notice, this list of conditions and the following disclaimer. |
+// | o Redistributions in binary form must reproduce the above copyright |
+// | notice, this list of conditions and the following disclaimer in the |
+// | documentation and/or other materials provided with the distribution.|
+// | o The names of the authors may not be used to endorse or promote |
+// | products derived from this software without specific prior written |
+// | permission. |
+// | |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+// | |
+// +-----------------------------------------------------------------------+
+// | Author: Alexey Borzov <[email protected]> |
+// +-----------------------------------------------------------------------+
+//
+// $Id: Listener.php,v 1.2 2003/10/26 10:28:29 avb Exp $
+//
+
+/**
+ * This class implements the Observer part of a Subject-Observer
+ * design pattern. It listens to the events sent by a
+ * HTTP_Request or HTTP_Response instance.
+ *
+ * @package HTTP_Request
+ * @author Alexey Borzov <[email protected]>
+ * @version $Revision: 1.2 $
+ */
+class HTTP_Request_Listener
+{
+ /**
+ * A listener's identifier
+ * @var string
+ */
+ var $_id;
+
+ /**
+ * Constructor, sets the object's identifier
+ *
+ * @access public
+ */
+ function HTTP_Request_Listener()
+ {
+ $this->_id = md5(uniqid('http_request_', 1));
+ }
+
+
+ /**
+ * Returns the listener's identifier
+ *
+ * @access public
+ * @return string
+ */
+ function getId()
+ {
+ return $this->_id;
+ }
+
+
+ /**
+ * This method is called when Listener is notified of an event
+ *
+ * @access public
+ * @param object an object the listener is attached to
+ * @param string Event name
+ * @param mixed Additional data
+ * @abstract
+ */
+ function update(&$subject, $event, $data = null)
+ {
+ echo "Notified of event: '$event'\n";
+ if (null !== $data) {
+ echo "Additional data: ";
+ var_dump($data);
+ }
+ }
+}
+?>
diff --git a/pearlib/Log.php b/pearlib/Log.php
new file mode 100644
index 00000000..631113a8
--- /dev/null
+++ b/pearlib/Log.php
@@ -0,0 +1,631 @@
+<?php
+/**
+ * $Header: /repository/pear/Log/Log.php,v 1.45 2004/01/19 08:02:38 jon Exp $
+ * $Horde: horde/lib/Log.php,v 1.15 2000/06/29 23:39:45 jon Exp $
+ *
+ * @version $Revision: 1.45 $
+ * @package Log
+ */
+
+define('PEAR_LOG_EMERG', 0); /** System is unusable */
+define('PEAR_LOG_ALERT', 1); /** Immediate action required */
+define('PEAR_LOG_CRIT', 2); /** Critical conditions */
+define('PEAR_LOG_ERR', 3); /** Error conditions */
+define('PEAR_LOG_WARNING', 4); /** Warning conditions */
+define('PEAR_LOG_NOTICE', 5); /** Normal but significant */
+define('PEAR_LOG_INFO', 6); /** Informational */
+define('PEAR_LOG_DEBUG', 7); /** Debug-level messages */
+
+define('PEAR_LOG_ALL', bindec('11111111')); /** All messages */
+define('PEAR_LOG_NONE', bindec('00000000')); /** No message */
+
+/* Log types for PHP's native error_log() function. */
+define('PEAR_LOG_TYPE_SYSTEM', 0); /** Use PHP's system logger */
+define('PEAR_LOG_TYPE_MAIL', 1); /** Use PHP's mail() function */
+define('PEAR_LOG_TYPE_DEBUG', 2); /** Use PHP's debugging connection */
+define('PEAR_LOG_TYPE_FILE', 3); /** Append to a file */
+
+/**
+ * The Log:: class implements both an abstraction for various logging
+ * mechanisms and the Subject end of a Subject-Observer pattern.
+ *
+ * @author Chuck Hagenbuch <[email protected]>
+ * @author Jon Parise <[email protected]>
+ * @since Horde 1.3
+ * @package Log
+ */
+class Log
+{
+ /**
+ * Indicates whether or not the log can been opened / connected.
+ *
+ * @var boolean
+ * @access private
+ */
+ var $_opened = false;
+
+ /**
+ * Instance-specific unique identification number.
+ *
+ * @var integer
+ * @access private
+ */
+ var $_id = 0;
+
+ /**
+ * The label that uniquely identifies this set of log messages.
+ *
+ * @var string
+ * @access private
+ */
+ var $_ident = '';
+
+ /**
+ * The default priority to use when logging an event.
+ *
+ * @var integer
+ * @access private
+ */
+ var $_priority = PEAR_LOG_INFO;
+
+ /**
+ * The bitmask of allowed log levels.
+ * @var integer
+ * @access private
+ */
+ var $_mask = PEAR_LOG_ALL;
+
+ /**
+ * Holds all Log_observer objects that wish to be notified of new messages.
+ *
+ * @var array
+ * @access private
+ */
+ var $_listeners = array();
+
+
+ /**
+ * Attempts to return a concrete Log instance of type $handler.
+ *
+ * @param string $handler The type of concrete Log subclass to return.
+ * Attempt to dynamically include the code for
+ * this subclass. Currently, valid values are
+ * 'console', 'syslog', 'sql', 'file', and 'mcal'.
+ *
+ * @param string $name The name of the actually log file, table, or
+ * other specific store to use. Defaults to an
+ * empty string, with which the subclass will
+ * attempt to do something intelligent.
+ *
+ * @param string $ident The identity reported to the log system.
+ *
+ * @param array $conf A hash containing any additional configuration
+ * information that a subclass might need.
+ *
+ * @param int $level Log messages up to and including this level.
+ *
+ * @return object Log The newly created concrete Log instance, or an
+ * false on an error.
+ * @access public
+ * @since Log 1.0
+ */
+ function &factory($handler, $name = '', $ident = '', $conf = array(),
+ $level = PEAR_LOG_DEBUG)
+ {
+ $handler = strtolower($handler);
+ $class = 'Log_' . $handler;
+ $classfile = 'Log/' . $handler . '.php';
+
+ /*
+ * Attempt to include our version of the named class, but don't treat
+ * a failure as fatal. The caller may have already included their own
+ * version of the named class.
+ */
+ @include_once $classfile;
+
+ /* If the class exists, return a new instance of it. */
+ if (class_exists($class)) {
+ return new $class($name, $ident, $conf, $level);
+ }
+
+ return false;
+ }
+
+ /**
+ * Attempts to return a reference to a concrete Log instance of type
+ * $handler, only creating a new instance if no log instance with the same
+ * parameters currently exists.
+ *
+ * You should use this if there are multiple places you might create a
+ * logger, you don't want to create multiple loggers, and you don't want to
+ * check for the existance of one each time. The singleton pattern does all
+ * the checking work for you.
+ *
+ * <b>You MUST call this method with the $var = &Log::singleton() syntax.
+ * Without the ampersand (&) in front of the method name, you will not get
+ * a reference, you will get a copy.</b>
+ *
+ * @param string $handler The type of concrete Log subclass to return.
+ * Attempt to dynamically include the code for
+ * this subclass. Currently, valid values are
+ * 'console', 'syslog', 'sql', 'file', and 'mcal'.
+ *
+ * @param string $name The name of the actually log file, table, or
+ * other specific store to use. Defaults to an
+ * empty string, with which the subclass will
+ * attempt to do something intelligent.
+ *
+ * @param string $ident The identity reported to the log system.
+ *
+ * @param array $conf A hash containing any additional configuration
+ * information that a subclass might need.
+ *
+ * @param int $level Log messages up to and including this level.
+ *
+ * @return object Log The newly created concrete Log instance, or an
+ * false on an error.
+ * @access public
+ * @since Log 1.0
+ */
+ function &singleton($handler, $name = '', $ident = '', $conf = array(),
+ $level = PEAR_LOG_DEBUG)
+ {
+ static $instances;
+ if (!isset($instances)) $instances = array();
+
+ $signature = serialize(array($handler, $name, $ident, $conf, $level));
+ if (!isset($instances[$signature])) {
+ $instances[$signature] = &Log::factory($handler, $name, $ident,
+ $conf, $level);
+ }
+
+ return $instances[$signature];
+ }
+
+ /**
+ * Abstract implementation of the open() method.
+ * @since Log 1.0
+ */
+ function open()
+ {
+ return false;
+ }
+
+ /**
+ * Abstract implementation of the close() method.
+ * @since Log 1.0
+ */
+ function close()
+ {
+ return false;
+ }
+
+ /**
+ * Abstract implementation of the flush() method.
+ * @since Log 1.8.2
+ */
+ function flush()
+ {
+ return false;
+ }
+
+ /**
+ * Abstract implementation of the log() method.
+ * @since Log 1.0
+ */
+ function log($message, $priority = null)
+ {
+ return false;
+ }
+
+ /**
+ * A convenience function for logging a emergency event. It will log a
+ * message at the PEAR_LOG_EMERG log level.
+ *
+ * @param mixed $message String or object containing the message
+ * to log.
+ *
+ * @return boolean True if the message was successfully logged.
+ *
+ * @access public
+ * @since Log 1.7.0
+ */
+ function emerg($message)
+ {
+ return $this->log($message, PEAR_LOG_EMERG);
+ }
+
+ /**
+ * A convenience function for logging an alert event. It will log a
+ * message at the PEAR_LOG_ALERT log level.
+ *
+ * @param mixed $message String or object containing the message
+ * to log.
+ *
+ * @return boolean True if the message was successfully logged.
+ *
+ * @access public
+ * @since Log 1.7.0
+ */
+ function alert($message)
+ {
+ return $this->log($message, PEAR_LOG_ALERT);
+ }
+
+ /**
+ * A convenience function for logging a critical event. It will log a
+ * message at the PEAR_LOG_CRIT log level.
+ *
+ * @param mixed $message String or object containing the message
+ * to log.
+ *
+ * @return boolean True if the message was successfully logged.
+ *
+ * @access public
+ * @since Log 1.7.0
+ */
+ function crit($message)
+ {
+ return $this->log($message, PEAR_LOG_CRIT);
+ }
+
+ /**
+ * A convenience function for logging a error event. It will log a
+ * message at the PEAR_LOG_ERR log level.
+ *
+ * @param mixed $message String or object containing the message
+ * to log.
+ *
+ * @return boolean True if the message was successfully logged.
+ *
+ * @access public
+ * @since Log 1.7.0
+ */
+ function err($message)
+ {
+ return $this->log($message, PEAR_LOG_ERR);
+ }
+
+ /**
+ * A convenience function for logging a warning event. It will log a
+ * message at the PEAR_LOG_WARNING log level.
+ *
+ * @param mixed $message String or object containing the message
+ * to log.
+ *
+ * @return boolean True if the message was successfully logged.
+ *
+ * @access public
+ * @since Log 1.7.0
+ */
+ function warning($message)
+ {
+ return $this->log($message, PEAR_LOG_WARNING);
+ }
+
+ /**
+ * A convenience function for logging a notice event. It will log a
+ * message at the PEAR_LOG_NOTICE log level.
+ *
+ * @param mixed $message String or object containing the message
+ * to log.
+ *
+ * @return boolean True if the message was successfully logged.
+ *
+ * @access public
+ * @since Log 1.7.0
+ */
+ function notice($message)
+ {
+ return $this->log($message, PEAR_LOG_NOTICE);
+ }
+
+ /**
+ * A convenience function for logging a information event. It will log a
+ * message at the PEAR_LOG_INFO log level.
+ *
+ * @param mixed $message String or object containing the message
+ * to log.
+ *
+ * @return boolean True if the message was successfully logged.
+ *
+ * @access public
+ * @since Log 1.7.0
+ */
+ function info($message)
+ {
+ return $this->log($message, PEAR_LOG_INFO);
+ }
+
+ /**
+ * A convenience function for logging a debug event. It will log a
+ * message at the PEAR_LOG_DEBUG log level.
+ *
+ * @param mixed $message String or object containing the message
+ * to log.
+ *
+ * @return boolean True if the message was successfully logged.
+ *
+ * @access public
+ * @since Log 1.7.0
+ */
+ function debug($message)
+ {
+ return $this->log($message, PEAR_LOG_DEBUG);
+ }
+
+ /**
+ * Returns the string representation of the message data.
+ *
+ * If $message is an object, _extractMessage() will attempt to extract
+ * the message text using a known method (such as a PEAR_Error object's
+ * getMessage() method). If a known method, cannot be found, the
+ * serialized representation of the object will be returned.
+ *
+ * If the message data is already a string, it will be returned unchanged.
+ *
+ * @param mixed $message The original message data. This may be a
+ * string or any object.
+ *
+ * @return string The string representation of the message.
+ *
+ * @access private
+ */
+ function _extractMessage($message)
+ {
+ /*
+ * If we've been given an object, attempt to extract the message using
+ * a known method. If we can't find such a method, default to the
+ * "human-readable" version of the object.
+ *
+ * We also use the human-readable format for arrays.
+ */
+ if (is_object($message)) {
+ if (method_exists($message, 'getmessage')) {
+ $message = $message->getMessage();
+ } else if (method_exists($message, 'tostring')) {
+ $message = $message->toString();
+ } else if (method_exists($message, '__tostring')) {
+ $message = (string)$message;
+ } else {
+ $message = print_r($message, true);
+ }
+ } else if (is_array($message)) {
+ $message = print_r($message, true);
+ }
+
+ /* Otherwise, we assume the message is a string. */
+ return $message;
+ }
+
+ /**
+ * Returns the string representation of a PEAR_LOG_* integer constant.
+ *
+ * @param int $priority A PEAR_LOG_* integer constant.
+ *
+ * @return string The string representation of $level.
+ *
+ * @since Log 1.0
+ */
+ function priorityToString($priority)
+ {
+ $levels = array(
+ PEAR_LOG_EMERG => 'emergency',
+ PEAR_LOG_ALERT => 'alert',
+ PEAR_LOG_CRIT => 'critical',
+ PEAR_LOG_ERR => 'error',
+ PEAR_LOG_WARNING => 'warning',
+ PEAR_LOG_NOTICE => 'notice',
+ PEAR_LOG_INFO => 'info',
+ PEAR_LOG_DEBUG => 'debug'
+ );
+
+ return $levels[$priority];
+ }
+
+ /**
+ * Calculate the log mask for the given priority.
+ *
+ * @param integer $priority The priority whose mask will be calculated.
+ *
+ * @return integer The calculated log mask.
+ *
+ * @access public
+ * @since Log 1.7.0
+ */
+ function MASK($priority)
+ {
+ return (1 << $priority);
+ }
+
+ /**
+ * Calculate the log mask for all priorities up to the given priority.
+ *
+ * @param integer $priority The maximum priority covered by this mask.
+ *
+ * @return integer The calculated log mask.
+ *
+ * @access public
+ * @since Log 1.7.0
+ */
+ function UPTO($priority)
+ {
+ return ((1 << ($priority + 1)) - 1);
+ }
+
+ /**
+ * Set and return the level mask for the current Log instance.
+ *
+ * @param integer $mask A bitwise mask of log levels.
+ *
+ * @return integer The current level mask.
+ *
+ * @access public
+ * @since Log 1.7.0
+ */
+ function setMask($mask)
+ {
+ $this->_mask = $mask;
+
+ return $this->_mask;
+ }
+
+ /**
+ * Returns the current level mask.
+ *
+ * @return interger The current level mask.
+ *
+ * @access public
+ * @since Log 1.7.0
+ */
+ function getMask()
+ {
+ return $this->_mask;
+ }
+
+ /**
+ * Check if the given priority is included in the current level mask.
+ *
+ * @param integer $priority The priority to check.
+ *
+ * @return boolean True if the given priority is included in the current
+ * log mask.
+ *
+ * @access private
+ * @since Log 1.7.0
+ */
+ function _isMasked($priority)
+ {
+ return (Log::MASK($priority) & $this->_mask);
+ }
+
+ /**
+ * Returns the current default priority.
+ *
+ * @return integer The current default priority.
+ *
+ * @access public
+ * @since Log 1.8.4
+ */
+ function getPriority()
+ {
+ return $this->_priority;
+ }
+
+ /**
+ * Sets the default priority to the specified value.
+ *
+ * @param integer $priority The new default priority.
+ *
+ * @access public
+ * @since Log 1.8.4
+ */
+ function setPriority($priority)
+ {
+ $this->_priority = $priority;
+ }
+
+ /**
+ * Adds a Log_observer instance to the list of observers that are listening
+ * for messages emitted by this Log instance.
+ *
+ * @param object $observer The Log_observer instance to attach as a
+ * listener.
+ *
+ * @param boolean True if the observer is successfully attached.
+ *
+ * @access public
+ * @since Log 1.0
+ */
+ function attach(&$observer)
+ {
+ if (!is_a($observer, 'Log_observer')) {
+ return false;
+ }
+
+ $this->_listeners[$observer->_id] = &$observer;
+
+ return true;
+ }
+
+ /**
+ * Removes a Log_observer instance from the list of observers.
+ *
+ * @param object $observer The Log_observer instance to detach from
+ * the list of listeners.
+ *
+ * @param boolean True if the observer is successfully detached.
+ *
+ * @access public
+ * @since Log 1.0
+ */
+ function detach($observer)
+ {
+ if (!is_a($observer, 'Log_observer') ||
+ !isset($this->_listeners[$observer->_id])) {
+ return false;
+ }
+
+ unset($this->_listeners[$observer->_id]);
+
+ return true;
+ }
+
+ /**
+ * Informs each registered observer instance that a new message has been
+ * logged.
+ *
+ * @param array $event A hash describing the log event.
+ *
+ * @access private
+ */
+ function _announce($event)
+ {
+ foreach ($this->_listeners as $id => $listener) {
+ if ($event['priority'] <= $this->_listeners[$id]->_priority) {
+ $this->_listeners[$id]->notify($event);
+ }
+ }
+ }
+
+ /**
+ * Indicates whether this is a composite class.
+ *
+ * @return boolean True if this is a composite class.
+ *
+ * @access public
+ * @since Log 1.0
+ */
+ function isComposite()
+ {
+ return false;
+ }
+
+ /**
+ * Sets this Log instance's identification string.
+ *
+ * @param string $ident The new identification string.
+ *
+ * @access public
+ * @since Log 1.6.3
+ */
+ function setIdent($ident)
+ {
+ $this->_ident = $ident;
+ }
+
+ /**
+ * Returns the current identification string.
+ *
+ * @return string The current Log instance's identification string.
+ *
+ * @access public
+ * @since Log 1.6.3
+ */
+ function getIdent()
+ {
+ return $this->_ident;
+ }
+}
+
+?>
diff --git a/pearlib/Log/composite.php b/pearlib/Log/composite.php
new file mode 100644
index 00000000..104c8966
--- /dev/null
+++ b/pearlib/Log/composite.php
@@ -0,0 +1,196 @@
+<?php
+/**
+ * $Header: /repository/pear/Log/Log/composite.php,v 1.23 2004/08/09 06:04:11 jon Exp $
+ * $Horde: horde/lib/Log/composite.php,v 1.2 2000/06/28 21:36:13 jon Exp $
+ *
+ * @version $Revision: 1.23 $
+ * @package Log
+ */
+
+/**
+ * The Log_composite:: class implements a Composite pattern which
+ * allows multiple Log implementations to receive the same events.
+ *
+ * @author Chuck Hagenbuch <[email protected]>
+ * @author Jon Parise <[email protected]>
+ *
+ * @since Horde 1.3
+ * @since Log 1.0
+ * @package Log
+ *
+ * @example composite.php Using the composite handler.
+ */
+class Log_composite extends Log
+{
+ /**
+ * Array holding all of the Log instances to which log events should be
+ * sent.
+ *
+ * @var array
+ * @access private
+ */
+ var $_children = array();
+
+
+ /**
+ * Constructs a new composite Log object.
+ *
+ * @param boolean $name This parameter is ignored.
+ * @param boolean $ident This parameter is ignored.
+ * @param boolean $conf This parameter is ignored.
+ * @param boolean $level This parameter is ignored.
+ *
+ * @access public
+ */
+ function Log_composite($name = false, $ident = false, $conf = false,
+ $level = PEAR_LOG_DEBUG)
+ {
+ }
+
+ /**
+ * Opens the child connections.
+ *
+ * @access public
+ */
+ function open()
+ {
+ if (!$this->_opened) {
+ foreach ($this->_children as $id => $child) {
+ $this->_children[$id]->open();
+ }
+ $this->_opened = true;
+ }
+ }
+
+ /**
+ * Closes any child instances.
+ *
+ * @access public
+ */
+ function close()
+ {
+ if ($this->_opened) {
+ foreach ($this->_children as $id => $child) {
+ $this->_children[$id]->close();
+ }
+ $this->_opened = false;
+ }
+ }
+
+ /**
+ * Flushes all open child instances.
+ *
+ * @access public
+ * @since Log 1.8.2
+ */
+ function flush()
+ {
+ if ($this->_opened) {
+ foreach ($this->_children as $id => $child) {
+ $this->_children[$id]->flush();
+ }
+ }
+ }
+
+ /**
+ * Sends $message and $priority to each child of this composite.
+ *
+ * @param mixed $message String or object containing the message
+ * to log.
+ * @param string $priority (optional) The priority of the message.
+ * Valid values are: PEAR_LOG_EMERG,
+ * PEAR_LOG_ALERT, PEAR_LOG_CRIT,
+ * PEAR_LOG_ERR, PEAR_LOG_WARNING,
+ * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and
+ * PEAR_LOG_DEBUG.
+ *
+ * @return boolean True if the entry is successfully logged.
+ *
+ * @access public
+ */
+ function log($message, $priority = null)
+ {
+ /* If a priority hasn't been specified, use the default value. */
+ if ($priority === null) {
+ $priority = $this->_priority;
+ }
+
+ foreach ($this->_children as $id => $child) {
+ $this->_children[$id]->log($message, $priority);
+ }
+
+ $this->_announce(array('priority' => $priority, 'message' => $message));
+
+ return true;
+ }
+
+ /**
+ * Returns true if this is a composite.
+ *
+ * @return boolean True if this is a composite class.
+ *
+ * @access public
+ */
+ function isComposite()
+ {
+ return true;
+ }
+
+ /**
+ * Sets this identification string for all of this composite's children.
+ *
+ * @param string $ident The new identification string.
+ *
+ * @access public
+ * @since Log 1.6.7
+ */
+ function setIdent($ident)
+ {
+ foreach ($this->_children as $id => $child) {
+ $this->_children[$id]->setIdent($ident);
+ }
+ }
+
+ /**
+ * Adds a Log instance to the list of children.
+ *
+ * @param object $child The Log instance to add.
+ *
+ * @return boolean True if the Log instance was successfully added.
+ *
+ * @access public
+ */
+ function addChild(&$child)
+ {
+ /* Make sure this is a Log instance. */
+ if (!is_a($child, 'Log')) {
+ return false;
+ }
+
+ $this->_children[$child->_id] = &$child;
+
+ return true;
+ }
+
+ /**
+ * Removes a Log instance from the list of children.
+ *
+ * @param object $child The Log instance to remove.
+ *
+ * @return boolean True if the Log instance was successfully removed.
+ *
+ * @access public
+ */
+ function removeChild($child)
+ {
+ if (!is_a($child, 'Log') || !isset($this->_children[$child->_id])) {
+ return false;
+ }
+
+ unset($this->_children[$child->_id]);
+
+ return true;
+ }
+}
+
+?>
diff --git a/pearlib/Log/console.php b/pearlib/Log/console.php
new file mode 100644
index 00000000..62032e79
--- /dev/null
+++ b/pearlib/Log/console.php
@@ -0,0 +1,190 @@
+<?php
+/**
+ * $Header: /repository/pear/Log/Log/console.php,v 1.19 2004/01/19 08:02:40 jon Exp $
+ *
+ * @version $Revision: 1.19 $
+ * @package Log
+ */
+
+/**
+ * The Log_console class is a concrete implementation of the Log::
+ * abstract class which writes message to the text console.
+ *
+ * @author Jon Parise <[email protected]>
+ * @since Log 1.1
+ * @package Log
+ *
+ * @example console.php Using the console handler.
+ */
+class Log_console extends Log
+{
+ /**
+ * Handle to the current output stream.
+ * @var resource
+ * @access private
+ */
+ var $_stream = STDOUT;
+
+ /**
+ * Should the output be buffered or displayed immediately?
+ * @var string
+ * @access private
+ */
+ var $_buffering = false;
+
+ /**
+ * String holding the buffered output.
+ * @var string
+ * @access private
+ */
+ var $_buffer = '';
+
+ /**
+ * String containing the format of a log line.
+ * @var string
+ * @access private
+ */
+ var $_lineFormat = '%1$s %2$s [%3$s] %4$s';
+
+ /**
+ * String containing the timestamp format. It will be passed directly to
+ * strftime(). Note that the timestamp string will generated using the
+ * current locale.
+ * @var string
+ * @access private
+ */
+ var $_timeFormat = '%b %d %H:%M:%S';
+
+ /**
+ * Hash that maps canonical format keys to position arguments for the
+ * "line format" string.
+ * @var array
+ * @access private
+ */
+ var $_formatMap = array('%{timestamp}' => '%1$s',
+ '%{ident}' => '%2$s',
+ '%{priority}' => '%3$s',
+ '%{message}' => '%4$s',
+ '%\{' => '%%{');
+
+ /**
+ * Constructs a new Log_console object.
+ *
+ * @param string $name Ignored.
+ * @param string $ident The identity string.
+ * @param array $conf The configuration array.
+ * @param int $level Log messages up to and including this level.
+ * @access public
+ */
+ function Log_console($name, $ident = '', $conf = array(),
+ $level = PEAR_LOG_DEBUG)
+ {
+ $this->_id = md5(microtime());
+ $this->_ident = $ident;
+ $this->_mask = Log::UPTO($level);
+
+ if (!empty($conf['stream'])) {
+ $this->_stream = $conf['stream'];
+ }
+
+ if (isset($conf['buffering'])) {
+ $this->_buffering = $conf['buffering'];
+ }
+
+ if (!empty($conf['lineFormat'])) {
+ $this->_lineFormat = str_replace(array_keys($this->_formatMap),
+ array_values($this->_formatMap),
+ $conf['lineFormat']);
+ }
+
+ if (!empty($conf['timeFormat'])) {
+ $this->_timeFormat = $conf['timeFormat'];
+ }
+
+ /*
+ * If output buffering has been requested, we need to register a
+ * shutdown function that will dump the buffer upon termination.
+ */
+ if ($this->_buffering) {
+ register_shutdown_function(array(&$this, '_Log_console'));
+ }
+ }
+
+ /**
+ * Destructor
+ */
+ function _Log_console()
+ {
+ $this->flush();
+ }
+
+ /**
+ * Flushes all pending ("buffered") data to the output stream.
+ *
+ * @access public
+ * @since Log 1.8.2
+ */
+ function flush()
+ {
+ /*
+ * If output buffering is enabled, dump the contents of the buffer to
+ * the output stream.
+ */
+ if ($this->_buffering && (strlen($this->_buffer) > 0)) {
+ fwrite($this->_stream, $this->_buffer);
+ $this->_buffer = '';
+ }
+
+ return fflush($this->_stream);
+ }
+
+ /**
+ * Writes $message to the text console. Also, passes the message
+ * along to any Log_observer instances that are observing this Log.
+ *
+ * @param mixed $message String or object containing the message to log.
+ * @param string $priority The priority of the message. Valid
+ * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT,
+ * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING,
+ * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG.
+ * @return boolean True on success or false on failure.
+ * @access public
+ */
+ function log($message, $priority = null)
+ {
+ /* If a priority hasn't been specified, use the default value. */
+ if ($priority === null) {
+ $priority = $this->_priority;
+ }
+
+ /* Abort early if the priority is above the maximum logging level. */
+ if (!$this->_isMasked($priority)) {
+ return false;
+ }
+
+ /* Extract the string representation of the message. */
+ $message = $this->_extractMessage($message);
+
+ /* Build the string containing the complete log line. */
+ $line = sprintf($this->_lineFormat, strftime($this->_timeFormat),
+ $this->_ident, $this->priorityToString($priority),
+ $message) . "\n";
+
+ /*
+ * If buffering is enabled, append this line to the output buffer.
+ * Otherwise, print the line to the output stream immediately.
+ */
+ if ($this->_buffering) {
+ $this->_buffer .= $line;
+ } else {
+ fwrite($this->_stream, $line);
+ }
+
+ /* Notify observers about this log message. */
+ $this->_announce(array('priority' => $priority, 'message' => $message));
+
+ return true;
+ }
+}
+
+?>
diff --git a/pearlib/Log/display.php b/pearlib/Log/display.php
new file mode 100644
index 00000000..e6ce624d
--- /dev/null
+++ b/pearlib/Log/display.php
@@ -0,0 +1,108 @@
+<?php
+/**
+ * $Header: /repository/pear/Log/Log/display.php,v 1.5 2004/01/19 08:02:40 jon Exp $
+ *
+ * @version $Revision: 1.5 $
+ * @package Log
+ */
+
+/**
+ * The Log_display class is a concrete implementation of the Log::
+ * abstract class which writes message into browser in usual PHP maner.
+ * This may be useful because when you use PEAR::setErrorHandling in
+ * PEAR_ERROR_CALLBACK mode error messages are not displayed by
+ * PHP error handler.
+ *
+ * @author Paul Yanchenko <[email protected]>
+ * @since Log 1.8.0
+ * @package Log
+ *
+ * @example display.php Using the display handler.
+ */
+class Log_display extends Log
+{
+ /**
+ * String to output before an error message
+ * @var string
+ * @access private
+ */
+ var $_error_prepend = '';
+
+ /**
+ * String to output after an error message
+ * @var string
+ * @access private
+ */
+ var $_error_append = '';
+
+
+ /**
+ * Constructs a new Log_display object.
+ *
+ * @param string $name Ignored.
+ * @param string $ident The identity string.
+ * @param array $conf The configuration array.
+ * @param int $level Log messages up to and including this level.
+ * @access public
+ */
+ function Log_display($name = '', $ident = '', $conf = array(),
+ $level = PEAR_LOG_DEBUG)
+ {
+ $this->_id = md5(microtime());
+ $this->_ident = $ident;
+ $this->_mask = Log::UPTO($level);
+
+ if (!empty($conf['error_prepend'])) {
+ $this->_error_prepend = $conf['error_prepend'];
+ } else {
+ $this->_error_prepend = ini_get('error_prepend_string');
+ }
+
+ if (!empty($conf['error_append'])) {
+ $this->_error_append = $conf['error_append'];
+ } else {
+ $this->_error_append = ini_get('error_append_string');
+ }
+ }
+
+ /**
+ * Writes $message to the text browser. Also, passes the message
+ * along to any Log_observer instances that are observing this Log.
+ *
+ * @param mixed $message String or object containing the message to log.
+ * @param string $priority The priority of the message. Valid
+ * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT,
+ * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING,
+ * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG.
+ * @return boolean True on success or false on failure.
+ * @access public
+ */
+ function log($message, $priority = null)
+ {
+ /* If a priority hasn't been specified, use the default value. */
+ if ($priority === null) {
+ $priority = $this->_priority;
+ }
+
+ /* Abort early if the priority is above the maximum logging level. */
+ if (!$this->_isMasked($priority)) {
+ return false;
+ }
+
+ /* Extract the string representation of the message. */
+ $message = $this->_extractMessage($message);
+
+ /* Build and output the complete log line. */
+ echo $this->_error_prepend .
+ '<b>' . ucfirst($this->priorityToString($priority)) . '</b>: '.
+ htmlspecialchars($message) .
+ $this->_error_append . "<br />\n";
+
+ /* Notify observers about this log message. */
+ $this->_announce(array('priority' => $priority, 'message' => $message));
+
+ return true;
+ }
+}
+
+?>
diff --git a/pearlib/Log/error_log.php b/pearlib/Log/error_log.php
new file mode 100644
index 00000000..04c0952c
--- /dev/null
+++ b/pearlib/Log/error_log.php
@@ -0,0 +1,104 @@
+<?php
+/**
+ * $Header: /repository/pear/Log/Log/error_log.php,v 1.6 2004/01/19 08:02:40 jon Exp $
+ *
+ * @version $Revision: 1.6 $
+ * @package Log
+ */
+
+/**
+ * The Log_error_log class is a concrete implementation of the Log abstract
+ * class that logs messages using PHP's error_log() function.
+ *
+ * @author Jon Parise <[email protected]>
+ * @since Log 1.7.0
+ * @package Log
+ *
+ * @example error_log.php Using the error_log handler.
+ */
+class Log_error_log extends Log
+{
+ /**
+ * The error_log() log type.
+ * @var integer
+ * @access private
+ */
+ var $_type = PEAR_LOG_TYPE_SYSTEM;
+
+ /**
+ * The type-specific destination value.
+ * @var string
+ * @access private
+ */
+ var $_destination = '';
+
+ /**
+ * Additional headers to pass to the mail() function when the
+ * PEAR_LOG_TYPE_MAIL type is used.
+ * @var string
+ * @access private
+ */
+ var $_extra_headers = '';
+
+ /**
+ * Constructs a new Log_error_log object.
+ *
+ * @param string $name Ignored.
+ * @param string $ident The identity string.
+ * @param array $conf The configuration array.
+ * @param int $level Log messages up to and including this level.
+ * @access public
+ */
+ function Log_error_log($name, $ident = '', $conf = array(),
+ $level = PEAR_LOG_DEBUG)
+ {
+ $this->_id = md5(microtime());
+ $this->_type = $name;
+ $this->_ident = $ident;
+ $this->_mask = Log::UPTO($level);
+
+ if (!empty($conf['destination'])) {
+ $this->_destination = $conf['destination'];
+ }
+ if (!empty($conf['extra_headers'])) {
+ $this->_extra_headers = $conf['extra_headers'];
+ }
+ }
+
+ /**
+ * Logs $message using PHP's error_log() function. The message is also
+ * passed along to any Log_observer instances that are observing this Log.
+ *
+ * @param mixed $message String or object containing the message to log.
+ * @param string $priority The priority of the message. Valid
+ * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT,
+ * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING,
+ * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG.
+ * @return boolean True on success or false on failure.
+ * @access public
+ */
+ function log($message, $priority = null)
+ {
+ /* If a priority hasn't been specified, use the default value. */
+ if ($priority === null) {
+ $priority = $this->_priority;
+ }
+
+ /* Abort early if the priority is above the maximum logging level. */
+ if (!$this->_isMasked($priority)) {
+ return false;
+ }
+
+ /* Extract the string representation of the message. */
+ $message = $this->_extractMessage($message);
+
+ $success = error_log($this->_ident . ': ' . $message, $this->_type,
+ $this->_destination, $this->_extra_headers);
+
+ $this->_announce(array('priority' => $priority, 'message' => $message));
+
+ return $success;
+ }
+}
+
+?>
diff --git a/pearlib/Log/file.php b/pearlib/Log/file.php
new file mode 100644
index 00000000..36f30b42
--- /dev/null
+++ b/pearlib/Log/file.php
@@ -0,0 +1,286 @@
+<?php
+/**
+ * $Header: /repository/pear/Log/Log/file.php,v 1.37 2004/01/19 08:02:40 jon Exp $
+ *
+ * @version $Revision: 1.37 $
+ * @package Log
+ */
+
+/**
+ * The Log_file class is a concrete implementation of the Log abstract
+ * class that logs messages to a text file.
+ *
+ * @author Jon Parise <[email protected]>
+ * @author Roman Neuhauser <[email protected]>
+ * @since Log 1.0
+ * @package Log
+ *
+ * @example file.php Using the file handler.
+ */
+class Log_file extends Log
+{
+ /**
+ * String containing the name of the log file.
+ * @var string
+ * @access private
+ */
+ var $_filename = 'php.log';
+
+ /**
+ * Handle to the log file.
+ * @var resource
+ * @access private
+ */
+ var $_fp = false;
+
+ /**
+ * Should new log entries be append to an existing log file, or should the
+ * a new log file overwrite an existing one?
+ * @var boolean
+ * @access private
+ */
+ var $_append = true;
+
+ /**
+ * Integer (in octal) containing the log file's permissions mode.
+ * @var integer
+ * @access private
+ */
+ var $_mode = 0644;
+
+ /**
+ * String containing the format of a log line.
+ * @var string
+ * @access private
+ */
+ var $_lineFormat = '%1$s %2$s [%3$s] %4$s';
+
+ /**
+ * String containing the timestamp format. It will be passed directly to
+ * strftime(). Note that the timestamp string will generated using the
+ * current locale.
+ * @var string
+ * @access private
+ */
+ var $_timeFormat = '%b %d %H:%M:%S';
+
+ /**
+ * Hash that maps canonical format keys to position arguments for the
+ * "line format" string.
+ * @var array
+ * @access private
+ */
+ var $_formatMap = array('%{timestamp}' => '%1$s',
+ '%{ident}' => '%2$s',
+ '%{priority}' => '%3$s',
+ '%{message}' => '%4$s',
+ '%\{' => '%%{');
+
+ /**
+ * String containing the end-on-line character sequence.
+ * @var string
+ * @access private
+ */
+ var $_eol = "\n";
+
+ /**
+ * Constructs a new Log_file object.
+ *
+ * @param string $name Ignored.
+ * @param string $ident The identity string.
+ * @param array $conf The configuration array.
+ * @param int $level Log messages up to and including this level.
+ * @access public
+ */
+ function Log_file($name, $ident = '', $conf = array(),
+ $level = PEAR_LOG_DEBUG)
+ {
+ $this->_id = md5(microtime());
+ $this->_filename = $name;
+ $this->_ident = $ident;
+ $this->_mask = Log::UPTO($level);
+
+ if (isset($conf['append'])) {
+ $this->_append = $conf['append'];
+ }
+
+ if (!empty($conf['mode'])) {
+ $this->_mode = $conf['mode'];
+ }
+
+ if (!empty($conf['lineFormat'])) {
+ $this->_lineFormat = str_replace(array_keys($this->_formatMap),
+ array_values($this->_formatMap),
+ $conf['lineFormat']);
+ }
+
+ if (!empty($conf['timeFormat'])) {
+ $this->_timeFormat = $conf['timeFormat'];
+ }
+
+ if (!empty($conf['eol'])) {
+ $this->_eol = $conf['eol'];
+ } else {
+ $this->_eol = (strstr(PHP_OS, 'WIN')) ? "\r\n" : "\n";
+ }
+
+ register_shutdown_function(array(&$this, '_Log_file'));
+ }
+
+ /**
+ * Destructor
+ */
+ function _Log_file()
+ {
+ if ($this->_opened) {
+ $this->close();
+ }
+ }
+
+ /**
+ * Creates the given directory path. If the parent directories don't
+ * already exist, they will be created, too.
+ *
+ * @param string $path The full directory path to create.
+ * @param integer $mode The permissions mode with which the
+ * directories will be created.
+ *
+ * @return True if the full path is successfully created or already
+ * exists.
+ *
+ * @access private
+ */
+ function _mkpath($path, $mode = 0700)
+ {
+ static $depth = 0;
+
+ /* Guard against potentially infinite recursion. */
+ if ($depth++ > 25) {
+ trigger_error("_mkpath(): Maximum recursion depth (25) exceeded",
+ E_USER_WARNING);
+ return false;
+ }
+
+ /* We're only interested in the directory component of the path. */
+ $path = dirname($path);
+
+ /* If the directory already exists, return success immediately. */
+ if (is_dir($path)) {
+ $depth = 0;
+ return true;
+ }
+
+ /*
+ * In order to understand recursion, you must first understand
+ * recursion ...
+ */
+ if ($this->_mkpath($path, $mode) === false) {
+ return false;
+ }
+
+ return @mkdir($path, $mode);
+ }
+
+ /**
+ * Opens the log file for output. If the specified log file does not
+ * already exist, it will be created. By default, new log entries are
+ * appended to the end of the log file.
+ *
+ * This is implicitly called by log(), if necessary.
+ *
+ * @access public
+ */
+ function open()
+ {
+ if (!$this->_opened) {
+ /* If the log file's directory doesn't exist, create it. */
+ if (!is_dir(dirname($this->_filename))) {
+ $this->_mkpath($this->_filename);
+ }
+
+ /* Obtain a handle to the log file. */
+ $this->_fp = fopen($this->_filename, ($this->_append) ? 'a' : 'w');
+
+ $this->_opened = ($this->_fp !== false);
+
+ /* Attempt to set the log file's mode. */
+ @chmod($this->_filename, $this->_mode);
+ }
+
+ return $this->_opened;
+ }
+
+ /**
+ * Closes the log file if it is open.
+ *
+ * @access public
+ */
+ function close()
+ {
+ /* If the log file is open, close it. */
+ if ($this->_opened && fclose($this->_fp)) {
+ $this->_opened = false;
+ }
+
+ return ($this->_opened === false);
+ }
+
+ /**
+ * Flushes all pending data to the file handle.
+ *
+ * @access public
+ * @since Log 1.8.2
+ */
+ function flush()
+ {
+ return fflush($this->_fp);
+ }
+
+ /**
+ * Logs $message to the output window. The message is also passed along
+ * to any Log_observer instances that are observing this Log.
+ *
+ * @param mixed $message String or object containing the message to log.
+ * @param string $priority The priority of the message. Valid
+ * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT,
+ * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING,
+ * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG.
+ * @return boolean True on success or false on failure.
+ * @access public
+ */
+ function log($message, $priority = null)
+ {
+ /* If a priority hasn't been specified, use the default value. */
+ if ($priority === null) {
+ $priority = $this->_priority;
+ }
+
+ /* Abort early if the priority is above the maximum logging level. */
+ if (!$this->_isMasked($priority)) {
+ return false;
+ }
+
+ /* If the log file isn't already open, open it now. */
+ if (!$this->_opened && !$this->open()) {
+ return false;
+ }
+
+ /* Extract the string representation of the message. */
+ $message = $this->_extractMessage($message);
+
+ /* Build the string containing the complete log line. */
+ $line = sprintf($this->_lineFormat, strftime($this->_timeFormat),
+ $this->_ident, $this->priorityToString($priority),
+ $message) . $this->_eol;
+
+ /* Write the log line to the log file. */
+ $success = (fwrite($this->_fp, $line) !== false);
+
+ /* Notify observers about this log message. */
+ $this->_announce(array('priority' => $priority, 'message' => $message));
+
+ return $success;
+ }
+}
+
+?>
diff --git a/pearlib/Log/mail.php b/pearlib/Log/mail.php
new file mode 100644
index 00000000..064f13c0
--- /dev/null
+++ b/pearlib/Log/mail.php
@@ -0,0 +1,222 @@
+<?php
+/**
+ * $Header: /repository/pear/Log/Log/mail.php,v 1.21 2004/01/19 08:02:40 jon Exp $
+ *
+ * @version $Revision: 1.21 $
+ * @package Log
+ */
+
+/**
+ * The Log_mail class is a concrete implementation of the Log:: abstract class
+ * which sends log messages to a mailbox.
+ * The mail is actually sent when you close() the logger, or when the destructor
+ * is called (when the script is terminated).
+ *
+ * PLEASE NOTE that you must create a Log_mail object using =&, like this :
+ * $logger =& Log::factory("mail", "[email protected]", ...)
+ *
+ * This is a PEAR requirement for destructors to work properly.
+ * See http://pear.php.net/manual/en/class.pear.php
+ *
+ * @author Ronnie Garcia <[email protected]>
+ * @author Jon Parise <[email protected]>
+ * @since Log 1.3
+ * @package Log
+ *
+ * @example mail.php Using the mail handler.
+ */
+class Log_mail extends Log
+{
+ /**
+ * String holding the recipient's email address.
+ * @var string
+ * @access private
+ */
+ var $_recipient = '';
+
+ /**
+ * String holding the sender's email address.
+ * @var string
+ * @access private
+ */
+ var $_from = '';
+
+ /**
+ * String holding the email's subject.
+ * @var string
+ * @access private
+ */
+ var $_subject = '[Log_mail] Log message';
+
+ /**
+ * String holding an optional preamble for the log messages.
+ * @var string
+ * @access private
+ */
+ var $_preamble = '';
+
+ /**
+ * String holding the mail message body.
+ * @var string
+ * @access private
+ */
+ var $_message = '';
+
+
+ /**
+ * Constructs a new Log_mail object.
+ *
+ * Here is how you can customize the mail driver with the conf[] hash :
+ * $conf['from'] : the mail's "From" header line,
+ * $conf['subject'] : the mail's "Subject" line.
+ *
+ * @param string $name The filename of the logfile.
+ * @param string $ident The identity string.
+ * @param array $conf The configuration array.
+ * @param int $level Log messages up to and including this level.
+ * @access public
+ */
+ function Log_mail($name, $ident = '', $conf = array(),
+ $level = PEAR_LOG_DEBUG)
+ {
+ $this->_id = md5(microtime());
+ $this->_recipient = $name;
+ $this->_ident = $ident;
+ $this->_mask = Log::UPTO($level);
+
+ if (!empty($conf['from'])) {
+ $this->_from = $conf['from'];
+ } else {
+ $this->_from = ini_get('sendmail_from');
+ }
+
+ if (!empty($conf['subject'])) {
+ $this->_subject = $conf['subject'];
+ }
+
+ if (!empty($conf['preamble'])) {
+ $this->_preamble = $conf['preamble'];
+ }
+
+ /* register the destructor */
+ register_shutdown_function(array(&$this, '_Log_mail'));
+ }
+
+ /**
+ * Destructor. Calls close().
+ *
+ * @access private
+ */
+ function _Log_mail()
+ {
+ $this->close();
+ }
+
+ /**
+ * Starts a new mail message.
+ * This is implicitly called by log(), if necessary.
+ *
+ * @access public
+ */
+ function open()
+ {
+ if (!$this->_opened) {
+ if (!empty($this->_preamble)) {
+ $this->_message = $this->_preamble . "\n\n";
+ }
+ $this->_opened = true;
+ }
+
+ return $this->_opened;
+ }
+
+ /**
+ * Closes the message, if it is open, and sends the mail.
+ * This is implicitly called by the destructor, if necessary.
+ *
+ * @access public
+ */
+ function close()
+ {
+ if ($this->_opened) {
+ if (!empty($this->_message)) {
+ $headers = "From: $this->_from\n";
+ $headers .= "User-Agent: Log_mail";
+
+ if (mail($this->_recipient, $this->_subject, $this->_message,
+ $headers) == false) {
+ error_log("Log_mail: Failure executing mail()", 0);
+ return false;
+ }
+
+ /* Clear the message string now that the email has been sent. */
+ $this->_message = '';
+ }
+ $this->_opened = false;
+ }
+
+ return ($this->_opened === false);
+ }
+
+ /**
+ * Flushes the log output by forcing the email message to be sent now.
+ * Events that are logged after flush() is called will be appended to a
+ * new email message.
+ *
+ * @access public
+ * @since Log 1.8.2
+ */
+ function flush()
+ {
+ /*
+ * It's sufficient to simply call close() to flush the output.
+ * The next call to log() will cause the handler to be reopened.
+ */
+ return $this->close();
+ }
+
+ /**
+ * Writes $message to the currently open mail message.
+ * Calls open(), if necessary.
+ *
+ * @param mixed $message String or object containing the message to log.
+ * @param string $priority The priority of the message. Valid
+ * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT,
+ * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING,
+ * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG.
+ * @return boolean True on success or false on failure.
+ * @access public
+ */
+ function log($message, $priority = null)
+ {
+ /* If a priority hasn't been specified, use the default value. */
+ if ($priority === null) {
+ $priority = $this->_priority;
+ }
+
+ /* Abort early if the priority is above the maximum logging level. */
+ if (!$this->_isMasked($priority)) {
+ return false;
+ }
+
+ /* If the message isn't open and can't be opened, return failure. */
+ if (!$this->_opened && !$this->open()) {
+ return false;
+ }
+
+ /* Extract the string representation of the message. */
+ $message = $this->_extractMessage($message);
+
+ $entry = sprintf("%s %s [%s] %s\n", strftime('%b %d %H:%M:%S'),
+ $this->_ident, Log::priorityToString($priority),
+ $message);
+
+ $this->_message .= $entry;
+
+ $this->_announce(array('priority' => $priority, 'message' => $message));
+
+ return true;
+ }
+}
+
+?>
diff --git a/pearlib/Log/mcal.php b/pearlib/Log/mcal.php
new file mode 100644
index 00000000..a5c46f3d
--- /dev/null
+++ b/pearlib/Log/mcal.php
@@ -0,0 +1,171 @@
+<?php
+/**
+ * $Header: /repository/pear/Log/Log/mcal.php,v 1.17 2004/01/19 08:02:40 jon Exp $
+ * $Horde: horde/lib/Log/mcal.php,v 1.2 2000/06/28 21:36:13 jon Exp $
+ *
+ * @version $Revision: 1.17 $
+ * @package Log
+ */
+
+/**
+ * The Log_mcal class is a concrete implementation of the Log::
+ * abstract class which sends messages to a local or remote calendar
+ * store accessed through MCAL.
+ *
+ * @author Chuck Hagenbuch <[email protected]>
+ * @since Horde 1.3
+ * @since Log 1.0
+ * @package Log
+ */
+class Log_mcal extends Log {
+
+ /**
+ * holding the calendar specification to connect to.
+ * @var string
+ * @access private
+ */
+ var $_calendar = '{localhost/mstore}';
+
+ /**
+ * holding the username to use.
+ * @var string
+ * @access private
+ */
+ var $_username = '';
+
+ /**
+ * holding the password to use.
+ * @var string
+ * @access private
+ */
+ var $_password = '';
+
+ /**
+ * holding the options to pass to the calendar stream.
+ * @var integer
+ * @access private
+ */
+ var $_options = 0;
+
+ /**
+ * ResourceID of the MCAL stream.
+ * @var string
+ * @access private
+ */
+ var $_stream = '';
+
+ /**
+ * Integer holding the log facility to use.
+ * @var string
+ * @access private
+ */
+ var $_name = LOG_SYSLOG;
+
+
+ /**
+ * Constructs a new Log_mcal object.
+ *
+ * @param string $name The category to use for our events.
+ * @param string $ident The identity string.
+ * @param array $conf The configuration array.
+ * @param int $level Log messages up to and including this level.
+ * @access public
+ */
+ function Log_mcal($name, $ident = '', $conf = array(),
+ $level = PEAR_LOG_DEBUG)
+ {
+ $this->_id = md5(microtime());
+ $this->_name = $name;
+ $this->_ident = $ident;
+ $this->_mask = Log::UPTO($level);
+ $this->_calendar = $conf['calendar'];
+ $this->_username = $conf['username'];
+ $this->_password = $conf['password'];
+ $this->_options = $conf['options'];
+ }
+
+ /**
+ * Opens a calendar stream, if it has not already been
+ * opened. This is implicitly called by log(), if necessary.
+ * @access public
+ */
+ function open()
+ {
+ if (!$this->_opened) {
+ $this->_stream = mcal_open($this->_calendar, $this->_username,
+ $this->_password, $this->_options);
+ $this->_opened = true;
+ }
+
+ return $this->_opened;
+ }
+
+ /**
+ * Closes the calendar stream, if it is open.
+ * @access public
+ */
+ function close()
+ {
+ if ($this->_opened) {
+ mcal_close($this->_stream);
+ $this->_opened = false;
+ }
+
+ return ($this->_opened === false);
+ }
+
+ /**
+ * Logs $message and associated information to the currently open
+ * calendar stream. Calls open() if necessary. Also passes the
+ * message along to any Log_observer instances that are observing
+ * this Log.
+ *
+ * @param mixed $message String or object containing the message to log.
+ * @param string $priority The priority of the message. Valid
+ * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT,
+ * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING,
+ * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG.
+ * @return boolean True on success or false on failure.
+ * @access public
+ */
+ function log($message, $priority = null)
+ {
+ /* If a priority hasn't been specified, use the default value. */
+ if ($priority === null) {
+ $priority = $this->_priority;
+ }
+
+ /* Abort early if the priority is above the maximum logging level. */
+ if (!$this->_isMasked($priority)) {
+ return false;
+ }
+
+ /* If the connection isn't open and can't be opened, return failure. */
+ if (!$this->_opened && !$this->open()) {
+ return false;
+ }
+
+ /* Extract the string representation of the message. */
+ $message = $this->_extractMessage($message);
+
+ $date_str = date('Y:n:j:G:i:s');
+ $dates = explode(':', $date_str);
+
+ mcal_event_init($this->_stream);
+ mcal_event_set_title($this->_stream, $this->_ident);
+ mcal_event_set_category($this->_stream, $this->_name);
+ mcal_event_set_description($this->_stream, $message);
+ mcal_event_add_attribute($this->_stream, 'priority', $priority);
+ mcal_event_set_start($this->_stream, $dates[0], $dates[1], $dates[2],
+ $dates[3], $dates[4], $dates[5]);
+ mcal_event_set_end($this->_stream, $dates[0], $dates[1], $dates[2],
+ $dates[3], $dates[4], $dates[5]);
+ mcal_append_event($this->_stream);
+
+ $this->_announce(array('priority' => $priority, 'message' => $message));
+
+ return true;
+ }
+}
+
+?>
diff --git a/pearlib/Log/null.php b/pearlib/Log/null.php
new file mode 100644
index 00000000..23de3f1a
--- /dev/null
+++ b/pearlib/Log/null.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * $Header: /repository/pear/Log/Log/null.php,v 1.3 2004/01/19 08:02:40 jon Exp $
+ *
+ * @version $Revision: 1.3 $
+ * @package Log
+ */
+
+/**
+ * The Log_null class is a concrete implementation of the Log:: abstract
+ * class. It simply consumes log events.
+ *
+ * @author Jon Parise <[email protected]>
+ * @since Log 1.8.2
+ * @package Log
+ *
+ * @example null.php Using the null handler.
+ */
+class Log_null extends Log
+{
+ /**
+ * Constructs a new Log_null object.
+ *
+ * @param string $name Ignored.
+ * @param string $ident The identity string.
+ * @param array $conf The configuration array.
+ * @param int $level Log messages up to and including this level.
+ * @access public
+ */
+ function Log_null($name, $ident = '', $conf = array(),
+ $level = PEAR_LOG_DEBUG)
+ {
+ $this->_id = md5(microtime());
+ $this->_ident = $ident;
+ $this->_mask = Log::UPTO($level);
+ }
+
+ /**
+ * Simply consumes the log event. The message will still be passed
+ * along to any Log_observer instances that are observing this Log.
+ *
+ * @param mixed $message String or object containing the message to log.
+ * @param string $priority The priority of the message. Valid
+ * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT,
+ * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING,
+ * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG.
+ * @return boolean True on success or false on failure.
+ * @access public
+ */
+ function log($message, $priority = null)
+ {
+ /* If a priority hasn't been specified, use the default value. */
+ if ($priority === null) {
+ $priority = $this->_priority;
+ }
+
+ /* Abort early if the priority is above the maximum logging level. */
+ if (!$this->_isMasked($priority)) {
+ return false;
+ }
+
+ $this->_announce(array('priority' => $priority, 'message' => $message));
+
+ return true;
+ }
+}
+
+?>
diff --git a/pearlib/Log/observer.php b/pearlib/Log/observer.php
new file mode 100644
index 00000000..187d5070
--- /dev/null
+++ b/pearlib/Log/observer.php
@@ -0,0 +1,126 @@
+<?php
+/**
+ * $Header: /repository/pear/Log/Log/observer.php,v 1.12 2004/01/11 20:49:49 jon Exp $
+ * $Horde: horde/lib/Log/observer.php,v 1.5 2000/06/28 21:36:13 jon Exp $
+ *
+ * @version $Revision: 1.12 $
+ * @package Log
+ */
+
+/**
+ * The Log_observer:: class implements the Observer end of a Subject-Observer
+ * pattern for watching log activity and taking actions on exceptional events.
+ *
+ * @author Chuck Hagenbuch <[email protected]>
+ * @since Horde 1.3
+ * @since Log 1.0
+ * @package Log
+ *
+ * @example observer_mail.php An example Log_observer implementation.
+ */
+class Log_observer
+{
+ /**
+ * Instance-specific unique identification number.
+ *
+ * @var integer
+ * @access private
+ */
+ var $_id = 0;
+
+ /**
+ * The minimum priority level of message that we want to hear about.
+ * PEAR_LOG_EMERG is the highest priority, so we will only hear messages
+ * with an integer priority value less than or equal to ours. It defaults
+ * to PEAR_LOG_INFO, which listens to everything except PEAR_LOG_DEBUG.
+ *
+ * @var string
+ * @access private
+ */
+ var $_priority = PEAR_LOG_INFO;
+
+ /**
+ * Creates a new basic Log_observer instance.
+ *
+ * @param integer $priority The highest priority at which to receive
+ * log event notifications.
+ *
+ * @access public
+ */
+ function Log_observer($priority = PEAR_LOG_INFO)
+ {
+ $this->_id = md5(microtime());
+ $this->_priority = $priority;
+ }
+
+ /**
+ * Attempts to return a new concrete Log_observer instance of the requested
+ * type.
+ *
+ * @param string $type The type of concreate Log_observer subclass
+ * to return.
+ * @param integer $priority The highest priority at which to receive
+ * log event notifications.
+ * @param array $conf Optional associative array of additional
+ * configuration values.
+ *
+ * @return object The newly created concrete Log_observer
+ * instance, or an false on an error.
+ */
+ function &factory($type, $priority = PEAR_LOG_INFO, $conf = array())
+ {
+ $type = strtolower($type);
+ $class = 'Log_observer_' . $type;
+
+ /* Support both the new-style and old-style file naming conventions. */
+ if (file_exists(dirname(__FILE__) . '/observer_' . $type . '.php')) {
+ $classfile = 'Log/observer_' . $type . '.php';
+ $newstyle = true;
+ } else {
+ $classfile = 'Log/' . $type . '.php';
+ $newstyle = false;
+ }
+
+ /* Issue a warning if the old-style conventions are being used. */
+ if (!$newstyle)
+ {
+ trigger_error('Using old-style Log_observer conventions',
+ E_USER_WARNING);
+ }
+
+ /*
+ * Attempt to include our version of the named class, but don't treat
+ * a failure as fatal. The caller may have already included their own
+ * version of the named class.
+ */
+ @include_once $classfile;
+
+ /* If the class exists, return a new instance of it. */
+ if (class_exists($class)) {
+ /* Support both new-style and old-style construction. */
+ if ($newstyle) {
+ return new $class($priority, $conf);
+ } else {
+ return new $class($priority);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * This is a stub method to make sure that Log_Observer classes do
+ * something when they are notified of a message. The default behavior
+ * is to just print the message, which is obviously not desireable in
+ * practically any situation - which is why you need to override this
+ * method. :)
+ *
+ * @param array $event A hash describing the log event.
+ */
+ function notify($event)
+ {
+ print_r($event);
+ }
+}
+
+?>
diff --git a/pearlib/Log/sql.php b/pearlib/Log/sql.php
new file mode 100644
index 00000000..3a20a0e2
--- /dev/null
+++ b/pearlib/Log/sql.php
@@ -0,0 +1,225 @@
+<?php
+/**
+ * $Header: /repository/pear/Log/Log/sql.php,v 1.34 2004/08/19 06:35:57 jon Exp $
+ * $Horde: horde/lib/Log/sql.php,v 1.12 2000/08/16 20:27:34 chuck Exp $
+ *
+ * @version $Revision: 1.34 $
+ * @package Log
+ */
+
+/** PEAR's DB package */
+require_once 'DB.php';
+
+/**
+ * The Log_sql class is a concrete implementation of the Log::
+ * abstract class which sends messages to an SQL server. Each entry
+ * occupies a separate row in the database.
+ *
+ * This implementation uses PHP's PEAR database abstraction layer.
+ *
+ * CREATE TABLE log_table (
+ * id INT NOT NULL,
+ * logtime TIMESTAMP NOT NULL,
+ * ident CHAR(16) NOT NULL,
+ * priority INT NOT NULL,
+ * message VARCHAR(200),
+ * PRIMARY KEY (id)
+ * );
+ *
+ * @author Jon Parise <[email protected]>
+ * @since Horde 1.3
+ * @since Log 1.0
+ * @package Log
+ *
+ * @example sql.php Using the SQL handler.
+ */
+class Log_sql extends Log {
+
+ /**
+ * Array containing the dsn information.
+ * @var string
+ * @access private
+ */
+ var $_dsn = '';
+
+ /**
+ * Object holding the database handle.
+ * @var object
+ * @access private
+ */
+ var $_db = null;
+
+ /**
+ * Flag indicating that we're using an existing database connection.
+ * @var boolean
+ * @access private
+ */
+ var $_existingConnection = false;
+
+ /**
+ * String holding the database table to use.
+ * @var string
+ * @access private
+ */
+ var $_table = 'log_table';
+
+ /**
+ * String holding the name of the ID sequence.
+ * @var string
+ * @access private
+ */
+ var $_sequence = 'log_id';
+
+ /**
+ * Maximum length of the $ident string. This corresponds to the size of
+ * the 'ident' column in the SQL table.
+ * @var integer
+ * @access private
+ */
+ var $_identLimit = 16;
+
+
+ /**
+ * Constructs a new sql logging object.
+ *
+ * @param string $name The target SQL table.
+ * @param string $ident The identification field.
+ * @param array $conf The connection configuration array.
+ * @param int $level Log messages up to and including this level.
+ * @access public
+ */
+ function Log_sql($name, $ident = '', $conf = array(),
+ $level = PEAR_LOG_DEBUG)
+ {
+ $this->_id = md5(microtime());
+ $this->_table = $name;
+ $this->_mask = Log::UPTO($level);
+
+ /* If a specific sequence name was provided, use it. */
+ if (!empty($conf['sequence'])) {
+ $this->_sequence = $conf['sequence'];
+ }
+
+ /* If a specific sequence name was provided, use it. */
+ if (isset($conf['identLimit'])) {
+ $this->_identLimit = $conf['identLimit'];
+ }
+
+ /* Now that the ident limit is confirmed, set the ident string. */
+ $this->setIdent($ident);
+
+ /* If an existing database connection was provided, use it. */
+ if (isset($conf['db'])) {
+ $this->_db = &$conf['db'];
+ $this->_existingConnection = true;
+ $this->_opened = true;
+ } else {
+ $this->_dsn = $conf['dsn'];
+ }
+ }
+
+ /**
+ * Opens a connection to the database, if it has not already
+ * been opened. This is implicitly called by log(), if necessary.
+ *
+ * @return boolean True on success, false on failure.
+ * @access public
+ */
+ function open()
+ {
+ if (!$this->_opened) {
+ $this->_db = &DB::connect($this->_dsn, true);
+ if (DB::isError($this->_db)) {
+ return false;
+ }
+ $this->_opened = true;
+ }
+
+ return $this->_opened;
+ }
+
+ /**
+ * Closes the connection to the database if it is still open and we were
+ * the ones that opened it. It is the caller's responsible to close an
+ * existing connection that was passed to us via $conf['db'].
+ *
+ * @return boolean True on success, false on failure.
+ * @access public
+ */
+ function close()
+ {
+ if ($this->_opened && !$this->_existingConnection) {
+ $this->_opened = false;
+ return $this->_db->disconnect();
+ }
+
+ return ($this->_opened === false);
+ }
+
+ /**
+ * Sets this Log instance's identification string. Note that this
+ * SQL-specific implementation will limit the length of the $ident string
+ * to sixteen (16) characters.
+ *
+ * @param string $ident The new identification string.
+ *
+ * @access public
+ * @since Log 1.8.5
+ */
+ function setIdent($ident)
+ {
+ $this->_ident = substr($ident, 0, $this->_identLimit);
+ }
+
+ /**
+ * Inserts $message to the currently open database. Calls open(),
+ * if necessary. Also passes the message along to any Log_observer
+ * instances that are observing this Log.
+ *
+ * @param mixed $message String or object containing the message to log.
+ * @param string $priority The priority of the message. Valid
+ * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT,
+ * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING,
+ * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG.
+ * @return boolean True on success or false on failure.
+ * @access public
+ */
+ function log($message, $priority = null)
+ {
+ /* If a priority hasn't been specified, use the default value. */
+ if ($priority === null) {
+ $priority = $this->_priority;
+ }
+
+ /* Abort early if the priority is above the maximum logging level. */
+ if (!$this->_isMasked($priority)) {
+ return false;
+ }
+
+ /* If the connection isn't open and can't be opened, return failure. */
+ if (!$this->_opened && !$this->open()) {
+ return false;
+ }
+
+ /* Extract the string representation of the message. */
+ $message = $this->_extractMessage($message);
+
+ /* Build the SQL query for this log entry insertion. */
+ $id = $this->_db->nextId($this->_sequence);
+ $q = sprintf('insert into %s (id, logtime, ident, priority, message)' .
+ 'values(%d, CURRENT_TIMESTAMP, %s, %d, %s)',
+ $this->_table, $id, $this->_db->quote($this->_ident),
+ $priority, $this->_db->quote($message));
+
+ $result = $this->_db->query($q);
+ if (DB::isError($result)) {
+ return false;
+ }
+
+ $this->_announce(array('priority' => $priority, 'message' => $message));
+
+ return true;
+ }
+}
+
+?>
diff --git a/pearlib/Log/sqlite.php b/pearlib/Log/sqlite.php
new file mode 100644
index 00000000..83b908a9
--- /dev/null
+++ b/pearlib/Log/sqlite.php
@@ -0,0 +1,238 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP version 4.0 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Bertrand Mansion <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: sqlite.php,v 1.3 2004/01/19 08:02:40 jon Exp $
+
+/**
+ * The Log_sqlite class is a concrete implementation of the Log::
+ * abstract class which sends messages to an Sqlite database.
+ * Each entry occupies a separate row in the database.
+ *
+ * This implementation uses PHP native Sqlite functions.
+ *
+ * CREATE TABLE log_table (
+ * id INTEGER PRIMARY KEY NOT NULL,
+ * logtime NOT NULL,
+ * ident CHAR(16) NOT NULL,
+ * priority INT NOT NULL,
+ * message
+ * );
+ *
+ * @author Bertrand Mansion <[email protected]>
+ * @author Jon Parise <[email protected]>
+ * @since Log 1.8.3
+ * @package Log
+ *
+ * @example sqlite.php Using the Sqlite handler.
+ */
+class Log_sqlite extends Log
+{
+ /**
+ * Array containing the connection defaults
+ * @var array
+ * @access private
+ */
+ var $_options = array('mode' => 0666,
+ 'persistent' => false);
+
+ /**
+ * Object holding the database handle.
+ * @var object
+ * @access private
+ */
+ var $_db = null;
+
+ /**
+ * Flag indicating that we're using an existing database connection.
+ * @var boolean
+ * @access private
+ */
+ var $_existingConnection = false;
+
+ /**
+ * String holding the database table to use.
+ * @var string
+ * @access private
+ */
+ var $_table = 'log_table';
+
+
+ /**
+ * Constructs a new sql logging object.
+ *
+ * @param string $name The target SQL table.
+ * @param string $ident The identification field.
+ * @param mixed $conf Can be an array of configuration options used
+ * to open a new database connection
+ * or an already opened sqlite connection.
+ * @param int $level Log messages up to and including this level.
+ * @access public
+ */
+ function Log_sqlite($name, $ident = '', &$conf, $level = PEAR_LOG_DEBUG)
+ {
+ $this->_id = md5(microtime());
+ $this->_table = $name;
+ $this->_ident = $ident;
+ $this->_mask = Log::UPTO($level);
+
+ if (is_array($conf)) {
+ foreach ($conf as $k => $opt) {
+ $this->_options[$k] = $opt;
+ }
+ } else {
+ // If an existing database connection was provided, use it.
+ $this->_db =& $conf;
+ $this->_existingConnection = true;
+ }
+ }
+
+ /**
+ * Opens a connection to the database, if it has not already
+ * been opened. This is implicitly called by log(), if necessary.
+ *
+ * @return boolean True on success, false on failure.
+ * @access public
+ */
+ function open()
+ {
+ if (is_resource($this->_db)) {
+ $this->_opened = true;
+ return $this->_createTable();
+ } else {
+ /* Set the connection function based on the 'persistent' option. */
+ if (empty($this->_options['persistent'])) {
+ $connectFunction = 'sqlite_open';
+ } else {
+ $connectFunction = 'sqlite_popen';
+ }
+
+ /* Attempt to connect to the database. */
+ if ($this->_db = $connectFunction($this->_options['filename'],
+ (int)$this->_options['mode'],
+ $error)) {
+ $this->_opened = true;
+ return $this->_createTable();
+ }
+ }
+
+ return $this->_opened;
+ }
+
+ /**
+ * Closes the connection to the database if it is still open and we were
+ * the ones that opened it. It is the caller's responsible to close an
+ * existing connection that was passed to us via $conf['db'].
+ *
+ * @return boolean True on success, false on failure.
+ * @access public
+ */
+ function close()
+ {
+ /* We never close existing connections. */
+ if ($this->_existingConnection) {
+ return false;
+ }
+
+ if ($this->_opened) {
+ $this->_opened = false;
+ sqlite_close($this->_db);
+ }
+
+ return ($this->_opened === false);
+ }
+
+ /**
+ * Inserts $message to the currently open database. Calls open(),
+ * if necessary. Also passes the message along to any Log_observer
+ * instances that are observing this Log.
+ *
+ * @param mixed $message String or object containing the message to log.
+ * @param string $priority The priority of the message. Valid
+ * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT,
+ * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING,
+ * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG.
+ * @return boolean True on success or false on failure.
+ * @access public
+ */
+ function log($message, $priority = null)
+ {
+ /* If a priority hasn't been specified, use the default value. */
+ if ($priority === null) {
+ $priority = $this->_priority;
+ }
+
+ /* Abort early if the priority is above the maximum logging level. */
+ if (!$this->_isMasked($priority)) {
+ return false;
+ }
+
+ /* If the connection isn't open and can't be opened, return failure. */
+ if (!$this->_opened && !$this->open()) {
+ return false;
+ }
+
+ // Extract the string representation of the message.
+ $message = $this->_extractMessage($message);
+
+ // Build the SQL query for this log entry insertion.
+ $q = sprintf('INSERT INTO [%s] (logtime, ident, priority, message) ' .
+ "VALUES ('%s', '%s', %d, '%s')",
+ $this->_table,
+ strftime('%Y-%m-%d %H:%M:%S', time()),
+ sqlite_escape_string($this->_ident),
+ $priority,
+ sqlite_escape_string($message));
+ if (!($res = @sqlite_unbuffered_query($this->_db, $q))) {
+ return false;
+ }
+ $this->_announce(array('priority' => $priority, 'message' => $message));
+
+ return true;
+ }
+
+ /**
+ * Checks whether the log table exists and creates it if necessary.
+ *
+ * @return boolean True on success or false on failure.
+ * @access private
+ */
+ function _createTable()
+ {
+ $q = "SELECT name FROM sqlite_master WHERE name='" . $this->_table .
+ "' AND type='table'";
+
+ $res = sqlite_query($this->_db, $q);
+
+ if (sqlite_num_rows($res) == 0) {
+ $q = 'CREATE TABLE [' . $this->_table . '] (' .
+ 'id INTEGER PRIMARY KEY NOT NULL, ' .
+ 'logtime NOT NULL, ' .
+ 'ident CHAR(16) NOT NULL, ' .
+ 'priority INT NOT NULL, ' .
+ 'message)';
+
+ if (!($res = sqlite_unbuffered_query($this->_db, $q))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
+
+?>
diff --git a/pearlib/Log/syslog.php b/pearlib/Log/syslog.php
new file mode 100644
index 00000000..4bafbaa2
--- /dev/null
+++ b/pearlib/Log/syslog.php
@@ -0,0 +1,160 @@
+<?php
+/**
+ * $Header: /repository/pear/Log/Log/syslog.php,v 1.22 2004/01/19 08:02:40 jon Exp $
+ * $Horde: horde/lib/Log/syslog.php,v 1.6 2000/06/28 21:36:13 jon Exp $
+ *
+ * @version $Revision: 1.22 $
+ * @package Log
+ */
+
+/**
+ * The Log_syslog class is a concrete implementation of the Log::
+ * abstract class which sends messages to syslog on UNIX-like machines
+ * (PHP emulates this with the Event Log on Windows machines).
+ *
+ * @author Chuck Hagenbuch <[email protected]>
+ * @since Horde 1.3
+ * @since Log 1.0
+ * @package Log
+ *
+ * @example syslog.php Using the syslog handler.
+ */
+class Log_syslog extends Log
+{
+ /**
+ * Integer holding the log facility to use.
+ * @var string
+ * @access private
+ */
+ var $_name = LOG_SYSLOG;
+
+ /**
+ * Constructs a new syslog object.
+ *
+ * @param string $name The syslog facility.
+ * @param string $ident The identity string.
+ * @param array $conf The configuration array.
+ * @param int $level Log messages up to and including this level.
+ * @access public
+ */
+ function Log_syslog($name, $ident = '', $conf = array(),
+ $level = PEAR_LOG_DEBUG)
+ {
+ /* Ensure we have a valid integer value for $name. */
+ if (empty($name) || !is_int($name)) {
+ $name = LOG_SYSLOG;
+ }
+
+ $this->_id = md5(microtime());
+ $this->_name = $name;
+ $this->_ident = $ident;
+ $this->_mask = Log::UPTO($level);
+ }
+
+ /**
+ * Opens a connection to the system logger, if it has not already
+ * been opened. This is implicitly called by log(), if necessary.
+ * @access public
+ */
+ function open()
+ {
+ if (!$this->_opened) {
+ openlog($this->_ident, LOG_PID, $this->_name);
+ $this->_opened = true;
+ }
+
+ return $this->_opened;
+ }
+
+ /**
+ * Closes the connection to the system logger, if it is open.
+ * @access public
+ */
+ function close()
+ {
+ if ($this->_opened) {
+ closelog();
+ $this->_opened = false;
+ }
+
+ return ($this->_opened === false);
+ }
+
+ /**
+ * Sends $message to the currently open syslog connection. Calls
+ * open() if necessary. Also passes the message along to any Log_observer
+ * instances that are observing this Log.
+ *
+ * @param mixed $message String or object containing the message to log.
+ * @param int $priority (optional) The priority of the message. Valid
+ * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT,
+ * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING,
+ * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG.
+ * @return boolean True on success or false on failure.
+ * @access public
+ */
+ function log($message, $priority = null)
+ {
+ /* If a priority hasn't been specified, use the default value. */
+ if ($priority === null) {
+ $priority = $this->_priority;
+ }
+
+ /* Abort early if the priority is above the maximum logging level. */
+ if (!$this->_isMasked($priority)) {
+ return false;
+ }
+
+ /* If the connection isn't open and can't be opened, return failure. */
+ if (!$this->_opened && !$this->open()) {
+ return false;
+ }
+
+ /* Extract the string representation of the message. */
+ $message = $this->_extractMessage($message);
+
+ if (!syslog($this->_toSyslog($priority), $message)) {
+ return false;
+ }
+
+ $this->_announce(array('priority' => $priority, 'message' => $message));
+
+ return true;
+ }
+
+ /**
+ * Converts a PEAR_LOG_* constant into a syslog LOG_* constant.
+ *
+ * This function exists because, under Windows, not all of the LOG_*
+ * constants have unique values. Instead, the PEAR_LOG_* were introduced
+ * for global use, with the conversion to the LOG_* constants kept local to
+ * to the syslog driver.
+ *
+ * @param int $priority PEAR_LOG_* value to convert to LOG_* value.
+ *
+ * @return The LOG_* representation of $priority.
+ *
+ * @access private
+ */
+ function _toSyslog($priority)
+ {
+ static $priorities = array(
+ PEAR_LOG_EMERG => LOG_EMERG,
+ PEAR_LOG_ALERT => LOG_ALERT,
+ PEAR_LOG_CRIT => LOG_CRIT,
+ PEAR_LOG_ERR => LOG_ERR,
+ PEAR_LOG_WARNING => LOG_WARNING,
+ PEAR_LOG_NOTICE => LOG_NOTICE,
+ PEAR_LOG_INFO => LOG_INFO,
+ PEAR_LOG_DEBUG => LOG_DEBUG
+ );
+
+ /* If we're passed an unknown priority, default to LOG_INFO. */
+ if (!is_int($priority) || !in_array($priority, $priorities)) {
+ return LOG_INFO;
+ }
+
+ return $priorities[$priority];
+ }
+}
+?>
diff --git a/pearlib/Log/win.php b/pearlib/Log/win.php
new file mode 100644
index 00000000..e8a09d16
--- /dev/null
+++ b/pearlib/Log/win.php
@@ -0,0 +1,256 @@
+<?php
+/**
+ * $Header: /repository/pear/Log/Log/win.php,v 1.16 2004/09/08 23:35:53 jon Exp $
+ *
+ * @version $Revision: 1.16 $
+ * @package Log
+ */
+
+/**
+ * The Log_win class is a concrete implementation of the Log abstract
+ * class that logs messages to a separate browser window.
+ *
+ * The concept for this log handler is based on part by Craig Davis' article
+ * entitled "JavaScript Power PHP Debugging:
+ *
+ * http://www.zend.com/zend/tut/tutorial-DebugLib.php
+ *
+ * @author Jon Parise <[email protected]>
+ * @since Log 1.7.0
+ * @package Log
+ *
+ * @example win.php Using the window handler.
+ */
+class Log_win extends Log
+{
+ /**
+ * The name of the output window.
+ * @var string
+ * @access private
+ */
+ var $_name = 'LogWindow';
+
+ /**
+ * The title of the output window.
+ * @var string
+ * @access private
+ */
+ var $_title = 'Log Output Window';
+
+ /**
+ * Mapping of log priorities to colors.
+ * @var array
+ * @access private
+ */
+ var $_colors = array(
+ PEAR_LOG_EMERG => 'red',
+ PEAR_LOG_ALERT => 'orange',
+ PEAR_LOG_CRIT => 'yellow',
+ PEAR_LOG_ERR => 'green',
+ PEAR_LOG_WARNING => 'blue',
+ PEAR_LOG_NOTICE => 'indigo',
+ PEAR_LOG_INFO => 'violet',
+ PEAR_LOG_DEBUG => 'black'
+ );
+
+ /**
+ * String buffer that holds line that are pending output.
+ * @var array
+ * @access private
+ */
+ var $_buffer = array();
+
+ /**
+ * Constructs a new Log_win object.
+ *
+ * @param string $name Ignored.
+ * @param string $ident The identity string.
+ * @param array $conf The configuration array.
+ * @param int $level Log messages up to and including this level.
+ * @access public
+ */
+ function Log_win($name, $ident = '', $conf = array(),
+ $level = PEAR_LOG_DEBUG)
+ {
+ $this->_id = md5(microtime());
+ $this->_name = $name;
+ $this->_ident = $ident;
+ $this->_mask = Log::UPTO($level);
+
+ if (isset($conf['title'])) {
+ $this->_title = $conf['title'];
+ }
+ if (isset($conf['colors']) && is_array($conf['colors'])) {
+ $this->_colors = $conf['colors'];
+ }
+
+ register_shutdown_function(array(&$this, '_Log_win'));
+ }
+
+ /**
+ * Destructor
+ */
+ function _Log_win()
+ {
+ if ($this->_opened || (count($this->_buffer) > 0)) {
+ $this->close();
+ }
+ }
+
+ /**
+ * The first time open() is called, it will open a new browser window and
+ * prepare it for output.
+ *
+ * This is implicitly called by log(), if necessary.
+ *
+ * @access public
+ */
+ function open()
+ {
+ if (!$this->_opened) {
+ $win = $this->_name;
+
+ if (!empty($this->_ident)) {
+ $identHeader = "$win.document.writeln('<th>Ident</th>')";
+ } else {
+ $identHeader = '';
+ }
+
+ echo <<< END_OF_SCRIPT
+<script language="JavaScript">
+$win = window.open('', '{$this->_name}', 'toolbar=no,scrollbars,width=600,height=400');
+$win.document.writeln('<html>');
+$win.document.writeln('<head>');
+$win.document.writeln('<title>{$this->_title}</title>');
+$win.document.writeln('<style type="text/css">');
+$win.document.writeln('body { font-family: monospace; font-size: 8pt; }');
+$win.document.writeln('td,th { font-size: 8pt; }');
+$win.document.writeln('td,th { border-bottom: #999999 solid 1px; }');
+$win.document.writeln('td,th { border-right: #999999 solid 1px; }');
+$win.document.writeln('</style>');
+$win.document.writeln('</head>');
+$win.document.writeln('<body>');
+$win.document.writeln('<table border="0" cellpadding="2" cellspacing="0">');
+$win.document.writeln('<tr><th>Time</th>');
+$identHeader
+$win.document.writeln('<th>Priority</th><th width="100%">Message</th></tr>');
+</script>
+END_OF_SCRIPT;
+ $this->_opened = true;
+ }
+
+ return $this->_opened;
+ }
+
+ /**
+ * Closes the output stream if it is open. If there are still pending
+ * lines in the output buffer, the output window will be opened so that
+ * the buffer can be drained.
+ *
+ * @access public
+ */
+ function close()
+ {
+ /*
+ * If there are still lines waiting to be written, open the output
+ * window so that we can drain the buffer.
+ */
+ if (!$this->_opened && (count($this->_buffer) > 0)) {
+ $this->open();
+ }
+
+ if ($this->_opened) {
+ $this->_writeln('</table>');
+ $this->_writeln('</body></html>');
+ $this->_opened = false;
+ }
+
+ return ($this->_opened === false);
+ }
+
+ /**
+ * Writes a single line of text to the output window.
+ *
+ * @param string $line The line of text to write.
+ *
+ * @access private
+ */
+ function _writeln($line)
+ {
+ /* Add this line to our output buffer. */
+ $this->_buffer[] = $line;
+
+ /* Buffer the output until this page's headers have been sent. */
+ if (!headers_sent()) {
+ return;
+ }
+
+ /* If we haven't already opened the output window, do so now. */
+ if (!$this->_opened && !$this->open()) {
+ return false;
+ }
+
+ /* Drain the buffer to the output window. */
+ $win = $this->_name;
+ foreach ($this->_buffer as $line) {
+ echo "<script language='JavaScript'>\n";
+ echo "$win.document.writeln('" . addslashes($line) . "');\n";
+ echo "self.focus();\n";
+ echo "</script>\n";
+ }
+
+ /* Now that the buffer has been drained, clear it. */
+ $this->_buffer = array();
+ }
+
+ /**
+ * Logs $message to the output window. The message is also passed along
+ * to any Log_observer instances that are observing this Log.
+ *
+ * @param mixed $message String or object containing the message to log.
+ * @param string $priority The priority of the message. Valid
+ * values are: PEAR_LOG_EMERG, PEAR_LOG_ALERT,
+ * PEAR_LOG_CRIT, PEAR_LOG_ERR, PEAR_LOG_WARNING,
+ * PEAR_LOG_NOTICE, PEAR_LOG_INFO, and PEAR_LOG_DEBUG.
+ * @return boolean True on success or false on failure.
+ * @access public
+ */
+ function log($message, $priority = null)
+ {
+ /* If a priority hasn't been specified, use the default value. */
+ if ($priority === null) {
+ $priority = $this->_priority;
+ }
+
+ /* Abort early if the priority is above the maximum logging level. */
+ if (!$this->_isMasked($priority)) {
+ return false;
+ }
+
+ /* Extract the string representation of the message. */
+ $message = $this->_extractMessage($message);
+
+ list($usec, $sec) = explode(' ', microtime());
+
+ /* Build the output line that contains the log entry row. */
+ $line = '<tr align="left" valign="top">';
+ $line .= sprintf('<td>%s.%s</td>',
+ strftime('%T', $sec), substr($usec, 2, 2));
+ if (!empty($this->_ident)) {
+ $line .= '<td>' . $this->_ident . '</td>';
+ }
+ $line .= '<td>' . ucfirst($this->priorityToString($priority)) . '</td>';
+ $line .= sprintf('<td style="color: %s">%s</td>',
+ $this->_colors[$priority],
+ preg_replace('/\r\n|\n|\r/', '<br />', $message));
+ $line .= '</tr>';
+
+ $this->_writeln($line);
+
+ $this->_announce(array('priority' => $priority, 'message' => $message));
+
+ return true;
+ }
+}
+
+?>
diff --git a/pearlib/Net/Socket.php b/pearlib/Net/Socket.php
new file mode 100644
index 00000000..415e2ef3
--- /dev/null
+++ b/pearlib/Net/Socket.php
@@ -0,0 +1,456 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Bakken <[email protected]> |
+// | Chuck Hagenbuch <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Socket.php,v 1.9 2004/04/26 23:02:41 chagenbu Exp $
+//
+
+require_once 'PEAR.php';
+
+/**
+ * Generalized Socket class. More docs to be written.
+ *
+ * @version 1.0
+ * @author Stig Bakken <[email protected]>
+ * @author Chuck Hagenbuch <[email protected]>
+ */
+class Net_Socket extends PEAR {
+ // {{{ properties
+
+ /** Socket file pointer. */
+ var $fp = null;
+
+ /** Whether the socket is blocking. */
+ var $blocking = true;
+
+ /** Whether the socket is persistent. */
+ var $persistent = false;
+
+ /** The IP address to connect to. */
+ var $addr = '';
+
+ /** The port number to connect to. */
+ var $port = 0;
+
+ /** Number of seconds to wait on socket connections before
+ assuming there's no more data. */
+ var $timeout = false;
+
+ /** Number of bytes to read at a time in readLine() and
+ readAll(). */
+ var $lineLength = 2048;
+ // }}}
+
+ // {{{ constructor
+ /**
+ * Constructs a new Net_Socket object.
+ *
+ * @access public
+ */
+ function Net_Socket()
+ {
+ $this->PEAR();
+ }
+ // }}}
+
+ // {{{ connect()
+ /**
+ * Connect to the specified port. If called when the socket is
+ * already connected, it disconnects and connects again.
+ *
+ * @param $addr string IP address or host name
+ * @param $port int TCP port number
+ * @param $persistent bool (optional) whether the connection is
+ * persistent (kept open between requests by the web server)
+ * @param $timeout int (optional) how long to wait for data
+ * @param $options array see options for stream_context_create
+ * @access public
+ * @return mixed true on success or error object
+ */
+ function connect($addr, $port, $persistent = null, $timeout = null, $options = null)
+ {
+ if (is_resource($this->fp)) {
+ @fclose($this->fp);
+ $this->fp = null;
+ }
+
+ if (strspn($addr, '.0123456789') == strlen($addr)) {
+ $this->addr = $addr;
+ } else {
+ $this->addr = gethostbyname($addr);
+ }
+ $this->port = $port % 65536;
+ if ($persistent !== null) {
+ $this->persistent = $persistent;
+ }
+ if ($timeout !== null) {
+ $this->timeout = $timeout;
+ }
+ $openfunc = $this->persistent ? 'pfsockopen' : 'fsockopen';
+ $errno = 0;
+ $errstr = '';
+ if ($options && function_exists('stream_context_create')) {
+ if ($this->timeout) {
+ $timeout = $this->timeout;
+ } else {
+ $timeout = 0;
+ }
+ $context = stream_context_create($options);
+ $fp = $openfunc($this->addr, $this->port, $errno, $errstr, $timeout, $context);
+ } else {
+ if ($this->timeout) {
+ $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $this->timeout);
+ } else {
+ $fp = @$openfunc($this->addr, $this->port, $errno, $errstr);
+ }
+ }
+
+ if (!$fp) {
+ return $this->raiseError($errstr, $errno);
+ }
+
+ $this->fp = $fp;
+
+ return $this->setBlocking($this->blocking);
+ }
+ // }}}
+
+ // {{{ disconnect()
+ /**
+ * Disconnects from the peer, closes the socket.
+ *
+ * @access public
+ * @return mixed true on success or an error object otherwise
+ */
+ function disconnect()
+ {
+ if (is_resource($this->fp)) {
+ fclose($this->fp);
+ $this->fp = null;
+ return true;
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ isBlocking()
+ /**
+ * Find out if the socket is in blocking mode.
+ *
+ * @access public
+ * @return bool the current blocking mode.
+ */
+ function isBlocking()
+ {
+ return $this->blocking;
+ }
+ // }}}
+
+ // {{{ setBlocking()
+ /**
+ * Sets whether the socket connection should be blocking or
+ * not. A read call to a non-blocking socket will return immediately
+ * if there is no data available, whereas it will block until there
+ * is data for blocking sockets.
+ *
+ * @param $mode bool true for blocking sockets, false for nonblocking
+ * @access public
+ * @return mixed true on success or an error object otherwise
+ */
+ function setBlocking($mode)
+ {
+ if (is_resource($this->fp)) {
+ $this->blocking = $mode;
+ socket_set_blocking($this->fp, $this->blocking);
+ return true;
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ setTimeout()
+ /**
+ * Sets the timeout value on socket descriptor,
+ * expressed in the sum of seconds and microseconds
+ *
+ * @param $seconds int seconds
+ * @param $microseconds int microseconds
+ * @access public
+ * @return mixed true on success or an error object otherwise
+ */
+ function setTimeout($seconds, $microseconds)
+ {
+ if (is_resource($this->fp)) {
+ socket_set_timeout($this->fp, $seconds, $microseconds);
+ return true;
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ getStatus()
+ /**
+ * Returns information about an existing socket resource.
+ * Currently returns four entries in the result array:
+ *
+ * <p>
+ * timed_out (bool) - The socket timed out waiting for data<br>
+ * blocked (bool) - The socket was blocked<br>
+ * eof (bool) - Indicates EOF event<br>
+ * unread_bytes (int) - Number of bytes left in the socket buffer<br>
+ * </p>
+ *
+ * @access public
+ * @return mixed Array containing information about existing socket resource or an error object otherwise
+ */
+ function getStatus()
+ {
+ if (is_resource($this->fp)) {
+ return socket_get_status($this->fp);
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ gets()
+ /**
+ * Get a specified line of data
+ *
+ * @access public
+ * @return $size bytes of data from the socket, or a PEAR_Error if
+ * not connected.
+ */
+ function gets($size)
+ {
+ if (is_resource($this->fp)) {
+ return fgets($this->fp, $size);
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ read()
+ /**
+ * Read a specified amount of data. This is guaranteed to return,
+ * and has the added benefit of getting everything in one fread()
+ * chunk; if you know the size of the data you're getting
+ * beforehand, this is definitely the way to go.
+ *
+ * @param $size The number of bytes to read from the socket.
+ * @access public
+ * @return $size bytes of data from the socket, or a PEAR_Error if
+ * not connected.
+ */
+ function read($size)
+ {
+ if (is_resource($this->fp)) {
+ return fread($this->fp, $size);
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ write()
+ /**
+ * Write a specified amount of data.
+ *
+ * @access public
+ * @return mixed true on success or an error object otherwise
+ */
+ function write($data)
+ {
+ if (is_resource($this->fp)) {
+ return fwrite($this->fp, $data);
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ writeLine()
+ /**
+ * Write a line of data to the socket, followed by a trailing "\r\n".
+ *
+ * @access public
+ * @return mixed fputs result, or an error
+ */
+ function writeLine ($data)
+ {
+ if (is_resource($this->fp)) {
+ return $this->write($data . "\r\n");
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ eof()
+ /**
+ * Tests for end-of-file on a socket descriptor
+ *
+ * @access public
+ * @return bool
+ */
+ function eof()
+ {
+ return (is_resource($this->fp) && feof($this->fp));
+ }
+ // }}}
+
+ // {{{ readByte()
+ /**
+ * Reads a byte of data
+ *
+ * @access public
+ * @return 1 byte of data from the socket, or a PEAR_Error if
+ * not connected.
+ */
+ function readByte()
+ {
+ if (is_resource($this->fp)) {
+ return ord($this->read(1));
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ readWord()
+ /**
+ * Reads a word of data
+ *
+ * @access public
+ * @return 1 word of data from the socket, or a PEAR_Error if
+ * not connected.
+ */
+ function readWord()
+ {
+ if (is_resource($this->fp)) {
+ $buf = $this->read(2);
+ return (ord($buf[0]) + (ord($buf[1]) << 8));
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ readInt()
+ /**
+ * Reads an int of data
+ *
+ * @access public
+ * @return 1 int of data from the socket, or a PEAR_Error if
+ * not connected.
+ */
+ function readInt()
+ {
+ if (is_resource($this->fp)) {
+ $buf = $this->read(4);
+ return (ord($buf[0]) + (ord($buf[1]) << 8) +
+ (ord($buf[2]) << 16) + (ord($buf[3]) << 24));
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ readString()
+ /**
+ * Reads a zeroterminated string of data
+ *
+ * @access public
+ * @return string, or a PEAR_Error if
+ * not connected.
+ */
+ function readString()
+ {
+ if (is_resource($this->fp)) {
+ $string = '';
+ while (($char = $this->read(1)) != "\x00") {
+ $string .= $char;
+ }
+ return $string;
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ readIPAddress()
+ /**
+ * Reads an IP Address and returns it in a dot formated string
+ *
+ * @access public
+ * @return Dot formated string, or a PEAR_Error if
+ * not connected.
+ */
+ function readIPAddress()
+ {
+ if (is_resource($this->fp)) {
+ $buf = $this->read(4);
+ return sprintf("%s.%s.%s.%s", ord($buf[0]), ord($buf[1]),
+ ord($buf[2]), ord($buf[3]));
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ readLine()
+ /**
+ * Read until either the end of the socket or a newline, whichever
+ * comes first. Strips the trailing newline from the returned data.
+ *
+ * @access public
+ * @return All available data up to a newline, without that
+ * newline, or until the end of the socket, or a PEAR_Error if
+ * not connected.
+ */
+ function readLine()
+ {
+ if (is_resource($this->fp)) {
+ $line = '';
+ $timeout = time() + $this->timeout;
+ while (!$this->eof() && (!$this->timeout || time() < $timeout)) {
+ $line .= $this->gets($this->lineLength);
+ if (substr($line, -2) == "\r\n" ||
+ substr($line, -1) == "\n") {
+ return rtrim($line, "\r\n");
+ }
+ }
+ return $line;
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+ // {{{ readAll()
+ /**
+ * Read until the socket closes. THIS FUNCTION WILL NOT EXIT if the
+ * socket is in blocking mode until the socket closes.
+ *
+ * @access public
+ * @return All data until the socket closes, or a PEAR_Error if
+ * not connected.
+ */
+ function readAll()
+ {
+ if (is_resource($this->fp)) {
+ $data = '';
+ while (!$this->eof())
+ $data .= $this->read($this->lineLength);
+ return $data;
+ }
+ return $this->raiseError("not connected");
+ }
+ // }}}
+
+}
diff --git a/pearlib/Net/URL.php b/pearlib/Net/URL.php
new file mode 100644
index 00000000..da48c746
--- /dev/null
+++ b/pearlib/Net/URL.php
@@ -0,0 +1,410 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Copyright (c) 2002-2004, Richard Heyes |
+// | All rights reserved. |
+// | |
+// | Redistribution and use in source and binary forms, with or without |
+// | modification, are permitted provided that the following conditions |
+// | are met: |
+// | |
+// | o Redistributions of source code must retain the above copyright |
+// | notice, this list of conditions and the following disclaimer. |
+// | o Redistributions in binary form must reproduce the above copyright |
+// | notice, this list of conditions and the following disclaimer in the |
+// | documentation and/or other materials provided with the distribution.|
+// | o The names of the authors may not be used to endorse or promote |
+// | products derived from this software without specific prior written |
+// | permission. |
+// | |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+// | |
+// +-----------------------------------------------------------------------+
+// | Author: Richard Heyes <richard at php net> |
+// +-----------------------------------------------------------------------+
+//
+// $Id: URL.php,v 1.36 2004/06/19 18:58:50 richard Exp $
+//
+// Net_URL Class
+
+class Net_URL
+{
+ /**
+ * Full url
+ * @var string
+ */
+ var $url;
+
+ /**
+ * Protocol
+ * @var string
+ */
+ var $protocol;
+
+ /**
+ * Username
+ * @var string
+ */
+ var $username;
+
+ /**
+ * Password
+ * @var string
+ */
+ var $password;
+
+ /**
+ * Host
+ * @var string
+ */
+ var $host;
+
+ /**
+ * Port
+ * @var integer
+ */
+ var $port;
+
+ /**
+ * Path
+ * @var string
+ */
+ var $path;
+
+ /**
+ * Query string
+ * @var array
+ */
+ var $querystring;
+
+ /**
+ * Anchor
+ * @var string
+ */
+ var $anchor;
+
+ /**
+ * Whether to use []
+ * @var bool
+ */
+ var $useBrackets;
+
+ /**
+ * PHP4 Constructor
+ *
+ * @see __construct()
+ */
+ function Net_URL($url = null, $useBrackets = true)
+ {
+ $this->__construct($url, $useBrackets);
+ }
+
+ /**
+ * PHP5 Constructor
+ *
+ * Parses the given url and stores the various parts
+ * Defaults are used in certain cases
+ *
+ * @param string $url Optional URL
+ * @param bool $useBrackets Whether to use square brackets when
+ * multiple querystrings with the same name
+ * exist
+ */
+ function __construct($url = null, $useBrackets = true)
+ {
+ $HTTP_SERVER_VARS = !empty($_SERVER) ? $_SERVER : $GLOBALS['HTTP_SERVER_VARS'];
+
+ $this->useBrackets = $useBrackets;
+ $this->url = $url;
+ $this->user = '';
+ $this->pass = '';
+ $this->host = '';
+ $this->port = 80;
+ $this->path = '';
+ $this->querystring = array();
+ $this->anchor = '';
+
+ // Only use defaults if not an absolute URL given
+ if (!preg_match('/^[a-z0-9]+:\/\//i', $url)) {
+
+ $this->protocol = (@$HTTP_SERVER_VARS['HTTPS'] == 'on' ? 'https' : 'http');
+
+ /**
+ * Figure out host/port
+ */
+ if (!empty($HTTP_SERVER_VARS['HTTP_HOST']) AND preg_match('/^(.*)(:([0-9]+))?$/U', $HTTP_SERVER_VARS['HTTP_HOST'], $matches)) {
+ $host = $matches[1];
+ if (!empty($matches[3])) {
+ $port = $matches[3];
+ } else {
+ $port = $this->getStandardPort($this->protocol);
+ }
+ }
+
+ $this->user = '';
+ $this->pass = '';
+ $this->host = !empty($host) ? $host : (isset($HTTP_SERVER_VARS['SERVER_NAME']) ? $HTTP_SERVER_VARS['SERVER_NAME'] : 'localhost');
+ $this->port = !empty($port) ? $port : (isset($HTTP_SERVER_VARS['SERVER_PORT']) ? $HTTP_SERVER_VARS['SERVER_PORT'] : $this->getStandardPort($this->protocol));
+ $this->path = !empty($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : '/';
+ $this->querystring = isset($HTTP_SERVER_VARS['QUERY_STRING']) ? $this->_parseRawQuerystring($HTTP_SERVER_VARS['QUERY_STRING']) : null;
+ $this->anchor = '';
+ }
+
+ // Parse the url and store the various parts
+ if (!empty($url)) {
+ $urlinfo = parse_url($url);
+
+ // Default querystring
+ $this->querystring = array();
+
+ foreach ($urlinfo as $key => $value) {
+ switch ($key) {
+ case 'scheme':
+ $this->protocol = $value;
+ $this->port = $this->getStandardPort($value);
+ break;
+
+ case 'user':
+ case 'pass':
+ case 'host':
+ case 'port':
+ $this->$key = $value;
+ break;
+
+ case 'path':
+ if ($value{0} == '/') {
+ $this->path = $value;
+ } else {
+ $path = dirname($this->path) == DIRECTORY_SEPARATOR ? '' : dirname($this->path);
+ $this->path = sprintf('%s/%s', $path, $value);
+ }
+ break;
+
+ case 'query':
+ $this->querystring = $this->_parseRawQueryString($value);
+ break;
+
+ case 'fragment':
+ $this->anchor = $value;
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns full url
+ *
+ * @return string Full url
+ * @access public
+ */
+ function getURL()
+ {
+ $querystring = $this->getQueryString();
+
+ $this->url = $this->protocol . '://'
+ . $this->user . (!empty($this->pass) ? ':' : '')
+ . $this->pass . (!empty($this->user) ? '@' : '')
+ . $this->host . ($this->port == $this->getStandardPort($this->protocol) ? '' : ':' . $this->port)
+ . $this->path
+ . (!empty($querystring) ? '?' . $querystring : '')
+ . (!empty($this->anchor) ? '#' . $this->anchor : '');
+
+ return $this->url;
+ }
+
+ /**
+ * Adds a querystring item
+ *
+ * @param string $name Name of item
+ * @param string $value Value of item
+ * @param bool $preencoded Whether value is urlencoded or not, default = not
+ * @access public
+ */
+ function addQueryString($name, $value, $preencoded = false)
+ {
+ if ($preencoded) {
+ $this->querystring[$name] = $value;
+ } else {
+ $this->querystring[$name] = is_array($value) ? array_map('rawurlencode', $value): rawurlencode($value);
+ }
+ }
+
+ /**
+ * Removes a querystring item
+ *
+ * @param string $name Name of item
+ * @access public
+ */
+ function removeQueryString($name)
+ {
+ if (isset($this->querystring[$name])) {
+ unset($this->querystring[$name]);
+ }
+ }
+
+ /**
+ * Sets the querystring to literally what you supply
+ *
+ * @param string $querystring The querystring data. Should be of the format foo=bar&x=y etc
+ * @access public
+ */
+ function addRawQueryString($querystring)
+ {
+ $this->querystring = $this->_parseRawQueryString($querystring);
+ }
+
+ /**
+ * Returns flat querystring
+ *
+ * @return string Querystring
+ * @access public
+ */
+ function getQueryString()
+ {
+ if (!empty($this->querystring)) {
+ foreach ($this->querystring as $name => $value) {
+ if (is_array($value)) {
+ foreach ($value as $k => $v) {
+ $querystring[] = $this->useBrackets ? sprintf('%s[%s]=%s', $name, $k, $v) : ($name . '=' . $v);
+ }
+ } elseif (!is_null($value)) {
+ $querystring[] = $name . '=' . $value;
+ } else {
+ $querystring[] = $name;
+ }
+ }
+ $querystring = implode(ini_get('arg_separator.output'), $querystring);
+ } else {
+ $querystring = '';
+ }
+
+ return $querystring;
+ }
+
+ /**
+ * Parses raw querystring and returns an array of it
+ *
+ * @param string $querystring The querystring to parse
+ * @return array An array of the querystring data
+ * @access private
+ */
+ function _parseRawQuerystring($querystring)
+ {
+ $parts = preg_split('/[' . preg_quote(ini_get('arg_separator.input'), '/') . ']/', $querystring, -1, PREG_SPLIT_NO_EMPTY);
+ $return = array();
+
+ foreach ($parts as $part) {
+ if (strpos($part, '=') !== false) {
+ $value = substr($part, strpos($part, '=') + 1);
+ $key = substr($part, 0, strpos($part, '='));
+ } else {
+ $value = null;
+ $key = $part;
+ }
+ if (substr($key, -2) == '[]') {
+ $key = substr($key, 0, -2);
+ if (@!is_array($return[$key])) {
+ $return[$key] = array();
+ $return[$key][] = $value;
+ } else {
+ $return[$key][] = $value;
+ }
+ } elseif (!$this->useBrackets AND !empty($return[$key])) {
+ $return[$key] = (array)$return[$key];
+ $return[$key][] = $value;
+ } else {
+ $return[$key] = $value;
+ }
+ }
+
+ return $return;
+ }
+
+ /**
+ * Resolves //, ../ and ./ from a path and returns
+ * the result. Eg:
+ *
+ * /foo/bar/../boo.php => /foo/boo.php
+ * /foo/bar/../../boo.php => /boo.php
+ * /foo/bar/.././/boo.php => /foo/boo.php
+ *
+ * This method can also be called statically.
+ *
+ * @param string $url URL path to resolve
+ * @return string The result
+ */
+ function resolvePath($path)
+ {
+ $path = explode('/', str_replace('//', '/', $path));
+
+ for ($i=0; $i<count($path); $i++) {
+ if ($path[$i] == '.') {
+ unset($path[$i]);
+ $path = array_values($path);
+ $i--;
+
+ } elseif ($path[$i] == '..' AND ($i > 1 OR ($i == 1 AND $path[0] != '') ) ) {
+ unset($path[$i]);
+ unset($path[$i-1]);
+ $path = array_values($path);
+ $i -= 2;
+
+ } elseif ($path[$i] == '..' AND $i == 1 AND $path[0] == '') {
+ unset($path[$i]);
+ $path = array_values($path);
+ $i--;
+
+ } else {
+ continue;
+ }
+ }
+
+ return implode('/', $path);
+ }
+
+ /**
+ * Returns the standard port number for a protocol
+ *
+ * @param string $scheme The protocol to lookup
+ * @return integer Port number or NULL if no scheme matches
+ *
+ * @author Philippe Jausions <[email protected]>
+ */
+ function getStandardPort($scheme)
+ {
+ switch (strtolower($scheme)) {
+ case 'http': return 80;
+ case 'https': return 443;
+ case 'ftp': return 21;
+ case 'imap': return 143;
+ case 'imaps': return 993;
+ case 'pop3': return 110;
+ case 'pop3s': return 995;
+ default: return null;
+ }
+ }
+
+ /**
+ * Forces the URL to a particular protocol
+ *
+ * @param string $protocol Protocol to force the URL to
+ * @param integer $port Optional port (standard port is used by default)
+ */
+ function setProtocol($protocol, $port = null)
+ {
+ $this->protocol = $protocol;
+ $this->port = is_null($port) ? $this->getStandardPort() : $port;
+ }
+
+}
+?>
diff --git a/pearlib/OS/Guess.php b/pearlib/OS/Guess.php
new file mode 100644
index 00000000..a85bf360
--- /dev/null
+++ b/pearlib/OS/Guess.php
@@ -0,0 +1,287 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Bakken <[email protected]> |
+// | |
+// +----------------------------------------------------------------------+
+//
+// $Id: Guess.php,v 1.13.4.1 2004/10/19 04:15:56 cellog Exp $
+
+// {{{ uname examples
+
+// php_uname() without args returns the same as 'uname -a', or a PHP-custom
+// string for Windows.
+// PHP versions prior to 4.3 return the uname of the host where PHP was built,
+// as of 4.3 it returns the uname of the host running the PHP code.
+//
+// PC RedHat Linux 7.1:
+// Linux host.example.com 2.4.2-2 #1 Sun Apr 8 20:41:30 EDT 2001 i686 unknown
+//
+// PC Debian Potato:
+// Linux host 2.4.17 #2 SMP Tue Feb 12 15:10:04 CET 2002 i686 unknown
+//
+// PC FreeBSD 3.3:
+// FreeBSD host.example.com 3.3-STABLE FreeBSD 3.3-STABLE #0: Mon Feb 21 00:42:31 CET 2000 [email protected]:/usr/src/sys/compile/CONFIG i386
+//
+// PC FreeBSD 4.3:
+// FreeBSD host.example.com 4.3-RELEASE FreeBSD 4.3-RELEASE #1: Mon Jun 25 11:19:43 EDT 2001 [email protected]:/usr/src/sys/compile/CONFIG i386
+//
+// PC FreeBSD 4.5:
+// FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb 6 23:59:23 CET 2002 [email protected]:/usr/src/sys/compile/CONFIG i386
+//
+// PC FreeBSD 4.5 w/uname from GNU shellutils:
+// FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb i386 unknown
+//
+// HP 9000/712 HP-UX 10:
+// HP-UX iq B.10.10 A 9000/712 2008429113 two-user license
+//
+// HP 9000/712 HP-UX 10 w/uname from GNU shellutils:
+// HP-UX host B.10.10 A 9000/712 unknown
+//
+// IBM RS6000/550 AIX 4.3:
+// AIX host 3 4 000003531C00
+//
+// AIX 4.3 w/uname from GNU shellutils:
+// AIX host 3 4 000003531C00 unknown
+//
+// SGI Onyx IRIX 6.5 w/uname from GNU shellutils:
+// IRIX64 host 6.5 01091820 IP19 mips
+//
+// SGI Onyx IRIX 6.5:
+// IRIX64 host 6.5 01091820 IP19
+//
+// SparcStation 20 Solaris 8 w/uname from GNU shellutils:
+// SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc
+//
+// SparcStation 20 Solaris 8:
+// SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc SUNW,SPARCstation-20
+//
+// Mac OS X (Darwin)
+// Darwin home-eden.local 7.5.0 Darwin Kernel Version 7.5.0: Thu Aug 5 19:26:16 PDT 2004; root:xnu/xnu-517.7.21.obj~3/RELEASE_PPC Power Macintosh
+//
+// Mac OS X early versions
+//
+
+// }}}
+
+/* TODO:
+ * - define endianness, to allow matchSignature("bigend") etc.
+ */
+
+class OS_Guess
+{
+ var $sysname;
+ var $nodename;
+ var $cpu;
+ var $release;
+ var $extra;
+
+ function OS_Guess($uname = null)
+ {
+ list($this->sysname,
+ $this->release,
+ $this->cpu,
+ $this->extra,
+ $this->nodename) = $this->parseSignature($uname);
+ }
+
+ function parseSignature($uname = null)
+ {
+ static $sysmap = array(
+ 'HP-UX' => 'hpux',
+ 'IRIX64' => 'irix',
+ );
+ static $cpumap = array(
+ 'i586' => 'i386',
+ 'i686' => 'i386',
+ 'ppc' => 'powerpc',
+ );
+ if ($uname === null) {
+ $uname = php_uname();
+ }
+ $parts = split('[[:space:]]+', trim($uname));
+ $n = count($parts);
+
+ $release = $machine = $cpu = '';
+ $sysname = $parts[0];
+ $nodename = $parts[1];
+ $cpu = $parts[$n-1];
+ $extra = '';
+ if ($cpu == 'unknown') {
+ $cpu = $parts[$n-2];
+ }
+
+ switch ($sysname) {
+ case 'AIX':
+ $release = "$parts[3].$parts[2]";
+ break;
+ case 'Windows':
+ switch ($parts[1]) {
+ case '95/98':
+ $release = '9x';
+ break;
+ default:
+ $release = $parts[1];
+ break;
+ }
+ $cpu = 'i386';
+ break;
+ case 'Linux':
+ $extra = $this->_detectGlibcVersion();
+ // use only the first two digits from the kernel version
+ $release = ereg_replace('^([[:digit:]]+\.[[:digit:]]+).*', '\1', $parts[2]);
+ break;
+ case 'Mac' :
+ $sysname = 'darwin';
+ $nodename = $parts[2];
+ $release = $parts[3];
+ if ($cpu == 'Macintosh') {
+ if ($parts[$n - 2] == 'Power') {
+ $cpu = 'powerpc';
+ }
+ }
+ break;
+ case 'Darwin' :
+ if ($cpu == 'Macintosh') {
+ if ($parts[$n - 2] == 'Power') {
+ $cpu = 'powerpc';
+ }
+ }
+ $release = ereg_replace('^([[:digit:]]+\.[[:digit:]]+).*', '\1', $parts[2]);
+ break;
+ default:
+ $release = ereg_replace('-.*', '', $parts[2]);
+ break;
+ }
+
+
+ if (isset($sysmap[$sysname])) {
+ $sysname = $sysmap[$sysname];
+ } else {
+ $sysname = strtolower($sysname);
+ }
+ if (isset($cpumap[$cpu])) {
+ $cpu = $cpumap[$cpu];
+ }
+ return array($sysname, $release, $cpu, $extra, $nodename);
+ }
+
+ function _detectGlibcVersion()
+ {
+ // Use glibc's <features.h> header file to
+ // get major and minor version number:
+ include_once "System.php";
+ $tmpfile = System::mktemp("glibctest");
+ $fp = fopen($tmpfile, "w");
+ fwrite($fp, "#include <features.h>\n__GLIBC__ __GLIBC_MINOR__\n");
+ fclose($fp);
+ $cpp = popen("/usr/bin/cpp $tmpfile", "r");
+ $major = $minor = 0;
+ while ($line = fgets($cpp, 1024)) {
+ if ($line{0} == '#' || trim($line) == '') {
+ continue;
+ }
+ if (list($major, $minor) = explode(' ', trim($line))) {
+ break;
+ }
+ }
+ pclose($cpp);
+ unlink($tmpfile);
+ if (!($major && $minor) && is_link('/lib/libc.so.6')) {
+ // Let's try reading the libc.so.6 symlink
+ if (ereg('^libc-([.*])\.so$', basename(readlink('/lib/libc.so.6')), $matches)) {
+ list($major, $minor) = explode('.', $matches);
+ }
+ }
+ if (!($major && $minor)) {
+ return '';
+ }
+ return "glibc{$major}.{$minor}";
+ }
+
+ function getSignature()
+ {
+ if (empty($this->extra)) {
+ return "{$this->sysname}-{$this->release}-{$this->cpu}";
+ }
+ return "{$this->sysname}-{$this->release}-{$this->cpu}-{$this->extra}";
+ }
+
+ function getSysname()
+ {
+ return $this->sysname;
+ }
+
+ function getNodename()
+ {
+ return $this->nodename;
+ }
+
+ function getCpu()
+ {
+ return $this->cpu;
+ }
+
+ function getRelease()
+ {
+ return $this->release;
+ }
+
+ function getExtra()
+ {
+ return $this->extra;
+ }
+
+ function matchSignature($match)
+ {
+ if (is_array($match)) {
+ $fragments = $match;
+ } else {
+ $fragments = explode('-', $match);
+ }
+ $n = count($fragments);
+ $matches = 0;
+ if ($n > 0) {
+ $matches += $this->_matchFragment($fragments[0], $this->sysname);
+ }
+ if ($n > 1) {
+ $matches += $this->_matchFragment($fragments[1], $this->release);
+ }
+ if ($n > 2) {
+ $matches += $this->_matchFragment($fragments[2], $this->cpu);
+ }
+ if ($n > 3) {
+ $matches += $this->_matchFragment($fragments[3], $this->extra);
+ }
+ return ($matches == $n);
+ }
+
+ function _matchFragment($fragment, $value)
+ {
+ if (strcspn($fragment, '*?') < strlen($fragment)) {
+ $reg = '^' . str_replace(array('*', '?', '/'), array('.*', '.', '\\/'), $fragment) . '$';
+ return eregi($reg, $value);
+ }
+ return ($fragment == '*' || !strcasecmp($fragment, $value));
+ }
+
+}
+/*
+ * Local Variables:
+ * indent-tabs-mode: nil
+ * c-basic-offset: 4
+ * End:
+ */
+?>
diff --git a/pearlib/PEAR.php b/pearlib/PEAR.php
new file mode 100644
index 00000000..f3058160
--- /dev/null
+++ b/pearlib/PEAR.php
@@ -0,0 +1,1056 @@
+<?php
+//
+// +--------------------------------------------------------------------+
+// | PEAR, the PHP Extension and Application Repository |
+// +--------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +--------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +--------------------------------------------------------------------+
+// | Authors: Sterling Hughes <[email protected]> |
+// | Stig Bakken <[email protected]> |
+// | Tomas V.V.Cox <[email protected]> |
+// +--------------------------------------------------------------------+
+//
+// $Id: PEAR.php,v 1.82.2.4 2004/10/22 05:53:36 cellog Exp $
+//
+
+define('PEAR_ERROR_RETURN', 1);
+define('PEAR_ERROR_PRINT', 2);
+define('PEAR_ERROR_TRIGGER', 4);
+define('PEAR_ERROR_DIE', 8);
+define('PEAR_ERROR_CALLBACK', 16);
+/**
+ * WARNING: obsolete
+ * @deprecated
+ */
+define('PEAR_ERROR_EXCEPTION', 32);
+define('PEAR_ZE2', (function_exists('version_compare') &&
+ version_compare(zend_version(), "2-dev", "ge")));
+
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ define('OS_WINDOWS', true);
+ define('OS_UNIX', false);
+ define('PEAR_OS', 'Windows');
+} else {
+ define('OS_WINDOWS', false);
+ define('OS_UNIX', true);
+ define('PEAR_OS', 'Unix'); // blatant assumption
+}
+
+// instant backwards compatibility
+if (!defined('PATH_SEPARATOR')) {
+ if (OS_WINDOWS) {
+ define('PATH_SEPARATOR', ';');
+ } else {
+ define('PATH_SEPARATOR', ':');
+ }
+}
+
+$GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN;
+$GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE;
+$GLOBALS['_PEAR_destructor_object_list'] = array();
+$GLOBALS['_PEAR_shutdown_funcs'] = array();
+$GLOBALS['_PEAR_error_handler_stack'] = array();
+
+ini_set('track_errors', true);
+
+/**
+ * Base class for other PEAR classes. Provides rudimentary
+ * emulation of destructors.
+ *
+ * If you want a destructor in your class, inherit PEAR and make a
+ * destructor method called _yourclassname (same name as the
+ * constructor, but with a "_" prefix). Also, in your constructor you
+ * have to call the PEAR constructor: $this->PEAR();.
+ * The destructor method will be called without parameters. Note that
+ * at in some SAPI implementations (such as Apache), any output during
+ * the request shutdown (in which destructors are called) seems to be
+ * discarded. If you need to get any debug information from your
+ * destructor, use error_log(), syslog() or something similar.
+ *
+ * IMPORTANT! To use the emulated destructors you need to create the
+ * objects by reference: $obj =& new PEAR_child;
+ *
+ * @since PHP 4.0.2
+ * @author Stig Bakken <[email protected]>
+ * @see http://pear.php.net/manual/
+ */
+class PEAR
+{
+ // {{{ properties
+
+ /**
+ * Whether to enable internal debug messages.
+ *
+ * @var bool
+ * @access private
+ */
+ var $_debug = false;
+
+ /**
+ * Default error mode for this object.
+ *
+ * @var int
+ * @access private
+ */
+ var $_default_error_mode = null;
+
+ /**
+ * Default error options used for this object when error mode
+ * is PEAR_ERROR_TRIGGER.
+ *
+ * @var int
+ * @access private
+ */
+ var $_default_error_options = null;
+
+ /**
+ * Default error handler (callback) for this object, if error mode is
+ * PEAR_ERROR_CALLBACK.
+ *
+ * @var string
+ * @access private
+ */
+ var $_default_error_handler = '';
+
+ /**
+ * Which class to use for error objects.
+ *
+ * @var string
+ * @access private
+ */
+ var $_error_class = 'PEAR_Error';
+
+ /**
+ * An array of expected errors.
+ *
+ * @var array
+ * @access private
+ */
+ var $_expected_errors = array();
+
+ // }}}
+
+ // {{{ constructor
+
+ /**
+ * Constructor. Registers this object in
+ * $_PEAR_destructor_object_list for destructor emulation if a
+ * destructor object exists.
+ *
+ * @param string $error_class (optional) which class to use for
+ * error objects, defaults to PEAR_Error.
+ * @access public
+ * @return void
+ */
+ function PEAR($error_class = null)
+ {
+ $classname = strtolower(get_class($this));
+ if ($this->_debug) {
+ print "PEAR constructor called, class=$classname\n";
+ }
+ if ($error_class !== null) {
+ $this->_error_class = $error_class;
+ }
+ while ($classname && strcasecmp($classname, "pear")) {
+ $destructor = "_$classname";
+ if (method_exists($this, $destructor)) {
+ global $_PEAR_destructor_object_list;
+ $_PEAR_destructor_object_list[] = &$this;
+ static $registered = false;
+ if (!$registered) {
+ register_shutdown_function("_PEAR_call_destructors");
+ $registered = true;
+ }
+ break;
+ } else {
+ $classname = get_parent_class($classname);
+ }
+ }
+ }
+
+ // }}}
+ // {{{ destructor
+
+ /**
+ * Destructor (the emulated type of...). Does nothing right now,
+ * but is included for forward compatibility, so subclass
+ * destructors should always call it.
+ *
+ * See the note in the class desciption about output from
+ * destructors.
+ *
+ * @access public
+ * @return void
+ */
+ function _PEAR() {
+ if ($this->_debug) {
+ printf("PEAR destructor called, class=%s\n", strtolower(get_class($this)));
+ }
+ }
+
+ // }}}
+ // {{{ getStaticProperty()
+
+ /**
+ * If you have a class that's mostly/entirely static, and you need static
+ * properties, you can use this method to simulate them. Eg. in your method(s)
+ * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar');
+ * You MUST use a reference, or they will not persist!
+ *
+ * @access public
+ * @param string $class The calling classname, to prevent clashes
+ * @param string $var The variable to retrieve.
+ * @return mixed A reference to the variable. If not set it will be
+ * auto initialised to NULL.
+ */
+ function &getStaticProperty($class, $var)
+ {
+ static $properties;
+ return $properties[$class][$var];
+ }
+
+ // }}}
+ // {{{ registerShutdownFunc()
+
+ /**
+ * Use this function to register a shutdown method for static
+ * classes.
+ *
+ * @access public
+ * @param mixed $func The function name (or array of class/method) to call
+ * @param mixed $args The arguments to pass to the function
+ * @return void
+ */
+ function registerShutdownFunc($func, $args = array())
+ {
+ $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
+ }
+
+ // }}}
+ // {{{ isError()
+
+ /**
+ * Tell whether a value is a PEAR error.
+ *
+ * @param mixed $data the value to test
+ * @param int $code if $data is an error object, return true
+ * only if $code is a string and
+ * $obj->getMessage() == $code or
+ * $code is an integer and $obj->getCode() == $code
+ * @access public
+ * @return bool true if parameter is an error
+ */
+ function isError($data, $code = null)
+ {
+ if (is_a($data, 'PEAR_Error')) {
+ if (is_null($code)) {
+ return true;
+ } elseif (is_string($code)) {
+ return $data->getMessage() == $code;
+ } else {
+ return $data->getCode() == $code;
+ }
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ setErrorHandling()
+
+ /**
+ * Sets how errors generated by this object should be handled.
+ * Can be invoked both in objects and statically. If called
+ * statically, setErrorHandling sets the default behaviour for all
+ * PEAR objects. If called in an object, setErrorHandling sets
+ * the default behaviour for that object.
+ *
+ * @param int $mode
+ * One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
+ * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
+ * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION.
+ *
+ * @param mixed $options
+ * When $mode is PEAR_ERROR_TRIGGER, this is the error level (one
+ * of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
+ *
+ * When $mode is PEAR_ERROR_CALLBACK, this parameter is expected
+ * to be the callback function or method. A callback
+ * function is a string with the name of the function, a
+ * callback method is an array of two elements: the element
+ * at index 0 is the object, and the element at index 1 is
+ * the name of the method to call in the object.
+ *
+ * When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is
+ * a printf format string used when printing the error
+ * message.
+ *
+ * @access public
+ * @return void
+ * @see PEAR_ERROR_RETURN
+ * @see PEAR_ERROR_PRINT
+ * @see PEAR_ERROR_TRIGGER
+ * @see PEAR_ERROR_DIE
+ * @see PEAR_ERROR_CALLBACK
+ * @see PEAR_ERROR_EXCEPTION
+ *
+ * @since PHP 4.0.5
+ */
+
+ function setErrorHandling($mode = null, $options = null)
+ {
+ if (isset($this) && is_a($this, 'PEAR')) {
+ $setmode = &$this->_default_error_mode;
+ $setoptions = &$this->_default_error_options;
+ } else {
+ $setmode = &$GLOBALS['_PEAR_default_error_mode'];
+ $setoptions = &$GLOBALS['_PEAR_default_error_options'];
+ }
+
+ switch ($mode) {
+ case PEAR_ERROR_EXCEPTION:
+ case PEAR_ERROR_RETURN:
+ case PEAR_ERROR_PRINT:
+ case PEAR_ERROR_TRIGGER:
+ case PEAR_ERROR_DIE:
+ case null:
+ $setmode = $mode;
+ $setoptions = $options;
+ break;
+
+ case PEAR_ERROR_CALLBACK:
+ $setmode = $mode;
+ // class/object method callback
+ if (is_callable($options)) {
+ $setoptions = $options;
+ } else {
+ trigger_error("invalid error callback", E_USER_WARNING);
+ }
+ break;
+
+ default:
+ trigger_error("invalid error mode", E_USER_WARNING);
+ break;
+ }
+ }
+
+ // }}}
+ // {{{ expectError()
+
+ /**
+ * This method is used to tell which errors you expect to get.
+ * Expected errors are always returned with error mode
+ * PEAR_ERROR_RETURN. Expected error codes are stored in a stack,
+ * and this method pushes a new element onto it. The list of
+ * expected errors are in effect until they are popped off the
+ * stack with the popExpect() method.
+ *
+ * Note that this method can not be called statically
+ *
+ * @param mixed $code a single error code or an array of error codes to expect
+ *
+ * @return int the new depth of the "expected errors" stack
+ * @access public
+ */
+ function expectError($code = '*')
+ {
+ if (is_array($code)) {
+ array_push($this->_expected_errors, $code);
+ } else {
+ array_push($this->_expected_errors, array($code));
+ }
+ return sizeof($this->_expected_errors);
+ }
+
+ // }}}
+ // {{{ popExpect()
+
+ /**
+ * This method pops one element off the expected error codes
+ * stack.
+ *
+ * @return array the list of error codes that were popped
+ */
+ function popExpect()
+ {
+ return array_pop($this->_expected_errors);
+ }
+
+ // }}}
+ // {{{ _checkDelExpect()
+
+ /**
+ * This method checks unsets an error code if available
+ *
+ * @param mixed error code
+ * @return bool true if the error code was unset, false otherwise
+ * @access private
+ * @since PHP 4.3.0
+ */
+ function _checkDelExpect($error_code)
+ {
+ $deleted = false;
+
+ foreach ($this->_expected_errors AS $key => $error_array) {
+ if (in_array($error_code, $error_array)) {
+ unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
+ $deleted = true;
+ }
+
+ // clean up empty arrays
+ if (0 == count($this->_expected_errors[$key])) {
+ unset($this->_expected_errors[$key]);
+ }
+ }
+ return $deleted;
+ }
+
+ // }}}
+ // {{{ delExpect()
+
+ /**
+ * This method deletes all occurences of the specified element from
+ * the expected error codes stack.
+ *
+ * @param mixed $error_code error code that should be deleted
+ * @return mixed list of error codes that were deleted or error
+ * @access public
+ * @since PHP 4.3.0
+ */
+ function delExpect($error_code)
+ {
+ $deleted = false;
+
+ if ((is_array($error_code) && (0 != count($error_code)))) {
+ // $error_code is a non-empty array here;
+ // we walk through it trying to unset all
+ // values
+ foreach($error_code as $key => $error) {
+ if ($this->_checkDelExpect($error)) {
+ $deleted = true;
+ } else {
+ $deleted = false;
+ }
+ }
+ return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
+ } elseif (!empty($error_code)) {
+ // $error_code comes alone, trying to unset it
+ if ($this->_checkDelExpect($error_code)) {
+ return true;
+ } else {
+ return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
+ }
+ } else {
+ // $error_code is empty
+ return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
+ }
+ }
+
+ // }}}
+ // {{{ raiseError()
+
+ /**
+ * This method is a wrapper that returns an instance of the
+ * configured error class with this object's default error
+ * handling applied. If the $mode and $options parameters are not
+ * specified, the object's defaults are used.
+ *
+ * @param mixed $message a text error message or a PEAR error object
+ *
+ * @param int $code a numeric error code (it is up to your class
+ * to define these if you want to use codes)
+ *
+ * @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
+ * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
+ * PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION.
+ *
+ * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter
+ * specifies the PHP-internal error level (one of
+ * E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
+ * If $mode is PEAR_ERROR_CALLBACK, this
+ * parameter specifies the callback function or
+ * method. In other error modes this parameter
+ * is ignored.
+ *
+ * @param string $userinfo If you need to pass along for example debug
+ * information, this parameter is meant for that.
+ *
+ * @param string $error_class The returned error object will be
+ * instantiated from this class, if specified.
+ *
+ * @param bool $skipmsg If true, raiseError will only pass error codes,
+ * the error message parameter will be dropped.
+ *
+ * @access public
+ * @return object a PEAR error object
+ * @see PEAR::setErrorHandling
+ * @since PHP 4.0.5
+ */
+ function raiseError($message = null,
+ $code = null,
+ $mode = null,
+ $options = null,
+ $userinfo = null,
+ $error_class = null,
+ $skipmsg = false)
+ {
+ // The error is yet a PEAR error object
+ if (is_object($message)) {
+ $code = $message->getCode();
+ $userinfo = $message->getUserInfo();
+ $error_class = $message->getType();
+ $message->error_message_prefix = '';
+ $message = $message->getMessage();
+ }
+
+ if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) {
+ if ($exp[0] == "*" ||
+ (is_int(reset($exp)) && in_array($code, $exp)) ||
+ (is_string(reset($exp)) && in_array($message, $exp))) {
+ $mode = PEAR_ERROR_RETURN;
+ }
+ }
+ // No mode given, try global ones
+ if ($mode === null) {
+ // Class error handler
+ if (isset($this) && isset($this->_default_error_mode)) {
+ $mode = $this->_default_error_mode;
+ $options = $this->_default_error_options;
+ // Global error handler
+ } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {
+ $mode = $GLOBALS['_PEAR_default_error_mode'];
+ $options = $GLOBALS['_PEAR_default_error_options'];
+ }
+ }
+
+ if ($error_class !== null) {
+ $ec = $error_class;
+ } elseif (isset($this) && isset($this->_error_class)) {
+ $ec = $this->_error_class;
+ } else {
+ $ec = 'PEAR_Error';
+ }
+ if ($skipmsg) {
+ return new $ec($code, $mode, $options, $userinfo);
+ } else {
+ return new $ec($message, $code, $mode, $options, $userinfo);
+ }
+ }
+
+ // }}}
+ // {{{ throwError()
+
+ /**
+ * Simpler form of raiseError with fewer options. In most cases
+ * message, code and userinfo are enough.
+ *
+ * @param string $message
+ *
+ */
+ function throwError($message = null,
+ $code = null,
+ $userinfo = null)
+ {
+ if (isset($this) && is_a($this, 'PEAR')) {
+ return $this->raiseError($message, $code, null, null, $userinfo);
+ } else {
+ return PEAR::raiseError($message, $code, null, null, $userinfo);
+ }
+ }
+
+ // }}}
+ function staticPushErrorHandling($mode, $options = null)
+ {
+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+ $def_mode = &$GLOBALS['_PEAR_default_error_mode'];
+ $def_options = &$GLOBALS['_PEAR_default_error_options'];
+ $stack[] = array($def_mode, $def_options);
+ switch ($mode) {
+ case PEAR_ERROR_EXCEPTION:
+ case PEAR_ERROR_RETURN:
+ case PEAR_ERROR_PRINT:
+ case PEAR_ERROR_TRIGGER:
+ case PEAR_ERROR_DIE:
+ case null:
+ $def_mode = $mode;
+ $def_options = $options;
+ break;
+
+ case PEAR_ERROR_CALLBACK:
+ $def_mode = $mode;
+ // class/object method callback
+ if (is_callable($options)) {
+ $def_options = $options;
+ } else {
+ trigger_error("invalid error callback", E_USER_WARNING);
+ }
+ break;
+
+ default:
+ trigger_error("invalid error mode", E_USER_WARNING);
+ break;
+ }
+ $stack[] = array($mode, $options);
+ return true;
+ }
+
+ function staticPopErrorHandling()
+ {
+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+ $setmode = &$GLOBALS['_PEAR_default_error_mode'];
+ $setoptions = &$GLOBALS['_PEAR_default_error_options'];
+ array_pop($stack);
+ list($mode, $options) = $stack[sizeof($stack) - 1];
+ array_pop($stack);
+ switch ($mode) {
+ case PEAR_ERROR_EXCEPTION:
+ case PEAR_ERROR_RETURN:
+ case PEAR_ERROR_PRINT:
+ case PEAR_ERROR_TRIGGER:
+ case PEAR_ERROR_DIE:
+ case null:
+ $setmode = $mode;
+ $setoptions = $options;
+ break;
+
+ case PEAR_ERROR_CALLBACK:
+ $setmode = $mode;
+ // class/object method callback
+ if (is_callable($options)) {
+ $setoptions = $options;
+ } else {
+ trigger_error("invalid error callback", E_USER_WARNING);
+ }
+ break;
+
+ default:
+ trigger_error("invalid error mode", E_USER_WARNING);
+ break;
+ }
+ return true;
+ }
+
+ // {{{ pushErrorHandling()
+
+ /**
+ * Push a new error handler on top of the error handler options stack. With this
+ * you can easily override the actual error handler for some code and restore
+ * it later with popErrorHandling.
+ *
+ * @param mixed $mode (same as setErrorHandling)
+ * @param mixed $options (same as setErrorHandling)
+ *
+ * @return bool Always true
+ *
+ * @see PEAR::setErrorHandling
+ */
+ function pushErrorHandling($mode, $options = null)
+ {
+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+ if (isset($this) && is_a($this, 'PEAR')) {
+ $def_mode = &$this->_default_error_mode;
+ $def_options = &$this->_default_error_options;
+ } else {
+ $def_mode = &$GLOBALS['_PEAR_default_error_mode'];
+ $def_options = &$GLOBALS['_PEAR_default_error_options'];
+ }
+ $stack[] = array($def_mode, $def_options);
+
+ if (isset($this) && is_a($this, 'PEAR')) {
+ $this->setErrorHandling($mode, $options);
+ } else {
+ PEAR::setErrorHandling($mode, $options);
+ }
+ $stack[] = array($mode, $options);
+ return true;
+ }
+
+ // }}}
+ // {{{ popErrorHandling()
+
+ /**
+ * Pop the last error handler used
+ *
+ * @return bool Always true
+ *
+ * @see PEAR::pushErrorHandling
+ */
+ function popErrorHandling()
+ {
+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+ array_pop($stack);
+ list($mode, $options) = $stack[sizeof($stack) - 1];
+ array_pop($stack);
+ if (isset($this) && is_a($this, 'PEAR')) {
+ $this->setErrorHandling($mode, $options);
+ } else {
+ PEAR::setErrorHandling($mode, $options);
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ loadExtension()
+
+ /**
+ * OS independant PHP extension load. Remember to take care
+ * on the correct extension name for case sensitive OSes.
+ *
+ * @param string $ext The extension name
+ * @return bool Success or not on the dl() call
+ */
+ function loadExtension($ext)
+ {
+ if (!extension_loaded($ext)) {
+ // if either returns true dl() will produce a FATAL error, stop that
+ if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) {
+ return false;
+ }
+ if (OS_WINDOWS) {
+ $suffix = '.dll';
+ } elseif (PHP_OS == 'HP-UX') {
+ $suffix = '.sl';
+ } elseif (PHP_OS == 'AIX') {
+ $suffix = '.a';
+ } elseif (PHP_OS == 'OSX') {
+ $suffix = '.bundle';
+ } else {
+ $suffix = '.so';
+ }
+ return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
+ }
+ return true;
+ }
+
+ // }}}
+}
+
+// {{{ _PEAR_call_destructors()
+
+function _PEAR_call_destructors()
+{
+ global $_PEAR_destructor_object_list;
+ if (is_array($_PEAR_destructor_object_list) &&
+ sizeof($_PEAR_destructor_object_list))
+ {
+ reset($_PEAR_destructor_object_list);
+ if (@PEAR::getStaticProperty('PEAR', 'destructlifo')) {
+ $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list);
+ }
+ while (list($k, $objref) = each($_PEAR_destructor_object_list)) {
+ $classname = get_class($objref);
+ while ($classname) {
+ $destructor = "_$classname";
+ if (method_exists($objref, $destructor)) {
+ $objref->$destructor();
+ break;
+ } else {
+ $classname = get_parent_class($classname);
+ }
+ }
+ }
+ // Empty the object list to ensure that destructors are
+ // not called more than once.
+ $_PEAR_destructor_object_list = array();
+ }
+
+ // Now call the shutdown functions
+ if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) {
+ foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
+ call_user_func_array($value[0], $value[1]);
+ }
+ }
+}
+
+// }}}
+
+class PEAR_Error
+{
+ // {{{ properties
+
+ var $error_message_prefix = '';
+ var $mode = PEAR_ERROR_RETURN;
+ var $level = E_USER_NOTICE;
+ var $code = -1;
+ var $message = '';
+ var $userinfo = '';
+ var $backtrace = null;
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * PEAR_Error constructor
+ *
+ * @param string $message message
+ *
+ * @param int $code (optional) error code
+ *
+ * @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN,
+ * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER,
+ * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION
+ *
+ * @param mixed $options (optional) error level, _OR_ in the case of
+ * PEAR_ERROR_CALLBACK, the callback function or object/method
+ * tuple.
+ *
+ * @param string $userinfo (optional) additional user/debug info
+ *
+ * @access public
+ *
+ */
+ function PEAR_Error($message = 'unknown error', $code = null,
+ $mode = null, $options = null, $userinfo = null)
+ {
+ if ($mode === null) {
+ $mode = PEAR_ERROR_RETURN;
+ }
+ $this->message = $message;
+ $this->code = $code;
+ $this->mode = $mode;
+ $this->userinfo = $userinfo;
+ if (function_exists("debug_backtrace")) {
+ if (@!PEAR::getStaticProperty('PEAR_Error', 'skiptrace')) {
+ $this->backtrace = debug_backtrace();
+ }
+ }
+ if ($mode & PEAR_ERROR_CALLBACK) {
+ $this->level = E_USER_NOTICE;
+ $this->callback = $options;
+ } else {
+ if ($options === null) {
+ $options = E_USER_NOTICE;
+ }
+ $this->level = $options;
+ $this->callback = null;
+ }
+ if ($this->mode & PEAR_ERROR_PRINT) {
+ if (is_null($options) || is_int($options)) {
+ $format = "%s";
+ } else {
+ $format = $options;
+ }
+ printf($format, $this->getMessage());
+ }
+ if ($this->mode & PEAR_ERROR_TRIGGER) {
+ trigger_error($this->getMessage(), $this->level);
+ }
+ if ($this->mode & PEAR_ERROR_DIE) {
+ $msg = $this->getMessage();
+ if (is_null($options) || is_int($options)) {
+ $format = "%s";
+ if (substr($msg, -1) != "\n") {
+ $msg .= "\n";
+ }
+ } else {
+ $format = $options;
+ }
+ die(sprintf($format, $msg));
+ }
+ if ($this->mode & PEAR_ERROR_CALLBACK) {
+ if (is_callable($this->callback)) {
+ call_user_func($this->callback, $this);
+ }
+ }
+ if ($this->mode & PEAR_ERROR_EXCEPTION) {
+ trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_ErrorStack for exceptions", E_USER_WARNING);
+ eval('$e = new Exception($this->message, $this->code);$e->PEAR_Error = $this;throw($e);');
+ }
+ }
+
+ // }}}
+ // {{{ getMode()
+
+ /**
+ * Get the error mode from an error object.
+ *
+ * @return int error mode
+ * @access public
+ */
+ function getMode() {
+ return $this->mode;
+ }
+
+ // }}}
+ // {{{ getCallback()
+
+ /**
+ * Get the callback function/method from an error object.
+ *
+ * @return mixed callback function or object/method array
+ * @access public
+ */
+ function getCallback() {
+ return $this->callback;
+ }
+
+ // }}}
+ // {{{ getMessage()
+
+
+ /**
+ * Get the error message from an error object.
+ *
+ * @return string full error message
+ * @access public
+ */
+ function getMessage()
+ {
+ return ($this->error_message_prefix . $this->message);
+ }
+
+
+ // }}}
+ // {{{ getCode()
+
+ /**
+ * Get error code from an error object
+ *
+ * @return int error code
+ * @access public
+ */
+ function getCode()
+ {
+ return $this->code;
+ }
+
+ // }}}
+ // {{{ getType()
+
+ /**
+ * Get the name of this error/exception.
+ *
+ * @return string error/exception name (type)
+ * @access public
+ */
+ function getType()
+ {
+ return get_class($this);
+ }
+
+ // }}}
+ // {{{ getUserInfo()
+
+ /**
+ * Get additional user-supplied information.
+ *
+ * @return string user-supplied information
+ * @access public
+ */
+ function getUserInfo()
+ {
+ return $this->userinfo;
+ }
+
+ // }}}
+ // {{{ getDebugInfo()
+
+ /**
+ * Get additional debug information supplied by the application.
+ *
+ * @return string debug information
+ * @access public
+ */
+ function getDebugInfo()
+ {
+ return $this->getUserInfo();
+ }
+
+ // }}}
+ // {{{ getBacktrace()
+
+ /**
+ * Get the call backtrace from where the error was generated.
+ * Supported with PHP 4.3.0 or newer.
+ *
+ * @param int $frame (optional) what frame to fetch
+ * @return array Backtrace, or NULL if not available.
+ * @access public
+ */
+ function getBacktrace($frame = null)
+ {
+ if ($frame === null) {
+ return $this->backtrace;
+ }
+ return $this->backtrace[$frame];
+ }
+
+ // }}}
+ // {{{ addUserInfo()
+
+ function addUserInfo($info)
+ {
+ if (empty($this->userinfo)) {
+ $this->userinfo = $info;
+ } else {
+ $this->userinfo .= " ** $info";
+ }
+ }
+
+ // }}}
+ // {{{ toString()
+
+ /**
+ * Make a string representation of this object.
+ *
+ * @return string a string with an object summary
+ * @access public
+ */
+ function toString() {
+ $modes = array();
+ $levels = array(E_USER_NOTICE => 'notice',
+ E_USER_WARNING => 'warning',
+ E_USER_ERROR => 'error');
+ if ($this->mode & PEAR_ERROR_CALLBACK) {
+ if (is_array($this->callback)) {
+ $callback = (is_object($this->callback[0]) ?
+ strtolower(get_class($this->callback[0])) :
+ $this->callback[0]) . '::' .
+ $this->callback[1];
+ } else {
+ $callback = $this->callback;
+ }
+ return sprintf('[%s: message="%s" code=%d mode=callback '.
+ 'callback=%s prefix="%s" info="%s"]',
+ strtolower(get_class($this)), $this->message, $this->code,
+ $callback, $this->error_message_prefix,
+ $this->userinfo);
+ }
+ if ($this->mode & PEAR_ERROR_PRINT) {
+ $modes[] = 'print';
+ }
+ if ($this->mode & PEAR_ERROR_TRIGGER) {
+ $modes[] = 'trigger';
+ }
+ if ($this->mode & PEAR_ERROR_DIE) {
+ $modes[] = 'die';
+ }
+ if ($this->mode & PEAR_ERROR_RETURN) {
+ $modes[] = 'return';
+ }
+ return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.
+ 'prefix="%s" info="%s"]',
+ strtolower(get_class($this)), $this->message, $this->code,
+ implode("|", $modes), $levels[$this->level],
+ $this->error_message_prefix,
+ $this->userinfo);
+ }
+
+ // }}}
+}
+
+/*
+ * Local Variables:
+ * mode: php
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+?>
diff --git a/pearlib/PEAR/Autoloader.php b/pearlib/PEAR/Autoloader.php
new file mode 100644
index 00000000..de0278d6
--- /dev/null
+++ b/pearlib/PEAR/Autoloader.php
@@ -0,0 +1,208 @@
+<?php
+// /* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <[email protected]> |
+// | |
+// +----------------------------------------------------------------------+
+//
+// $Id: Autoloader.php,v 1.11 2004/02/27 02:21:29 cellog Exp $
+
+if (!extension_loaded("overload")) {
+ // die hard without ext/overload
+ die("Rebuild PHP with the `overload' extension to use PEAR_Autoloader");
+}
+
+require_once "PEAR.php";
+
+/**
+ * This class is for objects where you want to separate the code for
+ * some methods into separate classes. This is useful if you have a
+ * class with not-frequently-used methods that contain lots of code
+ * that you would like to avoid always parsing.
+ *
+ * The PEAR_Autoloader class provides autoloading and aggregation.
+ * The autoloading lets you set up in which classes the separated
+ * methods are found. Aggregation is the technique used to import new
+ * methods, an instance of each class providing separated methods is
+ * stored and called every time the aggregated method is called.
+ *
+ * @author Stig S�ther Bakken <[email protected]>
+ */
+class PEAR_Autoloader extends PEAR
+{
+ // {{{ properties
+
+ /**
+ * Map of methods and classes where they are defined
+ *
+ * @var array
+ *
+ * @access private
+ */
+ var $_autoload_map = array();
+
+ /**
+ * Map of methods and aggregate objects
+ *
+ * @var array
+ *
+ * @access private
+ */
+ var $_method_map = array();
+
+ // }}}
+ // {{{ addAutoload()
+
+ /**
+ * Add one or more autoload entries.
+ *
+ * @param string $method which method to autoload
+ *
+ * @param string $classname (optional) which class to find the method in.
+ * If the $method parameter is an array, this
+ * parameter may be omitted (and will be ignored
+ * if not), and the $method parameter will be
+ * treated as an associative array with method
+ * names as keys and class names as values.
+ *
+ * @return void
+ *
+ * @access public
+ */
+ function addAutoload($method, $classname = null)
+ {
+ if (is_array($method)) {
+ array_walk($method, create_function('$a,&$b', '$b = strtolower($b);'));
+ $this->_autoload_map = array_merge($this->_autoload_map, $method);
+ } else {
+ $this->_autoload_map[strtolower($method)] = $classname;
+ }
+ }
+
+ // }}}
+ // {{{ removeAutoload()
+
+ /**
+ * Remove an autoload entry.
+ *
+ * @param string $method which method to remove the autoload entry for
+ *
+ * @return bool TRUE if an entry was removed, FALSE if not
+ *
+ * @access public
+ */
+ function removeAutoload($method)
+ {
+ $method = strtolower($method);
+ $ok = isset($this->_autoload_map[$method]);
+ unset($this->_autoload_map[$method]);
+ return $ok;
+ }
+
+ // }}}
+ // {{{ addAggregateObject()
+
+ /**
+ * Add an aggregate object to this object. If the specified class
+ * is not defined, loading it will be attempted following PEAR's
+ * file naming scheme. All the methods in the class will be
+ * aggregated, except private ones (name starting with an
+ * underscore) and constructors.
+ *
+ * @param string $classname what class to instantiate for the object.
+ *
+ * @return void
+ *
+ * @access public
+ */
+ function addAggregateObject($classname)
+ {
+ $classname = strtolower($classname);
+ if (!class_exists($classname)) {
+ $include_file = preg_replace('/[^a-z0-9]/i', '_', $classname);
+ include_once $include_file;
+ }
+ $obj =& new $classname;
+ $methods = get_class_methods($classname);
+ foreach ($methods as $method) {
+ // don't import priviate methods and constructors
+ if ($method{0} != '_' && $method != $classname) {
+ $this->_method_map[$method] = $obj;
+ }
+ }
+ }
+
+ // }}}
+ // {{{ removeAggregateObject()
+
+ /**
+ * Remove an aggregate object.
+ *
+ * @param string $classname the class of the object to remove
+ *
+ * @return bool TRUE if an object was removed, FALSE if not
+ *
+ * @access public
+ */
+ function removeAggregateObject($classname)
+ {
+ $ok = false;
+ $classname = strtolower($classname);
+ reset($this->_method_map);
+ while (list($method, $obj) = each($this->_method_map)) {
+ if (is_a($obj, $classname)) {
+ unset($this->_method_map[$method]);
+ $ok = true;
+ }
+ }
+ return $ok;
+ }
+
+ // }}}
+ // {{{ __call()
+
+ /**
+ * Overloaded object call handler, called each time an
+ * undefined/aggregated method is invoked. This method repeats
+ * the call in the right aggregate object and passes on the return
+ * value.
+ *
+ * @param string $method which method that was called
+ *
+ * @param string $args An array of the parameters passed in the
+ * original call
+ *
+ * @return mixed The return value from the aggregated method, or a PEAR
+ * error if the called method was unknown.
+ */
+ function __call($method, $args, &$retval)
+ {
+ $method = strtolower($method);
+ if (empty($this->_method_map[$method]) && isset($this->_autoload_map[$method])) {
+ $this->addAggregateObject($this->_autoload_map[$method]);
+ }
+ if (isset($this->_method_map[$method])) {
+ $retval = call_user_func_array(array($this->_method_map[$method], $method), $args);
+ return true;
+ }
+ return false;
+ }
+
+ // }}}
+}
+
+overload("PEAR_Autoloader");
+
+?>
diff --git a/pearlib/PEAR/Builder.php b/pearlib/PEAR/Builder.php
new file mode 100644
index 00000000..8d6b0836
--- /dev/null
+++ b/pearlib/PEAR/Builder.php
@@ -0,0 +1,392 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stig S�ther Bakken <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Builder.php,v 1.16.2.1 2004/10/19 04:27:53 cellog Exp $
+
+require_once 'PEAR/Common.php';
+
+/**
+ * Class to handle building (compiling) extensions.
+ *
+ * @author Stig S�ther Bakken <[email protected]>
+ */
+class PEAR_Builder extends PEAR_Common
+{
+ // {{{ properties
+
+ var $php_api_version = 0;
+ var $zend_module_api_no = 0;
+ var $zend_extension_api_no = 0;
+
+ var $extensions_built = array();
+
+ var $current_callback = null;
+
+ // used for msdev builds
+ var $_lastline = null;
+ var $_firstline = null;
+ // }}}
+ // {{{ constructor
+
+ /**
+ * PEAR_Builder constructor.
+ *
+ * @param object $ui user interface object (instance of PEAR_Frontend_*)
+ *
+ * @access public
+ */
+ function PEAR_Builder(&$ui)
+ {
+ parent::PEAR_Common();
+ $this->setFrontendObject($ui);
+ }
+
+ // }}}
+
+ // {{{ _build_win32()
+
+ /**
+ * Build an extension from source on windows.
+ * requires msdev
+ */
+ function _build_win32($descfile, $callback = null)
+ {
+ if (PEAR::isError($info = $this->infoFromDescriptionFile($descfile))) {
+ return $info;
+ }
+ $dir = dirname($descfile);
+ $old_cwd = getcwd();
+
+ if (!@chdir($dir)) {
+ return $this->raiseError("could not chdir to $dir");
+ }
+ $this->log(2, "building in $dir");
+
+ $dsp = $info['package'].'.dsp';
+ if (!@is_file("$dir/$dsp")) {
+ return $this->raiseError("The DSP $dsp does not exist.");
+ }
+ // XXX TODO: make release build type configurable
+ $command = 'msdev '.$dsp.' /MAKE "'.$info['package']. ' - Release"';
+
+ $this->current_callback = $callback;
+ $err = $this->_runCommand($command, array(&$this, 'msdevCallback'));
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+
+ // figure out the build platform and type
+ $platform = 'Win32';
+ $buildtype = 'Release';
+ if (preg_match('/.*?'.$info['package'].'\s-\s(\w+)\s(.*?)-+/i',$this->_firstline,$matches)) {
+ $platform = $matches[1];
+ $buildtype = $matches[2];
+ }
+
+ if (preg_match('/(.*)?\s-\s(\d+).*?(\d+)/',$this->_lastline,$matches)) {
+ if ($matches[2]) {
+ // there were errors in the build
+ return $this->raiseError("There were errors during compilation.");
+ }
+ $out = $matches[1];
+ } else {
+ return $this->raiseError("Did not understand the completion status returned from msdev.exe.");
+ }
+
+ // msdev doesn't tell us the output directory :/
+ // open the dsp, find /out and use that directory
+ $dsptext = join(file($dsp),'');
+
+ // this regex depends on the build platform and type having been
+ // correctly identified above.
+ $regex ='/.*?!IF\s+"\$\(CFG\)"\s+==\s+("'.
+ $info['package'].'\s-\s'.
+ $platform.'\s'.
+ $buildtype.'").*?'.
+ '\/out:"(.*?)"/is';
+
+ if ($dsptext && preg_match($regex,$dsptext,$matches)) {
+ // what we get back is a relative path to the output file itself.
+ $outfile = realpath($matches[2]);
+ } else {
+ return $this->raiseError("Could not retrieve output information from $dsp.");
+ }
+ if (@copy($outfile, "$dir/$out")) {
+ $outfile = "$dir/$out";
+ }
+
+ $built_files[] = array(
+ 'file' => "$outfile",
+ 'php_api' => $this->php_api_version,
+ 'zend_mod_api' => $this->zend_module_api_no,
+ 'zend_ext_api' => $this->zend_extension_api_no,
+ );
+
+ return $built_files;
+ }
+ // }}}
+
+ // {{{ msdevCallback()
+ function msdevCallback($what, $data)
+ {
+ if (!$this->_firstline)
+ $this->_firstline = $data;
+ $this->_lastline = $data;
+ }
+ // }}}
+
+ // {{{ build()
+
+ /**
+ * Build an extension from source. Runs "phpize" in the source
+ * directory, but compiles in a temporary directory
+ * (/var/tmp/pear-build-USER/PACKAGE-VERSION).
+ *
+ * @param string $descfile path to XML package description file
+ *
+ * @param mixed $callback callback function used to report output,
+ * see PEAR_Builder::_runCommand for details
+ *
+ * @return array an array of associative arrays with built files,
+ * format:
+ * array( array( 'file' => '/path/to/ext.so',
+ * 'php_api' => YYYYMMDD,
+ * 'zend_mod_api' => YYYYMMDD,
+ * 'zend_ext_api' => YYYYMMDD ),
+ * ... )
+ *
+ * @access public
+ *
+ * @see PEAR_Builder::_runCommand
+ * @see PEAR_Common::infoFromDescriptionFile
+ */
+ function build($descfile, $callback = null)
+ {
+ if (PEAR_OS == "Windows") {
+ return $this->_build_win32($descfile,$callback);
+ }
+ if (PEAR_OS != 'Unix') {
+ return $this->raiseError("building extensions not supported on this platform");
+ }
+ if (PEAR::isError($info = $this->infoFromDescriptionFile($descfile))) {
+ return $info;
+ }
+ $dir = dirname($descfile);
+ $old_cwd = getcwd();
+ if (!@chdir($dir)) {
+ return $this->raiseError("could not chdir to $dir");
+ }
+ $vdir = "$info[package]-$info[version]";
+ if (is_dir($vdir)) {
+ chdir($vdir);
+ }
+ $dir = getcwd();
+ $this->log(2, "building in $dir");
+ $this->current_callback = $callback;
+ putenv('PATH=' . $this->config->get('bin_dir') . ':' . getenv('PATH'));
+ $err = $this->_runCommand("phpize", array(&$this, 'phpizeCallback'));
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+ if (!$err) {
+ return $this->raiseError("`phpize' failed");
+ }
+
+ // {{{ start of interactive part
+ $configure_command = "$dir/configure";
+ if (isset($info['configure_options'])) {
+ foreach ($info['configure_options'] as $o) {
+ list($r) = $this->ui->userDialog('build',
+ array($o['prompt']),
+ array('text'),
+ array(@$o['default']));
+ if (substr($o['name'], 0, 5) == 'with-' &&
+ ($r == 'yes' || $r == 'autodetect')) {
+ $configure_command .= " --$o[name]";
+ } else {
+ $configure_command .= " --$o[name]=".trim($r);
+ }
+ }
+ }
+ // }}} end of interactive part
+
+ // FIXME make configurable
+ if(!$user=getenv('USER')){
+ $user='defaultuser';
+ }
+ $build_basedir = "/var/tmp/pear-build-$user";
+ $build_dir = "$build_basedir/$info[package]-$info[version]";
+ $this->log(1, "building in $build_dir");
+ if (is_dir($build_dir)) {
+ System::rm("-rf $build_dir");
+ }
+ if (!System::mkDir("-p $build_dir")) {
+ return $this->raiseError("could not create build dir: $build_dir");
+ }
+ $this->addTempFile($build_dir);
+ if (getenv('MAKE')) {
+ $make_command = getenv('MAKE');
+ } else {
+ $make_command = 'make';
+ }
+ $to_run = array(
+ $configure_command,
+ $make_command,
+ );
+ if (!@chdir($build_dir)) {
+ return $this->raiseError("could not chdir to $build_dir");
+ }
+ foreach ($to_run as $cmd) {
+ $err = $this->_runCommand($cmd, $callback);
+ if (PEAR::isError($err)) {
+ chdir($old_cwd);
+ return $err;
+ }
+ if (!$err) {
+ chdir($old_cwd);
+ return $this->raiseError("`$cmd' failed");
+ }
+ }
+ if (!($dp = opendir("modules"))) {
+ chdir($old_cwd);
+ return $this->raiseError("no `modules' directory found");
+ }
+ $built_files = array();
+ while ($ent = readdir($dp)) {
+ if ($ent{0} == '.' || substr($ent, -3) == '.la') {
+ continue;
+ }
+ // harvest!
+ if (@copy("modules/$ent", "$dir/$ent")) {
+ $built_files[] = array(
+ 'file' => "$dir/$ent",
+ 'php_api' => $this->php_api_version,
+ 'zend_mod_api' => $this->zend_module_api_no,
+ 'zend_ext_api' => $this->zend_extension_api_no,
+ );
+
+ $this->log(1, "$ent copied to $dir/$ent");
+ } else {
+ chdir($old_cwd);
+ return $this->raiseError("failed copying $ent to $dir");
+ }
+ }
+ closedir($dp);
+ chdir($old_cwd);
+ return $built_files;
+ }
+
+ // }}}
+ // {{{ phpizeCallback()
+
+ /**
+ * Message callback function used when running the "phpize"
+ * program. Extracts the API numbers used. Ignores other message
+ * types than "cmdoutput".
+ *
+ * @param string $what the type of message
+ * @param mixed $data the message
+ *
+ * @return void
+ *
+ * @access public
+ */
+ function phpizeCallback($what, $data)
+ {
+ if ($what != 'cmdoutput') {
+ return;
+ }
+ $this->log(1, rtrim($data));
+ if (preg_match('/You should update your .aclocal.m4/', $data)) {
+ return;
+ }
+ $matches = array();
+ if (preg_match('/^\s+(\S[^:]+):\s+(\d{8})/', $data, $matches)) {
+ $member = preg_replace('/[^a-z]/', '_', strtolower($matches[1]));
+ $apino = (int)$matches[2];
+ if (isset($this->$member)) {
+ $this->$member = $apino;
+ //$msg = sprintf("%-22s : %d", $matches[1], $apino);
+ //$this->log(1, $msg);
+ }
+ }
+ }
+
+ // }}}
+ // {{{ _runCommand()
+
+ /**
+ * Run an external command, using a message callback to report
+ * output. The command will be run through popen and output is
+ * reported for every line with a "cmdoutput" message with the
+ * line string, including newlines, as payload.
+ *
+ * @param string $command the command to run
+ *
+ * @param mixed $callback (optional) function to use as message
+ * callback
+ *
+ * @return bool whether the command was successful (exit code 0
+ * means success, any other means failure)
+ *
+ * @access private
+ */
+ function _runCommand($command, $callback = null)
+ {
+ $this->log(1, "running: $command");
+ $pp = @popen("$command 2>&1", "r");
+ if (!$pp) {
+ return $this->raiseError("failed to run `$command'");
+ }
+ if ($callback && $callback[0]->debug == 1) {
+ $olddbg = $callback[0]->debug;
+ $callback[0]->debug = 2;
+ }
+
+ while ($line = fgets($pp, 1024)) {
+ if ($callback) {
+ call_user_func($callback, 'cmdoutput', $line);
+ } else {
+ $this->log(2, rtrim($line));
+ }
+ }
+ if ($callback && isset($olddbg)) {
+ $callback[0]->debug = $olddbg;
+ }
+ $exitcode = @pclose($pp);
+ return ($exitcode == 0);
+ }
+
+ // }}}
+ // {{{ log()
+
+ function log($level, $msg)
+ {
+ if ($this->current_callback) {
+ if ($this->debug >= $level) {
+ call_user_func($this->current_callback, 'output', $msg);
+ }
+ return;
+ }
+ return PEAR_Common::log($level, $msg);
+ }
+
+ // }}}
+}
+
+?>
diff --git a/pearlib/PEAR/Command.php b/pearlib/PEAR/Command.php
new file mode 100644
index 00000000..2ea68743
--- /dev/null
+++ b/pearlib/PEAR/Command.php
@@ -0,0 +1,398 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Command.php,v 1.24 2004/05/16 15:43:30 pajoye Exp $
+
+
+require_once "PEAR.php";
+
+/**
+ * List of commands and what classes they are implemented in.
+ * @var array command => implementing class
+ */
+$GLOBALS['_PEAR_Command_commandlist'] = array();
+
+/**
+ * List of shortcuts to common commands.
+ * @var array shortcut => command
+ */
+$GLOBALS['_PEAR_Command_shortcuts'] = array();
+
+/**
+ * Array of command objects
+ * @var array class => object
+ */
+$GLOBALS['_PEAR_Command_objects'] = array();
+
+/**
+ * Which user interface class is being used.
+ * @var string class name
+ */
+$GLOBALS['_PEAR_Command_uiclass'] = 'PEAR_Frontend_CLI';
+
+/**
+ * Instance of $_PEAR_Command_uiclass.
+ * @var object
+ */
+$GLOBALS['_PEAR_Command_uiobject'] = null;
+
+/**
+ * PEAR command class, a simple factory class for administrative
+ * commands.
+ *
+ * How to implement command classes:
+ *
+ * - The class must be called PEAR_Command_Nnn, installed in the
+ * "PEAR/Common" subdir, with a method called getCommands() that
+ * returns an array of the commands implemented by the class (see
+ * PEAR/Command/Install.php for an example).
+ *
+ * - The class must implement a run() function that is called with three
+ * params:
+ *
+ * (string) command name
+ * (array) assoc array with options, freely defined by each
+ * command, for example:
+ * array('force' => true)
+ * (array) list of the other parameters
+ *
+ * The run() function returns a PEAR_CommandResponse object. Use
+ * these methods to get information:
+ *
+ * int getStatus() Returns PEAR_COMMAND_(SUCCESS|FAILURE|PARTIAL)
+ * *_PARTIAL means that you need to issue at least
+ * one more command to complete the operation
+ * (used for example for validation steps).
+ *
+ * string getMessage() Returns a message for the user. Remember,
+ * no HTML or other interface-specific markup.
+ *
+ * If something unexpected happens, run() returns a PEAR error.
+ *
+ * - DON'T OUTPUT ANYTHING! Return text for output instead.
+ *
+ * - DON'T USE HTML! The text you return will be used from both Gtk,
+ * web and command-line interfaces, so for now, keep everything to
+ * plain text.
+ *
+ * - DON'T USE EXIT OR DIE! Always use pear errors. From static
+ * classes do PEAR::raiseError(), from other classes do
+ * $this->raiseError().
+ */
+class PEAR_Command
+{
+ // {{{ factory()
+
+ /**
+ * Get the right object for executing a command.
+ *
+ * @param string $command The name of the command
+ * @param object $config Instance of PEAR_Config object
+ *
+ * @return object the command object or a PEAR error
+ *
+ * @access public
+ * @static
+ */
+ function factory($command, &$config)
+ {
+ if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
+ PEAR_Command::registerCommands();
+ }
+ if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) {
+ $command = $GLOBALS['_PEAR_Command_shortcuts'][$command];
+ }
+ if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
+ return PEAR::raiseError("unknown command `$command'");
+ }
+ $class = $GLOBALS['_PEAR_Command_commandlist'][$command];
+ if (!class_exists($class)) {
+ return PEAR::raiseError("unknown command `$command'");
+ }
+ $ui =& PEAR_Command::getFrontendObject();
+ $obj = &new $class($ui, $config);
+ return $obj;
+ }
+
+ // }}}
+ // {{{ & getFrontendObject()
+
+ /**
+ * Get instance of frontend object.
+ *
+ * @return object
+ * @static
+ */
+ function &getFrontendObject()
+ {
+ if (empty($GLOBALS['_PEAR_Command_uiobject'])) {
+ $GLOBALS['_PEAR_Command_uiobject'] = &new $GLOBALS['_PEAR_Command_uiclass'];
+ }
+ return $GLOBALS['_PEAR_Command_uiobject'];
+ }
+
+ // }}}
+ // {{{ & setFrontendClass()
+
+ /**
+ * Load current frontend class.
+ *
+ * @param string $uiclass Name of class implementing the frontend
+ *
+ * @return object the frontend object, or a PEAR error
+ * @static
+ */
+ function &setFrontendClass($uiclass)
+ {
+ if (is_object($GLOBALS['_PEAR_Command_uiobject']) &&
+ is_a($GLOBALS['_PEAR_Command_uiobject'], $uiclass)) {
+ return $GLOBALS['_PEAR_Command_uiobject'];
+ }
+ if (!class_exists($uiclass)) {
+ $file = str_replace('_', '/', $uiclass) . '.php';
+ if (PEAR_Command::isIncludeable($file)) {
+ include_once $file;
+ }
+ }
+ if (class_exists($uiclass)) {
+ $obj = &new $uiclass;
+ // quick test to see if this class implements a few of the most
+ // important frontend methods
+ if (method_exists($obj, 'userConfirm')) {
+ $GLOBALS['_PEAR_Command_uiobject'] = &$obj;
+ $GLOBALS['_PEAR_Command_uiclass'] = $uiclass;
+ return $obj;
+ } else {
+ $err = PEAR::raiseError("not a frontend class: $uiclass");
+ return $err;
+ }
+ }
+ $err = PEAR::raiseError("no such class: $uiclass");
+ return $err;
+ }
+
+ // }}}
+ // {{{ setFrontendType()
+
+ // }}}
+ // {{{ isIncludeable()
+
+ /**
+ * @param string $path relative or absolute include path
+ * @return boolean
+ * @static
+ */
+ function isIncludeable($path)
+ {
+ if (file_exists($path) && is_readable($path)) {
+ return true;
+ }
+ $ipath = explode(PATH_SEPARATOR, ini_get('include_path'));
+ foreach ($ipath as $include) {
+ $test = realpath($include . DIRECTORY_SEPARATOR . $path);
+ if (file_exists($test) && is_readable($test)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Set current frontend.
+ *
+ * @param string $uitype Name of the frontend type (for example "CLI")
+ *
+ * @return object the frontend object, or a PEAR error
+ * @static
+ */
+ function setFrontendType($uitype)
+ {
+ $uiclass = 'PEAR_Frontend_' . $uitype;
+ return PEAR_Command::setFrontendClass($uiclass);
+ }
+
+ // }}}
+ // {{{ registerCommands()
+
+ /**
+ * Scan through the Command directory looking for classes
+ * and see what commands they implement.
+ *
+ * @param bool (optional) if FALSE (default), the new list of
+ * commands should replace the current one. If TRUE,
+ * new entries will be merged with old.
+ *
+ * @param string (optional) where (what directory) to look for
+ * classes, defaults to the Command subdirectory of
+ * the directory from where this file (__FILE__) is
+ * included.
+ *
+ * @return bool TRUE on success, a PEAR error on failure
+ *
+ * @access public
+ * @static
+ */
+ function registerCommands($merge = false, $dir = null)
+ {
+ if ($dir === null) {
+ $dir = dirname(__FILE__) . '/Command';
+ }
+ $dp = @opendir($dir);
+ if (empty($dp)) {
+ return PEAR::raiseError("registerCommands: opendir($dir) failed");
+ }
+ if (!$merge) {
+ $GLOBALS['_PEAR_Command_commandlist'] = array();
+ }
+ while ($entry = readdir($dp)) {
+ if ($entry{0} == '.' || substr($entry, -4) != '.php' || $entry == 'Common.php') {
+ continue;
+ }
+ $class = "PEAR_Command_".substr($entry, 0, -4);
+ $file = "$dir/$entry";
+ include_once $file;
+ // List of commands
+ if (empty($GLOBALS['_PEAR_Command_objects'][$class])) {
+ $GLOBALS['_PEAR_Command_objects'][$class] = &new $class($ui, $config);
+ }
+ $implements = $GLOBALS['_PEAR_Command_objects'][$class]->getCommands();
+ foreach ($implements as $command => $desc) {
+ $GLOBALS['_PEAR_Command_commandlist'][$command] = $class;
+ $GLOBALS['_PEAR_Command_commanddesc'][$command] = $desc;
+ }
+ $shortcuts = $GLOBALS['_PEAR_Command_objects'][$class]->getShortcuts();
+ foreach ($shortcuts as $shortcut => $command) {
+ $GLOBALS['_PEAR_Command_shortcuts'][$shortcut] = $command;
+ }
+ }
+ @closedir($dp);
+ return true;
+ }
+
+ // }}}
+ // {{{ getCommands()
+
+ /**
+ * Get the list of currently supported commands, and what
+ * classes implement them.
+ *
+ * @return array command => implementing class
+ *
+ * @access public
+ * @static
+ */
+ function getCommands()
+ {
+ if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
+ PEAR_Command::registerCommands();
+ }
+ return $GLOBALS['_PEAR_Command_commandlist'];
+ }
+
+ // }}}
+ // {{{ getShortcuts()
+
+ /**
+ * Get the list of command shortcuts.
+ *
+ * @return array shortcut => command
+ *
+ * @access public
+ * @static
+ */
+ function getShortcuts()
+ {
+ if (empty($GLOBALS['_PEAR_Command_shortcuts'])) {
+ PEAR_Command::registerCommands();
+ }
+ return $GLOBALS['_PEAR_Command_shortcuts'];
+ }
+
+ // }}}
+ // {{{ getGetoptArgs()
+
+ /**
+ * Compiles arguments for getopt.
+ *
+ * @param string $command command to get optstring for
+ * @param string $short_args (reference) short getopt format
+ * @param array $long_args (reference) long getopt format
+ *
+ * @return void
+ *
+ * @access public
+ * @static
+ */
+ function getGetoptArgs($command, &$short_args, &$long_args)
+ {
+ if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
+ PEAR_Command::registerCommands();
+ }
+ if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
+ return null;
+ }
+ $class = $GLOBALS['_PEAR_Command_commandlist'][$command];
+ $obj = &$GLOBALS['_PEAR_Command_objects'][$class];
+ return $obj->getGetoptArgs($command, $short_args, $long_args);
+ }
+
+ // }}}
+ // {{{ getDescription()
+
+ /**
+ * Get description for a command.
+ *
+ * @param string $command Name of the command
+ *
+ * @return string command description
+ *
+ * @access public
+ * @static
+ */
+ function getDescription($command)
+ {
+ if (!isset($GLOBALS['_PEAR_Command_commanddesc'][$command])) {
+ return null;
+ }
+ return $GLOBALS['_PEAR_Command_commanddesc'][$command];
+ }
+
+ // }}}
+ // {{{ getHelp()
+
+ /**
+ * Get help for command.
+ *
+ * @param string $command Name of the command to return help for
+ *
+ * @access public
+ * @static
+ */
+ function getHelp($command)
+ {
+ $cmds = PEAR_Command::getCommands();
+ if (isset($cmds[$command])) {
+ $class = $cmds[$command];
+ return $GLOBALS['_PEAR_Command_objects'][$class]->getHelp($command);
+ }
+ return false;
+ }
+ // }}}
+}
+
+?>
diff --git a/pearlib/PEAR/Command/Auth.php b/pearlib/PEAR/Command/Auth.php
new file mode 100644
index 00000000..0b9d3d39
--- /dev/null
+++ b/pearlib/PEAR/Command/Auth.php
@@ -0,0 +1,155 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Auth.php,v 1.15 2004/01/08 17:33:13 sniper Exp $
+
+require_once "PEAR/Command/Common.php";
+require_once "PEAR/Remote.php";
+require_once "PEAR/Config.php";
+
+/**
+ * PEAR commands for managing configuration data.
+ *
+ */
+class PEAR_Command_Auth extends PEAR_Command_Common
+{
+ // {{{ properties
+
+ var $commands = array(
+ 'login' => array(
+ 'summary' => 'Connects and authenticates to remote server',
+ 'shortcut' => 'li',
+ 'function' => 'doLogin',
+ 'options' => array(),
+ 'doc' => '
+Log in to the remote server. To use remote functions in the installer
+that require any kind of privileges, you need to log in first. The
+username and password you enter here will be stored in your per-user
+PEAR configuration (~/.pearrc on Unix-like systems). After logging
+in, your username and password will be sent along in subsequent
+operations on the remote server.',
+ ),
+ 'logout' => array(
+ 'summary' => 'Logs out from the remote server',
+ 'shortcut' => 'lo',
+ 'function' => 'doLogout',
+ 'options' => array(),
+ 'doc' => '
+Logs out from the remote server. This command does not actually
+connect to the remote server, it only deletes the stored username and
+password from your user configuration.',
+ )
+
+ );
+
+ // }}}
+
+ // {{{ constructor
+
+ /**
+ * PEAR_Command_Auth constructor.
+ *
+ * @access public
+ */
+ function PEAR_Command_Auth(&$ui, &$config)
+ {
+ parent::PEAR_Command_Common($ui, $config);
+ }
+
+ // }}}
+
+ // {{{ doLogin()
+
+ /**
+ * Execute the 'login' command.
+ *
+ * @param string $command command name
+ *
+ * @param array $options option_name => value
+ *
+ * @param array $params list of additional parameters
+ *
+ * @return bool TRUE on success, FALSE for unknown commands, or
+ * a PEAR error on failure
+ *
+ * @access public
+ */
+ function doLogin($command, $options, $params)
+ {
+ $server = $this->config->get('master_server');
+ $remote = new PEAR_Remote($this->config);
+ $username = $this->config->get('username');
+ if (empty($username)) {
+ $username = @$_ENV['USER'];
+ }
+ $this->ui->outputData("Logging in to $server.", $command);
+
+ list($username, $password) = $this->ui->userDialog(
+ $command,
+ array('Username', 'Password'),
+ array('text', 'password'),
+ array($username, '')
+ );
+ $username = trim($username);
+ $password = trim($password);
+
+ $this->config->set('username', $username);
+ $this->config->set('password', $password);
+
+ $remote->expectError(401);
+ $ok = $remote->call('logintest');
+ $remote->popExpect();
+ if ($ok === true) {
+ $this->ui->outputData("Logged in.", $command);
+ $this->config->store();
+ } else {
+ return $this->raiseError("Login failed!");
+ }
+
+ }
+
+ // }}}
+ // {{{ doLogout()
+
+ /**
+ * Execute the 'logout' command.
+ *
+ * @param string $command command name
+ *
+ * @param array $options option_name => value
+ *
+ * @param array $params list of additional parameters
+ *
+ * @return bool TRUE on success, FALSE for unknown commands, or
+ * a PEAR error on failure
+ *
+ * @access public
+ */
+ function doLogout($command, $options, $params)
+ {
+ $server = $this->config->get('master_server');
+ $this->ui->outputData("Logging out from $server.", $command);
+ $this->config->remove('username');
+ $this->config->remove('password');
+ $this->config->store();
+ }
+
+ // }}}
+}
+
+?> \ No newline at end of file
diff --git a/pearlib/PEAR/Command/Build.php b/pearlib/PEAR/Command/Build.php
new file mode 100644
index 00000000..2ecbbc92
--- /dev/null
+++ b/pearlib/PEAR/Command/Build.php
@@ -0,0 +1,89 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <[email protected]> |
+// | Tomas V.V.Cox <[email protected]> |
+// | |
+// +----------------------------------------------------------------------+
+//
+// $Id: Build.php,v 1.9 2004/01/08 17:33:13 sniper Exp $
+
+require_once "PEAR/Command/Common.php";
+require_once "PEAR/Builder.php";
+
+/**
+ * PEAR commands for building extensions.
+ *
+ */
+class PEAR_Command_Build extends PEAR_Command_Common
+{
+ // {{{ properties
+
+ var $commands = array(
+ 'build' => array(
+ 'summary' => 'Build an Extension From C Source',
+ 'function' => 'doBuild',
+ 'shortcut' => 'b',
+ 'options' => array(),
+ 'doc' => '[package.xml]
+Builds one or more extensions contained in a package.'
+ ),
+ );
+
+ // }}}
+
+ // {{{ constructor
+
+ /**
+ * PEAR_Command_Build constructor.
+ *
+ * @access public
+ */
+ function PEAR_Command_Build(&$ui, &$config)
+ {
+ parent::PEAR_Command_Common($ui, $config);
+ }
+
+ // }}}
+
+ // {{{ doBuild()
+
+ function doBuild($command, $options, $params)
+ {
+ if (sizeof($params) < 1) {
+ $params[0] = 'package.xml';
+ }
+ $builder = &new PEAR_Builder($this->ui);
+ $this->debug = $this->config->get('verbose');
+ $err = $builder->build($params[0], array(&$this, 'buildCallback'));
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ buildCallback()
+
+ function buildCallback($what, $data)
+ {
+ if (($what == 'cmdoutput' && $this->debug > 1) ||
+ ($what == 'output' && $this->debug > 0)) {
+ $this->ui->outputData(rtrim($data), 'build');
+ }
+ }
+
+ // }}}
+}
diff --git a/pearlib/PEAR/Command/Common.php b/pearlib/PEAR/Command/Common.php
new file mode 100644
index 00000000..c6ace694
--- /dev/null
+++ b/pearlib/PEAR/Command/Common.php
@@ -0,0 +1,249 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig S�ther Bakken <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Common.php,v 1.24 2004/01/08 17:33:13 sniper Exp $
+
+require_once "PEAR.php";
+
+class PEAR_Command_Common extends PEAR
+{
+ // {{{ properties
+
+ /**
+ * PEAR_Config object used to pass user system and configuration
+ * on when executing commands
+ *
+ * @var object
+ */
+ var $config;
+
+ /**
+ * User Interface object, for all interaction with the user.
+ * @var object
+ */
+ var $ui;
+
+ var $_deps_rel_trans = array(
+ 'lt' => '<',
+ 'le' => '<=',
+ 'eq' => '=',
+ 'ne' => '!=',
+ 'gt' => '>',
+ 'ge' => '>=',
+ 'has' => '=='
+ );
+
+ var $_deps_type_trans = array(
+ 'pkg' => 'package',
+ 'extension' => 'extension',
+ 'php' => 'PHP',
+ 'prog' => 'external program',
+ 'ldlib' => 'external library for linking',
+ 'rtlib' => 'external runtime library',
+ 'os' => 'operating system',
+ 'websrv' => 'web server',
+ 'sapi' => 'SAPI backend'
+ );
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * PEAR_Command_Common constructor.
+ *
+ * @access public
+ */
+ function PEAR_Command_Common(&$ui, &$config)
+ {
+ parent::PEAR();
+ $this->config = &$config;
+ $this->ui = &$ui;
+ }
+
+ // }}}
+
+ // {{{ getCommands()
+
+ /**
+ * Return a list of all the commands defined by this class.
+ * @return array list of commands
+ * @access public
+ */
+ function getCommands()
+ {
+ $ret = array();
+ foreach (array_keys($this->commands) as $command) {
+ $ret[$command] = $this->commands[$command]['summary'];
+ }
+ return $ret;
+ }
+
+ // }}}
+ // {{{ getShortcuts()
+
+ /**
+ * Return a list of all the command shortcuts defined by this class.
+ * @return array shortcut => command
+ * @access public
+ */
+ function getShortcuts()
+ {
+ $ret = array();
+ foreach (array_keys($this->commands) as $command) {
+ if (isset($this->commands[$command]['shortcut'])) {
+ $ret[$this->commands[$command]['shortcut']] = $command;
+ }
+ }
+ return $ret;
+ }
+
+ // }}}
+ // {{{ getOptions()
+
+ function getOptions($command)
+ {
+ return @$this->commands[$command]['options'];
+ }
+
+ // }}}
+ // {{{ getGetoptArgs()
+
+ function getGetoptArgs($command, &$short_args, &$long_args)
+ {
+ $short_args = "";
+ $long_args = array();
+ if (empty($this->commands[$command])) {
+ return;
+ }
+ reset($this->commands[$command]);
+ while (list($option, $info) = each($this->commands[$command]['options'])) {
+ $larg = $sarg = '';
+ if (isset($info['arg'])) {
+ if ($info['arg']{0} == '(') {
+ $larg = '==';
+ $sarg = '::';
+ $arg = substr($info['arg'], 1, -1);
+ } else {
+ $larg = '=';
+ $sarg = ':';
+ $arg = $info['arg'];
+ }
+ }
+ if (isset($info['shortopt'])) {
+ $short_args .= $info['shortopt'] . $sarg;
+ }
+ $long_args[] = $option . $larg;
+ }
+ }
+
+ // }}}
+ // {{{ getHelp()
+ /**
+ * Returns the help message for the given command
+ *
+ * @param string $command The command
+ * @return mixed A fail string if the command does not have help or
+ * a two elements array containing [0]=>help string,
+ * [1]=> help string for the accepted cmd args
+ */
+ function getHelp($command)
+ {
+ $config = &PEAR_Config::singleton();
+ $help = @$this->commands[$command]['doc'];
+ if (empty($help)) {
+ // XXX (cox) Fallback to summary if there is no doc (show both?)
+ if (!$help = @$this->commands[$command]['summary']) {
+ return "No help for command \"$command\"";
+ }
+ }
+ if (preg_match_all('/{config\s+([^\}]+)}/e', $help, $matches)) {
+ foreach($matches[0] as $k => $v) {
+ $help = preg_replace("/$v/", $config->get($matches[1][$k]), $help);
+ }
+ }
+ return array($help, $this->getHelpArgs($command));
+ }
+
+ // }}}
+ // {{{ getHelpArgs()
+ /**
+ * Returns the help for the accepted arguments of a command
+ *
+ * @param string $command
+ * @return string The help string
+ */
+ function getHelpArgs($command)
+ {
+ if (isset($this->commands[$command]['options']) &&
+ count($this->commands[$command]['options']))
+ {
+ $help = "Options:\n";
+ foreach ($this->commands[$command]['options'] as $k => $v) {
+ if (isset($v['arg'])) {
+ if ($v['arg']{0} == '(') {
+ $arg = substr($v['arg'], 1, -1);
+ $sapp = " [$arg]";
+ $lapp = "[=$arg]";
+ } else {
+ $sapp = " $v[arg]";
+ $lapp = "=$v[arg]";
+ }
+ } else {
+ $sapp = $lapp = "";
+ }
+ if (isset($v['shortopt'])) {
+ $s = $v['shortopt'];
+ @$help .= " -$s$sapp, --$k$lapp\n";
+ } else {
+ @$help .= " --$k$lapp\n";
+ }
+ $p = " ";
+ $doc = rtrim(str_replace("\n", "\n$p", $v['doc']));
+ $help .= " $doc\n";
+ }
+ return $help;
+ }
+ return null;
+ }
+
+ // }}}
+ // {{{ run()
+
+ function run($command, $options, $params)
+ {
+ $func = @$this->commands[$command]['function'];
+ if (empty($func)) {
+ // look for shortcuts
+ foreach (array_keys($this->commands) as $cmd) {
+ if (@$this->commands[$cmd]['shortcut'] == $command) {
+ $command = $cmd;
+ $func = @$this->commands[$command]['function'];
+ if (empty($func)) {
+ return $this->raiseError("unknown command `$command'");
+ }
+ break;
+ }
+ }
+ }
+ return $this->$func($command, $options, $params);
+ }
+
+ // }}}
+}
+
+?> \ No newline at end of file
diff --git a/pearlib/PEAR/Command/Config.php b/pearlib/PEAR/Command/Config.php
new file mode 100644
index 00000000..474a2345
--- /dev/null
+++ b/pearlib/PEAR/Command/Config.php
@@ -0,0 +1,225 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <[email protected]> |
+// | Tomas V.V.Cox <[email protected]> |
+// | |
+// +----------------------------------------------------------------------+
+//
+// $Id: Config.php,v 1.27 2004/06/15 16:48:49 pajoye Exp $
+
+require_once "PEAR/Command/Common.php";
+require_once "PEAR/Config.php";
+
+/**
+ * PEAR commands for managing configuration data.
+ *
+ */
+class PEAR_Command_Config extends PEAR_Command_Common
+{
+ // {{{ properties
+
+ var $commands = array(
+ 'config-show' => array(
+ 'summary' => 'Show All Settings',
+ 'function' => 'doConfigShow',
+ 'shortcut' => 'csh',
+ 'options' => array(),
+ 'doc' => '
+Displays all configuration values. An optional argument
+may be used to tell which configuration layer to display. Valid
+configuration layers are "user", "system" and "default".
+',
+ ),
+ 'config-get' => array(
+ 'summary' => 'Show One Setting',
+ 'function' => 'doConfigGet',
+ 'shortcut' => 'cg',
+ 'options' => array(),
+ 'doc' => '<parameter> [layer]
+Displays the value of one configuration parameter. The
+first argument is the name of the parameter, an optional second argument
+may be used to tell which configuration layer to look in. Valid configuration
+layers are "user", "system" and "default". If no layer is specified, a value
+will be picked from the first layer that defines the parameter, in the order
+just specified.
+',
+ ),
+ 'config-set' => array(
+ 'summary' => 'Change Setting',
+ 'function' => 'doConfigSet',
+ 'shortcut' => 'cs',
+ 'options' => array(),
+ 'doc' => '<parameter> <value> [layer]
+Sets the value of one configuration parameter. The first argument is
+the name of the parameter, the second argument is the new value. Some
+parameters are subject to validation, and the command will fail with
+an error message if the new value does not make sense. An optional
+third argument may be used to specify in which layer to set the
+configuration parameter. The default layer is "user".
+',
+ ),
+ 'config-help' => array(
+ 'summary' => 'Show Information About Setting',
+ 'function' => 'doConfigHelp',
+ 'shortcut' => 'ch',
+ 'options' => array(),
+ 'doc' => '[parameter]
+Displays help for a configuration parameter. Without arguments it
+displays help for all configuration parameters.
+',
+ ),
+ );
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * PEAR_Command_Config constructor.
+ *
+ * @access public
+ */
+ function PEAR_Command_Config(&$ui, &$config)
+ {
+ parent::PEAR_Command_Common($ui, $config);
+ }
+
+ // }}}
+
+ // {{{ doConfigShow()
+
+ function doConfigShow($command, $options, $params)
+ {
+ // $params[0] -> the layer
+ if ($error = $this->_checkLayer(@$params[0])) {
+ return $this->raiseError($error);
+ }
+ $keys = $this->config->getKeys();
+ sort($keys);
+ $data = array('caption' => 'Configuration:');
+ foreach ($keys as $key) {
+ $type = $this->config->getType($key);
+ $value = $this->config->get($key, @$params[0]);
+ if ($type == 'password' && $value) {
+ $value = '********';
+ }
+ if ($value === false) {
+ $value = 'false';
+ } elseif ($value === true) {
+ $value = 'true';
+ }
+ $data['data'][$this->config->getGroup($key)][] = array($this->config->getPrompt($key) , $key, $value);
+ }
+ $this->ui->outputData($data, $command);
+ return true;
+ }
+
+ // }}}
+ // {{{ doConfigGet()
+
+ function doConfigGet($command, $options, $params)
+ {
+ // $params[0] -> the parameter
+ // $params[1] -> the layer
+ if ($error = $this->_checkLayer(@$params[1])) {
+ return $this->raiseError($error);
+ }
+ if (sizeof($params) < 1 || sizeof($params) > 2) {
+ return $this->raiseError("config-get expects 1 or 2 parameters");
+ } elseif (sizeof($params) == 1) {
+ $this->ui->outputData($this->config->get($params[0]), $command);
+ } else {
+ $data = $this->config->get($params[0], $params[1]);
+ $this->ui->outputData($data, $command);
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ doConfigSet()
+
+ function doConfigSet($command, $options, $params)
+ {
+ // $param[0] -> a parameter to set
+ // $param[1] -> the value for the parameter
+ // $param[2] -> the layer
+ $failmsg = '';
+ if (sizeof($params) < 2 || sizeof($params) > 3) {
+ $failmsg .= "config-set expects 2 or 3 parameters";
+ return PEAR::raiseError($failmsg);
+ }
+ if ($error = $this->_checkLayer(@$params[2])) {
+ $failmsg .= $error;
+ return PEAR::raiseError($failmsg);
+ }
+ if (!call_user_func_array(array(&$this->config, 'set'), $params))
+ {
+ $failmsg = "config-set (" . implode(", ", $params) . ") failed";
+ } else {
+ $this->config->store();
+ }
+ if ($failmsg) {
+ return $this->raiseError($failmsg);
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ doConfigHelp()
+
+ function doConfigHelp($command, $options, $params)
+ {
+ if (empty($params)) {
+ $params = $this->config->getKeys();
+ }
+ $data['caption'] = "Config help" . ((count($params) == 1) ? " for $params[0]" : '');
+ $data['headline'] = array('Name', 'Type', 'Description');
+ $data['border'] = true;
+ foreach ($params as $name) {
+ $type = $this->config->getType($name);
+ $docs = $this->config->getDocs($name);
+ if ($type == 'set') {
+ $docs = rtrim($docs) . "\nValid set: " .
+ implode(' ', $this->config->getSetValues($name));
+ }
+ $data['data'][] = array($name, $type, $docs);
+ }
+ $this->ui->outputData($data, $command);
+ }
+
+ // }}}
+ // {{{ _checkLayer()
+
+ /**
+ * Checks if a layer is defined or not
+ *
+ * @param string $layer The layer to search for
+ * @return mixed False on no error or the error message
+ */
+ function _checkLayer($layer = null)
+ {
+ if (!empty($layer) && $layer != 'default') {
+ $layers = $this->config->getLayers();
+ if (!in_array($layer, $layers)) {
+ return " only the layers: \"" . implode('" or "', $layers) . "\" are supported";
+ }
+ }
+ return false;
+ }
+
+ // }}}
+}
+
+?>
diff --git a/pearlib/PEAR/Command/Install.php b/pearlib/PEAR/Command/Install.php
new file mode 100644
index 00000000..dce52f01
--- /dev/null
+++ b/pearlib/PEAR/Command/Install.php
@@ -0,0 +1,470 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig S�ther Bakken <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Install.php,v 1.53.2.1 2004/10/19 04:08:42 cellog Exp $
+
+require_once "PEAR/Command/Common.php";
+require_once "PEAR/Installer.php";
+
+/**
+ * PEAR commands for installation or deinstallation/upgrading of
+ * packages.
+ *
+ */
+class PEAR_Command_Install extends PEAR_Command_Common
+{
+ // {{{ properties
+
+ var $commands = array(
+ 'install' => array(
+ 'summary' => 'Install Package',
+ 'function' => 'doInstall',
+ 'shortcut' => 'i',
+ 'options' => array(
+ 'force' => array(
+ 'shortopt' => 'f',
+ 'doc' => 'will overwrite newer installed packages',
+ ),
+ 'nodeps' => array(
+ 'shortopt' => 'n',
+ 'doc' => 'ignore dependencies, install anyway',
+ ),
+ 'register-only' => array(
+ 'shortopt' => 'r',
+ 'doc' => 'do not install files, only register the package as installed',
+ ),
+ 'soft' => array(
+ 'shortopt' => 's',
+ 'doc' => 'soft install, fail silently, or upgrade if already installed',
+ ),
+ 'nobuild' => array(
+ 'shortopt' => 'B',
+ 'doc' => 'don\'t build C extensions',
+ ),
+ 'nocompress' => array(
+ 'shortopt' => 'Z',
+ 'doc' => 'request uncompressed files when downloading',
+ ),
+ 'installroot' => array(
+ 'shortopt' => 'R',
+ 'arg' => 'DIR',
+ 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
+ ),
+ 'ignore-errors' => array(
+ 'doc' => 'force install even if there were errors',
+ ),
+ 'alldeps' => array(
+ 'shortopt' => 'a',
+ 'doc' => 'install all required and optional dependencies',
+ ),
+ 'onlyreqdeps' => array(
+ 'shortopt' => 'o',
+ 'doc' => 'install all required dependencies',
+ ),
+ ),
+ 'doc' => '<package> ...
+Installs one or more PEAR packages. You can specify a package to
+install in four ways:
+
+"Package-1.0.tgz" : installs from a local file
+
+"http://example.com/Package-1.0.tgz" : installs from
+anywhere on the net.
+
+"package.xml" : installs the package described in
+package.xml. Useful for testing, or for wrapping a PEAR package in
+another package manager such as RPM.
+
+"Package" : queries your configured server
+({config master_server}) and downloads the newest package with
+the preferred quality/state ({config preferred_state}).
+
+More than one package may be specified at once. It is ok to mix these
+four ways of specifying packages.
+'),
+ 'upgrade' => array(
+ 'summary' => 'Upgrade Package',
+ 'function' => 'doInstall',
+ 'shortcut' => 'up',
+ 'options' => array(
+ 'force' => array(
+ 'shortopt' => 'f',
+ 'doc' => 'overwrite newer installed packages',
+ ),
+ 'nodeps' => array(
+ 'shortopt' => 'n',
+ 'doc' => 'ignore dependencies, upgrade anyway',
+ ),
+ 'register-only' => array(
+ 'shortopt' => 'r',
+ 'doc' => 'do not install files, only register the package as upgraded',
+ ),
+ 'nobuild' => array(
+ 'shortopt' => 'B',
+ 'doc' => 'don\'t build C extensions',
+ ),
+ 'nocompress' => array(
+ 'shortopt' => 'Z',
+ 'doc' => 'request uncompressed files when downloading',
+ ),
+ 'installroot' => array(
+ 'shortopt' => 'R',
+ 'arg' => 'DIR',
+ 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
+ ),
+ 'ignore-errors' => array(
+ 'doc' => 'force install even if there were errors',
+ ),
+ 'alldeps' => array(
+ 'shortopt' => 'a',
+ 'doc' => 'install all required and optional dependencies',
+ ),
+ 'onlyreqdeps' => array(
+ 'shortopt' => 'o',
+ 'doc' => 'install all required dependencies',
+ ),
+ ),
+ 'doc' => '<package> ...
+Upgrades one or more PEAR packages. See documentation for the
+"install" command for ways to specify a package.
+
+When upgrading, your package will be updated if the provided new
+package has a higher version number (use the -f option if you need to
+upgrade anyway).
+
+More than one package may be specified at once.
+'),
+ 'upgrade-all' => array(
+ 'summary' => 'Upgrade All Packages',
+ 'function' => 'doInstall',
+ 'shortcut' => 'ua',
+ 'options' => array(
+ 'nodeps' => array(
+ 'shortopt' => 'n',
+ 'doc' => 'ignore dependencies, upgrade anyway',
+ ),
+ 'register-only' => array(
+ 'shortopt' => 'r',
+ 'doc' => 'do not install files, only register the package as upgraded',
+ ),
+ 'nobuild' => array(
+ 'shortopt' => 'B',
+ 'doc' => 'don\'t build C extensions',
+ ),
+ 'nocompress' => array(
+ 'shortopt' => 'Z',
+ 'doc' => 'request uncompressed files when downloading',
+ ),
+ 'installroot' => array(
+ 'shortopt' => 'R',
+ 'arg' => 'DIR',
+ 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
+ ),
+ 'ignore-errors' => array(
+ 'doc' => 'force install even if there were errors',
+ ),
+ ),
+ 'doc' => '
+Upgrades all packages that have a newer release available. Upgrades are
+done only if there is a release available of the state specified in
+"preferred_state" (currently {config preferred_state}), or a state considered
+more stable.
+'),
+ 'uninstall' => array(
+ 'summary' => 'Un-install Package',
+ 'function' => 'doUninstall',
+ 'shortcut' => 'un',
+ 'options' => array(
+ 'nodeps' => array(
+ 'shortopt' => 'n',
+ 'doc' => 'ignore dependencies, uninstall anyway',
+ ),
+ 'register-only' => array(
+ 'shortopt' => 'r',
+ 'doc' => 'do not remove files, only register the packages as not installed',
+ ),
+ 'installroot' => array(
+ 'shortopt' => 'R',
+ 'arg' => 'DIR',
+ 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
+ ),
+ 'ignore-errors' => array(
+ 'doc' => 'force install even if there were errors',
+ ),
+ ),
+ 'doc' => '<package> ...
+Uninstalls one or more PEAR packages. More than one package may be
+specified at once.
+'),
+ 'bundle' => array(
+ 'summary' => 'Unpacks a Pecl Package',
+ 'function' => 'doBundle',
+ 'shortcut' => 'bun',
+ 'options' => array(
+ 'destination' => array(
+ 'shortopt' => 'd',
+ 'arg' => 'DIR',
+ 'doc' => 'Optional destination directory for unpacking (defaults to current path or "ext" if exists)',
+ ),
+ 'force' => array(
+ 'shortopt' => 'f',
+ 'doc' => 'Force the unpacking even if there were errors in the package',
+ ),
+ ),
+ 'doc' => '<package>
+Unpacks a Pecl Package into the selected location. It will download the
+package if needed.
+'),
+ );
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * PEAR_Command_Install constructor.
+ *
+ * @access public
+ */
+ function PEAR_Command_Install(&$ui, &$config)
+ {
+ parent::PEAR_Command_Common($ui, $config);
+ }
+
+ // }}}
+
+ // {{{ doInstall()
+
+ function doInstall($command, $options, $params)
+ {
+ require_once 'PEAR/Downloader.php';
+ if (empty($this->installer)) {
+ $this->installer = &new PEAR_Installer($this->ui);
+ }
+ if ($command == 'upgrade') {
+ $options['upgrade'] = true;
+ }
+ if ($command == 'upgrade-all') {
+ include_once "PEAR/Remote.php";
+ $options['upgrade'] = true;
+ $remote = &new PEAR_Remote($this->config);
+ $state = $this->config->get('preferred_state');
+ if (empty($state) || $state == 'any') {
+ $latest = $remote->call("package.listLatestReleases");
+ } else {
+ $latest = $remote->call("package.listLatestReleases", $state);
+ }
+ if (PEAR::isError($latest)) {
+ return $latest;
+ }
+ $reg = new PEAR_Registry($this->config->get('php_dir'));
+ $installed = array_flip($reg->listPackages());
+ $params = array();
+ foreach ($latest as $package => $info) {
+ $package = strtolower($package);
+ if (!isset($installed[$package])) {
+ // skip packages we don't have installed
+ continue;
+ }
+ $inst_version = $reg->packageInfo($package, 'version');
+ if (version_compare("$info[version]", "$inst_version", "le")) {
+ // installed version is up-to-date
+ continue;
+ }
+ $params[] = $package;
+ $this->ui->outputData(array('data' => "Will upgrade $package"), $command);
+ }
+ }
+ $this->downloader = &new PEAR_Downloader($this->ui, $options, $this->config);
+ $errors = array();
+ $downloaded = array();
+ $this->downloader->download($params);
+ $errors = $this->downloader->getErrorMsgs();
+ if (count($errors)) {
+ $err['data'] = array($errors);
+ $err['headline'] = 'Install Errors';
+ $this->ui->outputData($err);
+ return $this->raiseError("$command failed");
+ }
+ $downloaded = $this->downloader->getDownloadedPackages();
+ $this->installer->sortPkgDeps($downloaded);
+ foreach ($downloaded as $pkg) {
+ PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+ $info = $this->installer->install($pkg['file'], $options, $this->config);
+ PEAR::popErrorHandling();
+ if (PEAR::isError($info)) {
+ $this->ui->outputData('ERROR: ' .$info->getMessage());
+ continue;
+ }
+ if (is_array($info)) {
+ if ($this->config->get('verbose') > 0) {
+ $label = "$info[package] $info[version]";
+ $out = array('data' => "$command ok: $label");
+ if (isset($info['release_warnings'])) {
+ $out['release_warnings'] = $info['release_warnings'];
+ }
+ $this->ui->outputData($out, $command);
+ }
+ } else {
+ return $this->raiseError("$command failed");
+ }
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ doUninstall()
+
+ function doUninstall($command, $options, $params)
+ {
+ if (empty($this->installer)) {
+ $this->installer = &new PEAR_Installer($this->ui);
+ }
+ if (sizeof($params) < 1) {
+ return $this->raiseError("Please supply the package(s) you want to uninstall");
+ }
+ include_once 'PEAR/Registry.php';
+ $reg = new PEAR_Registry($this->config->get('php_dir'));
+ $newparams = array();
+ $badparams = array();
+ foreach ($params as $pkg) {
+ $info = $reg->packageInfo($pkg);
+ if ($info === null) {
+ $badparams[] = $pkg;
+ } else {
+ $newparams[] = $info;
+ }
+ }
+ $this->installer->sortPkgDeps($newparams, true);
+ $params = array();
+ foreach($newparams as $info) {
+ $params[] = $info['info']['package'];
+ }
+ $params = array_merge($params, $badparams);
+ foreach ($params as $pkg) {
+ if ($this->installer->uninstall($pkg, $options)) {
+ if ($this->config->get('verbose') > 0) {
+ $this->ui->outputData("uninstall ok: $pkg", $command);
+ }
+ } else {
+ return $this->raiseError("uninstall failed: $pkg");
+ }
+ }
+ return true;
+ }
+
+ // }}}
+
+
+ // }}}
+ // {{{ doBundle()
+ /*
+ (cox) It just downloads and untars the package, does not do
+ any check that the PEAR_Installer::_installFile() does.
+ */
+
+ function doBundle($command, $options, $params)
+ {
+ if (empty($this->installer)) {
+ $this->installer = &new PEAR_Downloader($this->ui);
+ }
+ $installer = &$this->installer;
+ if (sizeof($params) < 1) {
+ return $this->raiseError("Please supply the package you want to bundle");
+ }
+ $pkgfile = $params[0];
+ $need_download = false;
+ if (preg_match('#^(http|ftp)://#', $pkgfile)) {
+ $need_download = true;
+ } elseif (!@is_file($pkgfile)) {
+ if ($installer->validPackageName($pkgfile)) {
+ $pkgfile = $installer->getPackageDownloadUrl($pkgfile);
+ $need_download = true;
+ } else {
+ if (strlen($pkgfile)) {
+ return $this->raiseError("Could not open the package file: $pkgfile");
+ } else {
+ return $this->raiseError("No package file given");
+ }
+ }
+ }
+
+ // Download package -----------------------------------------------
+ if ($need_download) {
+ $downloaddir = $installer->config->get('download_dir');
+ if (empty($downloaddir)) {
+ if (PEAR::isError($downloaddir = System::mktemp('-d'))) {
+ return $downloaddir;
+ }
+ $installer->log(2, '+ tmp dir created at ' . $downloaddir);
+ }
+ $callback = $this->ui ? array(&$installer, '_downloadCallback') : null;
+ $file = $installer->downloadHttp($pkgfile, $this->ui, $downloaddir, $callback);
+ if (PEAR::isError($file)) {
+ return $this->raiseError($file);
+ }
+ $pkgfile = $file;
+ }
+
+ // Parse xml file -----------------------------------------------
+ $pkginfo = $installer->infoFromTgzFile($pkgfile);
+ if (PEAR::isError($pkginfo)) {
+ return $this->raiseError($pkginfo);
+ }
+ $installer->validatePackageInfo($pkginfo, $errors, $warnings);
+ // XXX We allow warnings, do we have to do it?
+ if (count($errors)) {
+ if (empty($options['force'])) {
+ return $this->raiseError("The following errors where found:\n".
+ implode("\n", $errors));
+ } else {
+ $this->log(0, "warning : the following errors were found:\n".
+ implode("\n", $errors));
+ }
+ }
+ $pkgname = $pkginfo['package'];
+
+ // Unpacking -------------------------------------------------
+
+ if (isset($options['destination'])) {
+ if (!is_dir($options['destination'])) {
+ System::mkdir('-p ' . $options['destination']);
+ }
+ $dest = realpath($options['destination']);
+ } else {
+ $pwd = getcwd();
+ if (is_dir($pwd . DIRECTORY_SEPARATOR . 'ext')) {
+ $dest = $pwd . DIRECTORY_SEPARATOR . 'ext';
+ } else {
+ $dest = $pwd;
+ }
+ }
+ $dest .= DIRECTORY_SEPARATOR . $pkgname;
+ $orig = $pkgname . '-' . $pkginfo['version'];
+
+ $tar = new Archive_Tar($pkgfile);
+ if (!@$tar->extractModify($dest, $orig)) {
+ return $this->raiseError("unable to unpack $pkgfile");
+ }
+ $this->ui->outputData("Package ready at '$dest'");
+ // }}}
+ }
+
+ // }}}
+
+}
+?>
diff --git a/pearlib/PEAR/Command/Mirror.php b/pearlib/PEAR/Command/Mirror.php
new file mode 100644
index 00000000..bf56c3db
--- /dev/null
+++ b/pearlib/PEAR/Command/Mirror.php
@@ -0,0 +1,101 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Alexander Merz <[email protected]> |
+// | |
+// +----------------------------------------------------------------------+
+//
+// $Id: Mirror.php,v 1.5 2004/03/18 12:23:57 mj Exp $
+
+require_once "PEAR/Command/Common.php";
+require_once "PEAR/Command.php";
+require_once "PEAR/Remote.php";
+require_once "PEAR.php";
+
+/**
+ * PEAR commands for providing file mirrors
+ *
+ */
+class PEAR_Command_Mirror extends PEAR_Command_Common
+{
+ // {{{ properties
+
+ var $commands = array(
+ 'download-all' => array(
+ 'summary' => 'Downloads each available package from master_server',
+ 'function' => 'doDownloadAll',
+ 'shortcut' => 'da',
+ 'options' => array(),
+ 'doc' => '
+ Requests a list of available packages from the package server
+ (master_server) and downloads them to current working directory'
+ ),
+ );
+
+ // }}}
+
+ // {{{ constructor
+
+ /**
+ * PEAR_Command_Mirror constructor.
+ *
+ * @access public
+ * @param object PEAR_Frontend a reference to an frontend
+ * @param object PEAR_Config a reference to the configuration data
+ */
+ function PEAR_Command_Mirror(&$ui, &$config)
+ {
+ parent::PEAR_Command_Common($ui, $config);
+ }
+
+ // }}}
+
+ // {{{ doDownloadAll()
+ /**
+ * retrieves a list of avaible Packages from master server
+ * and downloads them
+ *
+ * @access public
+ * @param string $command the command
+ * @param array $options the command options before the command
+ * @param array $params the stuff after the command name
+ * @return bool true if succesful
+ * @throw PEAR_Error
+ */
+ function doDownloadAll($command, $options, $params)
+ {
+ $this->config->set("php_dir", ".");
+ $remote = &new PEAR_Remote($this->config);
+ $remoteInfo = $remote->call("package.listAll");
+ if (PEAR::isError($remoteInfo)) {
+ return $remoteInfo;
+ }
+ $cmd = &PEAR_Command::factory("download", $this->config);
+ if (PEAR::isError($cmd)) {
+ return $cmd;
+ }
+ foreach ($remoteInfo as $pkgn => $pkg) {
+ /**
+ * Error handling not neccesary, because already done by
+ * the download command
+ */
+ $cmd->run("download", array(), array($pkgn));
+ }
+
+ return true;
+ }
+
+ // }}}
+}
diff --git a/pearlib/PEAR/Command/Package.php b/pearlib/PEAR/Command/Package.php
new file mode 100644
index 00000000..61a16c15
--- /dev/null
+++ b/pearlib/PEAR/Command/Package.php
@@ -0,0 +1,751 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Bakken <[email protected]> |
+// | Martin Jansen <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Package.php,v 1.61.2.3 2004/10/23 17:01:26 cellog Exp $
+
+require_once 'PEAR/Common.php';
+require_once 'PEAR/Command/Common.php';
+
+class PEAR_Command_Package extends PEAR_Command_Common
+{
+ // {{{ properties
+
+ var $commands = array(
+ 'package' => array(
+ 'summary' => 'Build Package',
+ 'function' => 'doPackage',
+ 'shortcut' => 'p',
+ 'options' => array(
+ 'nocompress' => array(
+ 'shortopt' => 'Z',
+ 'doc' => 'Do not gzip the package file'
+ ),
+ 'showname' => array(
+ 'shortopt' => 'n',
+ 'doc' => 'Print the name of the packaged file.',
+ ),
+ ),
+ 'doc' => '[descfile]
+Creates a PEAR package from its description file (usually called
+package.xml).
+'
+ ),
+ 'package-validate' => array(
+ 'summary' => 'Validate Package Consistency',
+ 'function' => 'doPackageValidate',
+ 'shortcut' => 'pv',
+ 'options' => array(),
+ 'doc' => '
+',
+ ),
+ 'cvsdiff' => array(
+ 'summary' => 'Run a "cvs diff" for all files in a package',
+ 'function' => 'doCvsDiff',
+ 'shortcut' => 'cd',
+ 'options' => array(
+ 'quiet' => array(
+ 'shortopt' => 'q',
+ 'doc' => 'Be quiet',
+ ),
+ 'reallyquiet' => array(
+ 'shortopt' => 'Q',
+ 'doc' => 'Be really quiet',
+ ),
+ 'date' => array(
+ 'shortopt' => 'D',
+ 'doc' => 'Diff against revision of DATE',
+ 'arg' => 'DATE',
+ ),
+ 'release' => array(
+ 'shortopt' => 'R',
+ 'doc' => 'Diff against tag for package release REL',
+ 'arg' => 'REL',
+ ),
+ 'revision' => array(
+ 'shortopt' => 'r',
+ 'doc' => 'Diff against revision REV',
+ 'arg' => 'REV',
+ ),
+ 'context' => array(
+ 'shortopt' => 'c',
+ 'doc' => 'Generate context diff',
+ ),
+ 'unified' => array(
+ 'shortopt' => 'u',
+ 'doc' => 'Generate unified diff',
+ ),
+ 'ignore-case' => array(
+ 'shortopt' => 'i',
+ 'doc' => 'Ignore case, consider upper- and lower-case letters equivalent',
+ ),
+ 'ignore-whitespace' => array(
+ 'shortopt' => 'b',
+ 'doc' => 'Ignore changes in amount of white space',
+ ),
+ 'ignore-blank-lines' => array(
+ 'shortopt' => 'B',
+ 'doc' => 'Ignore changes that insert or delete blank lines',
+ ),
+ 'brief' => array(
+ 'doc' => 'Report only whether the files differ, no details',
+ ),
+ 'dry-run' => array(
+ 'shortopt' => 'n',
+ 'doc' => 'Don\'t do anything, just pretend',
+ ),
+ ),
+ 'doc' => '<package.xml>
+Compares all the files in a package. Without any options, this
+command will compare the current code with the last checked-in code.
+Using the -r or -R option you may compare the current code with that
+of a specific release.
+',
+ ),
+ 'cvstag' => array(
+ 'summary' => 'Set CVS Release Tag',
+ 'function' => 'doCvsTag',
+ 'shortcut' => 'ct',
+ 'options' => array(
+ 'quiet' => array(
+ 'shortopt' => 'q',
+ 'doc' => 'Be quiet',
+ ),
+ 'reallyquiet' => array(
+ 'shortopt' => 'Q',
+ 'doc' => 'Be really quiet',
+ ),
+ 'slide' => array(
+ 'shortopt' => 'F',
+ 'doc' => 'Move (slide) tag if it exists',
+ ),
+ 'delete' => array(
+ 'shortopt' => 'd',
+ 'doc' => 'Remove tag',
+ ),
+ 'dry-run' => array(
+ 'shortopt' => 'n',
+ 'doc' => 'Don\'t do anything, just pretend',
+ ),
+ ),
+ 'doc' => '<package.xml>
+Sets a CVS tag on all files in a package. Use this command after you have
+packaged a distribution tarball with the "package" command to tag what
+revisions of what files were in that release. If need to fix something
+after running cvstag once, but before the tarball is released to the public,
+use the "slide" option to move the release tag.
+',
+ ),
+ 'run-tests' => array(
+ 'summary' => 'Run Regression Tests',
+ 'function' => 'doRunTests',
+ 'shortcut' => 'rt',
+ 'options' => array(
+ 'recur' => array(
+ 'shortopt' => 'r',
+ 'doc' => 'Run tests in child directories, recursively. 4 dirs deep maximum',
+ )
+ ),
+ 'doc' => '[testfile|dir ...]
+Run regression tests with PHP\'s regression testing script (run-tests.php).',
+ ),
+ 'package-dependencies' => array(
+ 'summary' => 'Show package dependencies',
+ 'function' => 'doPackageDependencies',
+ 'shortcut' => 'pd',
+ 'options' => array(),
+ 'doc' => '
+List all depencies the package has.'
+ ),
+ 'sign' => array(
+ 'summary' => 'Sign a package distribution file',
+ 'function' => 'doSign',
+ 'shortcut' => 'si',
+ 'options' => array(),
+ 'doc' => '<package-file>
+Signs a package distribution (.tar or .tgz) file with GnuPG.',
+ ),
+ 'makerpm' => array(
+ 'summary' => 'Builds an RPM spec file from a PEAR package',
+ 'function' => 'doMakeRPM',
+ 'shortcut' => 'rpm',
+ 'options' => array(
+ 'spec-template' => array(
+ 'shortopt' => 't',
+ 'arg' => 'FILE',
+ 'doc' => 'Use FILE as RPM spec file template'
+ ),
+ 'rpm-pkgname' => array(
+ 'shortopt' => 'p',
+ 'arg' => 'FORMAT',
+ 'doc' => 'Use FORMAT as format string for RPM package name, %s is replaced
+by the PEAR package name, defaults to "PEAR::%s".',
+ ),
+ ),
+ 'doc' => '<package-file>
+
+Creates an RPM .spec file for wrapping a PEAR package inside an RPM
+package. Intended to be used from the SPECS directory, with the PEAR
+package tarball in the SOURCES directory:
+
+$ pear makerpm ../SOURCES/Net_Socket-1.0.tgz
+Wrote RPM spec file PEAR::Net_Geo-1.0.spec
+$ rpm -bb PEAR::Net_Socket-1.0.spec
+...
+Wrote: /usr/src/redhat/RPMS/i386/PEAR::Net_Socket-1.0-1.i386.rpm
+',
+ ),
+ );
+
+ var $output;
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * PEAR_Command_Package constructor.
+ *
+ * @access public
+ */
+ function PEAR_Command_Package(&$ui, &$config)
+ {
+ parent::PEAR_Command_Common($ui, $config);
+ }
+
+ // }}}
+
+ // {{{ _displayValidationResults()
+
+ function _displayValidationResults($err, $warn, $strict = false)
+ {
+ foreach ($err as $e) {
+ $this->output .= "Error: $e\n";
+ }
+ foreach ($warn as $w) {
+ $this->output .= "Warning: $w\n";
+ }
+ $this->output .= sprintf('Validation: %d error(s), %d warning(s)'."\n",
+ sizeof($err), sizeof($warn));
+ if ($strict && sizeof($err) > 0) {
+ $this->output .= "Fix these errors and try again.";
+ return false;
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ doPackage()
+
+ function doPackage($command, $options, $params)
+ {
+ $this->output = '';
+ include_once 'PEAR/Packager.php';
+ if (sizeof($params) < 1) {
+ $params[0] = "package.xml";
+ }
+ $pkginfofile = isset($params[0]) ? $params[0] : 'package.xml';
+ $packager =& new PEAR_Packager();
+ $err = $warn = array();
+ $dir = dirname($pkginfofile);
+ $compress = empty($options['nocompress']) ? true : false;
+ $result = $packager->package($pkginfofile, $compress);
+ if (PEAR::isError($result)) {
+ $this->ui->outputData($this->output, $command);
+ return $this->raiseError($result);
+ }
+ // Don't want output, only the package file name just created
+ if (isset($options['showname'])) {
+ $this->output = $result;
+ }
+ if (PEAR::isError($result)) {
+ $this->output .= "Package failed: ".$result->getMessage();
+ }
+ $this->ui->outputData($this->output, $command);
+ return true;
+ }
+
+ // }}}
+ // {{{ doPackageValidate()
+
+ function doPackageValidate($command, $options, $params)
+ {
+ $this->output = '';
+ if (sizeof($params) < 1) {
+ $params[0] = "package.xml";
+ }
+ $obj = new PEAR_Common;
+ $info = null;
+ if ($fp = @fopen($params[0], "r")) {
+ $test = fread($fp, 5);
+ fclose($fp);
+ if ($test == "<?xml") {
+ $info = $obj->infoFromDescriptionFile($params[0]);
+ }
+ }
+ if (empty($info)) {
+ $info = $obj->infoFromTgzFile($params[0]);
+ }
+ if (PEAR::isError($info)) {
+ return $this->raiseError($info);
+ }
+ $obj->validatePackageInfo($info, $err, $warn);
+ $this->_displayValidationResults($err, $warn);
+ $this->ui->outputData($this->output, $command);
+ return true;
+ }
+
+ // }}}
+ // {{{ doCvsTag()
+
+ function doCvsTag($command, $options, $params)
+ {
+ $this->output = '';
+ $_cmd = $command;
+ if (sizeof($params) < 1) {
+ $help = $this->getHelp($command);
+ return $this->raiseError("$command: missing parameter: $help[0]");
+ }
+ $obj = new PEAR_Common;
+ $info = $obj->infoFromDescriptionFile($params[0]);
+ if (PEAR::isError($info)) {
+ return $this->raiseError($info);
+ }
+ $err = $warn = array();
+ $obj->validatePackageInfo($info, $err, $warn);
+ if (!$this->_displayValidationResults($err, $warn, true)) {
+ $this->ui->outputData($this->output, $command);
+ break;
+ }
+ $version = $info['version'];
+ $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $version);
+ $cvstag = "RELEASE_$cvsversion";
+ $files = array_keys($info['filelist']);
+ $command = "cvs";
+ if (isset($options['quiet'])) {
+ $command .= ' -q';
+ }
+ if (isset($options['reallyquiet'])) {
+ $command .= ' -Q';
+ }
+ $command .= ' tag';
+ if (isset($options['slide'])) {
+ $command .= ' -F';
+ }
+ if (isset($options['delete'])) {
+ $command .= ' -d';
+ }
+ $command .= ' ' . $cvstag . ' ' . escapeshellarg($params[0]);
+ foreach ($files as $file) {
+ $command .= ' ' . escapeshellarg($file);
+ }
+ if ($this->config->get('verbose') > 1) {
+ $this->output .= "+ $command\n";
+ }
+ $this->output .= "+ $command\n";
+ if (empty($options['dry-run'])) {
+ $fp = popen($command, "r");
+ while ($line = fgets($fp, 1024)) {
+ $this->output .= rtrim($line)."\n";
+ }
+ pclose($fp);
+ }
+ $this->ui->outputData($this->output, $_cmd);
+ return true;
+ }
+
+ // }}}
+ // {{{ doCvsDiff()
+
+ function doCvsDiff($command, $options, $params)
+ {
+ $this->output = '';
+ if (sizeof($params) < 1) {
+ $help = $this->getHelp($command);
+ return $this->raiseError("$command: missing parameter: $help[0]");
+ }
+ $obj = new PEAR_Common;
+ $info = $obj->infoFromDescriptionFile($params[0]);
+ if (PEAR::isError($info)) {
+ return $this->raiseError($info);
+ }
+ $files = array_keys($info['filelist']);
+ $cmd = "cvs";
+ if (isset($options['quiet'])) {
+ $cmd .= ' -q';
+ unset($options['quiet']);
+ }
+ if (isset($options['reallyquiet'])) {
+ $cmd .= ' -Q';
+ unset($options['reallyquiet']);
+ }
+ if (isset($options['release'])) {
+ $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $options['release']);
+ $cvstag = "RELEASE_$cvsversion";
+ $options['revision'] = $cvstag;
+ unset($options['release']);
+ }
+ $execute = true;
+ if (isset($options['dry-run'])) {
+ $execute = false;
+ unset($options['dry-run']);
+ }
+ $cmd .= ' diff';
+ // the rest of the options are passed right on to "cvs diff"
+ foreach ($options as $option => $optarg) {
+ $arg = @$this->commands[$command]['options'][$option]['arg'];
+ $short = @$this->commands[$command]['options'][$option]['shortopt'];
+ $cmd .= $short ? " -$short" : " --$option";
+ if ($arg && $optarg) {
+ $cmd .= ($short ? '' : '=') . escapeshellarg($optarg);
+ }
+ }
+ foreach ($files as $file) {
+ $cmd .= ' ' . escapeshellarg($file);
+ }
+ if ($this->config->get('verbose') > 1) {
+ $this->output .= "+ $cmd\n";
+ }
+ if ($execute) {
+ $fp = popen($cmd, "r");
+ while ($line = fgets($fp, 1024)) {
+ $this->output .= rtrim($line)."\n";
+ }
+ pclose($fp);
+ }
+ $this->ui->outputData($this->output, $command);
+ return true;
+ }
+
+ // }}}
+ // {{{ doRunTests()
+
+ function doRunTests($command, $options, $params)
+ {
+ include_once 'PEAR/RunTest.php';
+ $log = new PEAR_Common;
+ $run = new PEAR_RunTest($log);
+ $tests = array();
+ if (isset($options['recur'])) {
+ $depth = 4;
+ } else {
+ $depth = 1;
+ }
+ foreach ($params as $p) {
+ if (is_dir($p)) {
+ $dir = System::find(array($p, '-type', 'f',
+ '-maxdepth', $depth,
+ '-name', '*.phpt'));
+ $tests = array_merge($tests, $dir);
+ } else {
+ $tests[] = $p;
+ }
+ }
+ foreach ($tests as $t) {
+ $run->run($t);
+ }
+
+ return true;
+ /*
+ $cwd = getcwd();
+ $php = $this->config->get('php_bin');
+ putenv("TEST_PHP_EXECUTABLE=$php");
+ // all core PEAR tests use this constant to determine whether they should be run or not
+ putenv("PHP_PEAR_RUNTESTS=1");
+ $ip = ini_get("include_path");
+ $ps = OS_WINDOWS ? ';' : ':';
+ $run_tests = $rtsts = $this->config->get('php_dir') . DIRECTORY_SEPARATOR . 'run-tests.php';
+ if (!file_exists($run_tests)) {
+ $run_tests = PEAR_INSTALL_DIR . DIRECTORY_SEPARATOR . 'run-tests.php';
+ if (!file_exists($run_tests)) {
+ return $this->raiseError("No run-tests.php file found. Please copy this ".
+ "file from the sources of your PHP distribution to $rtsts");
+ }
+ }
+ if (OS_WINDOWS) {
+ // note, this requires a slightly modified version of run-tests.php
+ // for some setups
+ // unofficial download location is in the pear-dev archives
+ $argv = $params;
+ array_unshift($argv, $run_tests);
+ $argc = count($argv);
+ include $run_tests;
+ } else {
+ $plist = implode(' ', $params);
+ $cmd = "$php -d include_path=$cwd$ps$ip -f $run_tests -- $plist";
+ system($cmd);
+ }
+ return true;
+ */
+ }
+
+ // }}}
+ // {{{ doPackageDependencies()
+
+ function doPackageDependencies($command, $options, $params)
+ {
+ // $params[0] -> the PEAR package to list its information
+ if (sizeof($params) != 1) {
+ return $this->raiseError("bad parameter(s), try \"help $command\"");
+ }
+
+ $obj = new PEAR_Common();
+ if (PEAR::isError($info = $obj->infoFromAny($params[0]))) {
+ return $this->raiseError($info);
+ }
+
+ if (is_array($info['release_deps'])) {
+ $data = array(
+ 'caption' => 'Dependencies for ' . $info['package'],
+ 'border' => true,
+ 'headline' => array("Type", "Name", "Relation", "Version"),
+ );
+
+ foreach ($info['release_deps'] as $d) {
+
+ if (isset($this->_deps_rel_trans[$d['rel']])) {
+ $rel = $this->_deps_rel_trans[$d['rel']];
+ } else {
+ $rel = $d['rel'];
+ }
+
+ if (isset($this->_deps_type_trans[$d['type']])) {
+ $type = ucfirst($this->_deps_type_trans[$d['type']]);
+ } else {
+ $type = $d['type'];
+ }
+
+ if (isset($d['name'])) {
+ $name = $d['name'];
+ } else {
+ $name = '';
+ }
+
+ if (isset($d['version'])) {
+ $version = $d['version'];
+ } else {
+ $version = '';
+ }
+
+ $data['data'][] = array($type, $name, $rel, $version);
+ }
+
+ $this->ui->outputData($data, $command);
+ return true;
+ }
+
+ // Fallback
+ $this->ui->outputData("This package does not have any dependencies.", $command);
+ }
+
+ // }}}
+ // {{{ doSign()
+
+ function doSign($command, $options, $params)
+ {
+ // should move most of this code into PEAR_Packager
+ // so it'll be easy to implement "pear package --sign"
+ if (sizeof($params) != 1) {
+ return $this->raiseError("bad parameter(s), try \"help $command\"");
+ }
+ if (!file_exists($params[0])) {
+ return $this->raiseError("file does not exist: $params[0]");
+ }
+ $obj = new PEAR_Common;
+ $info = $obj->infoFromTgzFile($params[0]);
+ if (PEAR::isError($info)) {
+ return $this->raiseError($info);
+ }
+ include_once "Archive/Tar.php";
+ include_once "System.php";
+ $tar = new Archive_Tar($params[0]);
+ $tmpdir = System::mktemp('-d pearsign');
+ if (!$tar->extractList('package.xml package.sig', $tmpdir)) {
+ return $this->raiseError("failed to extract tar file");
+ }
+ if (file_exists("$tmpdir/package.sig")) {
+ return $this->raiseError("package already signed");
+ }
+ @unlink("$tmpdir/package.sig");
+ $input = $this->ui->userDialog($command,
+ array('GnuPG Passphrase'),
+ array('password'));
+ $gpg = popen("gpg --batch --passphrase-fd 0 --armor --detach-sign --output $tmpdir/package.sig $tmpdir/package.xml 2>/dev/null", "w");
+ if (!$gpg) {
+ return $this->raiseError("gpg command failed");
+ }
+ fwrite($gpg, "$input[0]\r");
+ if (pclose($gpg) || !file_exists("$tmpdir/package.sig")) {
+ return $this->raiseError("gpg sign failed");
+ }
+ $tar->addModify("$tmpdir/package.sig", '', $tmpdir);
+ return true;
+ }
+
+ // }}}
+ // {{{ doMakeRPM()
+
+ /*
+
+ (cox)
+
+ TODO:
+
+ - Fill the rpm dependencies in the template file.
+
+ IDEAS:
+
+ - Instead of mapping the role to rpm vars, perhaps it's better
+
+ to use directly the pear cmd to install the files by itself
+
+ in %postrun so:
+
+ pear -d php_dir=%{_libdir}/php/pear -d test_dir=.. <package>
+
+ */
+
+ function doMakeRPM($command, $options, $params)
+ {
+ if (sizeof($params) != 1) {
+ return $this->raiseError("bad parameter(s), try \"help $command\"");
+ }
+ if (!file_exists($params[0])) {
+ return $this->raiseError("file does not exist: $params[0]");
+ }
+ include_once "Archive/Tar.php";
+ include_once "PEAR/Installer.php";
+ include_once "System.php";
+ $tar = new Archive_Tar($params[0]);
+ $tmpdir = System::mktemp('-d pear2rpm');
+ $instroot = System::mktemp('-d pear2rpm');
+ $tmp = $this->config->get('verbose');
+ $this->config->set('verbose', 0);
+ $installer = new PEAR_Installer($this->ui);
+ $info = $installer->install($params[0],
+ array('installroot' => $instroot,
+ 'nodeps' => true));
+ $pkgdir = "$info[package]-$info[version]";
+ $info['rpm_xml_dir'] = '/var/lib/pear';
+ $this->config->set('verbose', $tmp);
+ if (!$tar->extractList("package.xml", $tmpdir, $pkgdir)) {
+ return $this->raiseError("failed to extract $params[0]");
+ }
+ if (!file_exists("$tmpdir/package.xml")) {
+ return $this->raiseError("no package.xml found in $params[0]");
+ }
+ if (isset($options['spec-template'])) {
+ $spec_template = $options['spec-template'];
+ } else {
+ $spec_template = $this->config->get('data_dir') .
+ '/PEAR/template.spec';
+ }
+ if (isset($options['rpm-pkgname'])) {
+ $rpm_pkgname_format = $options['rpm-pkgname'];
+ } else {
+ $rpm_pkgname_format = "PEAR::%s";
+ }
+
+ $info['extra_headers'] = '';
+ $info['doc_files'] = '';
+ $info['files'] = '';
+ $info['rpm_package'] = sprintf($rpm_pkgname_format, $info['package']);
+ $srcfiles = 0;
+ foreach ($info['filelist'] as $name => $attr) {
+
+ if (!isset($attr['role'])) {
+ continue;
+ }
+ $name = preg_replace('![/:\\\\]!', '/', $name);
+ if ($attr['role'] == 'doc') {
+ $info['doc_files'] .= " $name";
+
+ // Map role to the rpm vars
+ } else {
+
+ $c_prefix = '%{_libdir}/php/pear';
+
+ switch ($attr['role']) {
+
+ case 'php':
+
+ $prefix = $c_prefix; break;
+
+ case 'ext':
+
+ $prefix = '%{_libdir}/php'; break; // XXX good place?
+
+ case 'src':
+
+ $srcfiles++;
+
+ $prefix = '%{_includedir}/php'; break; // XXX good place?
+
+ case 'test':
+
+ $prefix = "$c_prefix/tests/" . $info['package']; break;
+
+ case 'data':
+
+ $prefix = "$c_prefix/data/" . $info['package']; break;
+
+ case 'script':
+
+ $prefix = '%{_bindir}'; break;
+
+ }
+
+ $name = str_replace('\\', '/', $name);
+ $info['files'] .= "$prefix/$name\n";
+
+ }
+ }
+ if ($srcfiles > 0) {
+ include_once "OS/Guess.php";
+ $os = new OS_Guess;
+ $arch = $os->getCpu();
+ } else {
+ $arch = 'noarch';
+ }
+ $cfg = array('master_server', 'php_dir', 'ext_dir', 'doc_dir',
+ 'bin_dir', 'data_dir', 'test_dir');
+ foreach ($cfg as $k) {
+ $info[$k] = $this->config->get($k);
+ }
+ $info['arch'] = $arch;
+ $fp = @fopen($spec_template, "r");
+ if (!$fp) {
+ return $this->raiseError("could not open RPM spec file template $spec_template: $php_errormsg");
+ }
+ $spec_contents = preg_replace('/@([a-z0-9_-]+)@/e', '$info["\1"]', fread($fp, filesize($spec_template)));
+ fclose($fp);
+ $spec_file = "$info[rpm_package]-$info[version].spec";
+ $wp = fopen($spec_file, "wb");
+ if (!$wp) {
+ return $this->raiseError("could not write RPM spec file $spec_file: $php_errormsg");
+ }
+ fwrite($wp, $spec_contents);
+ fclose($wp);
+ $this->ui->outputData("Wrote RPM spec file $spec_file", $command);
+
+ return true;
+ }
+
+ // }}}
+}
+
+?>
diff --git a/pearlib/PEAR/Command/Registry.php b/pearlib/PEAR/Command/Registry.php
new file mode 100644
index 00000000..62cb4b00
--- /dev/null
+++ b/pearlib/PEAR/Command/Registry.php
@@ -0,0 +1,351 @@
+<?php
+// /* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <[email protected]> |
+// | |
+// +----------------------------------------------------------------------+
+//
+// $Id: Registry.php,v 1.36 2004/01/08 17:33:13 sniper Exp $
+
+require_once 'PEAR/Command/Common.php';
+require_once 'PEAR/Registry.php';
+require_once 'PEAR/Config.php';
+
+class PEAR_Command_Registry extends PEAR_Command_Common
+{
+ // {{{ properties
+
+ var $commands = array(
+ 'list' => array(
+ 'summary' => 'List Installed Packages',
+ 'function' => 'doList',
+ 'shortcut' => 'l',
+ 'options' => array(),
+ 'doc' => '[package]
+If invoked without parameters, this command lists the PEAR packages
+installed in your php_dir ({config php_dir)). With a parameter, it
+lists the files in that package.
+',
+ ),
+ 'shell-test' => array(
+ 'summary' => 'Shell Script Test',
+ 'function' => 'doShellTest',
+ 'shortcut' => 'st',
+ 'options' => array(),
+ 'doc' => '<package> [[relation] version]
+Tests if a package is installed in the system. Will exit(1) if it is not.
+ <relation> The version comparison operator. One of:
+ <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne
+ <version> The version to compare with
+'),
+ 'info' => array(
+ 'summary' => 'Display information about a package',
+ 'function' => 'doInfo',
+ 'shortcut' => 'in',
+ 'options' => array(),
+ 'doc' => '<package>
+Displays information about a package. The package argument may be a
+local package file, an URL to a package file, or the name of an
+installed package.'
+ )
+ );
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * PEAR_Command_Registry constructor.
+ *
+ * @access public
+ */
+ function PEAR_Command_Registry(&$ui, &$config)
+ {
+ parent::PEAR_Command_Common($ui, $config);
+ }
+
+ // }}}
+
+ // {{{ doList()
+
+ function _sortinfo($a, $b)
+ {
+ return strcmp($a['package'], $b['package']);
+ }
+
+ function doList($command, $options, $params)
+ {
+ $reg = new PEAR_Registry($this->config->get('php_dir'));
+ if (sizeof($params) == 0) {
+ $installed = $reg->packageInfo();
+ usort($installed, array(&$this, '_sortinfo'));
+ $i = $j = 0;
+ $data = array(
+ 'caption' => 'Installed packages:',
+ 'border' => true,
+ 'headline' => array('Package', 'Version', 'State')
+ );
+ foreach ($installed as $package) {
+ $data['data'][] = array($package['package'],
+ $package['version'],
+ @$package['release_state']);
+ }
+ if (count($installed)==0) {
+ $data = '(no packages installed)';
+ }
+ $this->ui->outputData($data, $command);
+ } else {
+ if (file_exists($params[0]) && !is_dir($params[0])) {
+ include_once "PEAR/Common.php";
+ $obj = &new PEAR_Common;
+ $info = $obj->infoFromAny($params[0]);
+ $headings = array('Package File', 'Install Path');
+ $installed = false;
+ } else {
+ $info = $reg->packageInfo($params[0]);
+ $headings = array('Type', 'Install Path');
+ $installed = true;
+ }
+ if (PEAR::isError($info)) {
+ return $this->raiseError($info);
+ }
+ if ($info === null) {
+ return $this->raiseError("`$params[0]' not installed");
+ }
+ $list = $info['filelist'];
+ if ($installed) {
+ $caption = 'Installed Files For ' . $params[0];
+ } else {
+ $caption = 'Contents of ' . basename($params[0]);
+ }
+ $data = array(
+ 'caption' => $caption,
+ 'border' => true,
+ 'headline' => $headings);
+ foreach ($list as $file => $att) {
+ if ($installed) {
+ if (empty($att['installed_as'])) {
+ continue;
+ }
+ $data['data'][] = array($att['role'], $att['installed_as']);
+ } else {
+ if (isset($att['baseinstalldir'])) {
+ $dest = $att['baseinstalldir'] . DIRECTORY_SEPARATOR .
+ $file;
+ } else {
+ $dest = $file;
+ }
+ switch ($att['role']) {
+ case 'test':
+ case 'data':
+ if ($installed) {
+ break 2;
+ }
+ $dest = '-- will not be installed --';
+ break;
+ case 'doc':
+ $dest = $this->config->get('doc_dir') . DIRECTORY_SEPARATOR .
+ $dest;
+ break;
+ case 'php':
+ default:
+ $dest = $this->config->get('php_dir') . DIRECTORY_SEPARATOR .
+ $dest;
+ }
+ $dest = preg_replace('!/+!', '/', $dest);
+ $file = preg_replace('!/+!', '/', $file);
+ $data['data'][] = array($file, $dest);
+ }
+ }
+ $this->ui->outputData($data, $command);
+
+
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ doShellTest()
+
+ function doShellTest($command, $options, $params)
+ {
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $reg = &new PEAR_Registry($this->config->get('php_dir'));
+ // "pear shell-test Foo"
+ if (sizeof($params) == 1) {
+ if (!$reg->packageExists($params[0])) {
+ exit(1);
+ }
+ // "pear shell-test Foo 1.0"
+ } elseif (sizeof($params) == 2) {
+ $v = $reg->packageInfo($params[0], 'version');
+ if (!$v || !version_compare("$v", "{$params[1]}", "ge")) {
+ exit(1);
+ }
+ // "pear shell-test Foo ge 1.0"
+ } elseif (sizeof($params) == 3) {
+ $v = $reg->packageInfo($params[0], 'version');
+ if (!$v || !version_compare("$v", "{$params[2]}", $params[1])) {
+ exit(1);
+ }
+ } else {
+ $this->popErrorHandling();
+ $this->raiseError("$command: expects 1 to 3 parameters");
+ exit(1);
+ }
+ }
+
+ // }}}
+ // {{{ doInfo
+
+ function doInfo($command, $options, $params)
+ {
+ // $params[0] The package for showing info
+ if (sizeof($params) != 1) {
+ return $this->raiseError("This command only accepts one param: ".
+ "the package you want information");
+ }
+ if (@is_file($params[0])) {
+ $obj = &new PEAR_Common();
+ $info = $obj->infoFromAny($params[0]);
+ } else {
+ $reg = &new PEAR_Registry($this->config->get('php_dir'));
+ $info = $reg->packageInfo($params[0]);
+ }
+ if (PEAR::isError($info)) {
+ return $info;
+ }
+ if (empty($info)) {
+ $this->raiseError("Nothing found for `$params[0]'");
+ return;
+ }
+ unset($info['filelist']);
+ unset($info['changelog']);
+ $keys = array_keys($info);
+ $longtext = array('description', 'summary');
+ foreach ($keys as $key) {
+ if (is_array($info[$key])) {
+ switch ($key) {
+ case 'maintainers': {
+ $i = 0;
+ $mstr = '';
+ foreach ($info[$key] as $m) {
+ if ($i++ > 0) {
+ $mstr .= "\n";
+ }
+ $mstr .= $m['name'] . " <";
+ if (isset($m['email'])) {
+ $mstr .= $m['email'];
+ } else {
+ $mstr .= $m['handle'] . '@php.net';
+ }
+ $mstr .= "> ($m[role])";
+ }
+ $info[$key] = $mstr;
+ break;
+ }
+ case 'release_deps': {
+ $i = 0;
+ $dstr = '';
+ foreach ($info[$key] as $d) {
+ if (isset($this->_deps_rel_trans[$d['rel']])) {
+ $rel = $this->_deps_rel_trans[$d['rel']];
+ } else {
+ $rel = $d['rel'];
+ }
+ if (isset($this->_deps_type_trans[$d['type']])) {
+ $type = ucfirst($this->_deps_type_trans[$d['type']]);
+ } else {
+ $type = $d['type'];
+ }
+ if (isset($d['name'])) {
+ $name = $d['name'] . ' ';
+ } else {
+ $name = '';
+ }
+ if (isset($d['version'])) {
+ $version = $d['version'] . ' ';
+ } else {
+ $version = '';
+ }
+ $dstr .= "$type $name$rel $version\n";
+ }
+ $info[$key] = $dstr;
+ break;
+ }
+ case 'provides' : {
+ $debug = $this->config->get('verbose');
+ if ($debug < 2) {
+ $pstr = 'Classes: ';
+ } else {
+ $pstr = '';
+ }
+ $i = 0;
+ foreach ($info[$key] as $p) {
+ if ($debug < 2 && $p['type'] != "class") {
+ continue;
+ }
+ // Only print classes when verbosity mode is < 2
+ if ($debug < 2) {
+ if ($i++ > 0) {
+ $pstr .= ", ";
+ }
+ $pstr .= $p['name'];
+ } else {
+ if ($i++ > 0) {
+ $pstr .= "\n";
+ }
+ $pstr .= ucfirst($p['type']) . " " . $p['name'];
+ if (isset($p['explicit']) && $p['explicit'] == 1) {
+ $pstr .= " (explicit)";
+ }
+ }
+ }
+ $info[$key] = $pstr;
+ break;
+ }
+ default: {
+ $info[$key] = implode(", ", $info[$key]);
+ break;
+ }
+ }
+ }
+ if ($key == '_lastmodified') {
+ $hdate = date('Y-m-d', $info[$key]);
+ unset($info[$key]);
+ $info['Last Modified'] = $hdate;
+ } else {
+ $info[$key] = trim($info[$key]);
+ if (in_array($key, $longtext)) {
+ $info[$key] = preg_replace('/ +/', ' ', $info[$key]);
+ }
+ }
+ }
+ $caption = 'About ' . $info['package'] . '-' . $info['version'];
+ $data = array(
+ 'caption' => $caption,
+ 'border' => true);
+ foreach ($info as $key => $value) {
+ $key = ucwords(trim(str_replace('_', ' ', $key)));
+ $data['data'][] = array($key, $value);
+ }
+ $data['raw'] = $info;
+
+ $this->ui->outputData($data, 'package-info');
+ }
+
+ // }}}
+}
+
+?>
diff --git a/pearlib/PEAR/Command/Remote.php b/pearlib/PEAR/Command/Remote.php
new file mode 100644
index 00000000..bbd16093
--- /dev/null
+++ b/pearlib/PEAR/Command/Remote.php
@@ -0,0 +1,435 @@
+<?php
+// /* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <[email protected]> |
+// | |
+// +----------------------------------------------------------------------+
+//
+// $Id: Remote.php,v 1.39 2004/04/03 15:56:00 cellog Exp $
+
+require_once 'PEAR/Command/Common.php';
+require_once 'PEAR/Common.php';
+require_once 'PEAR/Remote.php';
+require_once 'PEAR/Registry.php';
+
+class PEAR_Command_Remote extends PEAR_Command_Common
+{
+ // {{{ command definitions
+
+ var $commands = array(
+ 'remote-info' => array(
+ 'summary' => 'Information About Remote Packages',
+ 'function' => 'doRemoteInfo',
+ 'shortcut' => 'ri',
+ 'options' => array(),
+ 'doc' => '<package>
+Get details on a package from the server.',
+ ),
+ 'list-upgrades' => array(
+ 'summary' => 'List Available Upgrades',
+ 'function' => 'doListUpgrades',
+ 'shortcut' => 'lu',
+ 'options' => array(),
+ 'doc' => '
+List releases on the server of packages you have installed where
+a newer version is available with the same release state (stable etc.).'
+ ),
+ 'remote-list' => array(
+ 'summary' => 'List Remote Packages',
+ 'function' => 'doRemoteList',
+ 'shortcut' => 'rl',
+ 'options' => array(),
+ 'doc' => '
+Lists the packages available on the configured server along with the
+latest stable release of each package.',
+ ),
+ 'search' => array(
+ 'summary' => 'Search remote package database',
+ 'function' => 'doSearch',
+ 'shortcut' => 'sp',
+ 'options' => array(),
+ 'doc' => '
+Lists all packages which match the search parameters (first param
+is package name, second package info)',
+ ),
+ 'list-all' => array(
+ 'summary' => 'List All Packages',
+ 'function' => 'doListAll',
+ 'shortcut' => 'la',
+ 'options' => array(),
+ 'doc' => '
+Lists the packages available on the configured server along with the
+latest stable release of each package.',
+ ),
+ 'download' => array(
+ 'summary' => 'Download Package',
+ 'function' => 'doDownload',
+ 'shortcut' => 'd',
+ 'options' => array(
+ 'nocompress' => array(
+ 'shortopt' => 'Z',
+ 'doc' => 'download an uncompressed (.tar) file',
+ ),
+ ),
+ 'doc' => '{package|package-version}
+Download a package tarball. The file will be named as suggested by the
+server, for example if you download the DB package and the latest stable
+version of DB is 1.2, the downloaded file will be DB-1.2.tgz.',
+ ),
+ 'clear-cache' => array(
+ 'summary' => 'Clear XML-RPC Cache',
+ 'function' => 'doClearCache',
+ 'shortcut' => 'cc',
+ 'options' => array(),
+ 'doc' => '
+Clear the XML-RPC cache. See also the cache_ttl configuration
+parameter.
+',
+ ),
+ );
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * PEAR_Command_Remote constructor.
+ *
+ * @access public
+ */
+ function PEAR_Command_Remote(&$ui, &$config)
+ {
+ parent::PEAR_Command_Common($ui, $config);
+ }
+
+ // }}}
+
+ // {{{ doRemoteInfo()
+
+ function doRemoteInfo($command, $options, $params)
+ {
+ if (sizeof($params) != 1) {
+ return $this->raiseError("$command expects one param: the remote package name");
+ }
+ $r = new PEAR_Remote($this->config);
+ $info = $r->call('package.info', $params[0]);
+ if (PEAR::isError($info)) {
+ return $this->raiseError($info);
+ }
+
+ $reg = new PEAR_Registry($this->config->get('php_dir'));
+ $installed = $reg->packageInfo($info['name']);
+ $info['installed'] = $installed['version'] ? $installed['version'] : '- no -';
+
+ $this->ui->outputData($info, $command);
+
+ return true;
+ }
+
+ // }}}
+ // {{{ doRemoteList()
+
+ function doRemoteList($command, $options, $params)
+ {
+ $r = new PEAR_Remote($this->config);
+ $list_options = false;
+ if ($this->config->get('preferred_state') == 'stable')
+ $list_options = true;
+ $available = $r->call('package.listAll', $list_options);
+ if (PEAR::isError($available)) {
+ return $this->raiseError($available);
+ }
+ $i = $j = 0;
+ $data = array(
+ 'caption' => 'Available packages:',
+ 'border' => true,
+ 'headline' => array('Package', 'Version'),
+ );
+ foreach ($available as $name => $info) {
+ $data['data'][] = array($name, isset($info['stable']) ? $info['stable'] : '-n/a-');
+ }
+ if (count($available)==0) {
+ $data = '(no packages installed yet)';
+ }
+ $this->ui->outputData($data, $command);
+ return true;
+ }
+
+ // }}}
+ // {{{ doListAll()
+
+ function doListAll($command, $options, $params)
+ {
+ $r = new PEAR_Remote($this->config);
+ $reg = new PEAR_Registry($this->config->get('php_dir'));
+ $list_options = false;
+ if ($this->config->get('preferred_state') == 'stable')
+ $list_options = true;
+ $available = $r->call('package.listAll', $list_options);
+ if (PEAR::isError($available)) {
+ return $this->raiseError($available);
+ }
+ if (!is_array($available)) {
+ return $this->raiseError('The package list could not be fetched from the remote server. Please try again. (Debug info: "'.$available.'")');
+ }
+ $data = array(
+ 'caption' => 'All packages:',
+ 'border' => true,
+ 'headline' => array('Package', 'Latest', 'Local'),
+ );
+ $local_pkgs = $reg->listPackages();
+
+ foreach ($available as $name => $info) {
+ $installed = $reg->packageInfo($name);
+ $desc = $info['summary'];
+ if (isset($params[$name]))
+ $desc .= "\n\n".$info['description'];
+
+ if (isset($options['mode']))
+ {
+ if ($options['mode'] == 'installed' && !isset($installed['version']))
+ continue;
+ if ($options['mode'] == 'notinstalled' && isset($installed['version']))
+ continue;
+ if ($options['mode'] == 'upgrades'
+ && (!isset($installed['version']) || $installed['version'] == $info['stable']))
+ {
+ continue;
+ }
+ }
+ $pos = array_search(strtolower($name), $local_pkgs);
+ if ($pos !== false) {
+ unset($local_pkgs[$pos]);
+ }
+
+ $data['data'][$info['category']][] = array(
+ $name,
+ @$info['stable'],
+ @$installed['version'],
+ @$desc,
+ @$info['deps'],
+ );
+ }
+
+ foreach ($local_pkgs as $name) {
+ $info = $reg->packageInfo($name);
+ $data['data']['Local'][] = array(
+ $info['package'],
+ '',
+ $info['version'],
+ $info['summary'],
+ @$info['release_deps']
+ );
+ }
+
+ $this->ui->outputData($data, $command);
+ return true;
+ }
+
+ // }}}
+ // {{{ doSearch()
+
+ function doSearch($command, $options, $params)
+ {
+ if ((!isset($params[0]) || empty($params[0]))
+ && (!isset($params[1]) || empty($params[1])))
+ {
+ return $this->raiseError('no valid search string supplied');
+ };
+
+ $r = new PEAR_Remote($this->config);
+ $reg = new PEAR_Registry($this->config->get('php_dir'));
+ $available = $r->call('package.listAll', true, false);
+ if (PEAR::isError($available)) {
+ return $this->raiseError($available);
+ }
+ $data = array(
+ 'caption' => 'Matched packages:',
+ 'border' => true,
+ 'headline' => array('Package', 'Stable/(Latest)', 'Local'),
+ );
+
+ foreach ($available as $name => $info) {
+ $found = (!empty($params[0]) && stristr($name, $params[0]) !== false);
+ if (!$found && !(isset($params[1]) && !empty($params[1])
+ && (stristr($info['summary'], $params[1]) !== false
+ || stristr($info['description'], $params[1]) !== false)))
+ {
+ continue;
+ };
+
+ $installed = $reg->packageInfo($name);
+ $desc = $info['summary'];
+ if (isset($params[$name]))
+ $desc .= "\n\n".$info['description'];
+
+ $unstable = '';
+ if ($info['unstable']) {
+ $unstable = '/(' . $info['unstable'] . $info['state'] . ')';
+ }
+ if (!isset($info['stable']) || !$info['stable']) {
+ $info['stable'] = 'none';
+ }
+ $data['data'][$info['category']][] = array(
+ $name,
+ $info['stable'] . $unstable,
+ $installed['version'],
+ $desc,
+ );
+ }
+ if (!isset($data['data'])) {
+ return $this->raiseError('no packages found');
+ }
+ $this->ui->outputData($data, $command);
+ return true;
+ }
+
+ // }}}
+ // {{{ doDownload()
+
+ function doDownload($command, $options, $params)
+ {
+ //$params[0] -> The package to download
+ if (count($params) != 1) {
+ return PEAR::raiseError("download expects one argument: the package to download");
+ }
+ $server = $this->config->get('master_server');
+ if (!ereg('^http://', $params[0])) {
+ $getoption = isset($options['nocompress'])&&$options['nocompress']==1?'?uncompress=on':'';
+ $pkgfile = "http://$server/get/$params[0]".$getoption;
+ } else {
+ $pkgfile = $params[0];
+ }
+ $this->bytes_downloaded = 0;
+ $saved = PEAR_Common::downloadHttp($pkgfile, $this->ui, '.',
+ array(&$this, 'downloadCallback'));
+ if (PEAR::isError($saved)) {
+ return $this->raiseError($saved);
+ }
+ $fname = basename($saved);
+ $this->ui->outputData("File $fname downloaded ($this->bytes_downloaded bytes)", $command);
+ return true;
+ }
+
+ function downloadCallback($msg, $params = null)
+ {
+ if ($msg == 'done') {
+ $this->bytes_downloaded = $params;
+ }
+ }
+
+ // }}}
+ // {{{ doListUpgrades()
+
+ function doListUpgrades($command, $options, $params)
+ {
+ include_once "PEAR/Registry.php";
+ $remote = new PEAR_Remote($this->config);
+ if (empty($params[0])) {
+ $state = $this->config->get('preferred_state');
+ } else {
+ $state = $params[0];
+ }
+ $caption = 'Available Upgrades';
+ if (empty($state) || $state == 'any') {
+ $latest = $remote->call("package.listLatestReleases");
+ } else {
+ $latest = $remote->call("package.listLatestReleases", $state);
+ $caption .= ' (' . implode(', ', PEAR_Common::betterStates($state, true)) . ')';
+ }
+ $caption .= ':';
+ if (PEAR::isError($latest)) {
+ return $latest;
+ }
+ $reg = new PEAR_Registry($this->config->get('php_dir'));
+ $inst = array_flip($reg->listPackages());
+ $data = array(
+ 'caption' => $caption,
+ 'border' => 1,
+ 'headline' => array('Package', 'Local', 'Remote', 'Size'),
+ );
+ foreach ((array)$latest as $pkg => $info) {
+ $package = strtolower($pkg);
+ if (!isset($inst[$package])) {
+ // skip packages we don't have installed
+ continue;
+ }
+ extract($info);
+ $pkginfo = $reg->packageInfo($package);
+ $inst_version = $pkginfo['version'];
+ $inst_state = $pkginfo['release_state'];
+ if (version_compare("$version", "$inst_version", "le")) {
+ // installed version is up-to-date
+ continue;
+ }
+ if ($filesize >= 20480) {
+ $filesize += 1024 - ($filesize % 1024);
+ $fs = sprintf("%dkB", $filesize / 1024);
+ } elseif ($filesize > 0) {
+ $filesize += 103 - ($filesize % 103);
+ $fs = sprintf("%.1fkB", $filesize / 1024.0);
+ } else {
+ $fs = " -"; // XXX center instead
+ }
+ $data['data'][] = array($pkg, "$inst_version ($inst_state)", "$version ($state)", $fs);
+ }
+ if (empty($data['data'])) {
+ $this->ui->outputData('No upgrades available');
+ } else {
+ $this->ui->outputData($data, $command);
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ doClearCache()
+
+ function doClearCache($command, $options, $params)
+ {
+ $cache_dir = $this->config->get('cache_dir');
+ $verbose = $this->config->get('verbose');
+ $output = '';
+ if (!($dp = @opendir($cache_dir))) {
+ return $this->raiseError("opendir($cache_dir) failed: $php_errormsg");
+ }
+ if ($verbose >= 1) {
+ $output .= "reading directory $cache_dir\n";
+ }
+ $num = 0;
+ while ($ent = readdir($dp)) {
+ if (preg_match('/^xmlrpc_cache_[a-z0-9]{32}$/', $ent)) {
+ $path = $cache_dir . DIRECTORY_SEPARATOR . $ent;
+ $ok = @unlink($path);
+ if ($ok) {
+ if ($verbose >= 2) {
+ $output .= "deleted $path\n";
+ }
+ $num++;
+ } elseif ($verbose >= 1) {
+ $output .= "failed to delete $path\n";
+ }
+ }
+ }
+ closedir($dp);
+ if ($verbose >= 1) {
+ $output .= "$num cache entries cleared\n";
+ }
+ $this->ui->outputData(rtrim($output), $command);
+ return $num;
+ }
+
+ // }}}
+}
+
+?>
diff --git a/pearlib/PEAR/Common.php b/pearlib/PEAR/Common.php
new file mode 100644
index 00000000..7638b4dc
--- /dev/null
+++ b/pearlib/PEAR/Common.php
@@ -0,0 +1,2081 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Bakken <[email protected]> |
+// | Tomas V.V.Cox <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Common.php,v 1.126.2.1 2004/10/19 04:20:58 cellog Exp $
+
+require_once 'PEAR.php';
+require_once 'Archive/Tar.php';
+require_once 'System.php';
+require_once 'PEAR/Config.php';
+
+// {{{ constants and globals
+
+/**
+ * PEAR_Common error when an invalid PHP file is passed to PEAR_Common::analyzeSourceCode()
+ */
+define('PEAR_COMMON_ERROR_INVALIDPHP', 1);
+define('_PEAR_COMMON_PACKAGE_NAME_PREG', '[A-Za-z][a-zA-Z0-9_]+');
+define('PEAR_COMMON_PACKAGE_NAME_PREG', '/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '$/');
+
+// this should allow: 1, 1.0, 1.0RC1, 1.0dev, 1.0dev123234234234, 1.0a1, 1.0b1, 1.0pl1
+define('_PEAR_COMMON_PACKAGE_VERSION_PREG', '\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?');
+define('PEAR_COMMON_PACKAGE_VERSION_PREG', '/^' . _PEAR_COMMON_PACKAGE_VERSION_PREG . '$/i');
+
+// XXX far from perfect :-)
+define('PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '/^(' . _PEAR_COMMON_PACKAGE_NAME_PREG . ')(-([.0-9a-zA-Z]+))?$/');
+
+/**
+ * List of temporary files and directories registered by
+ * PEAR_Common::addTempFile().
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_tempfiles'] = array();
+
+/**
+ * Valid maintainer roles
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_maintainer_roles'] = array('lead','developer','contributor','helper');
+
+/**
+ * Valid release states
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_release_states'] = array('alpha','beta','stable','snapshot','devel');
+
+/**
+ * Valid dependency types
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_dependency_types'] = array('pkg','ext','php','prog','ldlib','rtlib','os','websrv','sapi');
+
+/**
+ * Valid dependency relations
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_dependency_relations'] = array('has','eq','lt','le','gt','ge','not');
+
+/**
+ * Valid file roles
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_file_roles'] = array('php','ext','test','doc','data','src','script');
+
+/**
+ * Valid replacement types
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_replacement_types'] = array('php-const', 'pear-config', 'package-info');
+
+/**
+ * Valid "provide" types
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_provide_types'] = array('ext', 'prog', 'class', 'function', 'feature', 'api');
+
+/**
+ * Valid "provide" types
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_script_phases'] = array('pre-install', 'post-install', 'pre-uninstall', 'post-uninstall', 'pre-build', 'post-build', 'pre-configure', 'post-configure', 'pre-setup', 'post-setup');
+
+// }}}
+
+/**
+ * Class providing common functionality for PEAR administration classes.
+ * @deprecated This class will disappear, and its components will be spread
+ * into smaller classes, like the AT&T breakup
+ */
+class PEAR_Common extends PEAR
+{
+ // {{{ properties
+
+ /** stack of elements, gives some sort of XML context */
+ var $element_stack = array();
+
+ /** name of currently parsed XML element */
+ var $current_element;
+
+ /** array of attributes of the currently parsed XML element */
+ var $current_attributes = array();
+
+ /** assoc with information about a package */
+ var $pkginfo = array();
+
+ /**
+ * User Interface object (PEAR_Frontend_* class). If null,
+ * the log() method uses print.
+ * @var object
+ */
+ var $ui = null;
+
+ /**
+ * Configuration object (PEAR_Config).
+ * @var object
+ */
+ var $config = null;
+
+ var $current_path = null;
+
+ /**
+ * PEAR_SourceAnalyzer instance
+ * @var object
+ */
+ var $source_analyzer = null;
+ /**
+ * Flag variable used to mark a valid package file
+ * @var boolean
+ * @access private
+ */
+ var $_validPackageFile;
+
+ // }}}
+
+ // {{{ constructor
+
+ /**
+ * PEAR_Common constructor
+ *
+ * @access public
+ */
+ function PEAR_Common()
+ {
+ parent::PEAR();
+ $this->config = &PEAR_Config::singleton();
+ $this->debug = $this->config->get('verbose');
+ }
+
+ // }}}
+ // {{{ destructor
+
+ /**
+ * PEAR_Common destructor
+ *
+ * @access private
+ */
+ function _PEAR_Common()
+ {
+ // doesn't work due to bug #14744
+ //$tempfiles = $this->_tempfiles;
+ $tempfiles =& $GLOBALS['_PEAR_Common_tempfiles'];
+ while ($file = array_shift($tempfiles)) {
+ if (@is_dir($file)) {
+ System::rm(array('-rf', $file));
+ } elseif (file_exists($file)) {
+ unlink($file);
+ }
+ }
+ }
+
+ // }}}
+ // {{{ addTempFile()
+
+ /**
+ * Register a temporary file or directory. When the destructor is
+ * executed, all registered temporary files and directories are
+ * removed.
+ *
+ * @param string $file name of file or directory
+ *
+ * @return void
+ *
+ * @access public
+ */
+ function addTempFile($file)
+ {
+ $GLOBALS['_PEAR_Common_tempfiles'][] = $file;
+ }
+
+ // }}}
+ // {{{ mkDirHier()
+
+ /**
+ * Wrapper to System::mkDir(), creates a directory as well as
+ * any necessary parent directories.
+ *
+ * @param string $dir directory name
+ *
+ * @return bool TRUE on success, or a PEAR error
+ *
+ * @access public
+ */
+ function mkDirHier($dir)
+ {
+ $this->log(2, "+ create dir $dir");
+ return System::mkDir(array('-p', $dir));
+ }
+
+ // }}}
+ // {{{ log()
+
+ /**
+ * Logging method.
+ *
+ * @param int $level log level (0 is quiet, higher is noisier)
+ * @param string $msg message to write to the log
+ *
+ * @return void
+ *
+ * @access public
+ */
+ function log($level, $msg, $append_crlf = true)
+ {
+ if ($this->debug >= $level) {
+ if (is_object($this->ui)) {
+ $this->ui->log($msg, $append_crlf);
+ } else {
+ print "$msg\n";
+ }
+ }
+ }
+
+ // }}}
+ // {{{ mkTempDir()
+
+ /**
+ * Create and register a temporary directory.
+ *
+ * @param string $tmpdir (optional) Directory to use as tmpdir.
+ * Will use system defaults (for example
+ * /tmp or c:\windows\temp) if not specified
+ *
+ * @return string name of created directory
+ *
+ * @access public
+ */
+ function mkTempDir($tmpdir = '')
+ {
+ if ($tmpdir) {
+ $topt = array('-t', $tmpdir);
+ } else {
+ $topt = array();
+ }
+ $topt = array_merge($topt, array('-d', 'pear'));
+ if (!$tmpdir = System::mktemp($topt)) {
+ return false;
+ }
+ $this->addTempFile($tmpdir);
+ return $tmpdir;
+ }
+
+ // }}}
+ // {{{ setFrontendObject()
+
+ /**
+ * Set object that represents the frontend to be used.
+ *
+ * @param object Reference of the frontend object
+ * @return void
+ * @access public
+ */
+ function setFrontendObject(&$ui)
+ {
+ $this->ui = &$ui;
+ }
+
+ // }}}
+
+ // {{{ _unIndent()
+
+ /**
+ * Unindent given string (?)
+ *
+ * @param string $str The string that has to be unindented.
+ * @return string
+ * @access private
+ */
+ function _unIndent($str)
+ {
+ // remove leading newlines
+ $str = preg_replace('/^[\r\n]+/', '', $str);
+ // find whitespace at the beginning of the first line
+ $indent_len = strspn($str, " \t");
+ $indent = substr($str, 0, $indent_len);
+ $data = '';
+ // remove the same amount of whitespace from following lines
+ foreach (explode("\n", $str) as $line) {
+ if (substr($line, 0, $indent_len) == $indent) {
+ $data .= substr($line, $indent_len) . "\n";
+ }
+ }
+ return $data;
+ }
+
+ // }}}
+ // {{{ _element_start()
+
+ /**
+ * XML parser callback for starting elements. Used while package
+ * format version is not yet known.
+ *
+ * @param resource $xp XML parser resource
+ * @param string $name name of starting element
+ * @param array $attribs element attributes, name => value
+ *
+ * @return void
+ *
+ * @access private
+ */
+ function _element_start($xp, $name, $attribs)
+ {
+ array_push($this->element_stack, $name);
+ $this->current_element = $name;
+ $spos = sizeof($this->element_stack) - 2;
+ $this->prev_element = ($spos >= 0) ? $this->element_stack[$spos] : '';
+ $this->current_attributes = $attribs;
+ switch ($name) {
+ case 'package': {
+ $this->_validPackageFile = true;
+ if (isset($attribs['version'])) {
+ $vs = preg_replace('/[^0-9a-z]/', '_', $attribs['version']);
+ } else {
+ $vs = '1_0';
+ }
+ $elem_start = '_element_start_'. $vs;
+ $elem_end = '_element_end_'. $vs;
+ $cdata = '_pkginfo_cdata_'. $vs;
+ if (!method_exists($this, $elem_start) ||
+ !method_exists($this, $elem_end) ||
+ !method_exists($this, $cdata)) {
+ $this->raiseError("No handlers for package.xml version $attribs[version]");
+ return;
+ }
+ xml_set_element_handler($xp, $elem_start, $elem_end);
+ xml_set_character_data_handler($xp, $cdata);
+ break;
+ }
+ }
+ }
+
+ // }}}
+ // {{{ _element_end()
+
+ /**
+ * XML parser callback for ending elements. Used while package
+ * format version is not yet known.
+ *
+ * @param resource $xp XML parser resource
+ * @param string $name name of ending element
+ *
+ * @return void
+ *
+ * @access private
+ */
+ function _element_end($xp, $name)
+ {
+ }
+
+ // }}}
+
+ // Support for package DTD v1.0:
+ // {{{ _element_start_1_0()
+
+ /**
+ * XML parser callback for ending elements. Used for version 1.0
+ * packages.
+ *
+ * @param resource $xp XML parser resource
+ * @param string $name name of ending element
+ *
+ * @return void
+ *
+ * @access private
+ */
+ function _element_start_1_0($xp, $name, $attribs)
+ {
+ array_push($this->element_stack, $name);
+ $this->current_element = $name;
+ $spos = sizeof($this->element_stack) - 2;
+ $this->prev_element = ($spos >= 0) ? $this->element_stack[$spos] : '';
+ $this->current_attributes = $attribs;
+ $this->cdata = '';
+ switch ($name) {
+ case 'dir':
+ if ($this->in_changelog) {
+ break;
+ }
+ if ($attribs['name'] != '/') {
+ $this->dir_names[] = $attribs['name'];
+ }
+ if (isset($attribs['baseinstalldir'])) {
+ $this->dir_install = $attribs['baseinstalldir'];
+ }
+ if (isset($attribs['role'])) {
+ $this->dir_role = $attribs['role'];
+ }
+ break;
+ case 'file':
+ if ($this->in_changelog) {
+ break;
+ }
+ if (isset($attribs['name'])) {
+ $path = '';
+ if (count($this->dir_names)) {
+ foreach ($this->dir_names as $dir) {
+ $path .= $dir . DIRECTORY_SEPARATOR;
+ }
+ }
+ $path .= $attribs['name'];
+ unset($attribs['name']);
+ $this->current_path = $path;
+ $this->filelist[$path] = $attribs;
+ // Set the baseinstalldir only if the file don't have this attrib
+ if (!isset($this->filelist[$path]['baseinstalldir']) &&
+ isset($this->dir_install))
+ {
+ $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
+ }
+ // Set the Role
+ if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
+ $this->filelist[$path]['role'] = $this->dir_role;
+ }
+ }
+ break;
+ case 'replace':
+ if (!$this->in_changelog) {
+ $this->filelist[$this->current_path]['replacements'][] = $attribs;
+ }
+ break;
+ case 'maintainers':
+ $this->pkginfo['maintainers'] = array();
+ $this->m_i = 0; // maintainers array index
+ break;
+ case 'maintainer':
+ // compatibility check
+ if (!isset($this->pkginfo['maintainers'])) {
+ $this->pkginfo['maintainers'] = array();
+ $this->m_i = 0;
+ }
+ $this->pkginfo['maintainers'][$this->m_i] = array();
+ $this->current_maintainer =& $this->pkginfo['maintainers'][$this->m_i];
+ break;
+ case 'changelog':
+ $this->pkginfo['changelog'] = array();
+ $this->c_i = 0; // changelog array index
+ $this->in_changelog = true;
+ break;
+ case 'release':
+ if ($this->in_changelog) {
+ $this->pkginfo['changelog'][$this->c_i] = array();
+ $this->current_release = &$this->pkginfo['changelog'][$this->c_i];
+ } else {
+ $this->current_release = &$this->pkginfo;
+ }
+ break;
+ case 'deps':
+ if (!$this->in_changelog) {
+ $this->pkginfo['release_deps'] = array();
+ }
+ break;
+ case 'dep':
+ // dependencies array index
+ if (!$this->in_changelog) {
+ $this->d_i++;
+ $this->pkginfo['release_deps'][$this->d_i] = $attribs;
+ }
+ break;
+ case 'configureoptions':
+ if (!$this->in_changelog) {
+ $this->pkginfo['configure_options'] = array();
+ }
+ break;
+ case 'configureoption':
+ if (!$this->in_changelog) {
+ $this->pkginfo['configure_options'][] = $attribs;
+ }
+ break;
+ case 'provides':
+ if (empty($attribs['type']) || empty($attribs['name'])) {
+ break;
+ }
+ $attribs['explicit'] = true;
+ $this->pkginfo['provides']["$attribs[type];$attribs[name]"] = $attribs;
+ break;
+ }
+ }
+
+ // }}}
+ // {{{ _element_end_1_0()
+
+ /**
+ * XML parser callback for ending elements. Used for version 1.0
+ * packages.
+ *
+ * @param resource $xp XML parser resource
+ * @param string $name name of ending element
+ *
+ * @return void
+ *
+ * @access private
+ */
+ function _element_end_1_0($xp, $name)
+ {
+ $data = trim($this->cdata);
+ switch ($name) {
+ case 'name':
+ switch ($this->prev_element) {
+ case 'package':
+ // XXX should we check the package name here?
+ $this->pkginfo['package'] = ereg_replace('[^a-zA-Z0-9._]', '_', $data);
+ break;
+ case 'maintainer':
+ $this->current_maintainer['name'] = $data;
+ break;
+ }
+ break;
+ case 'summary':
+ $this->pkginfo['summary'] = $data;
+ break;
+ case 'description':
+ $data = $this->_unIndent($this->cdata);
+ $this->pkginfo['description'] = $data;
+ break;
+ case 'user':
+ $this->current_maintainer['handle'] = $data;
+ break;
+ case 'email':
+ $this->current_maintainer['email'] = $data;
+ break;
+ case 'role':
+ $this->current_maintainer['role'] = $data;
+ break;
+ case 'version':
+ $data = ereg_replace ('[^a-zA-Z0-9._\-]', '_', $data);
+ if ($this->in_changelog) {
+ $this->current_release['version'] = $data;
+ } else {
+ $this->pkginfo['version'] = $data;
+ }
+ break;
+ case 'date':
+ if ($this->in_changelog) {
+ $this->current_release['release_date'] = $data;
+ } else {
+ $this->pkginfo['release_date'] = $data;
+ }
+ break;
+ case 'notes':
+ // try to "de-indent" release notes in case someone
+ // has been over-indenting their xml ;-)
+ $data = $this->_unIndent($this->cdata);
+ if ($this->in_changelog) {
+ $this->current_release['release_notes'] = $data;
+ } else {
+ $this->pkginfo['release_notes'] = $data;
+ }
+ break;
+ case 'warnings':
+ if ($this->in_changelog) {
+ $this->current_release['release_warnings'] = $data;
+ } else {
+ $this->pkginfo['release_warnings'] = $data;
+ }
+ break;
+ case 'state':
+ if ($this->in_changelog) {
+ $this->current_release['release_state'] = $data;
+ } else {
+ $this->pkginfo['release_state'] = $data;
+ }
+ break;
+ case 'license':
+ if ($this->in_changelog) {
+ $this->current_release['release_license'] = $data;
+ } else {
+ $this->pkginfo['release_license'] = $data;
+ }
+ break;
+ case 'dep':
+ if ($data && !$this->in_changelog) {
+ $this->pkginfo['release_deps'][$this->d_i]['name'] = $data;
+ }
+ break;
+ case 'dir':
+ if ($this->in_changelog) {
+ break;
+ }
+ array_pop($this->dir_names);
+ break;
+ case 'file':
+ if ($this->in_changelog) {
+ break;
+ }
+ if ($data) {
+ $path = '';
+ if (count($this->dir_names)) {
+ foreach ($this->dir_names as $dir) {
+ $path .= $dir . DIRECTORY_SEPARATOR;
+ }
+ }
+ $path .= $data;
+ $this->filelist[$path] = $this->current_attributes;
+ // Set the baseinstalldir only if the file don't have this attrib
+ if (!isset($this->filelist[$path]['baseinstalldir']) &&
+ isset($this->dir_install))
+ {
+ $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
+ }
+ // Set the Role
+ if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
+ $this->filelist[$path]['role'] = $this->dir_role;
+ }
+ }
+ break;
+ case 'maintainer':
+ if (empty($this->pkginfo['maintainers'][$this->m_i]['role'])) {
+ $this->pkginfo['maintainers'][$this->m_i]['role'] = 'lead';
+ }
+ $this->m_i++;
+ break;
+ case 'release':
+ if ($this->in_changelog) {
+ $this->c_i++;
+ }
+ break;
+ case 'changelog':
+ $this->in_changelog = false;
+ break;
+ }
+ array_pop($this->element_stack);
+ $spos = sizeof($this->element_stack) - 1;
+ $this->current_element = ($spos > 0) ? $this->element_stack[$spos] : '';
+ $this->cdata = '';
+ }
+
+ // }}}
+ // {{{ _pkginfo_cdata_1_0()
+
+ /**
+ * XML parser callback for character data. Used for version 1.0
+ * packages.
+ *
+ * @param resource $xp XML parser resource
+ * @param string $name character data
+ *
+ * @return void
+ *
+ * @access private
+ */
+ function _pkginfo_cdata_1_0($xp, $data)
+ {
+ if (isset($this->cdata)) {
+ $this->cdata .= $data;
+ }
+ }
+
+ // }}}
+
+ // {{{ infoFromTgzFile()
+
+ /**
+ * Returns information about a package file. Expects the name of
+ * a gzipped tar file as input.
+ *
+ * @param string $file name of .tgz file
+ *
+ * @return array array with package information
+ *
+ * @access public
+ *
+ */
+ function infoFromTgzFile($file)
+ {
+ if (!@is_file($file)) {
+ return $this->raiseError("could not open file \"$file\"");
+ }
+ $tar = new Archive_Tar($file);
+ if ($this->debug <= 1) {
+ $tar->pushErrorHandling(PEAR_ERROR_RETURN);
+ }
+ $content = $tar->listContent();
+ if ($this->debug <= 1) {
+ $tar->popErrorHandling();
+ }
+ if (!is_array($content)) {
+ $file = realpath($file);
+ return $this->raiseError("Could not get contents of package \"$file\"".
+ '. Invalid tgz file.');
+ }
+ $xml = null;
+ foreach ($content as $file) {
+ $name = $file['filename'];
+ if ($name == 'package.xml') {
+ $xml = $name;
+ break;
+ } elseif (ereg('package.xml$', $name, $match)) {
+ $xml = $match[0];
+ break;
+ }
+ }
+ $tmpdir = System::mkTemp(array('-d', 'pear'));
+ $this->addTempFile($tmpdir);
+ if (!$xml || !$tar->extractList(array($xml), $tmpdir)) {
+ return $this->raiseError('could not extract the package.xml file');
+ }
+ return $this->infoFromDescriptionFile("$tmpdir/$xml");
+ }
+
+ // }}}
+ // {{{ infoFromDescriptionFile()
+
+ /**
+ * Returns information about a package file. Expects the name of
+ * a package xml file as input.
+ *
+ * @param string $descfile name of package xml file
+ *
+ * @return array array with package information
+ *
+ * @access public
+ *
+ */
+ function infoFromDescriptionFile($descfile)
+ {
+ if (!@is_file($descfile) || !is_readable($descfile) ||
+ (!$fp = @fopen($descfile, 'r'))) {
+ return $this->raiseError("Unable to open $descfile");
+ }
+
+ // read the whole thing so we only get one cdata callback
+ // for each block of cdata
+ $data = fread($fp, filesize($descfile));
+ return $this->infoFromString($data);
+ }
+
+ // }}}
+ // {{{ infoFromString()
+
+ /**
+ * Returns information about a package file. Expects the contents
+ * of a package xml file as input.
+ *
+ * @param string $data name of package xml file
+ *
+ * @return array array with package information
+ *
+ * @access public
+ *
+ */
+ function infoFromString($data)
+ {
+ require_once('PEAR/Dependency.php');
+ if (PEAR_Dependency::checkExtension($error, 'xml')) {
+ return $this->raiseError($error);
+ }
+ $xp = @xml_parser_create();
+ if (!$xp) {
+ return $this->raiseError('Unable to create XML parser');
+ }
+ xml_set_object($xp, $this);
+ xml_set_element_handler($xp, '_element_start', '_element_end');
+ xml_set_character_data_handler($xp, '_pkginfo_cdata');
+ xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, false);
+
+ $this->element_stack = array();
+ $this->pkginfo = array('provides' => array());
+ $this->current_element = false;
+ unset($this->dir_install);
+ $this->pkginfo['filelist'] = array();
+ $this->filelist =& $this->pkginfo['filelist'];
+ $this->dir_names = array();
+ $this->in_changelog = false;
+ $this->d_i = 0;
+ $this->cdata = '';
+ $this->_validPackageFile = false;
+
+ if (!xml_parse($xp, $data, 1)) {
+ $code = xml_get_error_code($xp);
+ $msg = sprintf("XML error: %s at line %d",
+ xml_error_string($code),
+ xml_get_current_line_number($xp));
+ xml_parser_free($xp);
+ return $this->raiseError($msg, $code);
+ }
+
+ xml_parser_free($xp);
+
+ if (!$this->_validPackageFile) {
+ return $this->raiseError('Invalid Package File, no <package> tag');
+ }
+ foreach ($this->pkginfo as $k => $v) {
+ if (!is_array($v)) {
+ $this->pkginfo[$k] = trim($v);
+ }
+ }
+ return $this->pkginfo;
+ }
+ // }}}
+ // {{{ infoFromAny()
+
+ /**
+ * Returns package information from different sources
+ *
+ * This method is able to extract information about a package
+ * from a .tgz archive or from a XML package definition file.
+ *
+ * @access public
+ * @param string Filename of the source ('package.xml', '<package>.tgz')
+ * @return string
+ */
+ function infoFromAny($info)
+ {
+ if (is_string($info) && file_exists($info)) {
+ $tmp = substr($info, -4);
+ if ($tmp == '.xml') {
+ $info = $this->infoFromDescriptionFile($info);
+ } elseif ($tmp == '.tar' || $tmp == '.tgz') {
+ $info = $this->infoFromTgzFile($info);
+ } else {
+ $fp = fopen($info, "r");
+ $test = fread($fp, 5);
+ fclose($fp);
+ if ($test == "<?xml") {
+ $info = $this->infoFromDescriptionFile($info);
+ } else {
+ $info = $this->infoFromTgzFile($info);
+ }
+ }
+ if (PEAR::isError($info)) {
+ return $this->raiseError($info);
+ }
+ }
+ return $info;
+ }
+
+ // }}}
+ // {{{ xmlFromInfo()
+
+ /**
+ * Return an XML document based on the package info (as returned
+ * by the PEAR_Common::infoFrom* methods).
+ *
+ * @param array $pkginfo package info
+ *
+ * @return string XML data
+ *
+ * @access public
+ */
+ function xmlFromInfo($pkginfo)
+ {
+ static $maint_map = array(
+ "handle" => "user",
+ "name" => "name",
+ "email" => "email",
+ "role" => "role",
+ );
+ $ret = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
+ $ret .= "<!DOCTYPE package SYSTEM \"http://pear.php.net/dtd/package-1.0\">\n";
+ $ret .= "<package version=\"1.0\">
+ <name>$pkginfo[package]</name>
+ <summary>".htmlspecialchars($pkginfo['summary'])."</summary>
+ <description>".htmlspecialchars($pkginfo['description'])."</description>
+ <maintainers>
+";
+ foreach ($pkginfo['maintainers'] as $maint) {
+ $ret .= " <maintainer>\n";
+ foreach ($maint_map as $idx => $elm) {
+ $ret .= " <$elm>";
+ $ret .= htmlspecialchars($maint[$idx]);
+ $ret .= "</$elm>\n";
+ }
+ $ret .= " </maintainer>\n";
+ }
+ $ret .= " </maintainers>\n";
+ $ret .= $this->_makeReleaseXml($pkginfo);
+ if (@sizeof($pkginfo['changelog']) > 0) {
+ $ret .= " <changelog>\n";
+ foreach ($pkginfo['changelog'] as $oldrelease) {
+ $ret .= $this->_makeReleaseXml($oldrelease, true);
+ }
+ $ret .= " </changelog>\n";
+ }
+ $ret .= "</package>\n";
+ return $ret;
+ }
+
+ // }}}
+ // {{{ _makeReleaseXml()
+
+ /**
+ * Generate part of an XML description with release information.
+ *
+ * @param array $pkginfo array with release information
+ * @param bool $changelog whether the result will be in a changelog element
+ *
+ * @return string XML data
+ *
+ * @access private
+ */
+ function _makeReleaseXml($pkginfo, $changelog = false)
+ {
+ // XXX QUOTE ENTITIES IN PCDATA, OR EMBED IN CDATA BLOCKS!!
+ $indent = $changelog ? " " : "";
+ $ret = "$indent <release>\n";
+ if (!empty($pkginfo['version'])) {
+ $ret .= "$indent <version>$pkginfo[version]</version>\n";
+ }
+ if (!empty($pkginfo['release_date'])) {
+ $ret .= "$indent <date>$pkginfo[release_date]</date>\n";
+ }
+ if (!empty($pkginfo['release_license'])) {
+ $ret .= "$indent <license>$pkginfo[release_license]</license>\n";
+ }
+ if (!empty($pkginfo['release_state'])) {
+ $ret .= "$indent <state>$pkginfo[release_state]</state>\n";
+ }
+ if (!empty($pkginfo['release_notes'])) {
+ $ret .= "$indent <notes>".htmlspecialchars($pkginfo['release_notes'])."</notes>\n";
+ }
+ if (!empty($pkginfo['release_warnings'])) {
+ $ret .= "$indent <warnings>".htmlspecialchars($pkginfo['release_warnings'])."</warnings>\n";
+ }
+ if (isset($pkginfo['release_deps']) && sizeof($pkginfo['release_deps']) > 0) {
+ $ret .= "$indent <deps>\n";
+ foreach ($pkginfo['release_deps'] as $dep) {
+ $ret .= "$indent <dep type=\"$dep[type]\" rel=\"$dep[rel]\"";
+ if (isset($dep['version'])) {
+ $ret .= " version=\"$dep[version]\"";
+ }
+ if (isset($dep['optional'])) {
+ $ret .= " optional=\"$dep[optional]\"";
+ }
+ if (isset($dep['name'])) {
+ $ret .= ">$dep[name]</dep>\n";
+ } else {
+ $ret .= "/>\n";
+ }
+ }
+ $ret .= "$indent </deps>\n";
+ }
+ if (isset($pkginfo['configure_options'])) {
+ $ret .= "$indent <configureoptions>\n";
+ foreach ($pkginfo['configure_options'] as $c) {
+ $ret .= "$indent <configureoption name=\"".
+ htmlspecialchars($c['name']) . "\"";
+ if (isset($c['default'])) {
+ $ret .= " default=\"" . htmlspecialchars($c['default']) . "\"";
+ }
+ $ret .= " prompt=\"" . htmlspecialchars($c['prompt']) . "\"";
+ $ret .= "/>\n";
+ }
+ $ret .= "$indent </configureoptions>\n";
+ }
+ if (isset($pkginfo['provides'])) {
+ foreach ($pkginfo['provides'] as $key => $what) {
+ $ret .= "$indent <provides type=\"$what[type]\" ";
+ $ret .= "name=\"$what[name]\" ";
+ if (isset($what['extends'])) {
+ $ret .= "extends=\"$what[extends]\" ";
+ }
+ $ret .= "/>\n";
+ }
+ }
+ if (isset($pkginfo['filelist'])) {
+ $ret .= "$indent <filelist>\n";
+ foreach ($pkginfo['filelist'] as $file => $fa) {
+ @$ret .= "$indent <file role=\"$fa[role]\"";
+ if (isset($fa['baseinstalldir'])) {
+ $ret .= ' baseinstalldir="' .
+ htmlspecialchars($fa['baseinstalldir']) . '"';
+ }
+ if (isset($fa['md5sum'])) {
+ $ret .= " md5sum=\"$fa[md5sum]\"";
+ }
+ if (isset($fa['platform'])) {
+ $ret .= " platform=\"$fa[platform]\"";
+ }
+ if (!empty($fa['install-as'])) {
+ $ret .= ' install-as="' .
+ htmlspecialchars($fa['install-as']) . '"';
+ }
+ $ret .= ' name="' . htmlspecialchars($file) . '"';
+ if (empty($fa['replacements'])) {
+ $ret .= "/>\n";
+ } else {
+ $ret .= ">\n";
+ foreach ($fa['replacements'] as $r) {
+ $ret .= "$indent <replace";
+ foreach ($r as $k => $v) {
+ $ret .= " $k=\"" . htmlspecialchars($v) .'"';
+ }
+ $ret .= "/>\n";
+ }
+ @$ret .= "$indent </file>\n";
+ }
+ }
+ $ret .= "$indent </filelist>\n";
+ }
+ $ret .= "$indent </release>\n";
+ return $ret;
+ }
+
+ // }}}
+ // {{{ validatePackageInfo()
+
+ /**
+ * Validate XML package definition file.
+ *
+ * @param string $info Filename of the package archive or of the
+ * package definition file
+ * @param array $errors Array that will contain the errors
+ * @param array $warnings Array that will contain the warnings
+ * @param string $dir_prefix (optional) directory where source files
+ * may be found, or empty if they are not available
+ * @access public
+ * @return boolean
+ */
+ function validatePackageInfo($info, &$errors, &$warnings, $dir_prefix = '')
+ {
+ if (PEAR::isError($info = $this->infoFromAny($info))) {
+ return $this->raiseError($info);
+ }
+ if (!is_array($info)) {
+ return false;
+ }
+
+ $errors = array();
+ $warnings = array();
+ if (!isset($info['package'])) {
+ $errors[] = 'missing package name';
+ } elseif (!$this->validPackageName($info['package'])) {
+ $errors[] = 'invalid package name';
+ }
+ $this->_packageName = $pn = $info['package'];
+
+ if (empty($info['summary'])) {
+ $errors[] = 'missing summary';
+ } elseif (strpos(trim($info['summary']), "\n") !== false) {
+ $warnings[] = 'summary should be on a single line';
+ }
+ if (empty($info['description'])) {
+ $errors[] = 'missing description';
+ }
+ if (empty($info['release_license'])) {
+ $errors[] = 'missing license';
+ }
+ if (!isset($info['version'])) {
+ $errors[] = 'missing version';
+ } elseif (!$this->validPackageVersion($info['version'])) {
+ $errors[] = 'invalid package release version';
+ }
+ if (empty($info['release_state'])) {
+ $errors[] = 'missing release state';
+ } elseif (!in_array($info['release_state'], PEAR_Common::getReleaseStates())) {
+ $errors[] = "invalid release state `$info[release_state]', should be one of: "
+ . implode(' ', PEAR_Common::getReleaseStates());
+ }
+ if (empty($info['release_date'])) {
+ $errors[] = 'missing release date';
+ } elseif (!preg_match('/^\d{4}-\d\d-\d\d$/', $info['release_date'])) {
+ $errors[] = "invalid release date `$info[release_date]', format is YYYY-MM-DD";
+ }
+ if (empty($info['release_notes'])) {
+ $errors[] = "missing release notes";
+ }
+ if (empty($info['maintainers'])) {
+ $errors[] = 'no maintainer(s)';
+ } else {
+ $i = 1;
+ foreach ($info['maintainers'] as $m) {
+ if (empty($m['handle'])) {
+ $errors[] = "maintainer $i: missing handle";
+ }
+ if (empty($m['role'])) {
+ $errors[] = "maintainer $i: missing role";
+ } elseif (!in_array($m['role'], PEAR_Common::getUserRoles())) {
+ $errors[] = "maintainer $i: invalid role `$m[role]', should be one of: "
+ . implode(' ', PEAR_Common::getUserRoles());
+ }
+ if (empty($m['name'])) {
+ $errors[] = "maintainer $i: missing name";
+ }
+ if (empty($m['email'])) {
+ $errors[] = "maintainer $i: missing email";
+ }
+ $i++;
+ }
+ }
+ if (!empty($info['release_deps'])) {
+ $i = 1;
+ foreach ($info['release_deps'] as $d) {
+ if (empty($d['type'])) {
+ $errors[] = "dependency $i: missing type";
+ } elseif (!in_array($d['type'], PEAR_Common::getDependencyTypes())) {
+ $errors[] = "dependency $i: invalid type '$d[type]', should be one of: " .
+ implode(' ', PEAR_Common::getDependencyTypes());
+ }
+ if (empty($d['rel'])) {
+ $errors[] = "dependency $i: missing relation";
+ } elseif (!in_array($d['rel'], PEAR_Common::getDependencyRelations())) {
+ $errors[] = "dependency $i: invalid relation '$d[rel]', should be one of: "
+ . implode(' ', PEAR_Common::getDependencyRelations());
+ }
+ if (!empty($d['optional'])) {
+ if (!in_array($d['optional'], array('yes', 'no'))) {
+ $errors[] = "dependency $i: invalid relation optional attribute '$d[optional]', should be one of: yes no";
+ }
+ }
+ if ($d['rel'] != 'has' && empty($d['version'])) {
+ $warnings[] = "dependency $i: missing version";
+ } elseif ($d['rel'] == 'has' && !empty($d['version'])) {
+ $warnings[] = "dependency $i: version ignored for `has' dependencies";
+ }
+ if ($d['type'] == 'php' && !empty($d['name'])) {
+ $warnings[] = "dependency $i: name ignored for php type dependencies";
+ } elseif ($d['type'] != 'php' && empty($d['name'])) {
+ $errors[] = "dependency $i: missing name";
+ }
+ $i++;
+ }
+ }
+ if (!empty($info['configure_options'])) {
+ $i = 1;
+ foreach ($info['configure_options'] as $c) {
+ if (empty($c['name'])) {
+ $errors[] = "configure option $i: missing name";
+ }
+ if (empty($c['prompt'])) {
+ $errors[] = "configure option $i: missing prompt";
+ }
+ $i++;
+ }
+ }
+ if (empty($info['filelist'])) {
+ $errors[] = 'no files';
+ } else {
+ foreach ($info['filelist'] as $file => $fa) {
+ if (empty($fa['role'])) {
+ $errors[] = "file $file: missing role";
+ continue;
+ } elseif (!in_array($fa['role'], PEAR_Common::getFileRoles())) {
+ $errors[] = "file $file: invalid role, should be one of: "
+ . implode(' ', PEAR_Common::getFileRoles());
+ }
+ if ($fa['role'] == 'php' && $dir_prefix) {
+ $this->log(1, "Analyzing $file");
+ $srcinfo = $this->analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file);
+ if ($srcinfo) {
+ $this->buildProvidesArray($srcinfo);
+ }
+ }
+
+ // (ssb) Any checks we can do for baseinstalldir?
+ // (cox) Perhaps checks that either the target dir and
+ // baseInstall doesn't cointain "../../"
+ }
+ }
+ $this->_packageName = $pn = $info['package'];
+ $pnl = strlen($pn);
+ foreach ((array)$this->pkginfo['provides'] as $key => $what) {
+ if (isset($what['explicit'])) {
+ // skip conformance checks if the provides entry is
+ // specified in the package.xml file
+ continue;
+ }
+ extract($what);
+ if ($type == 'class') {
+ if (!strncasecmp($name, $pn, $pnl)) {
+ continue;
+ }
+ $warnings[] = "in $file: class \"$name\" not prefixed with package name \"$pn\"";
+ } elseif ($type == 'function') {
+ if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) {
+ continue;
+ }
+ $warnings[] = "in $file: function \"$name\" not prefixed with package name \"$pn\"";
+ }
+ }
+
+
+ return true;
+ }
+
+ // }}}
+ // {{{ buildProvidesArray()
+
+ /**
+ * Build a "provides" array from data returned by
+ * analyzeSourceCode(). The format of the built array is like
+ * this:
+ *
+ * array(
+ * 'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
+ * ...
+ * )
+ *
+ *
+ * @param array $srcinfo array with information about a source file
+ * as returned by the analyzeSourceCode() method.
+ *
+ * @return void
+ *
+ * @access public
+ *
+ */
+ function buildProvidesArray($srcinfo)
+ {
+ $file = basename($srcinfo['source_file']);
+ $pn = '';
+ if (isset($this->_packageName)) {
+ $pn = $this->_packageName;
+ }
+ $pnl = strlen($pn);
+ foreach ($srcinfo['declared_classes'] as $class) {
+ $key = "class;$class";
+ if (isset($this->pkginfo['provides'][$key])) {
+ continue;
+ }
+ $this->pkginfo['provides'][$key] =
+ array('file'=> $file, 'type' => 'class', 'name' => $class);
+ if (isset($srcinfo['inheritance'][$class])) {
+ $this->pkginfo['provides'][$key]['extends'] =
+ $srcinfo['inheritance'][$class];
+ }
+ }
+ foreach ($srcinfo['declared_methods'] as $class => $methods) {
+ foreach ($methods as $method) {
+ $function = "$class::$method";
+ $key = "function;$function";
+ if ($method{0} == '_' || !strcasecmp($method, $class) ||
+ isset($this->pkginfo['provides'][$key])) {
+ continue;
+ }
+ $this->pkginfo['provides'][$key] =
+ array('file'=> $file, 'type' => 'function', 'name' => $function);
+ }
+ }
+
+ foreach ($srcinfo['declared_functions'] as $function) {
+ $key = "function;$function";
+ if ($function{0} == '_' || isset($this->pkginfo['provides'][$key])) {
+ continue;
+ }
+ if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
+ $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
+ }
+ $this->pkginfo['provides'][$key] =
+ array('file'=> $file, 'type' => 'function', 'name' => $function);
+ }
+ }
+
+ // }}}
+ // {{{ analyzeSourceCode()
+
+ /**
+ * Analyze the source code of the given PHP file
+ *
+ * @param string Filename of the PHP file
+ * @return mixed
+ * @access public
+ */
+ function analyzeSourceCode($file)
+ {
+ if (!function_exists("token_get_all")) {
+ return false;
+ }
+ if (!defined('T_DOC_COMMENT')) {
+ define('T_DOC_COMMENT', T_COMMENT);
+ }
+ if (!defined('T_INTERFACE')) {
+ define('T_INTERFACE', -1);
+ }
+ if (!defined('T_IMPLEMENTS')) {
+ define('T_IMPLEMENTS', -1);
+ }
+ if (!$fp = @fopen($file, "r")) {
+ return false;
+ }
+ $contents = fread($fp, filesize($file));
+ $tokens = token_get_all($contents);
+/*
+ for ($i = 0; $i < sizeof($tokens); $i++) {
+ @list($token, $data) = $tokens[$i];
+ if (is_string($token)) {
+ var_dump($token);
+ } else {
+ print token_name($token) . ' ';
+ var_dump(rtrim($data));
+ }
+ }
+*/
+ $look_for = 0;
+ $paren_level = 0;
+ $bracket_level = 0;
+ $brace_level = 0;
+ $lastphpdoc = '';
+ $current_class = '';
+ $current_interface = '';
+ $current_class_level = -1;
+ $current_function = '';
+ $current_function_level = -1;
+ $declared_classes = array();
+ $declared_interfaces = array();
+ $declared_functions = array();
+ $declared_methods = array();
+ $used_classes = array();
+ $used_functions = array();
+ $extends = array();
+ $implements = array();
+ $nodeps = array();
+ $inquote = false;
+ $interface = false;
+ for ($i = 0; $i < sizeof($tokens); $i++) {
+ if (is_array($tokens[$i])) {
+ list($token, $data) = $tokens[$i];
+ } else {
+ $token = $tokens[$i];
+ $data = '';
+ }
+ if ($inquote) {
+ if ($token != '"') {
+ continue;
+ } else {
+ $inquote = false;
+ }
+ }
+ switch ($token) {
+ case T_WHITESPACE:
+ continue;
+ case ';':
+ if ($interface) {
+ $current_function = '';
+ $current_function_level = -1;
+ }
+ break;
+ case '"':
+ $inquote = true;
+ break;
+ case T_CURLY_OPEN:
+ case T_DOLLAR_OPEN_CURLY_BRACES:
+ case '{': $brace_level++; continue 2;
+ case '}':
+ $brace_level--;
+ if ($current_class_level == $brace_level) {
+ $current_class = '';
+ $current_class_level = -1;
+ }
+ if ($current_function_level == $brace_level) {
+ $current_function = '';
+ $current_function_level = -1;
+ }
+ continue 2;
+ case '[': $bracket_level++; continue 2;
+ case ']': $bracket_level--; continue 2;
+ case '(': $paren_level++; continue 2;
+ case ')': $paren_level--; continue 2;
+ case T_INTERFACE:
+ $interface = true;
+ case T_CLASS:
+ if (($current_class_level != -1) || ($current_function_level != -1)) {
+ PEAR::raiseError("Parser error: Invalid PHP file $file",
+ PEAR_COMMON_ERROR_INVALIDPHP);
+ return false;
+ }
+ case T_FUNCTION:
+ case T_NEW:
+ case T_EXTENDS:
+ case T_IMPLEMENTS:
+ $look_for = $token;
+ continue 2;
+ case T_STRING:
+ if (version_compare(zend_version(), '2.0', '<')) {
+ if (in_array(strtolower($data),
+ array('public', 'private', 'protected', 'abstract',
+ 'interface', 'implements', 'clone', 'throw')
+ )) {
+ PEAR::raiseError('Error: PHP5 packages must be packaged by php 5 PEAR');
+ return false;
+ }
+ }
+ if ($look_for == T_CLASS) {
+ $current_class = $data;
+ $current_class_level = $brace_level;
+ $declared_classes[] = $current_class;
+ } elseif ($look_for == T_INTERFACE) {
+ $current_interface = $data;
+ $current_class_level = $brace_level;
+ $declared_interfaces[] = $current_interface;
+ } elseif ($look_for == T_IMPLEMENTS) {
+ $implements[$current_class] = $data;
+ } elseif ($look_for == T_EXTENDS) {
+ $extends[$current_class] = $data;
+ } elseif ($look_for == T_FUNCTION) {
+ if ($current_class) {
+ $current_function = "$current_class::$data";
+ $declared_methods[$current_class][] = $data;
+ } elseif ($current_interface) {
+ $current_function = "$current_interface::$data";
+ $declared_methods[$current_interface][] = $data;
+ } else {
+ $current_function = $data;
+ $declared_functions[] = $current_function;
+ }
+ $current_function_level = $brace_level;
+ $m = array();
+ } elseif ($look_for == T_NEW) {
+ $used_classes[$data] = true;
+ }
+ $look_for = 0;
+ continue 2;
+ case T_VARIABLE:
+ $look_for = 0;
+ continue 2;
+ case T_DOC_COMMENT:
+ case T_COMMENT:
+ if (preg_match('!^/\*\*\s!', $data)) {
+ $lastphpdoc = $data;
+ if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
+ $nodeps = array_merge($nodeps, $m[1]);
+ }
+ }
+ continue 2;
+ case T_DOUBLE_COLON:
+ if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) {
+ PEAR::raiseError("Parser error: Invalid PHP file $file",
+ PEAR_COMMON_ERROR_INVALIDPHP);
+ return false;
+ }
+ $class = $tokens[$i - 1][1];
+ if (strtolower($class) != 'parent') {
+ $used_classes[$class] = true;
+ }
+ continue 2;
+ }
+ }
+ return array(
+ "source_file" => $file,
+ "declared_classes" => $declared_classes,
+ "declared_interfaces" => $declared_interfaces,
+ "declared_methods" => $declared_methods,
+ "declared_functions" => $declared_functions,
+ "used_classes" => array_diff(array_keys($used_classes), $nodeps),
+ "inheritance" => $extends,
+ "implements" => $implements,
+ );
+ }
+
+ // }}}
+ // {{{ betterStates()
+
+ /**
+ * Return an array containing all of the states that are more stable than
+ * or equal to the passed in state
+ *
+ * @param string Release state
+ * @param boolean Determines whether to include $state in the list
+ * @return false|array False if $state is not a valid release state
+ */
+ function betterStates($state, $include = false)
+ {
+ static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable');
+ $i = array_search($state, $states);
+ if ($i === false) {
+ return false;
+ }
+ if ($include) {
+ $i--;
+ }
+ return array_slice($states, $i + 1);
+ }
+
+ // }}}
+ // {{{ detectDependencies()
+
+ function detectDependencies($any, $status_callback = null)
+ {
+ if (!function_exists("token_get_all")) {
+ return false;
+ }
+ if (PEAR::isError($info = $this->infoFromAny($any))) {
+ return $this->raiseError($info);
+ }
+ if (!is_array($info)) {
+ return false;
+ }
+ $deps = array();
+ $used_c = $decl_c = $decl_f = $decl_m = array();
+ foreach ($info['filelist'] as $file => $fa) {
+ $tmp = $this->analyzeSourceCode($file);
+ $used_c = @array_merge($used_c, $tmp['used_classes']);
+ $decl_c = @array_merge($decl_c, $tmp['declared_classes']);
+ $decl_f = @array_merge($decl_f, $tmp['declared_functions']);
+ $decl_m = @array_merge($decl_m, $tmp['declared_methods']);
+ $inheri = @array_merge($inheri, $tmp['inheritance']);
+ }
+ $used_c = array_unique($used_c);
+ $decl_c = array_unique($decl_c);
+ $undecl_c = array_diff($used_c, $decl_c);
+ return array('used_classes' => $used_c,
+ 'declared_classes' => $decl_c,
+ 'declared_methods' => $decl_m,
+ 'declared_functions' => $decl_f,
+ 'undeclared_classes' => $undecl_c,
+ 'inheritance' => $inheri,
+ );
+ }
+
+ // }}}
+ // {{{ getUserRoles()
+
+ /**
+ * Get the valid roles for a PEAR package maintainer
+ *
+ * @return array
+ * @static
+ */
+ function getUserRoles()
+ {
+ return $GLOBALS['_PEAR_Common_maintainer_roles'];
+ }
+
+ // }}}
+ // {{{ getReleaseStates()
+
+ /**
+ * Get the valid package release states of packages
+ *
+ * @return array
+ * @static
+ */
+ function getReleaseStates()
+ {
+ return $GLOBALS['_PEAR_Common_release_states'];
+ }
+
+ // }}}
+ // {{{ getDependencyTypes()
+
+ /**
+ * Get the implemented dependency types (php, ext, pkg etc.)
+ *
+ * @return array
+ * @static
+ */
+ function getDependencyTypes()
+ {
+ return $GLOBALS['_PEAR_Common_dependency_types'];
+ }
+
+ // }}}
+ // {{{ getDependencyRelations()
+
+ /**
+ * Get the implemented dependency relations (has, lt, ge etc.)
+ *
+ * @return array
+ * @static
+ */
+ function getDependencyRelations()
+ {
+ return $GLOBALS['_PEAR_Common_dependency_relations'];
+ }
+
+ // }}}
+ // {{{ getFileRoles()
+
+ /**
+ * Get the implemented file roles
+ *
+ * @return array
+ * @static
+ */
+ function getFileRoles()
+ {
+ return $GLOBALS['_PEAR_Common_file_roles'];
+ }
+
+ // }}}
+ // {{{ getReplacementTypes()
+
+ /**
+ * Get the implemented file replacement types in
+ *
+ * @return array
+ * @static
+ */
+ function getReplacementTypes()
+ {
+ return $GLOBALS['_PEAR_Common_replacement_types'];
+ }
+
+ // }}}
+ // {{{ getProvideTypes()
+
+ /**
+ * Get the implemented file replacement types in
+ *
+ * @return array
+ * @static
+ */
+ function getProvideTypes()
+ {
+ return $GLOBALS['_PEAR_Common_provide_types'];
+ }
+
+ // }}}
+ // {{{ getScriptPhases()
+
+ /**
+ * Get the implemented file replacement types in
+ *
+ * @return array
+ * @static
+ */
+ function getScriptPhases()
+ {
+ return $GLOBALS['_PEAR_Common_script_phases'];
+ }
+
+ // }}}
+ // {{{ validPackageName()
+
+ /**
+ * Test whether a string contains a valid package name.
+ *
+ * @param string $name the package name to test
+ *
+ * @return bool
+ *
+ * @access public
+ */
+ function validPackageName($name)
+ {
+ return (bool)preg_match(PEAR_COMMON_PACKAGE_NAME_PREG, $name);
+ }
+
+
+ // }}}
+ // {{{ validPackageVersion()
+
+ /**
+ * Test whether a string contains a valid package version.
+ *
+ * @param string $ver the package version to test
+ *
+ * @return bool
+ *
+ * @access public
+ */
+ function validPackageVersion($ver)
+ {
+ return (bool)preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver);
+ }
+
+
+ // }}}
+
+ // {{{ downloadHttp()
+
+ /**
+ * Download a file through HTTP. Considers suggested file name in
+ * Content-disposition: header and can run a callback function for
+ * different events. The callback will be called with two
+ * parameters: the callback type, and parameters. The implemented
+ * callback types are:
+ *
+ * 'setup' called at the very beginning, parameter is a UI object
+ * that should be used for all output
+ * 'message' the parameter is a string with an informational message
+ * 'saveas' may be used to save with a different file name, the
+ * parameter is the filename that is about to be used.
+ * If a 'saveas' callback returns a non-empty string,
+ * that file name will be used as the filename instead.
+ * Note that $save_dir will not be affected by this, only
+ * the basename of the file.
+ * 'start' download is starting, parameter is number of bytes
+ * that are expected, or -1 if unknown
+ * 'bytesread' parameter is the number of bytes read so far
+ * 'done' download is complete, parameter is the total number
+ * of bytes read
+ * 'connfailed' if the TCP connection fails, this callback is called
+ * with array(host,port,errno,errmsg)
+ * 'writefailed' if writing to disk fails, this callback is called
+ * with array(destfile,errmsg)
+ *
+ * If an HTTP proxy has been configured (http_proxy PEAR_Config
+ * setting), the proxy will be used.
+ *
+ * @param string $url the URL to download
+ * @param object $ui PEAR_Frontend_* instance
+ * @param object $config PEAR_Config instance
+ * @param string $save_dir (optional) directory to save file in
+ * @param mixed $callback (optional) function/method to call for status
+ * updates
+ *
+ * @return string Returns the full path of the downloaded file or a PEAR
+ * error on failure. If the error is caused by
+ * socket-related errors, the error object will
+ * have the fsockopen error code available through
+ * getCode().
+ *
+ * @access public
+ */
+ function downloadHttp($url, &$ui, $save_dir = '.', $callback = null)
+ {
+ if ($callback) {
+ call_user_func($callback, 'setup', array(&$ui));
+ }
+ if (preg_match('!^http://([^/:?#]*)(:(\d+))?(/.*)!', $url, $matches)) {
+ list(,$host,,$port,$path) = $matches;
+ }
+ if (isset($this)) {
+ $config = &$this->config;
+ } else {
+ $config = &PEAR_Config::singleton();
+ }
+ $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
+ if ($proxy = parse_url($config->get('http_proxy'))) {
+ $proxy_host = @$proxy['host'];
+ $proxy_port = @$proxy['port'];
+ $proxy_user = @$proxy['user'];
+ $proxy_pass = @$proxy['pass'];
+
+ if ($proxy_port == '') {
+ $proxy_port = 8080;
+ }
+ if ($callback) {
+ call_user_func($callback, 'message', "Using HTTP proxy $host:$port");
+ }
+ }
+ if (empty($port)) {
+ $port = 80;
+ }
+ if ($proxy_host != '') {
+ $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr);
+ if (!$fp) {
+ if ($callback) {
+ call_user_func($callback, 'connfailed', array($proxy_host, $proxy_port,
+ $errno, $errstr));
+ }
+ return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", $errno);
+ }
+ $request = "GET $url HTTP/1.0\r\n";
+ } else {
+ $fp = @fsockopen($host, $port, $errno, $errstr);
+ if (!$fp) {
+ if ($callback) {
+ call_user_func($callback, 'connfailed', array($host, $port,
+ $errno, $errstr));
+ }
+ return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno);
+ }
+ $request = "GET $path HTTP/1.0\r\n";
+ }
+ $request .= "Host: $host:$port\r\n".
+ "User-Agent: PHP/".PHP_VERSION."\r\n";
+ if ($proxy_host != '' && $proxy_user != '') {
+ $request .= 'Proxy-Authorization: Basic ' .
+ base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n";
+ }
+ $request .= "\r\n";
+ fwrite($fp, $request);
+ $headers = array();
+ while (trim($line = fgets($fp, 1024))) {
+ if (preg_match('/^([^:]+):\s+(.*)\s*$/', $line, $matches)) {
+ $headers[strtolower($matches[1])] = trim($matches[2]);
+ } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
+ if ($matches[1] != 200) {
+ return PEAR::raiseError("File http://$host:$port$path not valid (received: $line)");
+ }
+ }
+ }
+ if (isset($headers['content-disposition']) &&
+ preg_match('/\sfilename=\"([^;]*\S)\"\s*(;|$)/', $headers['content-disposition'], $matches)) {
+ $save_as = basename($matches[1]);
+ } else {
+ $save_as = basename($url);
+ }
+ if ($callback) {
+ $tmp = call_user_func($callback, 'saveas', $save_as);
+ if ($tmp) {
+ $save_as = $tmp;
+ }
+ }
+ $dest_file = $save_dir . DIRECTORY_SEPARATOR . $save_as;
+ if (!$wp = @fopen($dest_file, 'wb')) {
+ fclose($fp);
+ if ($callback) {
+ call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg));
+ }
+ return PEAR::raiseError("could not open $dest_file for writing");
+ }
+ if (isset($headers['content-length'])) {
+ $length = $headers['content-length'];
+ } else {
+ $length = -1;
+ }
+ $bytes = 0;
+ if ($callback) {
+ call_user_func($callback, 'start', array(basename($dest_file), $length));
+ }
+ while ($data = @fread($fp, 1024)) {
+ $bytes += strlen($data);
+ if ($callback) {
+ call_user_func($callback, 'bytesread', $bytes);
+ }
+ if (!@fwrite($wp, $data)) {
+ fclose($fp);
+ if ($callback) {
+ call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg));
+ }
+ return PEAR::raiseError("$dest_file: write failed ($php_errormsg)");
+ }
+ }
+ fclose($fp);
+ fclose($wp);
+ if ($callback) {
+ call_user_func($callback, 'done', $bytes);
+ }
+ return $dest_file;
+ }
+
+ // }}}
+ // {{{ sortPkgDeps()
+
+ /**
+ * Sort a list of arrays of array(downloaded packagefilename) by dependency.
+ *
+ * It also removes duplicate dependencies
+ * @param array
+ * @param boolean Sort packages in reverse order if true
+ * @return array array of array(packagefilename, package.xml contents)
+ */
+ function sortPkgDeps(&$packages, $uninstall = false)
+ {
+ $ret = array();
+ if ($uninstall) {
+ foreach($packages as $packageinfo) {
+ $ret[] = array('info' => $packageinfo);
+ }
+ } else {
+ foreach($packages as $packagefile) {
+ if (!is_array($packagefile)) {
+ $ret[] = array('file' => $packagefile,
+ 'info' => $a = $this->infoFromAny($packagefile),
+ 'pkg' => $a['package']);
+ } else {
+ $ret[] = $packagefile;
+ }
+ }
+ }
+ $checkdupes = array();
+ $newret = array();
+ foreach($ret as $i => $p) {
+ if (!isset($checkdupes[$p['info']['package']])) {
+ $checkdupes[$p['info']['package']][] = $i;
+ $newret[] = $p;
+ }
+ }
+ $this->_packageSortTree = $this->_getPkgDepTree($newret);
+
+ $func = $uninstall ? '_sortPkgDepsRev' : '_sortPkgDeps';
+ usort($newret, array(&$this, $func));
+ $this->_packageSortTree = null;
+ $packages = $newret;
+ }
+
+ // }}}
+ // {{{ _sortPkgDeps()
+
+ /**
+ * Compare two package's package.xml, and sort
+ * so that dependencies are installed first
+ *
+ * This is a crude compare, real dependency checking is done on install.
+ * The only purpose this serves is to make the command-line
+ * order-independent (you can list a dependent package first, and
+ * installation occurs in the order required)
+ * @access private
+ */
+ function _sortPkgDeps($p1, $p2)
+ {
+ $p1name = $p1['info']['package'];
+ $p2name = $p2['info']['package'];
+ $p1deps = $this->_getPkgDeps($p1);
+ $p2deps = $this->_getPkgDeps($p2);
+ if (!count($p1deps) && !count($p2deps)) {
+ return 0; // order makes no difference
+ }
+ if (!count($p1deps)) {
+ return -1; // package 2 has dependencies, package 1 doesn't
+ }
+ if (!count($p2deps)) {
+ return 1; // package 1 has dependencies, package 2 doesn't
+ }
+ // both have dependencies
+ if (in_array($p1name, $p2deps)) {
+ return -1; // put package 1 first: package 2 depends on package 1
+ }
+ if (in_array($p2name, $p1deps)) {
+ return 1; // put package 2 first: package 1 depends on package 2
+ }
+ if ($this->_removedDependency($p1name, $p2name)) {
+ return -1; // put package 1 first: package 2 depends on packages that depend on package 1
+ }
+ if ($this->_removedDependency($p2name, $p1name)) {
+ return 1; // put package 2 first: package 1 depends on packages that depend on package 2
+ }
+ // doesn't really matter if neither depends on the other
+ return 0;
+ }
+
+ // }}}
+ // {{{ _sortPkgDepsRev()
+
+ /**
+ * Compare two package's package.xml, and sort
+ * so that dependencies are uninstalled last
+ *
+ * This is a crude compare, real dependency checking is done on uninstall.
+ * The only purpose this serves is to make the command-line
+ * order-independent (you can list a dependency first, and
+ * uninstallation occurs in the order required)
+ * @access private
+ */
+ function _sortPkgDepsRev($p1, $p2)
+ {
+ $p1name = $p1['info']['package'];
+ $p2name = $p2['info']['package'];
+ $p1deps = $this->_getRevPkgDeps($p1);
+ $p2deps = $this->_getRevPkgDeps($p2);
+ if (!count($p1deps) && !count($p2deps)) {
+ return 0; // order makes no difference
+ }
+ if (!count($p1deps)) {
+ return 1; // package 2 has dependencies, package 1 doesn't
+ }
+ if (!count($p2deps)) {
+ return -1; // package 2 has dependencies, package 1 doesn't
+ }
+ // both have dependencies
+ if (in_array($p1name, $p2deps)) {
+ return 1; // put package 1 last
+ }
+ if (in_array($p2name, $p1deps)) {
+ return -1; // put package 2 last
+ }
+ if ($this->_removedDependency($p1name, $p2name)) {
+ return 1; // put package 1 last: package 2 depends on packages that depend on package 1
+ }
+ if ($this->_removedDependency($p2name, $p1name)) {
+ return -1; // put package 2 last: package 1 depends on packages that depend on package 2
+ }
+ // doesn't really matter if neither depends on the other
+ return 0;
+ }
+
+ // }}}
+ // {{{ _getPkgDeps()
+
+ /**
+ * get an array of package dependency names
+ * @param array
+ * @return array
+ * @access private
+ */
+ function _getPkgDeps($p)
+ {
+ if (!isset($p['info']['releases'])) {
+ return $this->_getRevPkgDeps($p);
+ }
+ $rel = array_shift($p['info']['releases']);
+ if (!isset($rel['deps'])) {
+ return array();
+ }
+ $ret = array();
+ foreach($rel['deps'] as $dep) {
+ if ($dep['type'] == 'pkg') {
+ $ret[] = $dep['name'];
+ }
+ }
+ return $ret;
+ }
+
+ // }}}
+ // {{{ _getPkgDeps()
+
+ /**
+ * get an array representation of the package dependency tree
+ * @return array
+ * @access private
+ */
+ function _getPkgDepTree($packages)
+ {
+ $tree = array();
+ foreach ($packages as $p) {
+ $package = $p['info']['package'];
+ $deps = $this->_getPkgDeps($p);
+ $tree[$package] = $deps;
+ }
+ return $tree;
+ }
+
+ // }}}
+ // {{{ _removedDependency($p1, $p2)
+
+ /**
+ * get an array of package dependency names for uninstall
+ * @param string package 1 name
+ * @param string package 2 name
+ * @return bool
+ * @access private
+ */
+ function _removedDependency($p1, $p2)
+ {
+ if (empty($this->_packageSortTree[$p2])) {
+ return false;
+ }
+ if (!in_array($p1, $this->_packageSortTree[$p2])) {
+ foreach ($this->_packageSortTree[$p2] as $potential) {
+ if ($this->_removedDependency($p1, $potential)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ _getRevPkgDeps()
+
+ /**
+ * get an array of package dependency names for uninstall
+ * @param array
+ * @return array
+ * @access private
+ */
+ function _getRevPkgDeps($p)
+ {
+ if (!isset($p['info']['release_deps'])) {
+ return array();
+ }
+ $ret = array();
+ foreach($p['info']['release_deps'] as $dep) {
+ if ($dep['type'] == 'pkg') {
+ $ret[] = $dep['name'];
+ }
+ }
+ return $ret;
+ }
+
+ // }}}
+}
+
+?>
diff --git a/pearlib/PEAR/Config.php b/pearlib/PEAR/Config.php
new file mode 100644
index 00000000..ba641735
--- /dev/null
+++ b/pearlib/PEAR/Config.php
@@ -0,0 +1,1169 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Config.php,v 1.52 2004/01/08 17:33:12 sniper Exp $
+
+require_once 'PEAR.php';
+require_once 'System.php';
+
+/**
+ * Last created PEAR_Config instance.
+ * @var object
+ */
+$GLOBALS['_PEAR_Config_instance'] = null;
+if (!defined('PEAR_INSTALL_DIR') || !PEAR_INSTALL_DIR) {
+ $PEAR_INSTALL_DIR = PHP_LIBDIR . DIRECTORY_SEPARATOR . 'pear';
+} else {
+ $PEAR_INSTALL_DIR = PEAR_INSTALL_DIR;
+}
+
+// Below we define constants with default values for all configuration
+// parameters except username/password. All of them can have their
+// defaults set through environment variables. The reason we use the
+// PHP_ prefix is for some security, PHP protects environment
+// variables starting with PHP_*.
+
+if (getenv('PHP_PEAR_SYSCONF_DIR')) {
+ define('PEAR_CONFIG_SYSCONFDIR', getenv('PHP_PEAR_SYSCONF_DIR'));
+} elseif (getenv('SystemRoot')) {
+ define('PEAR_CONFIG_SYSCONFDIR', getenv('SystemRoot'));
+} else {
+ define('PEAR_CONFIG_SYSCONFDIR', PHP_SYSCONFDIR);
+}
+
+// Default for master_server
+if (getenv('PHP_PEAR_MASTER_SERVER')) {
+ define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', getenv('PHP_PEAR_MASTER_SERVER'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', 'pear.php.net');
+}
+
+// Default for http_proxy
+if (getenv('PHP_PEAR_HTTP_PROXY')) {
+ define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('PHP_PEAR_HTTP_PROXY'));
+} elseif (getenv('http_proxy')) {
+ define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('http_proxy'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', '');
+}
+
+// Default for php_dir
+if (getenv('PHP_PEAR_INSTALL_DIR')) {
+ define('PEAR_CONFIG_DEFAULT_PHP_DIR', getenv('PHP_PEAR_INSTALL_DIR'));
+} else {
+ if (@is_dir($PEAR_INSTALL_DIR)) {
+ define('PEAR_CONFIG_DEFAULT_PHP_DIR',
+ $PEAR_INSTALL_DIR);
+ } else {
+ define('PEAR_CONFIG_DEFAULT_PHP_DIR', $PEAR_INSTALL_DIR);
+ }
+}
+
+// Default for ext_dir
+if (getenv('PHP_PEAR_EXTENSION_DIR')) {
+ define('PEAR_CONFIG_DEFAULT_EXT_DIR', getenv('PHP_PEAR_EXTENSION_DIR'));
+} else {
+ if (ini_get('extension_dir')) {
+ define('PEAR_CONFIG_DEFAULT_EXT_DIR', ini_get('extension_dir'));
+ } elseif (defined('PEAR_EXTENSION_DIR') && @is_dir(PEAR_EXTENSION_DIR)) {
+ define('PEAR_CONFIG_DEFAULT_EXT_DIR', PEAR_EXTENSION_DIR);
+ } elseif (defined('PHP_EXTENSION_DIR')) {
+ define('PEAR_CONFIG_DEFAULT_EXT_DIR', PHP_EXTENSION_DIR);
+ } else {
+ define('PEAR_CONFIG_DEFAULT_EXT_DIR', '.');
+ }
+}
+
+// Default for doc_dir
+if (getenv('PHP_PEAR_DOC_DIR')) {
+ define('PEAR_CONFIG_DEFAULT_DOC_DIR', getenv('PHP_PEAR_DOC_DIR'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_DOC_DIR',
+ $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'docs');
+}
+
+// Default for bin_dir
+if (getenv('PHP_PEAR_BIN_DIR')) {
+ define('PEAR_CONFIG_DEFAULT_BIN_DIR', getenv('PHP_PEAR_BIN_DIR'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_BIN_DIR', PHP_BINDIR);
+}
+
+// Default for data_dir
+if (getenv('PHP_PEAR_DATA_DIR')) {
+ define('PEAR_CONFIG_DEFAULT_DATA_DIR', getenv('PHP_PEAR_DATA_DIR'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_DATA_DIR',
+ $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'data');
+}
+
+// Default for test_dir
+if (getenv('PHP_PEAR_TEST_DIR')) {
+ define('PEAR_CONFIG_DEFAULT_TEST_DIR', getenv('PHP_PEAR_TEST_DIR'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_TEST_DIR',
+ $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'tests');
+}
+
+// Default for cache_dir
+if (getenv('PHP_PEAR_CACHE_DIR')) {
+ define('PEAR_CONFIG_DEFAULT_CACHE_DIR', getenv('PHP_PEAR_CACHE_DIR'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_CACHE_DIR',
+ System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
+ DIRECTORY_SEPARATOR . 'cache');
+}
+
+// Default for php_bin
+if (getenv('PHP_PEAR_PHP_BIN')) {
+ define('PEAR_CONFIG_DEFAULT_PHP_BIN', getenv('PHP_PEAR_PHP_BIN'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_PHP_BIN', PEAR_CONFIG_DEFAULT_BIN_DIR.
+ DIRECTORY_SEPARATOR.'php'.(OS_WINDOWS ? '.exe' : ''));
+}
+
+// Default for verbose
+if (getenv('PHP_PEAR_VERBOSE')) {
+ define('PEAR_CONFIG_DEFAULT_VERBOSE', getenv('PHP_PEAR_VERBOSE'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_VERBOSE', 1);
+}
+
+// Default for preferred_state
+if (getenv('PHP_PEAR_PREFERRED_STATE')) {
+ define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', getenv('PHP_PEAR_PREFERRED_STATE'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', 'stable');
+}
+
+// Default for umask
+if (getenv('PHP_PEAR_UMASK')) {
+ define('PEAR_CONFIG_DEFAULT_UMASK', getenv('PHP_PEAR_UMASK'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_UMASK', decoct(umask()));
+}
+
+// Default for cache_ttl
+if (getenv('PHP_PEAR_CACHE_TTL')) {
+ define('PEAR_CONFIG_DEFAULT_CACHE_TTL', getenv('PHP_PEAR_CACHE_TTL'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_CACHE_TTL', 3600);
+}
+
+// Default for sig_type
+if (getenv('PHP_PEAR_SIG_TYPE')) {
+ define('PEAR_CONFIG_DEFAULT_SIG_TYPE', getenv('PHP_PEAR_SIG_TYPE'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_SIG_TYPE', 'gpg');
+}
+
+// Default for sig_bin
+if (getenv('PHP_PEAR_SIG_BIN')) {
+ define('PEAR_CONFIG_DEFAULT_SIG_BIN', getenv('PHP_PEAR_SIG_BIN'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_SIG_BIN',
+ System::which(
+ 'gpg', OS_WINDOWS ? 'c:\gnupg\gpg.exe' : '/usr/local/bin/gpg'));
+}
+
+// Default for sig_keydir
+if (getenv('PHP_PEAR_SIG_KEYDIR')) {
+ define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR', getenv('PHP_PEAR_SIG_KEYDIR'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR',
+ PEAR_CONFIG_SYSCONFDIR . DIRECTORY_SEPARATOR . 'pearkeys');
+}
+
+/**
+ * This is a class for storing configuration data, keeping track of
+ * which are system-defined, user-defined or defaulted.
+ */
+class PEAR_Config extends PEAR
+{
+ // {{{ properties
+
+ /**
+ * Array of config files used.
+ *
+ * @var array layer => config file
+ */
+ var $files = array(
+ 'system' => '',
+ 'user' => '',
+ );
+
+ var $layers = array();
+
+ /**
+ * Configuration data, two-dimensional array where the first
+ * dimension is the config layer ('user', 'system' and 'default'),
+ * and the second dimension is keyname => value.
+ *
+ * The order in the first dimension is important! Earlier
+ * layers will shadow later ones when a config value is
+ * requested (if a 'user' value exists, it will be returned first,
+ * then 'system' and finally 'default').
+ *
+ * @var array layer => array(keyname => value, ...)
+ */
+ var $configuration = array(
+ 'user' => array(),
+ 'system' => array(),
+ 'default' => array(),
+ );
+
+ /**
+ * Information about the configuration data. Stores the type,
+ * default value and a documentation string for each configuration
+ * value.
+ *
+ * @var array layer => array(infotype => value, ...)
+ */
+ var $configuration_info = array(
+ // Internet Access
+ 'master_server' => array(
+ 'type' => 'string',
+ 'default' => 'pear.php.net',
+ 'doc' => 'name of the main PEAR server',
+ 'prompt' => 'PEAR server',
+ 'group' => 'Internet Access',
+ ),
+ 'http_proxy' => array(
+ 'type' => 'string',
+ 'default' => PEAR_CONFIG_DEFAULT_HTTP_PROXY,
+ 'doc' => 'HTTP proxy (host:port) to use when downloading packages',
+ 'prompt' => 'HTTP Proxy Server Address',
+ 'group' => 'Internet Access',
+ ),
+ // File Locations
+ 'php_dir' => array(
+ 'type' => 'directory',
+ 'default' => PEAR_CONFIG_DEFAULT_PHP_DIR,
+ 'doc' => 'directory where .php files are installed',
+ 'prompt' => 'PEAR directory',
+ 'group' => 'File Locations',
+ ),
+ 'ext_dir' => array(
+ 'type' => 'directory',
+ 'default' => PEAR_CONFIG_DEFAULT_EXT_DIR,
+ 'doc' => 'directory where loadable extensions are installed',
+ 'prompt' => 'PHP extension directory',
+ 'group' => 'File Locations',
+ ),
+ 'doc_dir' => array(
+ 'type' => 'directory',
+ 'default' => PEAR_CONFIG_DEFAULT_DOC_DIR,
+ 'doc' => 'directory where documentation is installed',
+ 'prompt' => 'PEAR documentation directory',
+ 'group' => 'File Locations',
+ ),
+ 'bin_dir' => array(
+ 'type' => 'directory',
+ 'default' => PEAR_CONFIG_DEFAULT_BIN_DIR,
+ 'doc' => 'directory where executables are installed',
+ 'prompt' => 'PEAR executables directory',
+ 'group' => 'File Locations',
+ ),
+ 'data_dir' => array(
+ 'type' => 'directory',
+ 'default' => PEAR_CONFIG_DEFAULT_DATA_DIR,
+ 'doc' => 'directory where data files are installed',
+ 'prompt' => 'PEAR data directory',
+ 'group' => 'File Locations (Advanced)',
+ ),
+ 'test_dir' => array(
+ 'type' => 'directory',
+ 'default' => PEAR_CONFIG_DEFAULT_TEST_DIR,
+ 'doc' => 'directory where regression tests are installed',
+ 'prompt' => 'PEAR test directory',
+ 'group' => 'File Locations (Advanced)',
+ ),
+ 'cache_dir' => array(
+ 'type' => 'directory',
+ 'default' => PEAR_CONFIG_DEFAULT_CACHE_DIR,
+ 'doc' => 'directory which is used for XMLRPC cache',
+ 'prompt' => 'PEAR Installer cache directory',
+ 'group' => 'File Locations (Advanced)',
+ ),
+ 'php_bin' => array(
+ 'type' => 'file',
+ 'default' => PEAR_CONFIG_DEFAULT_PHP_BIN,
+ 'doc' => 'PHP CLI/CGI binary for executing scripts',
+ 'prompt' => 'PHP CLI/CGI binary',
+ 'group' => 'File Locations (Advanced)',
+ ),
+ // Maintainers
+ 'username' => array(
+ 'type' => 'string',
+ 'default' => '',
+ 'doc' => '(maintainers) your PEAR account name',
+ 'prompt' => 'PEAR username (for maintainers)',
+ 'group' => 'Maintainers',
+ ),
+ 'password' => array(
+ 'type' => 'password',
+ 'default' => '',
+ 'doc' => '(maintainers) your PEAR account password',
+ 'prompt' => 'PEAR password (for maintainers)',
+ 'group' => 'Maintainers',
+ ),
+ // Advanced
+ 'verbose' => array(
+ 'type' => 'integer',
+ 'default' => PEAR_CONFIG_DEFAULT_VERBOSE,
+ 'doc' => 'verbosity level
+0: really quiet
+1: somewhat quiet
+2: verbose
+3: debug',
+ 'prompt' => 'Debug Log Level',
+ 'group' => 'Advanced',
+ ),
+ 'preferred_state' => array(
+ 'type' => 'set',
+ 'default' => PEAR_CONFIG_DEFAULT_PREFERRED_STATE,
+ 'doc' => 'the installer will prefer releases with this state when installing packages without a version or state specified',
+ 'valid_set' => array(
+ 'stable', 'beta', 'alpha', 'devel', 'snapshot'),
+ 'prompt' => 'Preferred Package State',
+ 'group' => 'Advanced',
+ ),
+ 'umask' => array(
+ 'type' => 'mask',
+ 'default' => PEAR_CONFIG_DEFAULT_UMASK,
+ 'doc' => 'umask used when creating files (Unix-like systems only)',
+ 'prompt' => 'Unix file mask',
+ 'group' => 'Advanced',
+ ),
+ 'cache_ttl' => array(
+ 'type' => 'integer',
+ 'default' => PEAR_CONFIG_DEFAULT_CACHE_TTL,
+ 'doc' => 'amount of secs where the local cache is used and not updated',
+ 'prompt' => 'Cache TimeToLive',
+ 'group' => 'Advanced',
+ ),
+ 'sig_type' => array(
+ 'type' => 'set',
+ 'default' => PEAR_CONFIG_DEFAULT_SIG_TYPE,
+ 'doc' => 'which package signature mechanism to use',
+ 'valid_set' => array('gpg'),
+ 'prompt' => 'Package Signature Type',
+ 'group' => 'Maintainers',
+ ),
+ 'sig_bin' => array(
+ 'type' => 'string',
+ 'default' => PEAR_CONFIG_DEFAULT_SIG_BIN,
+ 'doc' => 'which package signature mechanism to use',
+ 'prompt' => 'Signature Handling Program',
+ 'group' => 'Maintainers',
+ ),
+ 'sig_keyid' => array(
+ 'type' => 'string',
+ 'default' => '',
+ 'doc' => 'which key to use for signing with',
+ 'prompt' => 'Signature Key Id',
+ 'group' => 'Maintainers',
+ ),
+ 'sig_keydir' => array(
+ 'type' => 'string',
+ 'default' => PEAR_CONFIG_DEFAULT_SIG_KEYDIR,
+ 'doc' => 'which package signature mechanism to use',
+ 'prompt' => 'Signature Key Directory',
+ 'group' => 'Maintainers',
+ ),
+ );
+
+ // }}}
+
+ // {{{ PEAR_Config([file], [defaults_file])
+
+ /**
+ * Constructor.
+ *
+ * @param string (optional) file to read user-defined options from
+ * @param string (optional) file to read system-wide defaults from
+ *
+ * @access public
+ *
+ * @see PEAR_Config::singleton
+ */
+ function PEAR_Config($user_file = '', $system_file = '')
+ {
+ $this->PEAR();
+ $sl = DIRECTORY_SEPARATOR;
+ if (empty($user_file)) {
+ if (OS_WINDOWS) {
+ $user_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini';
+ } else {
+ $user_file = getenv('HOME') . $sl . '.pearrc';
+ }
+ }
+ if (empty($system_file)) {
+ if (OS_WINDOWS) {
+ $system_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pearsys.ini';
+ } else {
+ $system_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.conf';
+ }
+ }
+ $this->layers = array_keys($this->configuration);
+ $this->files['user'] = $user_file;
+ $this->files['system'] = $system_file;
+ if ($user_file && file_exists($user_file)) {
+ $this->readConfigFile($user_file);
+ }
+ if ($system_file && file_exists($system_file)) {
+ $this->mergeConfigFile($system_file, false, 'system');
+ }
+ foreach ($this->configuration_info as $key => $info) {
+ $this->configuration['default'][$key] = $info['default'];
+ }
+ //$GLOBALS['_PEAR_Config_instance'] = &$this;
+ }
+
+ // }}}
+ // {{{ singleton([file], [defaults_file])
+
+ /**
+ * Static singleton method. If you want to keep only one instance
+ * of this class in use, this method will give you a reference to
+ * the last created PEAR_Config object if one exists, or create a
+ * new object.
+ *
+ * @param string (optional) file to read user-defined options from
+ * @param string (optional) file to read system-wide defaults from
+ *
+ * @return object an existing or new PEAR_Config instance
+ *
+ * @access public
+ *
+ * @see PEAR_Config::PEAR_Config
+ */
+ function &singleton($user_file = '', $system_file = '')
+ {
+ if (is_object($GLOBALS['_PEAR_Config_instance'])) {
+ return $GLOBALS['_PEAR_Config_instance'];
+ }
+ $GLOBALS['_PEAR_Config_instance'] =
+ &new PEAR_Config($user_file, $system_file);
+ return $GLOBALS['_PEAR_Config_instance'];
+ }
+
+ // }}}
+ // {{{ readConfigFile([file], [layer])
+
+ /**
+ * Reads configuration data from a file. All existing values in
+ * the config layer are discarded and replaced with data from the
+ * file.
+ *
+ * @param string (optional) file to read from, if NULL or not
+ * specified, the last-used file for the same layer (second param)
+ * is used
+ *
+ * @param string (optional) config layer to insert data into
+ * ('user' or 'system')
+ *
+ * @return bool TRUE on success or a PEAR error on failure
+ *
+ * @access public
+ */
+ function readConfigFile($file = null, $layer = 'user')
+ {
+ if (empty($this->files[$layer])) {
+ return $this->raiseError("unknown config file type `$layer'");
+ }
+ if ($file === null) {
+ $file = $this->files[$layer];
+ }
+ $data = $this->_readConfigDataFrom($file);
+ if (PEAR::isError($data)) {
+ return $data;
+ }
+ $this->_decodeInput($data);
+ $this->configuration[$layer] = $data;
+ return true;
+ }
+
+ // }}}
+ // {{{ mergeConfigFile(file, [override], [layer])
+
+ /**
+ * Merges data into a config layer from a file. Does the same
+ * thing as readConfigFile, except it does not replace all
+ * existing values in the config layer.
+ *
+ * @param string file to read from
+ *
+ * @param bool (optional) whether to overwrite existing data
+ * (default TRUE)
+ *
+ * @param string config layer to insert data into ('user' or
+ * 'system')
+ *
+ * @return bool TRUE on success or a PEAR error on failure
+ *
+ * @access public.
+ */
+ function mergeConfigFile($file, $override = true, $layer = 'user')
+ {
+ if (empty($this->files[$layer])) {
+ return $this->raiseError("unknown config file type `$layer'");
+ }
+ if ($file === null) {
+ $file = $this->files[$layer];
+ }
+ $data = $this->_readConfigDataFrom($file);
+ if (PEAR::isError($data)) {
+ return $data;
+ }
+ $this->_decodeInput($data);
+ if ($override) {
+ $this->configuration[$layer] = array_merge($this->configuration[$layer], $data);
+ } else {
+ $this->configuration[$layer] = array_merge($data, $this->configuration[$layer]);
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ writeConfigFile([file], [layer])
+
+ /**
+ * Writes data into a config layer from a file.
+ *
+ * @param string file to read from
+ *
+ * @param bool (optional) whether to overwrite existing data
+ * (default TRUE)
+ *
+ * @param string config layer to insert data into ('user' or
+ * 'system')
+ *
+ * @return bool TRUE on success or a PEAR error on failure
+ *
+ * @access public.
+ */
+ function writeConfigFile($file = null, $layer = 'user', $data = null)
+ {
+ if ($layer == 'both' || $layer == 'all') {
+ foreach ($this->files as $type => $file) {
+ $err = $this->writeConfigFile($file, $type, $data);
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+ }
+ return true;
+ }
+ if (empty($this->files[$layer])) {
+ return $this->raiseError("unknown config file type `$layer'");
+ }
+ if ($file === null) {
+ $file = $this->files[$layer];
+ }
+ $data = ($data === null) ? $this->configuration[$layer] : $data;
+ $this->_encodeOutput($data);
+ $opt = array('-p', dirname($file));
+ if (!@System::mkDir($opt)) {
+ return $this->raiseError("could not create directory: " . dirname($file));
+ }
+ if (@is_file($file) && !@is_writeable($file)) {
+ return $this->raiseError("no write access to $file!");
+ }
+ $fp = @fopen($file, "w");
+ if (!$fp) {
+ return $this->raiseError("PEAR_Config::writeConfigFile fopen('$file','w') failed");
+ }
+ $contents = "#PEAR_Config 0.9\n" . serialize($data);
+ if (!@fwrite($fp, $contents)) {
+ return $this->raiseError("PEAR_Config::writeConfigFile: fwrite failed");
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ _readConfigDataFrom(file)
+
+ /**
+ * Reads configuration data from a file and returns the parsed data
+ * in an array.
+ *
+ * @param string file to read from
+ *
+ * @return array configuration data or a PEAR error on failure
+ *
+ * @access private
+ */
+ function _readConfigDataFrom($file)
+ {
+ $fp = @fopen($file, "r");
+ if (!$fp) {
+ return $this->raiseError("PEAR_Config::readConfigFile fopen('$file','r') failed");
+ }
+ $size = filesize($file);
+ $rt = get_magic_quotes_runtime();
+ set_magic_quotes_runtime(0);
+ $contents = fread($fp, $size);
+ set_magic_quotes_runtime($rt);
+ fclose($fp);
+ $version = '0.1';
+ if (preg_match('/^#PEAR_Config\s+(\S+)\s+/si', $contents, $matches)) {
+ $version = $matches[1];
+ $contents = substr($contents, strlen($matches[0]));
+ }
+ if (version_compare("$version", '1', '<')) {
+ $data = unserialize($contents);
+ if (!is_array($data)) {
+ if (strlen(trim($contents)) > 0) {
+ $error = "PEAR_Config: bad data in $file";
+// if (isset($this)) {
+ return $this->raiseError($error);
+// } else {
+// return PEAR::raiseError($error);
+ } else {
+ $data = array();
+ }
+ }
+ // add parsing of newer formats here...
+ } else {
+ return $this->raiseError("$file: unknown version `$version'");
+ }
+ return $data;
+ }
+
+ // }}}
+ // {{{ getConfFile(layer)
+ /**
+ * Gets the file used for storing the config for a layer
+ *
+ * @param string $layer 'user' or 'system'
+ */
+
+ function getConfFile($layer)
+ {
+ return $this->files[$layer];
+ }
+
+ // }}}
+ // {{{ _encodeOutput(&data)
+
+ /**
+ * Encodes/scrambles configuration data before writing to files.
+ * Currently, 'password' values will be base64-encoded as to avoid
+ * that people spot cleartext passwords by accident.
+ *
+ * @param array (reference) array to encode values in
+ *
+ * @return bool TRUE on success
+ *
+ * @access private
+ */
+ function _encodeOutput(&$data)
+ {
+ foreach ($data as $key => $value) {
+ if (!isset($this->configuration_info[$key])) {
+ continue;
+ }
+ $type = $this->configuration_info[$key]['type'];
+ switch ($type) {
+ // we base64-encode passwords so they are at least
+ // not shown in plain by accident
+ case 'password': {
+ $data[$key] = base64_encode($data[$key]);
+ break;
+ }
+ case 'mask': {
+ $data[$key] = octdec($data[$key]);
+ break;
+ }
+ }
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ _decodeInput(&data)
+
+ /**
+ * Decodes/unscrambles configuration data after reading from files.
+ *
+ * @param array (reference) array to encode values in
+ *
+ * @return bool TRUE on success
+ *
+ * @access private
+ *
+ * @see PEAR_Config::_encodeOutput
+ */
+ function _decodeInput(&$data)
+ {
+ if (!is_array($data)) {
+ return true;
+ }
+ foreach ($data as $key => $value) {
+ if (!isset($this->configuration_info[$key])) {
+ continue;
+ }
+ $type = $this->configuration_info[$key]['type'];
+ switch ($type) {
+ case 'password': {
+ $data[$key] = base64_decode($data[$key]);
+ break;
+ }
+ case 'mask': {
+ $data[$key] = decoct($data[$key]);
+ break;
+ }
+ }
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ get(key, [layer])
+
+ /**
+ * Returns a configuration value, prioritizing layers as per the
+ * layers property.
+ *
+ * @param string config key
+ *
+ * @return mixed the config value, or NULL if not found
+ *
+ * @access public
+ */
+ function get($key, $layer = null)
+ {
+ if ($layer === null) {
+ foreach ($this->layers as $layer) {
+ if (isset($this->configuration[$layer][$key])) {
+ return $this->configuration[$layer][$key];
+ }
+ }
+ } elseif (isset($this->configuration[$layer][$key])) {
+ return $this->configuration[$layer][$key];
+ }
+ return null;
+ }
+
+ // }}}
+ // {{{ set(key, value, [layer])
+
+ /**
+ * Set a config value in a specific layer (defaults to 'user').
+ * Enforces the types defined in the configuration_info array. An
+ * integer config variable will be cast to int, and a set config
+ * variable will be validated against its legal values.
+ *
+ * @param string config key
+ *
+ * @param string config value
+ *
+ * @param string (optional) config layer
+ *
+ * @return bool TRUE on success, FALSE on failure
+ *
+ * @access public
+ */
+ function set($key, $value, $layer = 'user')
+ {
+ if (empty($this->configuration_info[$key])) {
+ return false;
+ }
+ extract($this->configuration_info[$key]);
+ switch ($type) {
+ case 'integer':
+ $value = (int)$value;
+ break;
+ case 'set': {
+ // If a valid_set is specified, require the value to
+ // be in the set. If there is no valid_set, accept
+ // any value.
+ if ($valid_set) {
+ reset($valid_set);
+ if ((key($valid_set) === 0 && !in_array($value, $valid_set)) ||
+ (key($valid_set) !== 0 && empty($valid_set[$value])))
+ {
+ return false;
+ }
+ }
+ break;
+ }
+ }
+ $this->configuration[$layer][$key] = $value;
+ return true;
+ }
+
+ // }}}
+ // {{{ getType(key)
+
+ /**
+ * Get the type of a config value.
+ *
+ * @param string config key
+ *
+ * @return string type, one of "string", "integer", "file",
+ * "directory", "set" or "password".
+ *
+ * @access public
+ *
+ */
+ function getType($key)
+ {
+ if (isset($this->configuration_info[$key])) {
+ return $this->configuration_info[$key]['type'];
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ getDocs(key)
+
+ /**
+ * Get the documentation for a config value.
+ *
+ * @param string config key
+ *
+ * @return string documentation string
+ *
+ * @access public
+ *
+ */
+ function getDocs($key)
+ {
+ if (isset($this->configuration_info[$key])) {
+ return $this->configuration_info[$key]['doc'];
+ }
+ return false;
+ }
+ // }}}
+ // {{{ getPrompt(key)
+
+ /**
+ * Get the short documentation for a config value.
+ *
+ * @param string config key
+ *
+ * @return string short documentation string
+ *
+ * @access public
+ *
+ */
+ function getPrompt($key)
+ {
+ if (isset($this->configuration_info[$key])) {
+ return $this->configuration_info[$key]['prompt'];
+ }
+ return false;
+ }
+ // }}}
+ // {{{ getGroup(key)
+
+ /**
+ * Get the parameter group for a config key.
+ *
+ * @param string config key
+ *
+ * @return string parameter group
+ *
+ * @access public
+ *
+ */
+ function getGroup($key)
+ {
+ if (isset($this->configuration_info[$key])) {
+ return $this->configuration_info[$key]['group'];
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ getGroups()
+
+ /**
+ * Get the list of parameter groups.
+ *
+ * @return array list of parameter groups
+ *
+ * @access public
+ *
+ */
+ function getGroups()
+ {
+ $tmp = array();
+ foreach ($this->configuration_info as $key => $info) {
+ $tmp[$info['group']] = 1;
+ }
+ return array_keys($tmp);
+ }
+
+ // }}}
+ // {{{ getGroupKeys()
+
+ /**
+ * Get the list of the parameters in a group.
+ *
+ * @param string $group parameter group
+ *
+ * @return array list of parameters in $group
+ *
+ * @access public
+ *
+ */
+ function getGroupKeys($group)
+ {
+ $keys = array();
+ foreach ($this->configuration_info as $key => $info) {
+ if ($info['group'] == $group) {
+ $keys[] = $key;
+ }
+ }
+ return $keys;
+ }
+
+ // }}}
+ // {{{ getSetValues(key)
+
+ /**
+ * Get the list of allowed set values for a config value. Returns
+ * NULL for config values that are not sets.
+ *
+ * @param string config key
+ *
+ * @return array enumerated array of set values, or NULL if the
+ * config key is unknown or not a set
+ *
+ * @access public
+ *
+ */
+ function getSetValues($key)
+ {
+ if (isset($this->configuration_info[$key]) &&
+ isset($this->configuration_info[$key]['type']) &&
+ $this->configuration_info[$key]['type'] == 'set')
+ {
+ $valid_set = $this->configuration_info[$key]['valid_set'];
+ reset($valid_set);
+ if (key($valid_set) === 0) {
+ return $valid_set;
+ }
+ return array_keys($valid_set);
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ getKeys()
+
+ /**
+ * Get all the current config keys.
+ *
+ * @return array simple array of config keys
+ *
+ * @access public
+ */
+ function getKeys()
+ {
+ $keys = array();
+ foreach ($this->layers as $layer) {
+ $keys = array_merge($keys, $this->configuration[$layer]);
+ }
+ return array_keys($keys);
+ }
+
+ // }}}
+ // {{{ remove(key, [layer])
+
+ /**
+ * Remove the a config key from a specific config layer.
+ *
+ * @param string config key
+ *
+ * @param string (optional) config layer
+ *
+ * @return bool TRUE on success, FALSE on failure
+ *
+ * @access public
+ */
+ function remove($key, $layer = 'user')
+ {
+ if (isset($this->configuration[$layer][$key])) {
+ unset($this->configuration[$layer][$key]);
+ return true;
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ removeLayer(layer)
+
+ /**
+ * Temporarily remove an entire config layer. USE WITH CARE!
+ *
+ * @param string config key
+ *
+ * @param string (optional) config layer
+ *
+ * @return bool TRUE on success, FALSE on failure
+ *
+ * @access public
+ */
+ function removeLayer($layer)
+ {
+ if (isset($this->configuration[$layer])) {
+ $this->configuration[$layer] = array();
+ return true;
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ store([layer])
+
+ /**
+ * Stores configuration data in a layer.
+ *
+ * @param string config layer to store
+ *
+ * @return bool TRUE on success, or PEAR error on failure
+ *
+ * @access public
+ */
+ function store($layer = 'user', $data = null)
+ {
+ return $this->writeConfigFile(null, $layer, $data);
+ }
+
+ // }}}
+ // {{{ toDefault(key)
+
+ /**
+ * Unset the user-defined value of a config key, reverting the
+ * value to the system-defined one.
+ *
+ * @param string config key
+ *
+ * @return bool TRUE on success, FALSE on failure
+ *
+ * @access public
+ */
+ function toDefault($key)
+ {
+ trigger_error("PEAR_Config::toDefault() deprecated, use PEAR_Config::remove() instead", E_USER_NOTICE);
+ return $this->remove($key, 'user');
+ }
+
+ // }}}
+ // {{{ definedBy(key)
+
+ /**
+ * Tells what config layer that gets to define a key.
+ *
+ * @param string config key
+ *
+ * @return string the config layer, or an empty string if not found
+ *
+ * @access public
+ */
+ function definedBy($key)
+ {
+ foreach ($this->layers as $layer) {
+ if (isset($this->configuration[$layer][$key])) {
+ return $layer;
+ }
+ }
+ return '';
+ }
+
+ // }}}
+ // {{{ isDefaulted(key)
+
+ /**
+ * Tells whether a config value has a system-defined value.
+ *
+ * @param string config key
+ *
+ * @return bool
+ *
+ * @access public
+ *
+ * @deprecated
+ */
+ function isDefaulted($key)
+ {
+ trigger_error("PEAR_Config::isDefaulted() deprecated, use PEAR_Config::definedBy() instead", E_USER_NOTICE);
+ return $this->definedBy($key) == 'system';
+ }
+
+ // }}}
+ // {{{ isDefined(key)
+
+ /**
+ * Tells whether a given key exists as a config value.
+ *
+ * @param string config key
+ *
+ * @return bool whether <config key> exists in this object
+ *
+ * @access public
+ */
+ function isDefined($key)
+ {
+ foreach ($this->layers as $layer) {
+ if (isset($this->configuration[$layer][$key])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ isDefinedLayer(key)
+
+ /**
+ * Tells whether a given config layer exists.
+ *
+ * @param string config layer
+ *
+ * @return bool whether <config layer> exists in this object
+ *
+ * @access public
+ */
+ function isDefinedLayer($layer)
+ {
+ return isset($this->configuration[$layer]);
+ }
+
+ // }}}
+ // {{{ getLayers()
+
+ /**
+ * Returns the layers defined (except the 'default' one)
+ *
+ * @return array of the defined layers
+ */
+ function getLayers()
+ {
+ $cf = $this->configuration;
+ unset($cf['default']);
+ return array_keys($cf);
+ }
+
+ // }}}
+}
+
+?>
diff --git a/pearlib/PEAR/Dependency.php b/pearlib/PEAR/Dependency.php
new file mode 100644
index 00000000..4a3a15ca
--- /dev/null
+++ b/pearlib/PEAR/Dependency.php
@@ -0,0 +1,486 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Tomas V.V.Cox <[email protected]> |
+// | Stig Bakken <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Dependency.php,v 1.36 2004/01/08 17:33:12 sniper Exp $
+
+require_once "PEAR.php";
+
+define('PEAR_DEPENDENCY_MISSING', -1);
+define('PEAR_DEPENDENCY_CONFLICT', -2);
+define('PEAR_DEPENDENCY_UPGRADE_MINOR', -3);
+define('PEAR_DEPENDENCY_UPGRADE_MAJOR', -4);
+define('PEAR_DEPENDENCY_BAD_DEPENDENCY', -5);
+define('PEAR_DEPENDENCY_MISSING_OPTIONAL', -6);
+define('PEAR_DEPENDENCY_CONFLICT_OPTIONAL', -7);
+define('PEAR_DEPENDENCY_UPGRADE_MINOR_OPTIONAL', -8);
+define('PEAR_DEPENDENCY_UPGRADE_MAJOR_OPTIONAL', -9);
+
+/**
+ * Dependency check for PEAR packages
+ *
+ * The class is based on the dependency RFC that can be found at
+ * http://cvs.php.net/cvs.php/pearweb/rfc. It requires PHP >= 4.1
+ *
+ * @author Tomas V.V.Vox <[email protected]>
+ * @author Stig Bakken <[email protected]>
+ */
+class PEAR_Dependency
+{
+ // {{{ constructor
+ /**
+ * Constructor
+ *
+ * @access public
+ * @param object Registry object
+ * @return void
+ */
+ function PEAR_Dependency(&$registry)
+ {
+ $this->registry = &$registry;
+ }
+
+ // }}}
+ // {{{ callCheckMethod()
+
+ /**
+ * This method maps the XML dependency definition to the
+ * corresponding one from PEAR_Dependency
+ *
+ * <pre>
+ * $opts => Array
+ * (
+ * [type] => pkg
+ * [rel] => ge
+ * [version] => 3.4
+ * [name] => HTML_Common
+ * [optional] => false
+ * )
+ * </pre>
+ *
+ * @param string Error message
+ * @param array Options
+ * @return boolean
+ */
+ function callCheckMethod(&$errmsg, $opts)
+ {
+ $rel = isset($opts['rel']) ? $opts['rel'] : 'has';
+ $req = isset($opts['version']) ? $opts['version'] : null;
+ $name = isset($opts['name']) ? $opts['name'] : null;
+ $opt = (isset($opts['optional']) && $opts['optional'] == 'yes') ?
+ $opts['optional'] : null;
+ $errmsg = '';
+ switch ($opts['type']) {
+ case 'pkg':
+ return $this->checkPackage($errmsg, $name, $req, $rel, $opt);
+ break;
+ case 'ext':
+ return $this->checkExtension($errmsg, $name, $req, $rel, $opt);
+ break;
+ case 'php':
+ return $this->checkPHP($errmsg, $req, $rel);
+ break;
+ case 'prog':
+ return $this->checkProgram($errmsg, $name);
+ break;
+ case 'os':
+ return $this->checkOS($errmsg, $name);
+ break;
+ case 'sapi':
+ return $this->checkSAPI($errmsg, $name);
+ break;
+ case 'zend':
+ return $this->checkZend($errmsg, $name);
+ break;
+ default:
+ return "'{$opts['type']}' dependency type not supported";
+ }
+ }
+
+ // }}}
+ // {{{ checkPackage()
+
+ /**
+ * Package dependencies check method
+ *
+ * @param string $errmsg Empty string, it will be populated with an error message, if any
+ * @param string $name Name of the package to test
+ * @param string $req The package version required
+ * @param string $relation How to compare versions with each other
+ * @param bool $opt Whether the relationship is optional
+ *
+ * @return mixed bool false if no error or the error string
+ */
+ function checkPackage(&$errmsg, $name, $req = null, $relation = 'has',
+ $opt = false)
+ {
+ if (is_string($req) && substr($req, 0, 2) == 'v.') {
+ $req = substr($req, 2);
+ }
+ switch ($relation) {
+ case 'has':
+ if (!$this->registry->packageExists($name)) {
+ if ($opt) {
+ $errmsg = "package `$name' is recommended to utilize some features.";
+ return PEAR_DEPENDENCY_MISSING_OPTIONAL;
+ }
+ $errmsg = "requires package `$name'";
+ return PEAR_DEPENDENCY_MISSING;
+ }
+ return false;
+ case 'not':
+ if ($this->registry->packageExists($name)) {
+ $errmsg = "conflicts with package `$name'";
+ return PEAR_DEPENDENCY_CONFLICT;
+ }
+ return false;
+ case 'lt':
+ case 'le':
+ case 'eq':
+ case 'ne':
+ case 'ge':
+ case 'gt':
+ $version = $this->registry->packageInfo($name, 'version');
+ if (!$this->registry->packageExists($name)
+ || !version_compare("$version", "$req", $relation))
+ {
+ $code = $this->codeFromRelation($relation, $version, $req, $opt);
+ if ($opt) {
+ $errmsg = "package `$name' version " . $this->signOperator($relation) .
+ " $req is recommended to utilize some features.";
+ if ($version) {
+ $errmsg .= " Installed version is $version";
+ }
+ return $code;
+ }
+ $errmsg = "requires package `$name' " .
+ $this->signOperator($relation) . " $req";
+ return $code;
+ }
+ return false;
+ }
+ $errmsg = "relation '$relation' with requirement '$req' is not supported (name=$name)";
+ return PEAR_DEPENDENCY_BAD_DEPENDENCY;
+ }
+
+ // }}}
+ // {{{ checkPackageUninstall()
+
+ /**
+ * Check package dependencies on uninstall
+ *
+ * @param string $error The resultant error string
+ * @param string $warning The resultant warning string
+ * @param string $name Name of the package to test
+ *
+ * @return bool true if there were errors
+ */
+ function checkPackageUninstall(&$error, &$warning, $package)
+ {
+ $error = null;
+ $packages = $this->registry->listPackages();
+ foreach ($packages as $pkg) {
+ if ($pkg == $package) {
+ continue;
+ }
+ $deps = $this->registry->packageInfo($pkg, 'release_deps');
+ if (empty($deps)) {
+ continue;
+ }
+ foreach ($deps as $dep) {
+ if ($dep['type'] == 'pkg' && strcasecmp($dep['name'], $package) == 0) {
+ if ($dep['rel'] == 'ne') {
+ continue;
+ }
+ if (isset($dep['optional']) && $dep['optional'] == 'yes') {
+ $warning .= "\nWarning: Package '$pkg' optionally depends on '$package'";
+ } else {
+ $error .= "Package '$pkg' depends on '$package'\n";
+ }
+ }
+ }
+ }
+ return ($error) ? true : false;
+ }
+
+ // }}}
+ // {{{ checkExtension()
+
+ /**
+ * Extension dependencies check method
+ *
+ * @param string $name Name of the extension to test
+ * @param string $req_ext_ver Required extension version to compare with
+ * @param string $relation How to compare versions with eachother
+ * @param bool $opt Whether the relationship is optional
+ *
+ * @return mixed bool false if no error or the error string
+ */
+ function checkExtension(&$errmsg, $name, $req = null, $relation = 'has',
+ $opt = false)
+ {
+ if ($relation == 'not') {
+ if (extension_loaded($name)) {
+ $errmsg = "conflicts with PHP extension '$name'";
+ return PEAR_DEPENDENCY_CONFLICT;
+ } else {
+ return false;
+ }
+ }
+
+ if (!extension_loaded($name)) {
+ if ($relation == 'ne') {
+ return false;
+ }
+ if ($opt) {
+ $errmsg = "'$name' PHP extension is recommended to utilize some features";
+ return PEAR_DEPENDENCY_MISSING_OPTIONAL;
+ }
+ $errmsg = "'$name' PHP extension is not installed";
+ return PEAR_DEPENDENCY_MISSING;
+ }
+ if ($relation == 'has') {
+ return false;
+ }
+ $code = false;
+ if (is_string($req) && substr($req, 0, 2) == 'v.') {
+ $req = substr($req, 2);
+ }
+ $ext_ver = phpversion($name);
+ $operator = $relation;
+ // Force params to be strings, otherwise the comparation will fail (ex. 0.9==0.90)
+ if (!version_compare("$ext_ver", "$req", $operator)) {
+ $errmsg = "'$name' PHP extension version " .
+ $this->signOperator($operator) . " $req is required";
+ $code = $this->codeFromRelation($relation, $ext_ver, $req, $opt);
+ if ($opt) {
+ $errmsg = "'$name' PHP extension version " . $this->signOperator($operator) .
+ " $req is recommended to utilize some features";
+ return $code;
+ }
+ }
+ return $code;
+ }
+
+ // }}}
+ // {{{ checkOS()
+
+ /**
+ * Operating system dependencies check method
+ *
+ * @param string $os Name of the operating system
+ *
+ * @return mixed bool false if no error or the error string
+ */
+ function checkOS(&$errmsg, $os)
+ {
+ // XXX Fixme: Implement a more flexible way, like
+ // comma separated values or something similar to PEAR_OS
+ static $myos;
+ if (empty($myos)) {
+ include_once "OS/Guess.php";
+ $myos = new OS_Guess();
+ }
+ // only 'has' relation is currently supported
+ if ($myos->matchSignature($os)) {
+ return false;
+ }
+ $errmsg = "'$os' operating system not supported";
+ return PEAR_DEPENDENCY_CONFLICT;
+ }
+
+ // }}}
+ // {{{ checkPHP()
+
+ /**
+ * PHP version check method
+ *
+ * @param string $req which version to compare
+ * @param string $relation how to compare the version
+ *
+ * @return mixed bool false if no error or the error string
+ */
+ function checkPHP(&$errmsg, $req, $relation = 'ge')
+ {
+ // this would be a bit stupid, but oh well :)
+ if ($relation == 'has') {
+ return false;
+ }
+ if ($relation == 'not') {
+ $errmsg = "Invalid dependency - 'not' is allowed when specifying PHP, you must run PHP in PHP";
+ return PEAR_DEPENDENCY_BAD_DEPENDENCY;
+ }
+ if (substr($req, 0, 2) == 'v.') {
+ $req = substr($req,2, strlen($req) - 2);
+ }
+ $php_ver = phpversion();
+ $operator = $relation;
+ if (!version_compare("$php_ver", "$req", $operator)) {
+ $errmsg = "PHP version " . $this->signOperator($operator) .
+ " $req is required";
+ return PEAR_DEPENDENCY_CONFLICT;
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ checkProgram()
+
+ /**
+ * External program check method. Looks for executable files in
+ * directories listed in the PATH environment variable.
+ *
+ * @param string $program which program to look for
+ *
+ * @return mixed bool false if no error or the error string
+ */
+ function checkProgram(&$errmsg, $program)
+ {
+ // XXX FIXME honor safe mode
+ $exe_suffix = OS_WINDOWS ? '.exe' : '';
+ $path_elements = explode(PATH_SEPARATOR, getenv('PATH'));
+ foreach ($path_elements as $dir) {
+ $file = $dir . DIRECTORY_SEPARATOR . $program . $exe_suffix;
+ if (@file_exists($file) && @is_executable($file)) {
+ return false;
+ }
+ }
+ $errmsg = "'$program' program is not present in the PATH";
+ return PEAR_DEPENDENCY_MISSING;
+ }
+
+ // }}}
+ // {{{ checkSAPI()
+
+ /**
+ * SAPI backend check method. Version comparison is not yet
+ * available here.
+ *
+ * @param string $name name of SAPI backend
+ * @param string $req which version to compare
+ * @param string $relation how to compare versions (currently
+ * hardcoded to 'has')
+ * @return mixed bool false if no error or the error string
+ */
+ function checkSAPI(&$errmsg, $name, $req = null, $relation = 'has')
+ {
+ // XXX Fixme: There is no way to know if the user has or
+ // not other SAPI backends installed than the installer one
+
+ $sapi_backend = php_sapi_name();
+ // Version comparisons not supported, sapi backends don't have
+ // version information yet.
+ if ($sapi_backend == $name) {
+ return false;
+ }
+ $errmsg = "'$sapi_backend' SAPI backend not supported";
+ return PEAR_DEPENDENCY_CONFLICT;
+ }
+
+ // }}}
+ // {{{ checkZend()
+
+ /**
+ * Zend version check method
+ *
+ * @param string $req which version to compare
+ * @param string $relation how to compare the version
+ *
+ * @return mixed bool false if no error or the error string
+ */
+ function checkZend(&$errmsg, $req, $relation = 'ge')
+ {
+ if (substr($req, 0, 2) == 'v.') {
+ $req = substr($req,2, strlen($req) - 2);
+ }
+ $zend_ver = zend_version();
+ $operator = substr($relation,0,2);
+ if (!version_compare("$zend_ver", "$req", $operator)) {
+ $errmsg = "Zend version " . $this->signOperator($operator) .
+ " $req is required";
+ return PEAR_DEPENDENCY_CONFLICT;
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ signOperator()
+
+ /**
+ * Converts text comparing operators to them sign equivalents
+ *
+ * Example: 'ge' to '>='
+ *
+ * @access public
+ * @param string Operator
+ * @return string Sign equivalent
+ */
+ function signOperator($operator)
+ {
+ switch($operator) {
+ case 'lt': return '<';
+ case 'le': return '<=';
+ case 'gt': return '>';
+ case 'ge': return '>=';
+ case 'eq': return '==';
+ case 'ne': return '!=';
+ default:
+ return $operator;
+ }
+ }
+
+ // }}}
+ // {{{ codeFromRelation()
+
+ /**
+ * Convert relation into corresponding code
+ *
+ * @access public
+ * @param string Relation
+ * @param string Version
+ * @param string Requirement
+ * @param bool Optional dependency indicator
+ * @return integer
+ */
+ function codeFromRelation($relation, $version, $req, $opt = false)
+ {
+ $code = PEAR_DEPENDENCY_BAD_DEPENDENCY;
+ switch ($relation) {
+ case 'gt': case 'ge': case 'eq':
+ // upgrade
+ $have_major = preg_replace('/\D.*/', '', $version);
+ $need_major = preg_replace('/\D.*/', '', $req);
+ if ($need_major > $have_major) {
+ $code = $opt ? PEAR_DEPENDENCY_UPGRADE_MAJOR_OPTIONAL :
+ PEAR_DEPENDENCY_UPGRADE_MAJOR;
+ } else {
+ $code = $opt ? PEAR_DEPENDENCY_UPGRADE_MINOR_OPTIONAL :
+ PEAR_DEPENDENCY_UPGRADE_MINOR;
+ }
+ break;
+ case 'lt': case 'le': case 'ne':
+ $code = $opt ? PEAR_DEPENDENCY_CONFLICT_OPTIONAL :
+ PEAR_DEPENDENCY_CONFLICT;
+ break;
+ }
+ return $code;
+ }
+
+ // }}}
+}
+?>
diff --git a/pearlib/PEAR/Downloader.php b/pearlib/PEAR/Downloader.php
new file mode 100644
index 00000000..5e16dc5f
--- /dev/null
+++ b/pearlib/PEAR/Downloader.php
@@ -0,0 +1,680 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Bakken <[email protected]> |
+// | Tomas V.V.Cox <[email protected]> |
+// | Martin Jansen <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Downloader.php,v 1.17.2.1 2004/10/22 22:54:03 cellog Exp $
+
+require_once 'PEAR/Common.php';
+require_once 'PEAR/Registry.php';
+require_once 'PEAR/Dependency.php';
+require_once 'PEAR/Remote.php';
+require_once 'System.php';
+
+
+define('PEAR_INSTALLER_OK', 1);
+define('PEAR_INSTALLER_FAILED', 0);
+define('PEAR_INSTALLER_SKIPPED', -1);
+define('PEAR_INSTALLER_ERROR_NO_PREF_STATE', 2);
+
+/**
+ * Administration class used to download PEAR packages and maintain the
+ * installed package database.
+ *
+ * @since PEAR 1.4
+ * @author Greg Beaver <[email protected]>
+ */
+class PEAR_Downloader extends PEAR_Common
+{
+ /**
+ * @var PEAR_Config
+ * @access private
+ */
+ var $_config;
+
+ /**
+ * @var PEAR_Registry
+ * @access private
+ */
+ var $_registry;
+
+ /**
+ * @var PEAR_Remote
+ * @access private
+ */
+ var $_remote;
+
+ /**
+ * Preferred Installation State (snapshot, devel, alpha, beta, stable)
+ * @var string|null
+ * @access private
+ */
+ var $_preferredState;
+
+ /**
+ * Options from command-line passed to Install.
+ *
+ * Recognized options:<br />
+ * - onlyreqdeps : install all required dependencies as well
+ * - alldeps : install all dependencies, including optional
+ * - installroot : base relative path to install files in
+ * - force : force a download even if warnings would prevent it
+ * @see PEAR_Command_Install
+ * @access private
+ * @var array
+ */
+ var $_options;
+
+ /**
+ * Downloaded Packages after a call to download().
+ *
+ * Format of each entry:
+ *
+ * <code>
+ * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
+ * 'info' => array() // parsed package.xml
+ * );
+ * </code>
+ * @access private
+ * @var array
+ */
+ var $_downloadedPackages = array();
+
+ /**
+ * Packages slated for download.
+ *
+ * This is used to prevent downloading a package more than once should it be a dependency
+ * for two packages to be installed.
+ * Format of each entry:
+ *
+ * <pre>
+ * array('package_name1' => parsed package.xml, 'package_name2' => parsed package.xml,
+ * );
+ * </pre>
+ * @access private
+ * @var array
+ */
+ var $_toDownload = array();
+
+ /**
+ * Array of every package installed, with names lower-cased.
+ *
+ * Format:
+ * <code>
+ * array('package1' => 0, 'package2' => 1, );
+ * </code>
+ * @var array
+ */
+ var $_installed = array();
+
+ /**
+ * @var array
+ * @access private
+ */
+ var $_errorStack = array();
+
+ // {{{ PEAR_Downloader()
+
+ function PEAR_Downloader(&$ui, $options, &$config)
+ {
+ $this->_options = $options;
+ $this->_config = &$config;
+ $this->_preferredState = $this->_config->get('preferred_state');
+ $this->ui = &$ui;
+ if (!$this->_preferredState) {
+ // don't inadvertantly use a non-set preferred_state
+ $this->_preferredState = null;
+ }
+
+ $php_dir = $this->_config->get('php_dir');
+ if (isset($this->_options['installroot'])) {
+ if (substr($this->_options['installroot'], -1) == DIRECTORY_SEPARATOR) {
+ $this->_options['installroot'] = substr($this->_options['installroot'], 0, -1);
+ }
+ $php_dir = $this->_prependPath($php_dir, $this->_options['installroot']);
+ }
+ $this->_registry = &new PEAR_Registry($php_dir);
+ $this->_remote = &new PEAR_Remote($config);
+
+ if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
+ $this->_installed = $this->_registry->listPackages();
+ array_walk($this->_installed, create_function('&$v,$k','$v = strtolower($v);'));
+ $this->_installed = array_flip($this->_installed);
+ }
+ parent::PEAR_Common();
+ }
+
+ // }}}
+ // {{{ configSet()
+ function configSet($key, $value, $layer = 'user')
+ {
+ $this->_config->set($key, $value, $layer);
+ $this->_preferredState = $this->_config->get('preferred_state');
+ if (!$this->_preferredState) {
+ // don't inadvertantly use a non-set preferred_state
+ $this->_preferredState = null;
+ }
+ }
+
+ // }}}
+ // {{{ setOptions()
+ function setOptions($options)
+ {
+ $this->_options = $options;
+ }
+
+ // }}}
+ // {{{ _downloadFile()
+ /**
+ * @param string filename to download
+ * @param string version/state
+ * @param string original value passed to command-line
+ * @param string|null preferred state (snapshot/devel/alpha/beta/stable)
+ * Defaults to configuration preferred state
+ * @return null|PEAR_Error|string
+ * @access private
+ */
+ function _downloadFile($pkgfile, $version, $origpkgfile, $state = null)
+ {
+ if (is_null($state)) {
+ $state = $this->_preferredState;
+ }
+ // {{{ check the package filename, and whether it's already installed
+ $need_download = false;
+ if (preg_match('#^(http|ftp)://#', $pkgfile)) {
+ $need_download = true;
+ } elseif (!@is_file($pkgfile)) {
+ if ($this->validPackageName($pkgfile)) {
+ if ($this->_registry->packageExists($pkgfile)) {
+ if (empty($this->_options['upgrade']) && empty($this->_options['force'])) {
+ $errors[] = "$pkgfile already installed";
+ return;
+ }
+ }
+ $pkgfile = $this->getPackageDownloadUrl($pkgfile, $version);
+ $need_download = true;
+ } else {
+ if (strlen($pkgfile)) {
+ $errors[] = "Could not open the package file: $pkgfile";
+ } else {
+ $errors[] = "No package file given";
+ }
+ return;
+ }
+ }
+ // }}}
+
+ // {{{ Download package -----------------------------------------------
+ if ($need_download) {
+ $downloaddir = $this->_config->get('download_dir');
+ if (empty($downloaddir)) {
+ if (PEAR::isError($downloaddir = System::mktemp('-d'))) {
+ return $downloaddir;
+ }
+ $this->log(3, '+ tmp dir created at ' . $downloaddir);
+ }
+ $callback = $this->ui ? array(&$this, '_downloadCallback') : null;
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $file = $this->downloadHttp($pkgfile, $this->ui, $downloaddir, $callback);
+ $this->popErrorHandling();
+ if (PEAR::isError($file)) {
+ if ($this->validPackageName($origpkgfile)) {
+ if (!PEAR::isError($info = $this->_remote->call('package.info',
+ $origpkgfile))) {
+ if (!count($info['releases'])) {
+ return $this->raiseError('Package ' . $origpkgfile .
+ ' has no releases');
+ } else {
+ return $this->raiseError('No releases of preferred state "'
+ . $state . '" exist for package ' . $origpkgfile .
+ '. Use ' . $origpkgfile . '-state to install another' .
+ ' state (like ' . $origpkgfile .'-beta)',
+ PEAR_INSTALLER_ERROR_NO_PREF_STATE);
+ }
+ } else {
+ return $pkgfile;
+ }
+ } else {
+ return $this->raiseError($file);
+ }
+ }
+ $pkgfile = $file;
+ }
+ // }}}
+ return $pkgfile;
+ }
+ // }}}
+ // {{{ getPackageDownloadUrl()
+
+ function getPackageDownloadUrl($package, $version = null)
+ {
+ if ($version) {
+ $package .= "-$version";
+ }
+ if ($this === null || $this->_config === null) {
+ $package = "http://pear.php.net/get/$package";
+ } else {
+ $package = "https://" . $this->_config->get('master_server') .
+ "/get/$package";
+ }
+ if (!extension_loaded("zlib")) {
+ $package .= '?uncompress=yes';
+ }
+ return $package;
+ }
+
+ // }}}
+ // {{{ extractDownloadFileName($pkgfile, &$version)
+
+ function extractDownloadFileName($pkgfile, &$version)
+ {
+ if (@is_file($pkgfile)) {
+ return $pkgfile;
+ }
+ // regex defined in Common.php
+ if (preg_match(PEAR_COMMON_PACKAGE_DOWNLOAD_PREG, $pkgfile, $m)) {
+ $version = (isset($m[3])) ? $m[3] : null;
+ return $m[1];
+ }
+ $version = null;
+ return $pkgfile;
+ }
+
+ // }}}
+
+ // }}}
+ // {{{ getDownloadedPackages()
+
+ /**
+ * Retrieve a list of downloaded packages after a call to {@link download()}.
+ *
+ * Also resets the list of downloaded packages.
+ * @return array
+ */
+ function getDownloadedPackages()
+ {
+ $ret = $this->_downloadedPackages;
+ $this->_downloadedPackages = array();
+ $this->_toDownload = array();
+ return $ret;
+ }
+
+ // }}}
+ // {{{ download()
+
+ /**
+ * Download any files and their dependencies, if necessary
+ *
+ * BC-compatible method name
+ * @param array a mixed list of package names, local files, or package.xml
+ */
+ function download($packages)
+ {
+ return $this->doDownload($packages);
+ }
+
+ // }}}
+ // {{{ doDownload()
+
+ /**
+ * Download any files and their dependencies, if necessary
+ *
+ * @param array a mixed list of package names, local files, or package.xml
+ */
+ function doDownload($packages)
+ {
+ $mywillinstall = array();
+ $state = $this->_preferredState;
+
+ // {{{ download files in this list if necessary
+ foreach($packages as $pkgfile) {
+ $need_download = false;
+ if (!is_file($pkgfile)) {
+ if (preg_match('#^(http|ftp)://#', $pkgfile)) {
+ $need_download = true;
+ }
+ $pkgfile = $this->_downloadNonFile($pkgfile);
+ if (PEAR::isError($pkgfile)) {
+ return $pkgfile;
+ }
+ if ($pkgfile === false) {
+ continue;
+ }
+ } // end is_file()
+
+ $tempinfo = $this->infoFromAny($pkgfile);
+ if ($need_download) {
+ $this->_toDownload[] = $tempinfo['package'];
+ }
+ if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
+ // ignore dependencies if there are any errors
+ if (!PEAR::isError($tempinfo)) {
+ $mywillinstall[strtolower($tempinfo['package'])] = @$tempinfo['release_deps'];
+ }
+ }
+ $this->_downloadedPackages[] = array('pkg' => $tempinfo['package'],
+ 'file' => $pkgfile, 'info' => $tempinfo);
+ } // end foreach($packages)
+ // }}}
+
+ // {{{ extract dependencies from downloaded files and then download
+ // them if necessary
+ if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
+ $deppackages = array();
+ foreach ($mywillinstall as $package => $alldeps) {
+ if (!is_array($alldeps)) {
+ // there are no dependencies
+ continue;
+ }
+ $fail = false;
+ foreach ($alldeps as $info) {
+ if ($info['type'] != 'pkg') {
+ continue;
+ }
+ $ret = $this->_processDependency($package, $info, $mywillinstall);
+ if ($ret === false) {
+ continue;
+ }
+ if ($ret === 0) {
+ $fail = true;
+ continue;
+ }
+ if (PEAR::isError($ret)) {
+ return $ret;
+ }
+ $deppackages[] = $ret;
+ } // foreach($alldeps
+ if ($fail) {
+ $deppackages = array();
+ }
+ }
+
+ if (count($deppackages)) {
+ $this->doDownload($deppackages);
+ }
+ } // }}} if --alldeps or --onlyreqdeps
+ }
+
+ // }}}
+ // {{{ _downloadNonFile($pkgfile)
+
+ /**
+ * @return false|PEAR_Error|string false if loop should be broken out of,
+ * string if the file was downloaded,
+ * PEAR_Error on exception
+ * @access private
+ */
+ function _downloadNonFile($pkgfile)
+ {
+ $origpkgfile = $pkgfile;
+ $state = null;
+ $pkgfile = $this->extractDownloadFileName($pkgfile, $version);
+ if (preg_match('#^(http|ftp)://#', $pkgfile)) {
+ return $this->_downloadFile($pkgfile, $version, $origpkgfile);
+ }
+ if (!$this->validPackageName($pkgfile)) {
+ return $this->raiseError("Package name '$pkgfile' not valid");
+ }
+ // ignore packages that are installed unless we are upgrading
+ $curinfo = $this->_registry->packageInfo($pkgfile);
+ if ($this->_registry->packageExists($pkgfile)
+ && empty($this->_options['upgrade']) && empty($this->_options['force'])) {
+ $this->log(0, "Package '{$curinfo['package']}' already installed, skipping");
+ return false;
+ }
+ if (in_array($pkgfile, $this->_toDownload)) {
+ return false;
+ }
+ $releases = $this->_remote->call('package.info', $pkgfile, 'releases', true);
+ if (!count($releases)) {
+ return $this->raiseError("No releases found for package '$pkgfile'");
+ }
+ // Want a specific version/state
+ if ($version !== null) {
+ // Passed Foo-1.2
+ if ($this->validPackageVersion($version)) {
+ if (!isset($releases[$version])) {
+ return $this->raiseError("No release with version '$version' found for '$pkgfile'");
+ }
+ // Passed Foo-alpha
+ } elseif (in_array($version, $this->getReleaseStates())) {
+ $state = $version;
+ $version = 0;
+ foreach ($releases as $ver => $inf) {
+ if ($inf['state'] == $state && version_compare("$version", "$ver") < 0) {
+ $version = $ver;
+ break;
+ }
+ }
+ if ($version == 0) {
+ return $this->raiseError("No release with state '$state' found for '$pkgfile'");
+ }
+ // invalid suffix passed
+ } else {
+ return $this->raiseError("Invalid suffix '-$version', be sure to pass a valid PEAR ".
+ "version number or release state");
+ }
+ // Guess what to download
+ } else {
+ $states = $this->betterStates($this->_preferredState, true);
+ $possible = false;
+ $version = 0;
+ $higher_version = 0;
+ $prev_hi_ver = 0;
+ foreach ($releases as $ver => $inf) {
+ if (in_array($inf['state'], $states) && version_compare("$version", "$ver") < 0) {
+ $version = $ver;
+ break;
+ } else {
+ $ver = (string)$ver;
+ if (version_compare($prev_hi_ver, $ver) < 0) {
+ $prev_hi_ver = $higher_version = $ver;
+ }
+ }
+ }
+ if ($version === 0 && !isset($this->_options['force'])) {
+ return $this->raiseError('No release with state equal to: \'' . implode(', ', $states) .
+ "' found for '$pkgfile'");
+ } elseif ($version === 0) {
+ $this->log(0, "Warning: $pkgfile is state '" . $releases[$higher_version]['state'] . "' which is less stable " .
+ "than state '$this->_preferredState'");
+ }
+ }
+ // Check if we haven't already the version
+ if (empty($this->_options['force']) && !is_null($curinfo)) {
+ if ($curinfo['version'] == $version) {
+ $this->log(0, "Package '{$curinfo['package']}-{$curinfo['version']}' already installed, skipping");
+ return false;
+ } elseif (version_compare("$version", "{$curinfo['version']}") < 0) {
+ $this->log(0, "Package '{$curinfo['package']}' version '{$curinfo['version']}' " .
+ " is installed and {$curinfo['version']} is > requested '$version', skipping");
+ return false;
+ }
+ }
+ $this->_toDownload[] = $pkgfile;
+ return $this->_downloadFile($pkgfile, $version, $origpkgfile, $state);
+ }
+
+ // }}}
+ // {{{ _processDependency($package, $info, $mywillinstall)
+
+ /**
+ * Process a dependency, download if necessary
+ * @param array dependency information from PEAR_Remote call
+ * @param array packages that will be installed in this iteration
+ * @return false|string|PEAR_Error
+ * @access private
+ * @todo Add test for relation 'lt'/'le' -> make sure that the dependency requested is
+ * in fact lower than the required value. This will be very important for BC dependencies
+ */
+ function _processDependency($package, $info, $mywillinstall)
+ {
+ $state = $this->_preferredState;
+ if (!isset($this->_options['alldeps']) && isset($info['optional']) &&
+ $info['optional'] == 'yes') {
+ // skip optional deps
+ $this->log(0, "skipping Package '$package' optional dependency '$info[name]'");
+ return false;
+ }
+ // {{{ get releases
+ $releases = $this->_remote->call('package.info', $info['name'], 'releases', true);
+ if (PEAR::isError($releases)) {
+ return $releases;
+ }
+ if (!count($releases)) {
+ if (!isset($this->_installed[strtolower($info['name'])])) {
+ $this->pushError("Package '$package' dependency '$info[name]' ".
+ "has no releases");
+ }
+ return false;
+ }
+ $found = false;
+ $save = $releases;
+ while(count($releases) && !$found) {
+ if (!empty($state) && $state != 'any') {
+ list($release_version, $release) = each($releases);
+ if ($state != $release['state'] &&
+ !in_array($release['state'], $this->betterStates($state)))
+ {
+ // drop this release - it ain't stable enough
+ array_shift($releases);
+ } else {
+ $found = true;
+ }
+ } else {
+ $found = true;
+ }
+ }
+ if (!count($releases) && !$found) {
+ $get = array();
+ foreach($save as $release) {
+ $get = array_merge($get,
+ $this->betterStates($release['state'], true));
+ }
+ $savestate = array_shift($get);
+ $this->pushError( "Release for '$package' dependency '$info[name]' " .
+ "has state '$savestate', requires '$state'");
+ return 0;
+ }
+ if (in_array(strtolower($info['name']), $this->_toDownload) ||
+ isset($mywillinstall[strtolower($info['name'])])) {
+ // skip upgrade check for packages we will install
+ return false;
+ }
+ if (!isset($this->_installed[strtolower($info['name'])])) {
+ // check to see if we can install the specific version required
+ if ($info['rel'] == 'eq') {
+ return $info['name'] . '-' . $info['version'];
+ }
+ // skip upgrade check for packages we don't have installed
+ return $info['name'];
+ }
+ // }}}
+
+ // {{{ see if a dependency must be upgraded
+ $inst_version = $this->_registry->packageInfo($info['name'], 'version');
+ if (!isset($info['version'])) {
+ // this is a rel='has' dependency, check against latest
+ if (version_compare($release_version, $inst_version, 'le')) {
+ return false;
+ } else {
+ return $info['name'];
+ }
+ }
+ if (version_compare($info['version'], $inst_version, 'le')) {
+ // installed version is up-to-date
+ return false;
+ }
+ return $info['name'];
+ }
+
+ // }}}
+ // {{{ _downloadCallback()
+
+ function _downloadCallback($msg, $params = null)
+ {
+ switch ($msg) {
+ case 'saveas':
+ $this->log(1, "downloading $params ...");
+ break;
+ case 'done':
+ $this->log(1, '...done: ' . number_format($params, 0, '', ',') . ' bytes');
+ break;
+ case 'bytesread':
+ static $bytes;
+ if (empty($bytes)) {
+ $bytes = 0;
+ }
+ if (!($bytes % 10240)) {
+ $this->log(1, '.', false);
+ }
+ $bytes += $params;
+ break;
+ case 'start':
+ $this->log(1, "Starting to download {$params[0]} (".number_format($params[1], 0, '', ',')." bytes)");
+ break;
+ }
+ if (method_exists($this->ui, '_downloadCallback'))
+ $this->ui->_downloadCallback($msg, $params);
+ }
+
+ // }}}
+ // {{{ _prependPath($path, $prepend)
+
+ function _prependPath($path, $prepend)
+ {
+ if (strlen($prepend) > 0) {
+ if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
+ $path = $prepend . substr($path, 2);
+ } else {
+ $path = $prepend . $path;
+ }
+ }
+ return $path;
+ }
+ // }}}
+ // {{{ pushError($errmsg, $code)
+
+ /**
+ * @param string
+ * @param integer
+ */
+ function pushError($errmsg, $code = -1)
+ {
+ array_push($this->_errorStack, array($errmsg, $code));
+ }
+
+ // }}}
+ // {{{ getErrorMsgs()
+
+ function getErrorMsgs()
+ {
+ $msgs = array();
+ $errs = $this->_errorStack;
+ foreach ($errs as $err) {
+ $msgs[] = $err[0];
+ }
+ $this->_errorStack = array();
+ return $msgs;
+ }
+
+ // }}}
+}
+// }}}
+
+?>
diff --git a/pearlib/PEAR/ErrorStack.php b/pearlib/PEAR/ErrorStack.php
new file mode 100644
index 00000000..18e7cc4a
--- /dev/null
+++ b/pearlib/PEAR/ErrorStack.php
@@ -0,0 +1,955 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Gregory Beaver <[email protected]> |
+// | |
+// +----------------------------------------------------------------------+
+//
+// $Id: ErrorStack.php,v 1.7.2.4 2004/10/26 19:06:43 cellog Exp $
+
+/**
+ * Error Stack Implementation
+ *
+ * This is an incredibly simple implementation of a very complex error handling
+ * facility. It contains the ability
+ * to track multiple errors from multiple packages simultaneously. In addition,
+ * it can track errors of many levels, save data along with the error, context
+ * information such as the exact file, line number, class and function that
+ * generated the error, and if necessary, it can raise a traditional PEAR_Error.
+ * It has built-in support for PEAR::Log, to log errors as they occur
+ *
+ * Since version 0.2alpha, it is also possible to selectively ignore errors,
+ * through the use of an error callback, see {@link pushCallback()}
+ *
+ * Since version 0.3alpha, it is possible to specify the exception class
+ * returned from {@link push()}
+ *
+ * Since version PEAR1.3.2, ErrorStack no longer instantiates an exception class. This can
+ * still be done quite handily in an error callback or by manipulating the returned array
+ * @author Greg Beaver <[email protected]>
+ * @version PEAR1.3.2 (beta)
+ * @package PEAR_ErrorStack
+ * @category Debugging
+ * @license http://www.php.net/license/3_0.txt PHP License v3.0
+ */
+
+/**
+ * Singleton storage
+ *
+ * Format:
+ * <pre>
+ * array(
+ * 'package1' => PEAR_ErrorStack object,
+ * 'package2' => PEAR_ErrorStack object,
+ * ...
+ * )
+ * </pre>
+ * @access private
+ * @global array $GLOBALS['_PEAR_ERRORSTACK_SINGLETON']
+ */
+$GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] = array();
+
+/**
+ * Global error callback (default)
+ *
+ * This is only used if set to non-false. * is the default callback for
+ * all packages, whereas specific packages may set a default callback
+ * for all instances, regardless of whether they are a singleton or not.
+ *
+ * To exclude non-singletons, only set the local callback for the singleton
+ * @see PEAR_ErrorStack::setDefaultCallback()
+ * @access private
+ * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']
+ */
+$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] = array(
+ '*' => false,
+);
+
+/**
+ * Global Log object (default)
+ *
+ * This is only used if set to non-false. Use to set a default log object for
+ * all stacks, regardless of instantiation order or location
+ * @see PEAR_ErrorStack::setDefaultLogger()
+ * @access private
+ * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
+ */
+$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = false;
+
+/**
+ * Global Overriding Callback
+ *
+ * This callback will override any error callbacks that specific loggers have set.
+ * Use with EXTREME caution
+ * @see PEAR_ErrorStack::staticPushCallback()
+ * @access private
+ * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
+ */
+$GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
+
+/**#@+
+ * One of four possible return values from the error Callback
+ * @see PEAR_ErrorStack::_errorCallback()
+ */
+/**
+ * If this is returned, then the error will be both pushed onto the stack
+ * and logged.
+ */
+define('PEAR_ERRORSTACK_PUSHANDLOG', 1);
+/**
+ * If this is returned, then the error will only be pushed onto the stack,
+ * and not logged.
+ */
+define('PEAR_ERRORSTACK_PUSH', 2);
+/**
+ * If this is returned, then the error will only be logged, but not pushed
+ * onto the error stack.
+ */
+define('PEAR_ERRORSTACK_LOG', 3);
+/**
+ * If this is returned, then the error is completely ignored.
+ */
+define('PEAR_ERRORSTACK_IGNORE', 4);
+/**#@-*/
+
+/**
+ * Error code for an attempt to instantiate a non-class as a PEAR_ErrorStack in
+ * the singleton method.
+ */
+define('PEAR_ERRORSTACK_ERR_NONCLASS', 1);
+
+/**
+ * Error code for an attempt to pass an object into {@link PEAR_ErrorStack::getMessage()}
+ * that has no __toString() method
+ */
+define('PEAR_ERRORSTACK_ERR_OBJTOSTRING', 2);
+/**
+ * Error Stack Implementation
+ *
+ * Usage:
+ * <code>
+ * // global error stack
+ * $global_stack = &PEAR_ErrorStack::singleton('MyPackage');
+ * // local error stack
+ * $local_stack = new PEAR_ErrorStack('MyPackage');
+ * </code>
+ * @copyright 2004 Gregory Beaver
+ * @package PEAR_ErrorStack
+ * @license http://www.php.net/license/3_0.txt PHP License
+ */
+class PEAR_ErrorStack {
+ /**
+ * Errors are stored in the order that they are pushed on the stack.
+ * @since 0.4alpha Errors are no longer organized by error level.
+ * This renders pop() nearly unusable, and levels could be more easily
+ * handled in a callback anyway
+ * @var array
+ * @access private
+ */
+ var $_errors = array();
+
+ /**
+ * Storage of errors by level.
+ *
+ * Allows easy retrieval and deletion of only errors from a particular level
+ * @since PEAR 1.4.0dev
+ * @var array
+ * @access private
+ */
+ var $_errorsByLevel = array();
+
+ /**
+ * Package name this error stack represents
+ * @var string
+ * @access protected
+ */
+ var $_package;
+
+ /**
+ * Determines whether a PEAR_Error is thrown upon every error addition
+ * @var boolean
+ * @access private
+ */
+ var $_compat = false;
+
+ /**
+ * If set to a valid callback, this will be used to generate the error
+ * message from the error code, otherwise the message passed in will be
+ * used
+ * @var false|string|array
+ * @access private
+ */
+ var $_msgCallback = false;
+
+ /**
+ * If set to a valid callback, this will be used to generate the error
+ * context for an error. For PHP-related errors, this will be a file
+ * and line number as retrieved from debug_backtrace(), but can be
+ * customized for other purposes. The error might actually be in a separate
+ * configuration file, or in a database query.
+ * @var false|string|array
+ * @access protected
+ */
+ var $_contextCallback = false;
+
+ /**
+ * If set to a valid callback, this will be called every time an error
+ * is pushed onto the stack. The return value will be used to determine
+ * whether to allow an error to be pushed or logged.
+ *
+ * The return value must be one an PEAR_ERRORSTACK_* constant
+ * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
+ * @var false|string|array
+ * @access protected
+ */
+ var $_errorCallback = array();
+
+ /**
+ * PEAR::Log object for logging errors
+ * @var false|Log
+ * @access protected
+ */
+ var $_logger = false;
+
+ /**
+ * Error messages - designed to be overridden
+ * @var array
+ * @abstract
+ */
+ var $_errorMsgs = array();
+
+ /**
+ * Set up a new error stack
+ *
+ * @param string $package name of the package this error stack represents
+ * @param callback $msgCallback callback used for error message generation
+ * @param callback $contextCallback callback used for context generation,
+ * defaults to {@link getFileLine()}
+ * @param boolean $throwPEAR_Error
+ */
+ function PEAR_ErrorStack($package, $msgCallback = false, $contextCallback = false,
+ $throwPEAR_Error = false)
+ {
+ $this->_package = $package;
+ $this->setMessageCallback($msgCallback);
+ $this->setContextCallback($contextCallback);
+ $this->_compat = $throwPEAR_Error;
+ }
+
+ /**
+ * Return a single error stack for this package.
+ *
+ * Note that all parameters are ignored if the stack for package $package
+ * has already been instantiated
+ * @param string $package name of the package this error stack represents
+ * @param callback $msgCallback callback used for error message generation
+ * @param callback $contextCallback callback used for context generation,
+ * defaults to {@link getFileLine()}
+ * @param boolean $throwPEAR_Error
+ * @param string $stackClass class to instantiate
+ * @static
+ * @return PEAR_ErrorStack
+ */
+ function &singleton($package, $msgCallback = false, $contextCallback = false,
+ $throwPEAR_Error = false, $stackClass = 'PEAR_ErrorStack')
+ {
+ if (isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
+ return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package];
+ }
+ if (!class_exists($stackClass)) {
+ if (function_exists('debug_backtrace')) {
+ $trace = debug_backtrace();
+ }
+ PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_NONCLASS,
+ 'exception', array('stackclass' => $stackClass),
+ 'stack class "%stackclass%" is not a valid class name (should be like PEAR_ErrorStack)',
+ false, $trace);
+ }
+ return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package] =
+ &new $stackClass($package, $msgCallback, $contextCallback, $throwPEAR_Error);
+ }
+
+ /**
+ * Internal error handler for PEAR_ErrorStack class
+ *
+ * Dies if the error is an exception (and would have died anyway)
+ * @access private
+ */
+ function _handleError($err)
+ {
+ if ($err['level'] == 'exception') {
+ $message = $err['message'];
+ if (isset($_SERVER['REQUEST_URI'])) {
+ echo '<br />';
+ } else {
+ echo "\n";
+ }
+ var_dump($err['context']);
+ die($message);
+ }
+ }
+
+ /**
+ * Set up a PEAR::Log object for all error stacks that don't have one
+ * @param Log $log
+ * @static
+ */
+ function setDefaultLogger(&$log)
+ {
+ $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log;
+ }
+
+ /**
+ * Set up a PEAR::Log object for this error stack
+ * @param Log $log
+ */
+ function setLogger(&$log)
+ {
+ $this->_logger = &$log;
+ }
+
+ /**
+ * Set an error code => error message mapping callback
+ *
+ * This method sets the callback that can be used to generate error
+ * messages for any instance
+ * @param array|string Callback function/method
+ */
+ function setMessageCallback($msgCallback)
+ {
+ if (!$msgCallback) {
+ $this->_msgCallback = array(&$this, 'getErrorMessage');
+ } else {
+ if (is_callable($msgCallback)) {
+ $this->_msgCallback = $msgCallback;
+ }
+ }
+ }
+
+ /**
+ * Get an error code => error message mapping callback
+ *
+ * This method returns the current callback that can be used to generate error
+ * messages
+ * @return array|string|false Callback function/method or false if none
+ */
+ function getMessageCallback()
+ {
+ return $this->_msgCallback;
+ }
+
+ /**
+ * Sets a default callback to be used by all error stacks
+ *
+ * This method sets the callback that can be used to generate error
+ * messages for a singleton
+ * @param array|string Callback function/method
+ * @param string Package name, or false for all packages
+ * @static
+ */
+ function setDefaultCallback($callback = false, $package = false)
+ {
+ if (!is_callable($callback)) {
+ $callback = false;
+ }
+ $package = $package ? $package : '*';
+ $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$package] = $callback;
+ }
+
+ /**
+ * Set a callback that generates context information (location of error) for an error stack
+ *
+ * This method sets the callback that can be used to generate context
+ * information for an error. Passing in NULL will disable context generation
+ * and remove the expensive call to debug_backtrace()
+ * @param array|string|null Callback function/method
+ */
+ function setContextCallback($contextCallback)
+ {
+ if ($contextCallback === null) {
+ return $this->_contextCallback = false;
+ }
+ if (!$contextCallback) {
+ $this->_contextCallback = array(&$this, 'getFileLine');
+ } else {
+ if (is_callable($contextCallback)) {
+ $this->_contextCallback = $contextCallback;
+ }
+ }
+ }
+
+ /**
+ * Set an error Callback
+ * If set to a valid callback, this will be called every time an error
+ * is pushed onto the stack. The return value will be used to determine
+ * whether to allow an error to be pushed or logged.
+ *
+ * The return value must be one of the ERRORSTACK_* constants.
+ *
+ * This functionality can be used to emulate PEAR's pushErrorHandling, and
+ * the PEAR_ERROR_CALLBACK mode, without affecting the integrity of
+ * the error stack or logging
+ * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
+ * @see popCallback()
+ * @param string|array $cb
+ */
+ function pushCallback($cb)
+ {
+ array_push($this->_errorCallback, $cb);
+ }
+
+ /**
+ * Remove a callback from the error callback stack
+ * @see pushCallback()
+ * @return array|string|false
+ */
+ function popCallback()
+ {
+ if (!count($this->_errorCallback)) {
+ return false;
+ }
+ return array_pop($this->_errorCallback);
+ }
+
+ /**
+ * Set a temporary overriding error callback for every package error stack
+ *
+ * Use this to temporarily disable all existing callbacks (can be used
+ * to emulate the @ operator, for instance)
+ * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
+ * @see staticPopCallback(), pushCallback()
+ * @param string|array $cb
+ * @static
+ */
+ function staticPushCallback($cb)
+ {
+ array_push($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'], $cb);
+ }
+
+ /**
+ * Remove a temporary overriding error callback
+ * @see staticPushCallback()
+ * @return array|string|false
+ * @static
+ */
+ function staticPopCallback()
+ {
+ $ret = array_pop($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK']);
+ if (!is_array($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'])) {
+ $GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
+ }
+ return $ret;
+ }
+
+ /**
+ * Add an error to the stack
+ *
+ * If the message generator exists, it is called with 2 parameters.
+ * - the current Error Stack object
+ * - an array that is in the same format as an error. Available indices
+ * are 'code', 'package', 'time', 'params', 'level', and 'context'
+ *
+ * Next, if the error should contain context information, this is
+ * handled by the context grabbing method.
+ * Finally, the error is pushed onto the proper error stack
+ * @param int $code Package-specific error code
+ * @param string $level Error level. This is NOT spell-checked
+ * @param array $params associative array of error parameters
+ * @param string $msg Error message, or a portion of it if the message
+ * is to be generated
+ * @param array $repackage If this error re-packages an error pushed by
+ * another package, place the array returned from
+ * {@link pop()} in this parameter
+ * @param array $backtrace Protected parameter: use this to pass in the
+ * {@link debug_backtrace()} that should be used
+ * to find error context
+ * @return PEAR_Error|array|Exception
+ * if compatibility mode is on, a PEAR_Error is also
+ * thrown. If the class Exception exists, then one
+ * is returned to allow code like:
+ * <code>
+ * throw ($stack->push(MY_ERROR_CODE, 'error', array('username' => 'grob')));
+ * </code>
+ *
+ * The errorData property of the exception class will be set to the array
+ * that would normally be returned. If a PEAR_Error is returned, the userinfo
+ * property is set to the array
+ *
+ * Otherwise, an array is returned in this format:
+ * <code>
+ * array(
+ * 'code' => $code,
+ * 'params' => $params,
+ * 'package' => $this->_package,
+ * 'level' => $level,
+ * 'time' => time(),
+ * 'context' => $context,
+ * 'message' => $msg,
+ * //['repackage' => $err] repackaged error array/Exception class
+ * );
+ * </code>
+ */
+ function push($code, $level = 'error', $params = array(), $msg = false,
+ $repackage = false, $backtrace = false)
+ {
+ $context = false;
+ // grab error context
+ if ($this->_contextCallback) {
+ if (!$backtrace) {
+ $backtrace = debug_backtrace();
+ }
+ $context = call_user_func($this->_contextCallback, $code, $params, $backtrace);
+ }
+
+ // save error
+ $time = explode(' ', microtime());
+ $time = $time[1] + $time[0];
+ $err = array(
+ 'code' => $code,
+ 'params' => $params,
+ 'package' => $this->_package,
+ 'level' => $level,
+ 'time' => $time,
+ 'context' => $context,
+ 'message' => $msg,
+ );
+
+ // set up the error message, if necessary
+ if ($this->_msgCallback) {
+ $msg = call_user_func_array($this->_msgCallback,
+ array(&$this, $err));
+ $err['message'] = $msg;
+ }
+
+ if ($repackage) {
+ $err['repackage'] = $repackage;
+ }
+ $push = $log = true;
+ // try the overriding callback first
+ $callback = $this->staticPopCallback();
+ if ($callback) {
+ $this->staticPushCallback($callback);
+ }
+ if (!is_callable($callback)) {
+ // try the local callback next
+ $callback = $this->popCallback();
+ if (is_callable($callback)) {
+ $this->pushCallback($callback);
+ } else {
+ // try the default callback
+ $callback = isset($GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package]) ?
+ $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package] :
+ $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']['*'];
+ }
+ }
+ if (is_callable($callback)) {
+ switch(call_user_func($callback, $err)){
+ case PEAR_ERRORSTACK_IGNORE:
+ return $err;
+ break;
+ case PEAR_ERRORSTACK_PUSH:
+ $log = false;
+ break;
+ case PEAR_ERRORSTACK_LOG:
+ $push = false;
+ break;
+ // anything else returned has the same effect as pushandlog
+ }
+ }
+ if ($push) {
+ array_unshift($this->_errors, $err);
+ $this->_errorsByLevel[$err['level']][] = &$this->_errors[0];
+ }
+ if ($log) {
+ if ($this->_logger || $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']) {
+ $this->_log($err);
+ }
+ }
+ if ($this->_compat && $push) {
+ return $this->raiseError($msg, $code, null, null, $err);
+ }
+ return $err;
+ }
+
+ /**
+ * Static version of {@link push()}
+ *
+ * @param string $package Package name this error belongs to
+ * @param int $code Package-specific error code
+ * @param string $level Error level. This is NOT spell-checked
+ * @param array $params associative array of error parameters
+ * @param string $msg Error message, or a portion of it if the message
+ * is to be generated
+ * @param array $repackage If this error re-packages an error pushed by
+ * another package, place the array returned from
+ * {@link pop()} in this parameter
+ * @param array $backtrace Protected parameter: use this to pass in the
+ * {@link debug_backtrace()} that should be used
+ * to find error context
+ * @return PEAR_Error|null|Exception
+ * if compatibility mode is on, a PEAR_Error is also
+ * thrown. If the class Exception exists, then one
+ * is returned to allow code like:
+ * <code>
+ * throw ($stack->push(MY_ERROR_CODE, 'error', array('username' => 'grob')));
+ * </code>
+ * @static
+ */
+ function staticPush($package, $code, $level = 'error', $params = array(),
+ $msg = false, $repackage = false, $backtrace = false)
+ {
+ $s = &PEAR_ErrorStack::singleton($package);
+ if ($s->_contextCallback) {
+ if (!$backtrace) {
+ if (function_exists('debug_backtrace')) {
+ $backtrace = debug_backtrace();
+ }
+ }
+ }
+ return $s->push($code, $level, $params, $msg, $repackage, $backtrace);
+ }
+
+ /**
+ * Log an error using PEAR::Log
+ * @param array $err Error array
+ * @param array $levels Error level => Log constant map
+ * @access protected
+ */
+ function _log($err, $levels = array(
+ 'exception' => PEAR_LOG_CRIT,
+ 'alert' => PEAR_LOG_ALERT,
+ 'critical' => PEAR_LOG_CRIT,
+ 'error' => PEAR_LOG_ERR,
+ 'warning' => PEAR_LOG_WARNING,
+ 'notice' => PEAR_LOG_NOTICE,
+ 'info' => PEAR_LOG_INFO,
+ 'debug' => PEAR_LOG_DEBUG))
+ {
+ if (isset($levels[$err['level']])) {
+ $level = $levels[$err['level']];
+ } else {
+ $level = PEAR_LOG_INFO;
+ }
+ if ($this->_logger) {
+ $this->_logger->log($err['message'], $level, $err);
+ } else {
+ $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']->log($err['message'], $level, $err);
+ }
+ }
+
+
+ /**
+ * Pop an error off of the error stack
+ *
+ * @return false|array
+ * @since 0.4alpha it is no longer possible to specify a specific error
+ * level to return - the last error pushed will be returned, instead
+ */
+ function pop()
+ {
+ return @array_shift($this->_errors);
+ }
+
+ /**
+ * Determine whether there are any errors on the stack
+ * @param string|array Level name. Use to determine if any errors
+ * of level (string), or levels (array) have been pushed
+ * @return boolean
+ */
+ function hasErrors($level = false)
+ {
+ if ($level) {
+ return isset($this->_errorsByLevel[$level]);
+ }
+ return count($this->_errors);
+ }
+
+ /**
+ * Retrieve all errors since last purge
+ *
+ * @param boolean set in order to empty the error stack
+ * @param string level name, to return only errors of a particular severity
+ * @return array
+ */
+ function getErrors($purge = false, $level = false)
+ {
+ if (!$purge) {
+ if ($level) {
+ if (!isset($this->_errorsByLevel[$level])) {
+ return array();
+ } else {
+ return $this->_errorsByLevel[$level];
+ }
+ } else {
+ return $this->_errors;
+ }
+ }
+ if ($level) {
+ $ret = $this->_errorsByLevel[$level];
+ foreach ($this->_errorsByLevel[$level] as $i => $unused) {
+ // entries are references to the $_errors array
+ $this->_errorsByLevel[$level][$i] = false;
+ }
+ // array_filter removes all entries === false
+ $this->_errors = array_filter($this->_errors);
+ unset($this->_errorsByLevel[$level]);
+ return $ret;
+ }
+ $ret = $this->_errors;
+ $this->_errors = array();
+ $this->_errorsByLevel = array();
+ return $ret;
+ }
+
+ /**
+ * Determine whether there are any errors on a single error stack, or on any error stack
+ *
+ * The optional parameter can be used to test the existence of any errors without the need of
+ * singleton instantiation
+ * @param string|false Package name to check for errors
+ * @param string Level name to check for a particular severity
+ * @return boolean
+ * @static
+ */
+ function staticHasErrors($package = false, $level = false)
+ {
+ if ($package) {
+ if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
+ return false;
+ }
+ return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->hasErrors($level);
+ }
+ foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
+ if ($obj->hasErrors($level)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get a list of all errors since last purge, organized by package
+ * @since PEAR 1.4.0dev BC break! $level is now in the place $merge used to be
+ * @param boolean $purge Set to purge the error stack of existing errors
+ * @param string $level Set to a level name in order to retrieve only errors of a particular level
+ * @param boolean $merge Set to return a flat array, not organized by package
+ * @param array $sortfunc Function used to sort a merged array - default
+ * sorts by time, and should be good for most cases
+ * @static
+ * @return array
+ */
+ function staticGetErrors($purge = false, $level = false, $merge = false, $sortfunc = array('PEAR_ErrorStack', '_sortErrors'))
+ {
+ $ret = array();
+ if (!is_callable($sortfunc)) {
+ $sortfunc = array('PEAR_ErrorStack', '_sortErrors');
+ }
+ foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
+ $test = $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->getErrors($purge, $level);
+ if ($test) {
+ if ($merge) {
+ $ret = array_merge($ret, $test);
+ } else {
+ $ret[$package] = $test;
+ }
+ }
+ }
+ if ($merge) {
+ usort($ret, $sortfunc);
+ }
+ return $ret;
+ }
+
+ /**
+ * Error sorting function, sorts by time
+ * @access private
+ */
+ function _sortErrors($a, $b)
+ {
+ if ($a['time'] == $b['time']) {
+ return 0;
+ }
+ if ($a['time'] < $b['time']) {
+ return 1;
+ }
+ return -1;
+ }
+
+ /**
+ * Standard file/line number/function/class context callback
+ *
+ * This function uses a backtrace generated from {@link debug_backtrace()}
+ * and so will not work at all in PHP < 4.3.0. The frame should
+ * reference the frame that contains the source of the error.
+ * @return array|false either array('file' => file, 'line' => line,
+ * 'function' => function name, 'class' => class name) or
+ * if this doesn't work, then false
+ * @param unused
+ * @param integer backtrace frame.
+ * @param array Results of debug_backtrace()
+ * @static
+ */
+ function getFileLine($code, $params, $backtrace = null)
+ {
+ if ($backtrace === null) {
+ return false;
+ }
+ $frame = 0;
+ $functionframe = 1;
+ if (!isset($backtrace[1])) {
+ $functionframe = 0;
+ } else {
+ while (isset($backtrace[$functionframe]['function']) &&
+ $backtrace[$functionframe]['function'] == 'eval' &&
+ isset($backtrace[$functionframe + 1])) {
+ $functionframe++;
+ }
+ }
+ if (isset($backtrace[$frame])) {
+ if (!isset($backtrace[$frame]['file'])) {
+ $frame++;
+ }
+ $funcbacktrace = $backtrace[$functionframe];
+ $filebacktrace = $backtrace[$frame];
+ $ret = array('file' => $filebacktrace['file'],
+ 'line' => $filebacktrace['line']);
+ // rearrange for eval'd code or create function errors
+ if (strpos($filebacktrace['file'], '(') &&
+ preg_match(';^(.*?)\((\d+)\) : (.*?)$;', $filebacktrace['file'],
+ $matches)) {
+ $ret['file'] = $matches[1];
+ $ret['line'] = $matches[2] + 0;
+ }
+ if (isset($funcbacktrace['function']) && isset($backtrace[1])) {
+ if ($funcbacktrace['function'] != 'eval') {
+ if ($funcbacktrace['function'] == '__lambda_func') {
+ $ret['function'] = 'create_function() code';
+ } else {
+ $ret['function'] = $funcbacktrace['function'];
+ }
+ }
+ }
+ if (isset($funcbacktrace['class']) && isset($backtrace[1])) {
+ $ret['class'] = $funcbacktrace['class'];
+ }
+ return $ret;
+ }
+ return false;
+ }
+
+ /**
+ * Standard error message generation callback
+ *
+ * This method may also be called by a custom error message generator
+ * to fill in template values from the params array, simply
+ * set the third parameter to the error message template string to use
+ *
+ * The special variable %__msg% is reserved: use it only to specify
+ * where a message passed in by the user should be placed in the template,
+ * like so:
+ *
+ * Error message: %msg% - internal error
+ *
+ * If the message passed like so:
+ *
+ * <code>
+ * $stack->push(ERROR_CODE, 'error', array(), 'server error 500');
+ * </code>
+ *
+ * The returned error message will be "Error message: server error 500 -
+ * internal error"
+ * @param PEAR_ErrorStack
+ * @param array
+ * @param string|false Pre-generated error message template
+ * @static
+ * @return string
+ */
+ function getErrorMessage(&$stack, $err, $template = false)
+ {
+ if ($template) {
+ $mainmsg = $template;
+ } else {
+ $mainmsg = $stack->getErrorMessageTemplate($err['code']);
+ }
+ $mainmsg = str_replace('%__msg%', $err['message'], $mainmsg);
+ if (count($err['params'])) {
+ foreach ($err['params'] as $name => $val) {
+ if (is_array($val)) {
+ // @ is needed in case $val is a multi-dimensional array
+ $val = @implode(', ', $val);
+ }
+ if (is_object($val)) {
+ if (method_exists($val, '__toString')) {
+ $val = $val->__toString();
+ } else {
+ PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_OBJTOSTRING,
+ 'warning', array('obj' => get_class($val)),
+ 'object %obj% passed into getErrorMessage, but has no __toString() method');
+ $val = 'Object';
+ }
+ }
+ $mainmsg = str_replace('%' . $name . '%', $val, $mainmsg);
+ }
+ }
+ return $mainmsg;
+ }
+
+ /**
+ * Standard Error Message Template generator from code
+ * @return string
+ */
+ function getErrorMessageTemplate($code)
+ {
+ if (!isset($this->_errorMsgs[$code])) {
+ return '%__msg%';
+ }
+ return $this->_errorMsgs[$code];
+ }
+
+ /**
+ * Set the Error Message Template array
+ *
+ * The array format must be:
+ * <pre>
+ * array(error code => 'message template',...)
+ * </pre>
+ *
+ * Error message parameters passed into {@link push()} will be used as input
+ * for the error message. If the template is 'message %foo% was %bar%', and the
+ * parameters are array('foo' => 'one', 'bar' => 'six'), the error message returned will
+ * be 'message one was six'
+ * @return string
+ */
+ function setErrorMessageTemplate($template)
+ {
+ $this->_errorMsgs = $template;
+ }
+
+
+ /**
+ * emulate PEAR::raiseError()
+ *
+ * @return PEAR_Error
+ */
+ function raiseError()
+ {
+ require_once 'PEAR.php';
+ $args = func_get_args();
+ return call_user_func_array(array('PEAR', 'raiseError'), $args);
+ }
+}
+$stack = &PEAR_ErrorStack::singleton('PEAR_ErrorStack');
+$stack->pushCallback(array('PEAR_ErrorStack', '_handleError'));
+?>
diff --git a/pearlib/PEAR/Exception.php b/pearlib/PEAR/Exception.php
new file mode 100644
index 00000000..c2ee33ff
--- /dev/null
+++ b/pearlib/PEAR/Exception.php
@@ -0,0 +1,353 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
+// +----------------------------------------------------------------------+
+// | PEAR_Exception |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 2004 The PEAR Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Tomas V.V.Cox <[email protected]> |
+// | Hans Lellelid <[email protected]> |
+// | Bertrand Mansion <[email protected]> |
+// | Greg Beaver <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Exception.php,v 1.4.2.1 2004/10/19 04:13:16 cellog Exp $
+
+
+/**
+ * Base PEAR_Exception Class
+ *
+ * WARNING: This code should be considered stable, but the API is
+ * subject to immediate and drastic change, so API stability is
+ * at best alpha
+ *
+ * 1) Features:
+ *
+ * - Nestable exceptions (throw new PEAR_Exception($msg, $prev_exception))
+ * - Definable triggers, shot when exceptions occur
+ * - Pretty and informative error messages
+ * - Added more context info available (like class, method or cause)
+ * - cause can be a PEAR_Exception or an array of mixed
+ * PEAR_Exceptions/PEAR_ErrorStack warnings
+ * - callbacks for specific exception classes and their children
+ *
+ * 2) Ideas:
+ *
+ * - Maybe a way to define a 'template' for the output
+ *
+ * 3) Inherited properties from PHP Exception Class:
+ *
+ * protected $message
+ * protected $code
+ * protected $line
+ * protected $file
+ * private $trace
+ *
+ * 4) Inherited methods from PHP Exception Class:
+ *
+ * __clone
+ * __construct
+ * getMessage
+ * getCode
+ * getFile
+ * getLine
+ * getTraceSafe
+ * getTraceSafeAsString
+ * __toString
+ *
+ * 5) Usage example
+ *
+ * <code>
+ * require_once 'PEAR/Exception.php';
+ *
+ * class Test {
+ * function foo() {
+ * throw new PEAR_Exception('Error Message', ERROR_CODE);
+ * }
+ * }
+ *
+ * function myLogger($pear_exception) {
+ * echo $pear_exception->getMessage();
+ * }
+ * // each time a exception is thrown the 'myLogger' will be called
+ * // (its use is completely optional)
+ * PEAR_Exception::addObserver('myLogger');
+ * $test = new Test;
+ * try {
+ * $test->foo();
+ * } catch (PEAR_Exception $e) {
+ * print $e;
+ * }
+ * </code>
+ *
+ * @since PHP 5
+ * @package PEAR
+ * @version $Revision: 1.4.2.1 $
+ * @author Tomas V.V.Cox <[email protected]>
+ * @author Hans Lellelid <[email protected]>
+ * @author Bertrand Mansion <[email protected]>
+ *
+ */
+class PEAR_Exception extends Exception
+{
+ const OBSERVER_PRINT = -2;
+ const OBSERVER_TRIGGER = -4;
+ const OBSERVER_DIE = -8;
+ protected $cause;
+ private static $_observers = array();
+ private static $_uniqueid = 0;
+ private $_trace;
+
+ /**
+ * Supported signatures:
+ * PEAR_Exception(string $message);
+ * PEAR_Exception(string $message, int $code);
+ * PEAR_Exception(string $message, Exception $cause);
+ * PEAR_Exception(string $message, Exception $cause, int $code);
+ * PEAR_Exception(string $message, array $causes);
+ * PEAR_Exception(string $message, array $causes, int $code);
+ */
+ public function __construct($message, $p2 = null, $p3 = null)
+ {
+ if (is_int($p2)) {
+ $code = $p2;
+ $this->cause = null;
+ } elseif ($p2 instanceof Exception || is_array($p2)) {
+ $code = $p3;
+ if (is_array($p2) && isset($p2['message'])) {
+ // fix potential problem of passing in a single warning
+ $p2 = array($p2);
+ }
+ $this->cause = $p2;
+ } else {
+ $code = null;
+ $this->cause = null;
+ }
+ parent::__construct($message, $code);
+ $this->signal();
+ }
+
+ /**
+ * @param mixed $callback - A valid php callback, see php func is_callable()
+ * - A PEAR_Exception::OBSERVER_* constant
+ * - An array(const PEAR_Exception::OBSERVER_*,
+ * mixed $options)
+ * @param string $label The name of the observer. Use this if you want
+ * to remove it later with removeObserver()
+ */
+ public static function addObserver($callback, $label = 'default')
+ {
+ self::$_observers[$label] = $callback;
+ }
+
+ public static function removeObserver($label = 'default')
+ {
+ unset(self::$_observers[$label]);
+ }
+
+ /**
+ * @return int unique identifier for an observer
+ */
+ public static function getUniqueId()
+ {
+ return self::$_uniqueid++;
+ }
+
+ private function signal()
+ {
+ foreach (self::$_observers as $func) {
+ if (is_callable($func)) {
+ call_user_func($func, $this);
+ continue;
+ }
+ settype($func, 'array');
+ switch ($func[0]) {
+ case self::OBSERVER_PRINT :
+ $f = (isset($func[1])) ? $func[1] : '%s';
+ printf($f, $this->getMessage());
+ break;
+ case self::OBSERVER_TRIGGER :
+ $f = (isset($func[1])) ? $func[1] : E_USER_NOTICE;
+ trigger_error($this->getMessage(), $f);
+ break;
+ case self::OBSERVER_DIE :
+ $f = (isset($func[1])) ? $func[1] : '%s';
+ die(printf($f, $this->getMessage()));
+ break;
+ default:
+ trigger_error('invalid observer type', E_USER_WARNING);
+ }
+ }
+ }
+
+ /**
+ * Return specific error information that can be used for more detailed
+ * error messages or translation.
+ *
+ * This method may be overridden in child exception classes in order
+ * to add functionality not present in PEAR_Exception and is a placeholder
+ * to define API
+ *
+ * The returned array must be an associative array of parameter => value like so:
+ * <pre>
+ * array('name' => $name, 'context' => array(...))
+ * </pre>
+ * @return array
+ */
+ public function getErrorData()
+ {
+ return array();
+ }
+
+ /**
+ * Returns the exception that caused this exception to be thrown
+ * @access public
+ * @return Exception|array The context of the exception
+ */
+ public function getCause()
+ {
+ return $this->cause;
+ }
+
+ /**
+ * Function must be public to call on caused exceptions
+ * @param array
+ */
+ public function getCauseMessage(&$causes)
+ {
+ $trace = $this->getTraceSafe();
+ $causes[] = array('class' => get_class($this),
+ 'message' => $this->message,
+ 'file' => $trace[0]['file'],
+ 'line' => $trace[0]['line']);
+ if ($this->cause instanceof PEAR_Exception) {
+ $this->cause->getCauseMessage($causes);
+ }
+ if (is_array($this->cause)) {
+ foreach ($this->cause as $cause) {
+ if ($cause instanceof PEAR_Exception) {
+ $cause->getCauseMessage($causes);
+ } elseif (is_array($cause) && isset($cause['message'])) {
+ // PEAR_ErrorStack warning
+ $causes[] = array(
+ 'class' => $cause['package'],
+ 'message' => $cause['message'],
+ 'file' => isset($cause['context']['file']) ?
+ $cause['context']['file'] :
+ 'unknown',
+ 'line' => isset($cause['context']['line']) ?
+ $cause['context']['line'] :
+ 'unknown',
+ );
+ }
+ }
+ }
+ }
+
+ public function getTraceSafe()
+ {
+ if (!isset($this->_trace)) {
+ $this->_trace = $this->getTrace();
+ if (empty($this->_trace)) {
+ $backtrace = debug_backtrace();
+ $this->_trace = array($backtrace[count($backtrace)-1]);
+ }
+ }
+ return $this->_trace;
+ }
+
+ public function getErrorClass()
+ {
+ $trace = $this->getTraceSafe();
+ return $trace[0]['class'];
+ }
+
+ public function getErrorMethod()
+ {
+ $trace = $this->getTraceSafe();
+ return $trace[0]['function'];
+ }
+
+ public function __toString()
+ {
+ if (isset($_SERVER['REQUEST_URI'])) {
+ return $this->toHtml();
+ }
+ return $this->toText();
+ }
+
+ public function toHtml()
+ {
+ $trace = $this->getTraceSafe();
+ $causes = array();
+ $this->getCauseMessage($causes);
+ $html = '<table border="1" cellspacing="0">' . "\n";
+ foreach ($causes as $i => $cause) {
+ $html .= '<tr><td colspan="3" bgcolor="#ff9999">'
+ . str_repeat('-', $i) . ' <b>' . $cause['class'] . '</b>: '
+ . htmlspecialchars($cause['message']) . ' in <b>' . $cause['file'] . '</b> '
+ . 'on line <b>' . $cause['line'] . '</b>'
+ . "</td></tr>\n";
+ }
+ $html .= '<tr><td colspan="3" bgcolor="#aaaaaa" align="center"><b>Exception trace</b></td></tr>' . "\n"
+ . '<tr><td align="center" bgcolor="#cccccc" width="20"><b>#</b></td>'
+ . '<td align="center" bgcolor="#cccccc"><b>Function</b></td>'
+ . '<td align="center" bgcolor="#cccccc"><b>Location</b></td></tr>' . "\n";
+
+ foreach ($trace as $k => $v) {
+ $html .= '<tr><td align="center">' . $k . '</td>'
+ . '<td>';
+ if (!empty($v['class'])) {
+ $html .= $v['class'] . $v['type'];
+ }
+ $html .= $v['function'];
+ $args = array();
+ if (!empty($v['args'])) {
+ foreach ($v['args'] as $arg) {
+ if (is_null($arg)) $args[] = 'null';
+ elseif (is_array($arg)) $args[] = 'Array';
+ elseif (is_object($arg)) $args[] = 'Object('.get_class($arg).')';
+ elseif (is_bool($arg)) $args[] = $arg ? 'true' : 'false';
+ elseif (is_int($arg) || is_double($arg)) $args[] = $arg;
+ else {
+ $arg = (string)$arg;
+ $str = htmlspecialchars(substr($arg, 0, 16));
+ if (strlen($arg) > 16) $str .= '&hellip;';
+ $args[] = "'" . $str . "'";
+ }
+ }
+ }
+ $html .= '(' . implode(', ',$args) . ')'
+ . '</td>'
+ . '<td>' . $v['file'] . ':' . $v['line'] . '</td></tr>' . "\n";
+ }
+ $html .= '<tr><td align="center">' . ($k+1) . '</td>'
+ . '<td>{main}</td>'
+ . '<td>&nbsp;</td></tr>' . "\n"
+ . '</table>';
+ return $html;
+ }
+
+ public function toText()
+ {
+ $causes = array();
+ $this->getCauseMessage($causes);
+ $causeMsg = '';
+ foreach ($causes as $i => $cause) {
+ $causeMsg .= str_repeat(' ', $i) . $cause['class'] . ': '
+ . $cause['message'] . ' in ' . $cause['file']
+ . ' on line ' . $cause['line'] . "\n";
+ }
+ return $causeMsg . $this->getTraceAsString();
+ }
+}
+
+?> \ No newline at end of file
diff --git a/pearlib/PEAR/Frontend/CLI.php b/pearlib/PEAR/Frontend/CLI.php
new file mode 100644
index 00000000..832ddf09
--- /dev/null
+++ b/pearlib/PEAR/Frontend/CLI.php
@@ -0,0 +1,509 @@
+<?php
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2004 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.0 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_0.txt. |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | [email protected] so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Stig S�ther Bakken <[email protected]> |
+ +----------------------------------------------------------------------+
+
+ $Id: CLI.php,v 1.41 2004/02/17 05:49:16 ssb Exp $
+*/
+
+require_once "PEAR.php";
+
+class PEAR_Frontend_CLI extends PEAR
+{
+ // {{{ properties
+
+ /**
+ * What type of user interface this frontend is for.
+ * @var string
+ * @access public
+ */
+ var $type = 'CLI';
+ var $lp = ''; // line prefix
+
+ var $params = array();
+ var $term = array(
+ 'bold' => '',
+ 'normal' => '',
+ );
+
+ // }}}
+
+ // {{{ constructor
+
+ function PEAR_Frontend_CLI()
+ {
+ parent::PEAR();
+ $term = getenv('TERM'); //(cox) $_ENV is empty for me in 4.1.1
+ if (function_exists('posix_isatty') && !posix_isatty(1)) {
+ // output is being redirected to a file or through a pipe
+ } elseif ($term) {
+ // XXX can use ncurses extension here, if available
+ if (preg_match('/^(xterm|vt220|linux)/', $term)) {
+ $this->term['bold'] = sprintf("%c%c%c%c", 27, 91, 49, 109);
+ $this->term['normal']=sprintf("%c%c%c", 27, 91, 109);
+ } elseif (preg_match('/^vt100/', $term)) {
+ $this->term['bold'] = sprintf("%c%c%c%c%c%c", 27, 91, 49, 109, 0, 0);
+ $this->term['normal']=sprintf("%c%c%c%c%c", 27, 91, 109, 0, 0);
+ }
+ } elseif (OS_WINDOWS) {
+ // XXX add ANSI codes here
+ }
+ }
+
+ // }}}
+
+ // {{{ displayLine(text)
+
+ function displayLine($text)
+ {
+ trigger_error("PEAR_Frontend_CLI::displayLine deprecated", E_USER_ERROR);
+ }
+
+ function _displayLine($text)
+ {
+ print "$this->lp$text\n";
+ }
+
+ // }}}
+ // {{{ display(text)
+
+ function display($text)
+ {
+ trigger_error("PEAR_Frontend_CLI::display deprecated", E_USER_ERROR);
+ }
+
+ function _display($text)
+ {
+ print $text;
+ }
+
+ // }}}
+ // {{{ displayError(eobj)
+
+ /**
+ * @param object PEAR_Error object
+ */
+ function displayError($eobj)
+ {
+ return $this->_displayLine($eobj->getMessage());
+ }
+
+ // }}}
+ // {{{ displayFatalError(eobj)
+
+ /**
+ * @param object PEAR_Error object
+ */
+ function displayFatalError($eobj)
+ {
+ $this->displayError($eobj);
+ exit(1);
+ }
+
+ // }}}
+ // {{{ displayHeading(title)
+
+ function displayHeading($title)
+ {
+ trigger_error("PEAR_Frontend_CLI::displayHeading deprecated", E_USER_ERROR);
+ }
+
+ function _displayHeading($title)
+ {
+ print $this->lp.$this->bold($title)."\n";
+ print $this->lp.str_repeat("=", strlen($title))."\n";
+ }
+
+ // }}}
+ // {{{ userDialog(prompt, [type], [default])
+
+ function userDialog($command, $prompts, $types = array(), $defaults = array())
+ {
+ $result = array();
+ if (is_array($prompts)) {
+ $fp = fopen("php://stdin", "r");
+ foreach ($prompts as $key => $prompt) {
+ $type = $types[$key];
+ $default = @$defaults[$key];
+ if ($type == 'password') {
+ system('stty -echo');
+ }
+ print "$this->lp$prompt ";
+ if ($default) {
+ print "[$default] ";
+ }
+ print ": ";
+ $line = fgets($fp, 2048);
+ if ($type == 'password') {
+ system('stty echo');
+ print "\n";
+ }
+ if ($default && trim($line) == "") {
+ $result[$key] = $default;
+ } else {
+ $result[$key] = $line;
+ }
+ }
+ fclose($fp);
+ }
+ return $result;
+ }
+
+ // }}}
+ // {{{ userConfirm(prompt, [default])
+
+ function userConfirm($prompt, $default = 'yes')
+ {
+ trigger_error("PEAR_Frontend_CLI::userConfirm not yet converted", E_USER_ERROR);
+ static $positives = array('y', 'yes', 'on', '1');
+ static $negatives = array('n', 'no', 'off', '0');
+ print "$this->lp$prompt [$default] : ";
+ $fp = fopen("php://stdin", "r");
+ $line = fgets($fp, 2048);
+ fclose($fp);
+ $answer = strtolower(trim($line));
+ if (empty($answer)) {
+ $answer = $default;
+ }
+ if (in_array($answer, $positives)) {
+ return true;
+ }
+ if (in_array($answer, $negatives)) {
+ return false;
+ }
+ if (in_array($default, $positives)) {
+ return true;
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ startTable([params])
+
+ function startTable($params = array())
+ {
+ trigger_error("PEAR_Frontend_CLI::startTable deprecated", E_USER_ERROR);
+ }
+
+ function _startTable($params = array())
+ {
+ $params['table_data'] = array();
+ $params['widest'] = array(); // indexed by column
+ $params['highest'] = array(); // indexed by row
+ $params['ncols'] = 0;
+ $this->params = $params;
+ }
+
+ // }}}
+ // {{{ tableRow(columns, [rowparams], [colparams])
+
+ function tableRow($columns, $rowparams = array(), $colparams = array())
+ {
+ trigger_error("PEAR_Frontend_CLI::tableRow deprecated", E_USER_ERROR);
+ }
+
+ function _tableRow($columns, $rowparams = array(), $colparams = array())
+ {
+ $highest = 1;
+ for ($i = 0; $i < sizeof($columns); $i++) {
+ $col = &$columns[$i];
+ if (isset($colparams[$i]) && !empty($colparams[$i]['wrap'])) {
+ $col = wordwrap($col, $colparams[$i]['wrap'], "\n", 0);
+ }
+ if (strpos($col, "\n") !== false) {
+ $multiline = explode("\n", $col);
+ $w = 0;
+ foreach ($multiline as $n => $line) {
+ if (strlen($line) > $w) {
+ $w = strlen($line);
+ }
+ }
+ $lines = sizeof($multiline);
+ } else {
+ $w = strlen($col);
+ }
+ if ($w > @$this->params['widest'][$i]) {
+ $this->params['widest'][$i] = $w;
+ }
+ $tmp = count_chars($columns[$i], 1);
+ // handle unix, mac and windows formats
+ $lines = (isset($tmp[10]) ? $tmp[10] : @$tmp[13]) + 1;
+ if ($lines > $highest) {
+ $highest = $lines;
+ }
+ }
+ if (sizeof($columns) > $this->params['ncols']) {
+ $this->params['ncols'] = sizeof($columns);
+ }
+ $new_row = array(
+ 'data' => $columns,
+ 'height' => $highest,
+ 'rowparams' => $rowparams,
+ 'colparams' => $colparams,
+ );
+ $this->params['table_data'][] = $new_row;
+ }
+
+ // }}}
+ // {{{ endTable()
+
+ function endTable()
+ {
+ trigger_error("PEAR_Frontend_CLI::endTable deprecated", E_USER_ERROR);
+ }
+
+ function _endTable()
+ {
+ extract($this->params);
+ if (!empty($caption)) {
+ $this->_displayHeading($caption);
+ }
+ if (count($table_data) == 0) {
+ return;
+ }
+ if (!isset($width)) {
+ $width = $widest;
+ } else {
+ for ($i = 0; $i < $ncols; $i++) {
+ if (!isset($width[$i])) {
+ $width[$i] = $widest[$i];
+ }
+ }
+ }
+ $border = false;
+ if (empty($border)) {
+ $cellstart = '';
+ $cellend = ' ';
+ $rowend = '';
+ $padrowend = false;
+ $borderline = '';
+ } else {
+ $cellstart = '| ';
+ $cellend = ' ';
+ $rowend = '|';
+ $padrowend = true;
+ $borderline = '+';
+ foreach ($width as $w) {
+ $borderline .= str_repeat('-', $w + strlen($cellstart) + strlen($cellend) - 1);
+ $borderline .= '+';
+ }
+ }
+ if ($borderline) {
+ $this->_displayLine($borderline);
+ }
+ for ($i = 0; $i < sizeof($table_data); $i++) {
+ extract($table_data[$i]);
+ if (!is_array($rowparams)) {
+ $rowparams = array();
+ }
+ if (!is_array($colparams)) {
+ $colparams = array();
+ }
+ $rowlines = array();
+ if ($height > 1) {
+ for ($c = 0; $c < sizeof($data); $c++) {
+ $rowlines[$c] = preg_split('/(\r?\n|\r)/', $data[$c]);
+ if (sizeof($rowlines[$c]) < $height) {
+ $rowlines[$c] = array_pad($rowlines[$c], $height, '');
+ }
+ }
+ } else {
+ for ($c = 0; $c < sizeof($data); $c++) {
+ $rowlines[$c] = array($data[$c]);
+ }
+ }
+ for ($r = 0; $r < $height; $r++) {
+ $rowtext = '';
+ for ($c = 0; $c < sizeof($data); $c++) {
+ if (isset($colparams[$c])) {
+ $attribs = array_merge($rowparams, $colparams);
+ } else {
+ $attribs = $rowparams;
+ }
+ $w = isset($width[$c]) ? $width[$c] : 0;
+ //$cell = $data[$c];
+ $cell = $rowlines[$c][$r];
+ $l = strlen($cell);
+ if ($l > $w) {
+ $cell = substr($cell, 0, $w);
+ }
+ if (isset($attribs['bold'])) {
+ $cell = $this->bold($cell);
+ }
+ if ($l < $w) {
+ // not using str_pad here because we may
+ // add bold escape characters to $cell
+ $cell .= str_repeat(' ', $w - $l);
+ }
+
+ $rowtext .= $cellstart . $cell . $cellend;
+ }
+ if (!$border) {
+ $rowtext = rtrim($rowtext);
+ }
+ $rowtext .= $rowend;
+ $this->_displayLine($rowtext);
+ }
+ }
+ if ($borderline) {
+ $this->_displayLine($borderline);
+ }
+ }
+
+ // }}}
+ // {{{ outputData()
+
+ function outputData($data, $command = '_default')
+ {
+ switch ($command) {
+ case 'install':
+ case 'upgrade':
+ case 'upgrade-all':
+ if (isset($data['release_warnings'])) {
+ $this->_displayLine('');
+ $this->_startTable(array(
+ 'border' => false,
+ 'caption' => 'Release Warnings'
+ ));
+ $this->_tableRow(array($data['release_warnings']), null, array(1 => array('wrap' => 55)));
+ $this->_endTable();
+ $this->_displayLine('');
+ }
+ $this->_displayLine($data['data']);
+ break;
+ case 'search':
+ $this->_startTable($data);
+ if (isset($data['headline']) && is_array($data['headline'])) {
+ $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55)));
+ }
+
+ foreach($data['data'] as $category) {
+ foreach($category as $pkg) {
+ $this->_tableRow($pkg, null, array(1 => array('wrap' => 55)));
+ }
+ };
+ $this->_endTable();
+ break;
+ case 'list-all':
+ $this->_startTable($data);
+ if (isset($data['headline']) && is_array($data['headline'])) {
+ $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55)));
+ }
+
+ foreach($data['data'] as $category) {
+ foreach($category as $pkg) {
+ unset($pkg[3]);
+ unset($pkg[4]);
+ $this->_tableRow($pkg, null, array(1 => array('wrap' => 55)));
+ }
+ };
+ $this->_endTable();
+ break;
+ case 'config-show':
+ $data['border'] = false;
+ $opts = array(0 => array('wrap' => 30),
+ 1 => array('wrap' => 20),
+ 2 => array('wrap' => 35));
+ $this->_startTable($data);
+ if (isset($data['headline']) && is_array($data['headline'])) {
+ $this->_tableRow($data['headline'],
+ array('bold' => true),
+ $opts);
+ }
+ foreach($data['data'] as $group) {
+ foreach($group as $value) {
+ if ($value[2] == '') {
+ $value[2] = "<not set>";
+ }
+ $this->_tableRow($value, null, $opts);
+ }
+ }
+ $this->_endTable();
+ break;
+ case 'remote-info':
+ $data = array(
+ 'caption' => 'Package details:',
+ 'border' => false,
+ 'data' => array(
+ array("Latest", $data['stable']),
+ array("Installed", $data['installed']),
+ array("Package", $data['name']),
+ array("License", $data['license']),
+ array("Category", $data['category']),
+ array("Summary", $data['summary']),
+ array("Description", $data['description']),
+ ),
+ );
+ default: {
+ if (is_array($data)) {
+ $this->_startTable($data);
+ $count = count($data['data'][0]);
+ if ($count == 2) {
+ $opts = array(0 => array('wrap' => 25),
+ 1 => array('wrap' => 48)
+ );
+ } elseif ($count == 3) {
+ $opts = array(0 => array('wrap' => 30),
+ 1 => array('wrap' => 20),
+ 2 => array('wrap' => 35)
+ );
+ } else {
+ $opts = null;
+ }
+ if (isset($data['headline']) && is_array($data['headline'])) {
+ $this->_tableRow($data['headline'],
+ array('bold' => true),
+ $opts);
+ }
+ foreach($data['data'] as $row) {
+ $this->_tableRow($row, null, $opts);
+ }
+ $this->_endTable();
+ } else {
+ $this->_displayLine($data);
+ }
+ }
+ }
+ }
+
+ // }}}
+ // {{{ log(text)
+
+
+ function log($text, $append_crlf = true)
+ {
+ if ($append_crlf) {
+ return $this->_displayLine($text);
+ }
+ return $this->_display($text);
+ }
+
+
+ // }}}
+ // {{{ bold($text)
+
+ function bold($text)
+ {
+ if (empty($this->term['bold'])) {
+ return strtoupper($text);
+ }
+ return $this->term['bold'] . $text . $this->term['normal'];
+ }
+
+ // }}}
+}
+
+?>
diff --git a/pearlib/PEAR/Installer.php b/pearlib/PEAR/Installer.php
new file mode 100644
index 00000000..bada6fbb
--- /dev/null
+++ b/pearlib/PEAR/Installer.php
@@ -0,0 +1,1053 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Bakken <[email protected]> |
+// | Tomas V.V.Cox <[email protected]> |
+// | Martin Jansen <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Installer.php,v 1.150.2.1 2004/10/22 23:03:53 cellog Exp $
+
+require_once 'PEAR/Downloader.php';
+
+/**
+ * Administration class used to install PEAR packages and maintain the
+ * installed package database.
+ *
+ * TODO:
+ * - Check dependencies break on package uninstall (when no force given)
+ * - add a guessInstallDest() method with the code from _installFile() and
+ * use that method in Registry::_rebuildFileMap() & Command_Registry::doList(),
+ * others..
+ *
+ * @since PHP 4.0.2
+ * @author Stig Bakken <[email protected]>
+ * @author Martin Jansen <[email protected]>
+ * @author Greg Beaver <[email protected]>
+ */
+class PEAR_Installer extends PEAR_Downloader
+{
+ // {{{ properties
+
+ /** name of the package directory, for example Foo-1.0
+ * @var string
+ */
+ var $pkgdir;
+
+ /** directory where PHP code files go
+ * @var string
+ */
+ var $phpdir;
+
+ /** directory where PHP extension files go
+ * @var string
+ */
+ var $extdir;
+
+ /** directory where documentation goes
+ * @var string
+ */
+ var $docdir;
+
+ /** installation root directory (ala PHP's INSTALL_ROOT or
+ * automake's DESTDIR
+ * @var string
+ */
+ var $installroot = '';
+
+ /** debug level
+ * @var int
+ */
+ var $debug = 1;
+
+ /** temporary directory
+ * @var string
+ */
+ var $tmpdir;
+
+ /** PEAR_Registry object used by the installer
+ * @var object
+ */
+ var $registry;
+
+ /** List of file transactions queued for an install/upgrade/uninstall.
+ *
+ * Format:
+ * array(
+ * 0 => array("rename => array("from-file", "to-file")),
+ * 1 => array("delete" => array("file-to-delete")),
+ * ...
+ * )
+ *
+ * @var array
+ */
+ var $file_operations = array();
+
+ // }}}
+
+ // {{{ constructor
+
+ /**
+ * PEAR_Installer constructor.
+ *
+ * @param object $ui user interface object (instance of PEAR_Frontend_*)
+ *
+ * @access public
+ */
+ function PEAR_Installer(&$ui)
+ {
+ parent::PEAR_Common();
+ $this->setFrontendObject($ui);
+ $this->debug = $this->config->get('verbose');
+ //$this->registry = &new PEAR_Registry($this->config->get('php_dir'));
+ }
+
+ // }}}
+
+ // {{{ _deletePackageFiles()
+
+ /**
+ * Delete a package's installed files, does not remove empty directories.
+ *
+ * @param string $package package name
+ *
+ * @return bool TRUE on success, or a PEAR error on failure
+ *
+ * @access private
+ */
+ function _deletePackageFiles($package)
+ {
+ if (!strlen($package)) {
+ return $this->raiseError("No package to uninstall given");
+ }
+ $filelist = $this->registry->packageInfo($package, 'filelist');
+ if ($filelist == null) {
+ return $this->raiseError("$package not installed");
+ }
+ foreach ($filelist as $file => $props) {
+ if (empty($props['installed_as'])) {
+ continue;
+ }
+ $path = $this->_prependPath($props['installed_as'], $this->installroot);
+ $this->addFileOperation('delete', array($path));
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ _installFile()
+
+ /**
+ * @param string filename
+ * @param array attributes from <file> tag in package.xml
+ * @param string path to install the file in
+ * @param array options from command-line
+ * @access private
+ */
+ function _installFile($file, $atts, $tmp_path, $options)
+ {
+ // {{{ return if this file is meant for another platform
+ static $os;
+ if (isset($atts['platform'])) {
+ if (empty($os)) {
+ include_once "OS/Guess.php";
+ $os = new OS_Guess();
+ }
+ if (strlen($atts['platform']) && $atts['platform']{0} == '!') {
+ $negate = true;
+ $platform = substr($atts['platform'], 1);
+ } else {
+ $negate = false;
+ $platform = $atts['platform'];
+ }
+ if ((bool) $os->matchSignature($platform) === $negate) {
+ $this->log(3, "skipped $file (meant for $atts[platform], we are ".$os->getSignature().")");
+ return PEAR_INSTALLER_SKIPPED;
+ }
+ }
+ // }}}
+
+ // {{{ assemble the destination paths
+ switch ($atts['role']) {
+ case 'doc':
+ case 'data':
+ case 'test':
+ $dest_dir = $this->config->get($atts['role'] . '_dir') .
+ DIRECTORY_SEPARATOR . $this->pkginfo['package'];
+ unset($atts['baseinstalldir']);
+ break;
+ case 'ext':
+ case 'php':
+ $dest_dir = $this->config->get($atts['role'] . '_dir');
+ break;
+ case 'script':
+ $dest_dir = $this->config->get('bin_dir');
+ break;
+ case 'src':
+ case 'extsrc':
+ $this->source_files++;
+ return;
+ default:
+ return $this->raiseError("Invalid role `$atts[role]' for file $file");
+ }
+ $save_destdir = $dest_dir;
+ if (!empty($atts['baseinstalldir'])) {
+ $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
+ }
+ if (dirname($file) != '.' && empty($atts['install-as'])) {
+ $dest_dir .= DIRECTORY_SEPARATOR . dirname($file);
+ }
+ if (empty($atts['install-as'])) {
+ $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file);
+ } else {
+ $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as'];
+ }
+ $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file;
+
+ // Clean up the DIRECTORY_SEPARATOR mess
+ $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
+ list($dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"),
+ DIRECTORY_SEPARATOR,
+ array($dest_file, $orig_file));
+ $installed_as = $dest_file;
+ $final_dest_file = $this->_prependPath($dest_file, $this->installroot);
+ $dest_dir = dirname($final_dest_file);
+ $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
+ // }}}
+
+ if (!@is_dir($dest_dir)) {
+ if (!$this->mkDirHier($dest_dir)) {
+ return $this->raiseError("failed to mkdir $dest_dir",
+ PEAR_INSTALLER_FAILED);
+ }
+ $this->log(3, "+ mkdir $dest_dir");
+ }
+ if (empty($atts['replacements'])) {
+ if (!file_exists($orig_file)) {
+ return $this->raiseError("file does not exist",
+ PEAR_INSTALLER_FAILED);
+ }
+ if (!@copy($orig_file, $dest_file)) {
+ return $this->raiseError("failed to write $dest_file",
+ PEAR_INSTALLER_FAILED);
+ }
+ $this->log(3, "+ cp $orig_file $dest_file");
+ if (isset($atts['md5sum'])) {
+ $md5sum = md5_file($dest_file);
+ }
+ } else {
+ // {{{ file with replacements
+ if (!file_exists($orig_file)) {
+ return $this->raiseError("file does not exist",
+ PEAR_INSTALLER_FAILED);
+ }
+ $fp = fopen($orig_file, "r");
+ $contents = fread($fp, filesize($orig_file));
+ fclose($fp);
+ if (isset($atts['md5sum'])) {
+ $md5sum = md5($contents);
+ }
+ $subst_from = $subst_to = array();
+ foreach ($atts['replacements'] as $a) {
+ $to = '';
+ if ($a['type'] == 'php-const') {
+ if (preg_match('/^[a-z0-9_]+$/i', $a['to'])) {
+ eval("\$to = $a[to];");
+ } else {
+ $this->log(0, "invalid php-const replacement: $a[to]");
+ continue;
+ }
+ } elseif ($a['type'] == 'pear-config') {
+ $to = $this->config->get($a['to']);
+ if (is_null($to)) {
+ $this->log(0, "invalid pear-config replacement: $a[to]");
+ continue;
+ }
+ } elseif ($a['type'] == 'package-info') {
+ if (isset($this->pkginfo[$a['to']]) && is_string($this->pkginfo[$a['to']])) {
+ $to = $this->pkginfo[$a['to']];
+ } else {
+ $this->log(0, "invalid package-info replacement: $a[to]");
+ continue;
+ }
+ }
+ if (!is_null($to)) {
+ $subst_from[] = $a['from'];
+ $subst_to[] = $to;
+ }
+ }
+ $this->log(3, "doing ".sizeof($subst_from)." substitution(s) for $final_dest_file");
+ if (sizeof($subst_from)) {
+ $contents = str_replace($subst_from, $subst_to, $contents);
+ }
+ $wp = @fopen($dest_file, "wb");
+ if (!is_resource($wp)) {
+ return $this->raiseError("failed to create $dest_file: $php_errormsg",
+ PEAR_INSTALLER_FAILED);
+ }
+ if (!fwrite($wp, $contents)) {
+ return $this->raiseError("failed writing to $dest_file: $php_errormsg",
+ PEAR_INSTALLER_FAILED);
+ }
+ fclose($wp);
+ // }}}
+ }
+ // {{{ check the md5
+ if (isset($md5sum)) {
+ if (strtolower($md5sum) == strtolower($atts['md5sum'])) {
+ $this->log(2, "md5sum ok: $final_dest_file");
+ } else {
+ if (empty($options['force'])) {
+ // delete the file
+ @unlink($dest_file);
+ return $this->raiseError("bad md5sum for file $final_dest_file",
+ PEAR_INSTALLER_FAILED);
+ } else {
+ $this->log(0, "warning : bad md5sum for file $final_dest_file");
+ }
+ }
+ }
+ // }}}
+ // {{{ set file permissions
+ if (!OS_WINDOWS) {
+ if ($atts['role'] == 'script') {
+ $mode = 0777 & ~(int)octdec($this->config->get('umask'));
+ $this->log(3, "+ chmod +x $dest_file");
+ } else {
+ $mode = 0666 & ~(int)octdec($this->config->get('umask'));
+ }
+ $this->addFileOperation("chmod", array($mode, $dest_file));
+ if (!@chmod($dest_file, $mode)) {
+ $this->log(0, "failed to change mode of $dest_file");
+ }
+ }
+ // }}}
+ $this->addFileOperation("rename", array($dest_file, $final_dest_file));
+ // Store the full path where the file was installed for easy unistall
+ $this->addFileOperation("installed_as", array($file, $installed_as,
+ $save_destdir, dirname(substr($dest_file, strlen($save_destdir)))));
+
+ //$this->log(2, "installed: $dest_file");
+ return PEAR_INSTALLER_OK;
+ }
+
+ // }}}
+ // {{{ addFileOperation()
+
+ /**
+ * Add a file operation to the current file transaction.
+ *
+ * @see startFileTransaction()
+ * @var string $type This can be one of:
+ * - rename: rename a file ($data has 2 values)
+ * - chmod: change permissions on a file ($data has 2 values)
+ * - delete: delete a file ($data has 1 value)
+ * - rmdir: delete a directory if empty ($data has 1 value)
+ * - installed_as: mark a file as installed ($data has 4 values).
+ * @var array $data For all file operations, this array must contain the
+ * full path to the file or directory that is being operated on. For
+ * the rename command, the first parameter must be the file to rename,
+ * the second its new name.
+ *
+ * The installed_as operation contains 4 elements in this order:
+ * 1. Filename as listed in the filelist element from package.xml
+ * 2. Full path to the installed file
+ * 3. Full path from the php_dir configuration variable used in this
+ * installation
+ * 4. Relative path from the php_dir that this file is installed in
+ */
+ function addFileOperation($type, $data)
+ {
+ if (!is_array($data)) {
+ return $this->raiseError('Internal Error: $data in addFileOperation'
+ . ' must be an array, was ' . gettype($data));
+ }
+ if ($type == 'chmod') {
+ $octmode = decoct($data[0]);
+ $this->log(3, "adding to transaction: $type $octmode $data[1]");
+ } else {
+ $this->log(3, "adding to transaction: $type " . implode(" ", $data));
+ }
+ $this->file_operations[] = array($type, $data);
+ }
+
+ // }}}
+ // {{{ startFileTransaction()
+
+ function startFileTransaction($rollback_in_case = false)
+ {
+ if (count($this->file_operations) && $rollback_in_case) {
+ $this->rollbackFileTransaction();
+ }
+ $this->file_operations = array();
+ }
+
+ // }}}
+ // {{{ commitFileTransaction()
+
+ function commitFileTransaction()
+ {
+ $n = count($this->file_operations);
+ $this->log(2, "about to commit $n file operations");
+ // {{{ first, check permissions and such manually
+ $errors = array();
+ foreach ($this->file_operations as $tr) {
+ list($type, $data) = $tr;
+ switch ($type) {
+ case 'rename':
+ if (!file_exists($data[0])) {
+ $errors[] = "cannot rename file $data[0], doesn't exist";
+ }
+ // check that dest dir. is writable
+ if (!is_writable(dirname($data[1]))) {
+ $errors[] = "permission denied ($type): $data[1]";
+ }
+ break;
+ case 'chmod':
+ // check that file is writable
+ if (!is_writable($data[1])) {
+ $errors[] = "permission denied ($type): $data[1] " . decoct($data[0]);
+ }
+ break;
+ case 'delete':
+ if (!file_exists($data[0])) {
+ $this->log(2, "warning: file $data[0] doesn't exist, can't be deleted");
+ }
+ // check that directory is writable
+ if (file_exists($data[0]) && !is_writable(dirname($data[0]))) {
+ $errors[] = "permission denied ($type): $data[0]";
+ }
+ break;
+ }
+
+ }
+ // }}}
+ $m = sizeof($errors);
+ if ($m > 0) {
+ foreach ($errors as $error) {
+ $this->log(1, $error);
+ }
+ return false;
+ }
+ // {{{ really commit the transaction
+ foreach ($this->file_operations as $tr) {
+ list($type, $data) = $tr;
+ switch ($type) {
+ case 'rename':
+ @unlink($data[1]);
+ @rename($data[0], $data[1]);
+ $this->log(3, "+ mv $data[0] $data[1]");
+ break;
+ case 'chmod':
+ @chmod($data[1], $data[0]);
+ $octmode = decoct($data[0]);
+ $this->log(3, "+ chmod $octmode $data[1]");
+ break;
+ case 'delete':
+ @unlink($data[0]);
+ $this->log(3, "+ rm $data[0]");
+ break;
+ case 'rmdir':
+ @rmdir($data[0]);
+ $this->log(3, "+ rmdir $data[0]");
+ break;
+ case 'installed_as':
+ $this->pkginfo['filelist'][$data[0]]['installed_as'] = $data[1];
+ if (!isset($this->pkginfo['filelist']['dirtree'][dirname($data[1])])) {
+ $this->pkginfo['filelist']['dirtree'][dirname($data[1])] = true;
+ while(!empty($data[3]) && $data[3] != '/' && $data[3] != '\\'
+ && $data[3] != '.') {
+ $this->pkginfo['filelist']['dirtree']
+ [$this->_prependPath($data[3], $data[2])] = true;
+ $data[3] = dirname($data[3]);
+ }
+ }
+ break;
+ }
+ }
+ // }}}
+ $this->log(2, "successfully committed $n file operations");
+ $this->file_operations = array();
+ return true;
+ }
+
+ // }}}
+ // {{{ rollbackFileTransaction()
+
+ function rollbackFileTransaction()
+ {
+ $n = count($this->file_operations);
+ $this->log(2, "rolling back $n file operations");
+ foreach ($this->file_operations as $tr) {
+ list($type, $data) = $tr;
+ switch ($type) {
+ case 'rename':
+ @unlink($data[0]);
+ $this->log(3, "+ rm $data[0]");
+ break;
+ case 'mkdir':
+ @rmdir($data[0]);
+ $this->log(3, "+ rmdir $data[0]");
+ break;
+ case 'chmod':
+ break;
+ case 'delete':
+ break;
+ case 'installed_as':
+ if (isset($this->pkginfo['filelist'])) {
+ unset($this->pkginfo['filelist'][$data[0]]['installed_as']);
+ }
+ if (isset($this->pkginfo['filelist']['dirtree'][dirname($data[1])])) {
+ unset($this->pkginfo['filelist']['dirtree'][dirname($data[1])]);
+ while(!empty($data[3]) && $data[3] != '/' && $data[3] != '\\'
+ && $data[3] != '.') {
+ unset($this->pkginfo['filelist']['dirtree']
+ [$this->_prependPath($data[3], $data[2])]);
+ $data[3] = dirname($data[3]);
+ }
+ }
+ if (isset($this->pkginfo['filelist']['dirtree'])
+ && !count($this->pkginfo['filelist']['dirtree'])) {
+ unset($this->pkginfo['filelist']['dirtree']);
+ }
+ break;
+ }
+ }
+ $this->file_operations = array();
+ }
+
+ // }}}
+ // {{{ mkDirHier($dir)
+
+ function mkDirHier($dir)
+ {
+ $this->addFileOperation('mkdir', array($dir));
+ return parent::mkDirHier($dir);
+ }
+
+ // }}}
+ // {{{ download()
+
+ /**
+ * Download any files and their dependencies, if necessary
+ *
+ * @param array a mixed list of package names, local files, or package.xml
+ * @param PEAR_Config
+ * @param array options from the command line
+ * @param array this is the array that will be populated with packages to
+ * install. Format of each entry:
+ *
+ * <code>
+ * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
+ * 'info' => array() // parsed package.xml
+ * );
+ * </code>
+ * @param array this will be populated with any error messages
+ * @param false private recursion variable
+ * @param false private recursion variable
+ * @param false private recursion variable
+ * @deprecated in favor of PEAR_Downloader
+ */
+ function download($packages, $options, &$config, &$installpackages,
+ &$errors, $installed = false, $willinstall = false, $state = false)
+ {
+ // trickiness: initialize here
+ parent::PEAR_Downloader($this->ui, $options, $config);
+ $ret = parent::download($packages);
+ $errors = $this->getErrorMsgs();
+ $installpackages = $this->getDownloadedPackages();
+ trigger_error("PEAR Warning: PEAR_Installer::download() is deprecated " .
+ "in favor of PEAR_Downloader class", E_USER_WARNING);
+ return $ret;
+ }
+
+ // }}}
+ // {{{ install()
+
+ /**
+ * Installs the files within the package file specified.
+ *
+ * @param string $pkgfile path to the package file
+ * @param array $options
+ * recognized options:
+ * - installroot : optional prefix directory for installation
+ * - force : force installation
+ * - register-only : update registry but don't install files
+ * - upgrade : upgrade existing install
+ * - soft : fail silently
+ * - nodeps : ignore dependency conflicts/missing dependencies
+ * - alldeps : install all dependencies
+ * - onlyreqdeps : install only required dependencies
+ *
+ * @return array|PEAR_Error package info if successful
+ */
+
+ function install($pkgfile, $options = array())
+ {
+ $php_dir = $this->config->get('php_dir');
+ if (isset($options['installroot'])) {
+ if (substr($options['installroot'], -1) == DIRECTORY_SEPARATOR) {
+ $options['installroot'] = substr($options['installroot'], 0, -1);
+ }
+ $php_dir = $this->_prependPath($php_dir, $options['installroot']);
+ $this->installroot = $options['installroot'];
+ } else {
+ $this->installroot = '';
+ }
+ $this->registry = &new PEAR_Registry($php_dir);
+ // ==> XXX should be removed later on
+ $flag_old_format = false;
+
+ if (substr($pkgfile, -4) == '.xml') {
+ $descfile = $pkgfile;
+ } else {
+ // {{{ Decompress pack in tmp dir -------------------------------------
+
+ // To allow relative package file names
+ $pkgfile = realpath($pkgfile);
+
+ if (PEAR::isError($tmpdir = System::mktemp('-d'))) {
+ return $tmpdir;
+ }
+ $this->log(3, '+ tmp dir created at ' . $tmpdir);
+
+ $tar = new Archive_Tar($pkgfile);
+ if (!@$tar->extract($tmpdir)) {
+ return $this->raiseError("unable to unpack $pkgfile");
+ }
+
+ // {{{ Look for existing package file
+ $descfile = $tmpdir . DIRECTORY_SEPARATOR . 'package.xml';
+
+ if (!is_file($descfile)) {
+ // ----- Look for old package archive format
+ // In this format the package.xml file was inside the
+ // Package-n.n directory
+ $dp = opendir($tmpdir);
+ do {
+ $pkgdir = readdir($dp);
+ } while ($pkgdir{0} == '.');
+
+ $descfile = $tmpdir . DIRECTORY_SEPARATOR . $pkgdir . DIRECTORY_SEPARATOR . 'package.xml';
+ $flag_old_format = true;
+ $this->log(0, "warning : you are using an archive with an old format");
+ }
+ // }}}
+ // <== XXX This part should be removed later on
+ // }}}
+ }
+
+ if (!is_file($descfile)) {
+ return $this->raiseError("no package.xml file after extracting the archive");
+ }
+
+ // Parse xml file -----------------------------------------------
+ $pkginfo = $this->infoFromDescriptionFile($descfile);
+ if (PEAR::isError($pkginfo)) {
+ return $pkginfo;
+ }
+ $this->validatePackageInfo($pkginfo, $errors, $warnings);
+ // XXX We allow warnings, do we have to do it?
+ if (count($errors)) {
+ if (empty($options['force'])) {
+ return $this->raiseError("The following errors where found (use force option to install anyway):\n".
+ implode("\n", $errors));
+ } else {
+ $this->log(0, "warning : the following errors were found:\n".
+ implode("\n", $errors));
+ }
+ }
+
+ $pkgname = $pkginfo['package'];
+
+ // {{{ Check dependencies -------------------------------------------
+ if (isset($pkginfo['release_deps']) && empty($options['nodeps'])) {
+ $dep_errors = '';
+ $error = $this->checkDeps($pkginfo, $dep_errors);
+ if ($error == true) {
+ if (empty($options['soft'])) {
+ $this->log(0, substr($dep_errors, 1));
+ }
+ return $this->raiseError("$pkgname: Dependencies failed");
+ } else if (!empty($dep_errors)) {
+ // Print optional dependencies
+ if (empty($options['soft'])) {
+ $this->log(0, $dep_errors);
+ }
+ }
+ }
+ // }}}
+
+ // {{{ checks to do when not in "force" mode
+ if (empty($options['force'])) {
+ $test = $this->registry->checkFileMap($pkginfo);
+ if (sizeof($test)) {
+ $tmp = $test;
+ foreach ($tmp as $file => $pkg) {
+ if ($pkg == $pkgname) {
+ unset($test[$file]);
+ }
+ }
+ if (sizeof($test)) {
+ $msg = "$pkgname: conflicting files found:\n";
+ $longest = max(array_map("strlen", array_keys($test)));
+ $fmt = "%${longest}s (%s)\n";
+ foreach ($test as $file => $pkg) {
+ $msg .= sprintf($fmt, $file, $pkg);
+ }
+ return $this->raiseError($msg);
+ }
+ }
+ }
+ // }}}
+
+ $this->startFileTransaction();
+
+ if (empty($options['upgrade'])) {
+ // checks to do only when installing new packages
+ if (empty($options['force']) && $this->registry->packageExists($pkgname)) {
+ return $this->raiseError("$pkgname already installed");
+ }
+ } else {
+ if ($this->registry->packageExists($pkgname)) {
+ $v1 = $this->registry->packageInfo($pkgname, 'version');
+ $v2 = $pkginfo['version'];
+ $cmp = version_compare("$v1", "$v2", 'gt');
+ if (empty($options['force']) && !version_compare("$v2", "$v1", 'gt')) {
+ return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)");
+ }
+ if (empty($options['register-only'])) {
+ // when upgrading, remove old release's files first:
+ if (PEAR::isError($err = $this->_deletePackageFiles($pkgname))) {
+ return $this->raiseError($err);
+ }
+ }
+ }
+ }
+
+ // {{{ Copy files to dest dir ---------------------------------------
+
+ // info from the package it self we want to access from _installFile
+ $this->pkginfo = &$pkginfo;
+ // used to determine whether we should build any C code
+ $this->source_files = 0;
+
+ if (empty($options['register-only'])) {
+ if (!is_dir($php_dir)) {
+ return $this->raiseError("no script destination directory\n",
+ null, PEAR_ERROR_DIE);
+ }
+
+ $tmp_path = dirname($descfile);
+ if (substr($pkgfile, -4) != '.xml') {
+ $tmp_path .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkginfo['version'];
+ }
+
+ // ==> XXX This part should be removed later on
+ if ($flag_old_format) {
+ $tmp_path = dirname($descfile);
+ }
+ // <== XXX This part should be removed later on
+
+ // {{{ install files
+ foreach ($pkginfo['filelist'] as $file => $atts) {
+ $this->expectError(PEAR_INSTALLER_FAILED);
+ $res = $this->_installFile($file, $atts, $tmp_path, $options);
+ $this->popExpect();
+ if (PEAR::isError($res)) {
+ if (empty($options['ignore-errors'])) {
+ $this->rollbackFileTransaction();
+ if ($res->getMessage() == "file does not exist") {
+ $this->raiseError("file $file in package.xml does not exist");
+ }
+ return $this->raiseError($res);
+ } else {
+ $this->log(0, "Warning: " . $res->getMessage());
+ }
+ }
+ if ($res != PEAR_INSTALLER_OK) {
+ // Do not register files that were not installed
+ unset($pkginfo['filelist'][$file]);
+ }
+ }
+ // }}}
+
+ // {{{ compile and install source files
+ if ($this->source_files > 0 && empty($options['nobuild'])) {
+ $this->log(1, "$this->source_files source files, building");
+ $bob = &new PEAR_Builder($this->ui);
+ $bob->debug = $this->debug;
+ $built = $bob->build($descfile, array(&$this, '_buildCallback'));
+ if (PEAR::isError($built)) {
+ $this->rollbackFileTransaction();
+ return $built;
+ }
+ $this->log(1, "\nBuild process completed successfully");
+ foreach ($built as $ext) {
+ $bn = basename($ext['file']);
+ list($_ext_name, ) = explode('.', $bn);
+ if (extension_loaded($_ext_name)) {
+ $this->raiseError("Extension '$_ext_name' already loaded. Please unload it ".
+ "in your php.ini file prior to install or upgrade it.");
+ }
+ // extension dir must be created if it doesn't exist
+ // patch by Tomas Cox (modified by Greg Beaver)
+ $ext_dir = $this->config->get('ext_dir');
+ if (!@is_dir($ext_dir) && !System::mkdir(array('-p', $ext_dir))) {
+ $this->log(3, "+ mkdir -p $ext_dir");
+ return $this->raiseError("failed to create extension dir '$ext_dir'");
+ }
+ $dest = $ext_dir . DIRECTORY_SEPARATOR . $bn;
+ $this->log(1, "Installing '$bn' at ext_dir ($dest)");
+ $this->log(3, "+ cp $ext[file] ext_dir ($dest)");
+ $copyto = $this->_prependPath($dest, $this->installroot);
+ if (!@copy($ext['file'], $copyto)) {
+ $this->rollbackFileTransaction();
+ return $this->raiseError("failed to copy $bn to $copyto");
+ }
+ $pkginfo['filelist'][$bn] = array(
+ 'role' => 'ext',
+ 'installed_as' => $dest,
+ 'php_api' => $ext['php_api'],
+ 'zend_mod_api' => $ext['zend_mod_api'],
+ 'zend_ext_api' => $ext['zend_ext_api'],
+ );
+ }
+ }
+ // }}}
+ }
+
+ if (!$this->commitFileTransaction()) {
+ $this->rollbackFileTransaction();
+ return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED);
+ }
+ // }}}
+
+ $ret = false;
+ // {{{ Register that the package is installed -----------------------
+ if (empty($options['upgrade'])) {
+ // if 'force' is used, replace the info in registry
+ if (!empty($options['force']) && $this->registry->packageExists($pkgname)) {
+ $this->registry->deletePackage($pkgname);
+ }
+ $ret = $this->registry->addPackage($pkgname, $pkginfo);
+ } else {
+ // new: upgrade installs a package if it isn't installed
+ if (!$this->registry->packageExists($pkgname)) {
+ $ret = $this->registry->addPackage($pkgname, $pkginfo);
+ } else {
+ $ret = $this->registry->updatePackage($pkgname, $pkginfo, false);
+ }
+ }
+ if (!$ret) {
+ return $this->raiseError("Adding package $pkgname to registry failed");
+ }
+ // }}}
+ return $pkginfo;
+ }
+
+ // }}}
+ // {{{ uninstall()
+
+ /**
+ * Uninstall a package
+ *
+ * This method removes all files installed by the application, and then
+ * removes any empty directories.
+ * @param string package name
+ * @param array Command-line options. Possibilities include:
+ *
+ * - installroot: base installation dir, if not the default
+ * - nodeps: do not process dependencies of other packages to ensure
+ * uninstallation does not break things
+ */
+ function uninstall($package, $options = array())
+ {
+ $php_dir = $this->config->get('php_dir');
+ if (isset($options['installroot'])) {
+ if (substr($options['installroot'], -1) == DIRECTORY_SEPARATOR) {
+ $options['installroot'] = substr($options['installroot'], 0, -1);
+ }
+ $this->installroot = $options['installroot'];
+ $php_dir = $this->_prependPath($php_dir, $this->installroot);
+ } else {
+ $this->installroot = '';
+ }
+ $this->registry = &new PEAR_Registry($php_dir);
+ $filelist = $this->registry->packageInfo($package, 'filelist');
+ if ($filelist == null) {
+ return $this->raiseError("$package not installed");
+ }
+ if (empty($options['nodeps'])) {
+ $depchecker = &new PEAR_Dependency($this->registry);
+ $error = $depchecker->checkPackageUninstall($errors, $warning, $package);
+ if ($error) {
+ return $this->raiseError($errors . 'uninstall failed');
+ }
+ if ($warning) {
+ $this->log(0, $warning);
+ }
+ }
+ // {{{ Delete the files
+ $this->startFileTransaction();
+ if (PEAR::isError($err = $this->_deletePackageFiles($package))) {
+ $this->rollbackFileTransaction();
+ return $this->raiseError($err);
+ }
+ if (!$this->commitFileTransaction()) {
+ $this->rollbackFileTransaction();
+ return $this->raiseError("uninstall failed");
+ } else {
+ $this->startFileTransaction();
+ if (!isset($filelist['dirtree']) || !count($filelist['dirtree'])) {
+ return $this->registry->deletePackage($package);
+ }
+ // attempt to delete empty directories
+ uksort($filelist['dirtree'], array($this, '_sortDirs'));
+ foreach($filelist['dirtree'] as $dir => $notused) {
+ $this->addFileOperation('rmdir', array($dir));
+ }
+ if (!$this->commitFileTransaction()) {
+ $this->rollbackFileTransaction();
+ }
+ }
+ // }}}
+
+ // Register that the package is no longer installed
+ return $this->registry->deletePackage($package);
+ }
+
+ // }}}
+ // {{{ _sortDirs()
+ function _sortDirs($a, $b)
+ {
+ if (strnatcmp($a, $b) == -1) return 1;
+ if (strnatcmp($a, $b) == 1) return -1;
+ return 0;
+ }
+
+ // }}}
+ // {{{ checkDeps()
+
+ /**
+ * Check if the package meets all dependencies
+ *
+ * @param array Package information (passed by reference)
+ * @param string Error message (passed by reference)
+ * @return boolean False when no error occured, otherwise true
+ */
+ function checkDeps(&$pkginfo, &$errors)
+ {
+ if (empty($this->registry)) {
+ $this->registry = &new PEAR_Registry($this->config->get('php_dir'));
+ }
+ $depchecker = &new PEAR_Dependency($this->registry);
+ $error = $errors = '';
+ $failed_deps = $optional_deps = array();
+ if (is_array($pkginfo['release_deps'])) {
+ foreach($pkginfo['release_deps'] as $dep) {
+ $code = $depchecker->callCheckMethod($error, $dep);
+ if ($code) {
+ if (isset($dep['optional']) && $dep['optional'] == 'yes') {
+ $optional_deps[] = array($dep, $code, $error);
+ } else {
+ $failed_deps[] = array($dep, $code, $error);
+ }
+ }
+ }
+ // {{{ failed dependencies
+ $n = count($failed_deps);
+ if ($n > 0) {
+ for ($i = 0; $i < $n; $i++) {
+ if (isset($failed_deps[$i]['type'])) {
+ $type = $failed_deps[$i]['type'];
+ } else {
+ $type = 'pkg';
+ }
+ switch ($failed_deps[$i][1]) {
+ case PEAR_DEPENDENCY_MISSING:
+ if ($type == 'pkg') {
+ // install
+ }
+ $errors .= "\n" . $failed_deps[$i][2];
+ break;
+ case PEAR_DEPENDENCY_UPGRADE_MINOR:
+ if ($type == 'pkg') {
+ // upgrade
+ }
+ $errors .= "\n" . $failed_deps[$i][2];
+ break;
+ default:
+ $errors .= "\n" . $failed_deps[$i][2];
+ break;
+ }
+ }
+ return true;
+ }
+ // }}}
+
+ // {{{ optional dependencies
+ $count_optional = count($optional_deps);
+ if ($count_optional > 0) {
+ $errors = "Optional dependencies:";
+
+ for ($i = 0; $i < $count_optional; $i++) {
+ if (isset($optional_deps[$i]['type'])) {
+ $type = $optional_deps[$i]['type'];
+ } else {
+ $type = 'pkg';
+ }
+ switch ($optional_deps[$i][1]) {
+ case PEAR_DEPENDENCY_MISSING:
+ case PEAR_DEPENDENCY_UPGRADE_MINOR:
+ default:
+ $errors .= "\n" . $optional_deps[$i][2];
+ break;
+ }
+ }
+ return false;
+ }
+ // }}}
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ _buildCallback()
+
+ function _buildCallback($what, $data)
+ {
+ if (($what == 'cmdoutput' && $this->debug > 1) ||
+ ($what == 'output' && $this->debug > 0)) {
+ $this->ui->outputData(rtrim($data), 'build');
+ }
+ }
+
+ // }}}
+}
+
+// {{{ md5_file() utility function
+if (!function_exists("md5_file")) {
+ function md5_file($filename) {
+ $fp = fopen($filename, "r");
+ if (!$fp) return null;
+ $contents = fread($fp, filesize($filename));
+ fclose($fp);
+ return md5($contents);
+ }
+}
+// }}}
+
+?>
diff --git a/pearlib/PEAR/Packager.php b/pearlib/PEAR/Packager.php
new file mode 100644
index 00000000..3ed209be
--- /dev/null
+++ b/pearlib/PEAR/Packager.php
@@ -0,0 +1,165 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Bakken <[email protected]> |
+// | Tomas V.V.Cox <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Packager.php,v 1.53 2004/06/13 14:06:01 pajoye Exp $
+
+require_once 'PEAR/Common.php';
+require_once 'System.php';
+
+/**
+ * Administration class used to make a PEAR release tarball.
+ *
+ * TODO:
+ * - add an extra param the dir where to place the created package
+ *
+ * @since PHP 4.0.2
+ * @author Stig Bakken <[email protected]>
+ */
+class PEAR_Packager extends PEAR_Common
+{
+ // {{{ constructor
+
+ function PEAR_Packager()
+ {
+ parent::PEAR_Common();
+ }
+
+ // }}}
+ // {{{ destructor
+
+ function _PEAR_Packager()
+ {
+ parent::_PEAR_Common();
+ }
+
+ // }}}
+
+ // {{{ package()
+
+ function package($pkgfile = null, $compress = true)
+ {
+ // {{{ validate supplied package.xml file
+ if (empty($pkgfile)) {
+ $pkgfile = 'package.xml';
+ }
+ // $this->pkginfo gets populated inside
+ $pkginfo = $this->infoFromDescriptionFile($pkgfile);
+ if (PEAR::isError($pkginfo)) {
+ return $this->raiseError($pkginfo);
+ }
+
+ $pkgdir = dirname(realpath($pkgfile));
+ $pkgfile = basename($pkgfile);
+
+ $errors = $warnings = array();
+ $this->validatePackageInfo($pkginfo, $errors, $warnings, $pkgdir);
+ foreach ($warnings as $w) {
+ $this->log(1, "Warning: $w");
+ }
+ foreach ($errors as $e) {
+ $this->log(0, "Error: $e");
+ }
+ if (sizeof($errors) > 0) {
+ return $this->raiseError('Errors in package');
+ }
+ // }}}
+
+ $pkgver = $pkginfo['package'] . '-' . $pkginfo['version'];
+
+ // {{{ Create the package file list
+ $filelist = array();
+ $i = 0;
+
+ foreach ($pkginfo['filelist'] as $fname => $atts) {
+ $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
+ if (!file_exists($file)) {
+ return $this->raiseError("File does not exist: $fname");
+ } else {
+ $filelist[$i++] = $file;
+ if (empty($pkginfo['filelist'][$fname]['md5sum'])) {
+ $md5sum = md5_file($file);
+ $pkginfo['filelist'][$fname]['md5sum'] = $md5sum;
+ }
+ $this->log(2, "Adding file $fname");
+ }
+ }
+ // }}}
+
+ // {{{ regenerate package.xml
+ $new_xml = $this->xmlFromInfo($pkginfo);
+ if (PEAR::isError($new_xml)) {
+ return $this->raiseError($new_xml);
+ }
+ if (!($tmpdir = System::mktemp(array('-d')))) {
+ return $this->raiseError("PEAR_Packager: mktemp failed");
+ }
+ $newpkgfile = $tmpdir . DIRECTORY_SEPARATOR . 'package.xml';
+ $np = @fopen($newpkgfile, 'wb');
+ if (!$np) {
+ return $this->raiseError("PEAR_Packager: unable to rewrite $pkgfile as $newpkgfile");
+ }
+ fwrite($np, $new_xml);
+ fclose($np);
+ // }}}
+
+ // {{{ TAR the Package -------------------------------------------
+ $ext = $compress ? '.tgz' : '.tar';
+ $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext;
+ $tar =& new Archive_Tar($dest_package, $compress);
+ $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors
+ // ----- Creates with the package.xml file
+ $ok = $tar->createModify(array($newpkgfile), '', $tmpdir);
+ if (PEAR::isError($ok)) {
+ return $this->raiseError($ok);
+ } elseif (!$ok) {
+ return $this->raiseError('PEAR_Packager: tarball creation failed');
+ }
+ // ----- Add the content of the package
+ if (!$tar->addModify($filelist, $pkgver, $pkgdir)) {
+ return $this->raiseError('PEAR_Packager: tarball creation failed');
+ }
+ $this->log(1, "Package $dest_package done");
+ if (file_exists("$pkgdir/CVS/Root")) {
+ $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $pkginfo['version']);
+ $cvstag = "RELEASE_$cvsversion";
+ $this->log(1, "Tag the released code with `pear cvstag $pkgfile'");
+ $this->log(1, "(or set the CVS tag $cvstag by hand)");
+ }
+ // }}}
+
+ return $dest_package;
+ }
+
+ // }}}
+}
+
+// {{{ md5_file() utility function
+if (!function_exists('md5_file')) {
+ function md5_file($file) {
+ if (!$fd = @fopen($file, 'r')) {
+ return false;
+ }
+ $md5 = md5(fread($fd, filesize($file)));
+ fclose($fd);
+ return $md5;
+ }
+}
+// }}}
+
+?>
diff --git a/pearlib/PEAR/Registry.php b/pearlib/PEAR/Registry.php
new file mode 100644
index 00000000..f2510a38
--- /dev/null
+++ b/pearlib/PEAR/Registry.php
@@ -0,0 +1,538 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <[email protected]> |
+// | Tomas V.V.Cox <[email protected]> |
+// | |
+// +----------------------------------------------------------------------+
+//
+// $Id: Registry.php,v 1.50.4.3 2004/10/26 19:19:56 cellog Exp $
+
+/*
+TODO:
+ - Transform into singleton()
+ - Add application level lock (avoid change the registry from the cmdline
+ while using the GTK interface, for ex.)
+*/
+require_once "System.php";
+require_once "PEAR.php";
+
+define('PEAR_REGISTRY_ERROR_LOCK', -2);
+define('PEAR_REGISTRY_ERROR_FORMAT', -3);
+define('PEAR_REGISTRY_ERROR_FILE', -4);
+
+/**
+ * Administration class used to maintain the installed package database.
+ */
+class PEAR_Registry extends PEAR
+{
+ // {{{ properties
+
+ /** Directory where registry files are stored.
+ * @var string
+ */
+ var $statedir = '';
+
+ /** File where the file map is stored
+ * @var string
+ */
+ var $filemap = '';
+
+ /** Name of file used for locking the registry
+ * @var string
+ */
+ var $lockfile = '';
+
+ /** File descriptor used during locking
+ * @var resource
+ */
+ var $lock_fp = null;
+
+ /** Mode used during locking
+ * @var int
+ */
+ var $lock_mode = 0; // XXX UNUSED
+
+ /** Cache of package information. Structure:
+ * array(
+ * 'package' => array('id' => ... ),
+ * ... )
+ * @var array
+ */
+ var $pkginfo_cache = array();
+
+ /** Cache of file map. Structure:
+ * array( '/path/to/file' => 'package', ... )
+ * @var array
+ */
+ var $filemap_cache = array();
+
+ // }}}
+
+ // {{{ constructor
+
+ /**
+ * PEAR_Registry constructor.
+ *
+ * @param string (optional) PEAR install directory (for .php files)
+ *
+ * @access public
+ */
+ function PEAR_Registry($pear_install_dir = PEAR_INSTALL_DIR)
+ {
+ parent::PEAR();
+ $ds = DIRECTORY_SEPARATOR;
+ $this->install_dir = $pear_install_dir;
+ $this->statedir = $pear_install_dir.$ds.'.registry';
+ $this->filemap = $pear_install_dir.$ds.'.filemap';
+ $this->lockfile = $pear_install_dir.$ds.'.lock';
+
+ // XXX Compatibility code should be removed in the future
+ // rename all registry files if any to lowercase
+ if (!OS_WINDOWS && $handle = @opendir($this->statedir)) {
+ $dest = $this->statedir . DIRECTORY_SEPARATOR;
+ while (false !== ($file = readdir($handle))) {
+ if (preg_match('/^.*[A-Z].*\.reg$/', $file)) {
+ rename($dest . $file, $dest . strtolower($file));
+ }
+ }
+ closedir($handle);
+ }
+ if (!file_exists($this->filemap)) {
+ $this->rebuildFileMap();
+ }
+ }
+
+ // }}}
+ // {{{ destructor
+
+ /**
+ * PEAR_Registry destructor. Makes sure no locks are forgotten.
+ *
+ * @access private
+ */
+ function _PEAR_Registry()
+ {
+ parent::_PEAR();
+ if (is_resource($this->lock_fp)) {
+ $this->_unlock();
+ }
+ }
+
+ // }}}
+
+ // {{{ _assertStateDir()
+
+ /**
+ * Make sure the directory where we keep registry files exists.
+ *
+ * @return bool TRUE if directory exists, FALSE if it could not be
+ * created
+ *
+ * @access private
+ */
+ function _assertStateDir()
+ {
+ if (!@is_dir($this->statedir)) {
+ if (!System::mkdir(array('-p', $this->statedir))) {
+ return $this->raiseError("could not create directory '{$this->statedir}'");
+ }
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ _packageFileName()
+
+ /**
+ * Get the name of the file where data for a given package is stored.
+ *
+ * @param string package name
+ *
+ * @return string registry file name
+ *
+ * @access public
+ */
+ function _packageFileName($package)
+ {
+ return $this->statedir . DIRECTORY_SEPARATOR . strtolower($package) . '.reg';
+ }
+
+ // }}}
+ // {{{ _openPackageFile()
+
+ function _openPackageFile($package, $mode)
+ {
+ $this->_assertStateDir();
+ $file = $this->_packageFileName($package);
+ $fp = @fopen($file, $mode);
+ if (!$fp) {
+ return null;
+ }
+ return $fp;
+ }
+
+ // }}}
+ // {{{ _closePackageFile()
+
+ function _closePackageFile($fp)
+ {
+ fclose($fp);
+ }
+
+ // }}}
+ // {{{ rebuildFileMap()
+
+ function rebuildFileMap()
+ {
+ $packages = $this->listPackages();
+ $files = array();
+ foreach ($packages as $package) {
+ $version = $this->packageInfo($package, 'version');
+ $filelist = $this->packageInfo($package, 'filelist');
+ if (!is_array($filelist)) {
+ continue;
+ }
+ foreach ($filelist as $name => $attrs) {
+ if (isset($attrs['role']) && $attrs['role'] != 'php') {
+ continue;
+ }
+ if (isset($attrs['baseinstalldir'])) {
+ $file = $attrs['baseinstalldir'].DIRECTORY_SEPARATOR.$name;
+ } else {
+ $file = $name;
+ }
+ $file = preg_replace(',^/+,', '', $file);
+ $files[$file] = $package;
+ }
+ }
+ $this->_assertStateDir();
+ $fp = @fopen($this->filemap, 'wb');
+ if (!$fp) {
+ return false;
+ }
+ $this->filemap_cache = $files;
+ fwrite($fp, serialize($files));
+ fclose($fp);
+ return true;
+ }
+
+ // }}}
+ // {{{ readFileMap()
+
+ function readFileMap()
+ {
+ $fp = @fopen($this->filemap, 'r');
+ if (!$fp) {
+ return $this->raiseError('PEAR_Registry: could not open filemap', PEAR_REGISTRY_ERROR_FILE, null, null, $php_errormsg);
+ }
+ $fsize = filesize($this->filemap);
+ $rt = get_magic_quotes_runtime();
+ set_magic_quotes_runtime(0);
+ $data = fread($fp, $fsize);
+ set_magic_quotes_runtime($rt);
+ fclose($fp);
+ $tmp = unserialize($data);
+ if (!$tmp && $fsize > 7) {
+ return $this->raiseError('PEAR_Registry: invalid filemap data', PEAR_REGISTRY_ERROR_FORMAT, null, null, $data);
+ }
+ $this->filemap_cache = $tmp;
+ return true;
+ }
+
+ // }}}
+ // {{{ _lock()
+
+ /**
+ * Lock the registry.
+ *
+ * @param integer lock mode, one of LOCK_EX, LOCK_SH or LOCK_UN.
+ * See flock manual for more information.
+ *
+ * @return bool TRUE on success, FALSE if locking failed, or a
+ * PEAR error if some other error occurs (such as the
+ * lock file not being writable).
+ *
+ * @access private
+ */
+ function _lock($mode = LOCK_EX)
+ {
+ if (!eregi('Windows 9', php_uname())) {
+ if ($mode != LOCK_UN && is_resource($this->lock_fp)) {
+ // XXX does not check type of lock (LOCK_SH/LOCK_EX)
+ return true;
+ }
+ if (PEAR::isError($err = $this->_assertStateDir())) {
+ return $err;
+ }
+ $open_mode = 'w';
+ // XXX People reported problems with LOCK_SH and 'w'
+ if ($mode === LOCK_SH || $mode === LOCK_UN) {
+ if (@!is_file($this->lockfile)) {
+ touch($this->lockfile);
+ }
+ $open_mode = 'r';
+ }
+
+ if (!is_resource($this->lock_fp)) {
+ $this->lock_fp = @fopen($this->lockfile, $open_mode);
+ }
+
+ if (!is_resource($this->lock_fp)) {
+ return $this->raiseError("could not create lock file" .
+ (isset($php_errormsg) ? ": " . $php_errormsg : ""));
+ }
+ if (!(int)flock($this->lock_fp, $mode)) {
+ switch ($mode) {
+ case LOCK_SH: $str = 'shared'; break;
+ case LOCK_EX: $str = 'exclusive'; break;
+ case LOCK_UN: $str = 'unlock'; break;
+ default: $str = 'unknown'; break;
+ }
+ return $this->raiseError("could not acquire $str lock ($this->lockfile)",
+ PEAR_REGISTRY_ERROR_LOCK);
+ }
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ _unlock()
+
+ function _unlock()
+ {
+ $ret = $this->_lock(LOCK_UN);
+ if (is_resource($this->lock_fp)) {
+ fclose($this->lock_fp);
+ }
+ $this->lock_fp = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ _packageExists()
+
+ function _packageExists($package)
+ {
+ return file_exists($this->_packageFileName($package));
+ }
+
+ // }}}
+ // {{{ _packageInfo()
+
+ function _packageInfo($package = null, $key = null)
+ {
+ if ($package === null) {
+ return array_map(array($this, '_packageInfo'),
+ $this->_listPackages());
+ }
+ $fp = $this->_openPackageFile($package, 'r');
+ if ($fp === null) {
+ return null;
+ }
+ $rt = get_magic_quotes_runtime();
+ set_magic_quotes_runtime(0);
+ $data = fread($fp, filesize($this->_packageFileName($package)));
+ set_magic_quotes_runtime($rt);
+ $this->_closePackageFile($fp);
+ $data = unserialize($data);
+ if ($key === null) {
+ return $data;
+ }
+ if (isset($data[$key])) {
+ return $data[$key];
+ }
+ return null;
+ }
+
+ // }}}
+ // {{{ _listPackages()
+
+ function _listPackages()
+ {
+ $pkglist = array();
+ $dp = @opendir($this->statedir);
+ if (!$dp) {
+ return $pkglist;
+ }
+ while ($ent = readdir($dp)) {
+ if ($ent{0} == '.' || substr($ent, -4) != '.reg') {
+ continue;
+ }
+ $pkglist[] = substr($ent, 0, -4);
+ }
+ return $pkglist;
+ }
+
+ // }}}
+
+ // {{{ packageExists()
+
+ function packageExists($package)
+ {
+ if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
+ return $e;
+ }
+ $ret = $this->_packageExists($package);
+ $this->_unlock();
+ return $ret;
+ }
+
+ // }}}
+ // {{{ packageInfo()
+
+ function packageInfo($package = null, $key = null)
+ {
+ if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
+ return $e;
+ }
+ $ret = $this->_packageInfo($package, $key);
+ $this->_unlock();
+ return $ret;
+ }
+
+ // }}}
+ // {{{ listPackages()
+
+ function listPackages()
+ {
+ if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
+ return $e;
+ }
+ $ret = $this->_listPackages();
+ $this->_unlock();
+ return $ret;
+ }
+
+ // }}}
+ // {{{ addPackage()
+
+ function addPackage($package, $info)
+ {
+ if ($this->packageExists($package)) {
+ return false;
+ }
+ if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
+ return $e;
+ }
+ $fp = $this->_openPackageFile($package, 'wb');
+ if ($fp === null) {
+ $this->_unlock();
+ return false;
+ }
+ $info['_lastmodified'] = time();
+ fwrite($fp, serialize($info));
+ $this->_closePackageFile($fp);
+ $this->_unlock();
+ return true;
+ }
+
+ // }}}
+ // {{{ deletePackage()
+
+ function deletePackage($package)
+ {
+ if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
+ return $e;
+ }
+ $file = $this->_packageFileName($package);
+ $ret = @unlink($file);
+ $this->rebuildFileMap();
+ $this->_unlock();
+ return $ret;
+ }
+
+ // }}}
+ // {{{ updatePackage()
+
+ function updatePackage($package, $info, $merge = true)
+ {
+ $oldinfo = $this->packageInfo($package);
+ if (empty($oldinfo)) {
+ return false;
+ }
+ if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
+ return $e;
+ }
+ $fp = $this->_openPackageFile($package, 'w');
+ if ($fp === null) {
+ $this->_unlock();
+ return false;
+ }
+ $info['_lastmodified'] = time();
+ if ($merge) {
+ fwrite($fp, serialize(array_merge($oldinfo, $info)));
+ } else {
+ fwrite($fp, serialize($info));
+ }
+ $this->_closePackageFile($fp);
+ if (isset($info['filelist'])) {
+ $this->rebuildFileMap();
+ }
+ $this->_unlock();
+ return true;
+ }
+
+ // }}}
+ // {{{ checkFileMap()
+
+ /**
+ * Test whether a file belongs to a package.
+ *
+ * @param string $path file path, absolute or relative to the pear
+ * install dir
+ *
+ * @return string which package the file belongs to, or an empty
+ * string if the file does not belong to an installed package
+ *
+ * @access public
+ */
+ function checkFileMap($path)
+ {
+ if (is_array($path)) {
+ static $notempty;
+ if (empty($notempty)) {
+ $notempty = create_function('$a','return !empty($a);');
+ }
+ $pkgs = array();
+ foreach ($path as $name => $attrs) {
+ if (is_array($attrs) && isset($attrs['baseinstalldir'])) {
+ $name = $attrs['baseinstalldir'].DIRECTORY_SEPARATOR.$name;
+ }
+ $pkgs[$name] = $this->checkFileMap($name);
+ }
+ return array_filter($pkgs, $notempty);
+ }
+ if (empty($this->filemap_cache) && PEAR::isError($this->readFileMap())) {
+ return $err;
+ }
+ if (isset($this->filemap_cache[$path])) {
+ return $this->filemap_cache[$path];
+ }
+ $l = strlen($this->install_dir);
+ if (substr($path, 0, $l) == $this->install_dir) {
+ $path = preg_replace('!^'.DIRECTORY_SEPARATOR.'+!', '', substr($path, $l));
+ }
+ if (isset($this->filemap_cache[$path])) {
+ return $this->filemap_cache[$path];
+ }
+ return '';
+ }
+
+ // }}}
+
+}
+
+?>
diff --git a/pearlib/PEAR/Remote.php b/pearlib/PEAR/Remote.php
new file mode 100644
index 00000000..7b1e314f
--- /dev/null
+++ b/pearlib/PEAR/Remote.php
@@ -0,0 +1,394 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Remote.php,v 1.50 2004/06/08 18:03:46 cellog Exp $
+
+require_once 'PEAR.php';
+require_once 'PEAR/Config.php';
+
+/**
+ * This is a class for doing remote operations against the central
+ * PEAR database.
+ *
+ * @nodep XML_RPC_Value
+ * @nodep XML_RPC_Message
+ * @nodep XML_RPC_Client
+ */
+class PEAR_Remote extends PEAR
+{
+ // {{{ properties
+
+ var $config = null;
+ var $cache = null;
+
+ // }}}
+
+ // {{{ PEAR_Remote(config_object)
+
+ function PEAR_Remote(&$config)
+ {
+ $this->PEAR();
+ $this->config = &$config;
+ }
+
+ // }}}
+
+ // {{{ getCache()
+
+
+ function getCache($args)
+ {
+ $id = md5(serialize($args));
+ $cachedir = $this->config->get('cache_dir');
+ $filename = $cachedir . DIRECTORY_SEPARATOR . 'xmlrpc_cache_' . $id;
+ if (!file_exists($filename)) {
+ return null;
+ };
+
+ $fp = fopen($filename, 'rb');
+ if (!$fp) {
+ return null;
+ }
+ $content = fread($fp, filesize($filename));
+ fclose($fp);
+ $result = array(
+ 'age' => time() - filemtime($filename),
+ 'lastChange' => filemtime($filename),
+ 'content' => unserialize($content),
+ );
+ return $result;
+ }
+
+ // }}}
+
+ // {{{ saveCache()
+
+ function saveCache($args, $data)
+ {
+ $id = md5(serialize($args));
+ $cachedir = $this->config->get('cache_dir');
+ if (!file_exists($cachedir)) {
+ System::mkdir(array('-p', $cachedir));
+ }
+ $filename = $cachedir.'/xmlrpc_cache_'.$id;
+
+ $fp = @fopen($filename, "wb");
+ if ($fp) {
+ fwrite($fp, serialize($data));
+ fclose($fp);
+ };
+ }
+
+ // }}}
+
+ // {{{ call(method, [args...])
+
+ function call($method)
+ {
+ $_args = $args = func_get_args();
+
+ $this->cache = $this->getCache($args);
+ $cachettl = $this->config->get('cache_ttl');
+ // If cache is newer than $cachettl seconds, we use the cache!
+ if ($this->cache !== null && $this->cache['age'] < $cachettl) {
+ return $this->cache['content'];
+ };
+
+ if (extension_loaded("xmlrpc")) {
+ $result = call_user_func_array(array(&$this, 'call_epi'), $args);
+ if (!PEAR::isError($result)) {
+ $this->saveCache($_args, $result);
+ };
+ return $result;
+ }
+ if (!@include_once("XML/RPC.php")) {
+ return $this->raiseError("For this remote PEAR operation you need to install the XML_RPC package");
+ }
+ array_shift($args);
+ $server_host = $this->config->get('master_server');
+ $username = $this->config->get('username');
+ $password = $this->config->get('password');
+ $eargs = array();
+ foreach($args as $arg) $eargs[] = $this->_encode($arg);
+ $f = new XML_RPC_Message($method, $eargs);
+ if ($this->cache !== null) {
+ $maxAge = '?maxAge='.$this->cache['lastChange'];
+ } else {
+ $maxAge = '';
+ };
+ $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
+ if ($proxy = parse_url($this->config->get('http_proxy'))) {
+ $proxy_host = @$proxy['host'];
+ $proxy_port = @$proxy['port'];
+ $proxy_user = @urldecode(@$proxy['user']);
+ $proxy_pass = @urldecode(@$proxy['pass']);
+ }
+ $c = new XML_RPC_Client('/xmlrpc.php'.$maxAge, $server_host, 80, $proxy_host, $proxy_port, $proxy_user, $proxy_pass);
+ if ($username && $password) {
+ $c->setCredentials($username, $password);
+ }
+ if ($this->config->get('verbose') >= 3) {
+ $c->setDebug(1);
+ }
+ $r = $c->send($f);
+ if (!$r) {
+ return $this->raiseError("XML_RPC send failed");
+ }
+ $v = $r->value();
+ if ($e = $r->faultCode()) {
+ if ($e == $GLOBALS['XML_RPC_err']['http_error'] && strstr($r->faultString(), '304 Not Modified') !== false) {
+ return $this->cache['content'];
+ }
+ return $this->raiseError($r->faultString(), $e);
+ }
+
+ $result = XML_RPC_decode($v);
+ $this->saveCache($_args, $result);
+ return $result;
+ }
+
+ // }}}
+
+ // {{{ call_epi(method, [args...])
+
+ function call_epi($method)
+ {
+ do {
+ if (extension_loaded("xmlrpc")) {
+ break;
+ }
+ if (OS_WINDOWS) {
+ $ext = 'dll';
+ } elseif (PHP_OS == 'HP-UX') {
+ $ext = 'sl';
+ } elseif (PHP_OS == 'AIX') {
+ $ext = 'a';
+ } else {
+ $ext = 'so';
+ }
+ $ext = OS_WINDOWS ? 'dll' : 'so';
+ @dl("xmlrpc-epi.$ext");
+ if (extension_loaded("xmlrpc")) {
+ break;
+ }
+ @dl("xmlrpc.$ext");
+ if (extension_loaded("xmlrpc")) {
+ break;
+ }
+ return $this->raiseError("unable to load xmlrpc extension");
+ } while (false);
+ $params = func_get_args();
+ array_shift($params);
+ $method = str_replace("_", ".", $method);
+ $request = xmlrpc_encode_request($method, $params);
+ $server_host = $this->config->get("master_server");
+ if (empty($server_host)) {
+ return $this->raiseError("PEAR_Remote::call: no master_server configured");
+ }
+ $server_port = 80;
+ if ($http_proxy = $this->config->get('http_proxy')) {
+ $proxy = parse_url($http_proxy);
+ $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
+ $proxy_host = @$proxy['host'];
+ $proxy_port = @$proxy['port'];
+ $proxy_user = @urldecode(@$proxy['user']);
+ $proxy_pass = @urldecode(@$proxy['pass']);
+ $fp = @fsockopen($proxy_host, $proxy_port);
+ $use_proxy = true;
+ } else {
+ $use_proxy = false;
+ $fp = @fsockopen($server_host, $server_port);
+ }
+ if (!$fp && $http_proxy) {
+ return $this->raiseError("PEAR_Remote::call: fsockopen(`$proxy_host', $proxy_port) failed");
+ } elseif (!$fp) {
+ return $this->raiseError("PEAR_Remote::call: fsockopen(`$server_host', $server_port) failed");
+ }
+ $len = strlen($request);
+ $req_headers = "Host: $server_host:$server_port\r\n" .
+ "Content-type: text/xml\r\n" .
+ "Content-length: $len\r\n";
+ $username = $this->config->get('username');
+ $password = $this->config->get('password');
+ if ($username && $password) {
+ $req_headers .= "Cookie: PEAR_USER=$username; PEAR_PW=$password\r\n";
+ $tmp = base64_encode("$username:$password");
+ $req_headers .= "Authorization: Basic $tmp\r\n";
+ }
+ if ($this->cache !== null) {
+ $maxAge = '?maxAge='.$this->cache['lastChange'];
+ } else {
+ $maxAge = '';
+ };
+
+ if ($use_proxy && $proxy_host != '' && $proxy_user != '') {
+ $req_headers .= 'Proxy-Authorization: Basic '
+ .base64_encode($proxy_user.':'.$proxy_pass)
+ ."\r\n";
+ }
+
+ if ($this->config->get('verbose') > 3) {
+ print "XMLRPC REQUEST HEADERS:\n";
+ var_dump($req_headers);
+ print "XMLRPC REQUEST BODY:\n";
+ var_dump($request);
+ }
+
+ if ($use_proxy && $proxy_host != '') {
+ $post_string = "POST http://".$server_host;
+ if ($proxy_port > '') {
+ $post_string .= ':'.$server_port;
+ }
+ } else {
+ $post_string = "POST ";
+ }
+
+ fwrite($fp, ($post_string."/xmlrpc.php$maxAge HTTP/1.0\r\n$req_headers\r\n$request"));
+ $response = '';
+ $line1 = fgets($fp, 2048);
+ if (!preg_match('!^HTTP/[0-9\.]+ (\d+) (.*)!', $line1, $matches)) {
+ return $this->raiseError("PEAR_Remote: invalid HTTP response from XML-RPC server");
+ }
+ switch ($matches[1]) {
+ case "200": // OK
+ break;
+ case "304": // Not Modified
+ return $this->cache['content'];
+ case "401": // Unauthorized
+ if ($username && $password) {
+ return $this->raiseError("PEAR_Remote: authorization failed", 401);
+ } else {
+ return $this->raiseError("PEAR_Remote: authorization required, please log in first", 401);
+ }
+ default:
+ return $this->raiseError("PEAR_Remote: unexpected HTTP response", (int)$matches[1], null, null, "$matches[1] $matches[2]");
+ }
+ while (trim(fgets($fp, 2048)) != ''); // skip rest of headers
+ while ($chunk = fread($fp, 10240)) {
+ $response .= $chunk;
+ }
+ fclose($fp);
+ if ($this->config->get('verbose') > 3) {
+ print "XMLRPC RESPONSE:\n";
+ var_dump($response);
+ }
+ $ret = xmlrpc_decode($response);
+ if (is_array($ret) && isset($ret['__PEAR_TYPE__'])) {
+ if ($ret['__PEAR_TYPE__'] == 'error') {
+ if (isset($ret['__PEAR_CLASS__'])) {
+ $class = $ret['__PEAR_CLASS__'];
+ } else {
+ $class = "PEAR_Error";
+ }
+ if ($ret['code'] === '') $ret['code'] = null;
+ if ($ret['message'] === '') $ret['message'] = null;
+ if ($ret['userinfo'] === '') $ret['userinfo'] = null;
+ if (strtolower($class) == 'db_error') {
+ $ret = $this->raiseError(PEAR::errorMessage($ret['code']),
+ $ret['code'], null, null,
+ $ret['userinfo']);
+ } else {
+ $ret = $this->raiseError($ret['message'], $ret['code'],
+ null, null, $ret['userinfo']);
+ }
+ }
+ } elseif (is_array($ret) && sizeof($ret) == 1 && isset($ret[0])
+ && is_array($ret[0]) &&
+ !empty($ret[0]['faultString']) &&
+ !empty($ret[0]['faultCode'])) {
+ extract($ret[0]);
+ $faultString = "XML-RPC Server Fault: " .
+ str_replace("\n", " ", $faultString);
+ return $this->raiseError($faultString, $faultCode);
+ }
+ return $ret;
+ }
+
+ // }}}
+
+ // {{{ _encode
+
+ // a slightly extended version of XML_RPC_encode
+ function _encode($php_val)
+ {
+ global $XML_RPC_Boolean, $XML_RPC_Int, $XML_RPC_Double;
+ global $XML_RPC_String, $XML_RPC_Array, $XML_RPC_Struct;
+
+ $type = gettype($php_val);
+ $xmlrpcval = new XML_RPC_Value;
+
+ switch($type) {
+ case "array":
+ reset($php_val);
+ $firstkey = key($php_val);
+ end($php_val);
+ $lastkey = key($php_val);
+ if ($firstkey === 0 && is_int($lastkey) &&
+ ($lastkey + 1) == count($php_val)) {
+ $is_continuous = true;
+ reset($php_val);
+ $size = count($php_val);
+ for ($expect = 0; $expect < $size; $expect++, next($php_val)) {
+ if (key($php_val) !== $expect) {
+ $is_continuous = false;
+ break;
+ }
+ }
+ if ($is_continuous) {
+ reset($php_val);
+ $arr = array();
+ while (list($k, $v) = each($php_val)) {
+ $arr[$k] = $this->_encode($v);
+ }
+ $xmlrpcval->addArray($arr);
+ break;
+ }
+ }
+ // fall though if not numerical and continuous
+ case "object":
+ $arr = array();
+ while (list($k, $v) = each($php_val)) {
+ $arr[$k] = $this->_encode($v);
+ }
+ $xmlrpcval->addStruct($arr);
+ break;
+ case "integer":
+ $xmlrpcval->addScalar($php_val, $XML_RPC_Int);
+ break;
+ case "double":
+ $xmlrpcval->addScalar($php_val, $XML_RPC_Double);
+ break;
+ case "string":
+ case "NULL":
+ $xmlrpcval->addScalar($php_val, $XML_RPC_String);
+ break;
+ case "boolean":
+ $xmlrpcval->addScalar($php_val, $XML_RPC_Boolean);
+ break;
+ case "unknown type":
+ default:
+ return null;
+ }
+ return $xmlrpcval;
+ }
+
+ // }}}
+
+}
+
+?>
diff --git a/pearlib/System.php b/pearlib/System.php
new file mode 100644
index 00000000..dc3acee9
--- /dev/null
+++ b/pearlib/System.php
@@ -0,0 +1,540 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Tomas V.V.Cox <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: System.php,v 1.36 2004/06/15 16:33:46 pajoye Exp $
+//
+
+require_once 'PEAR.php';
+require_once 'Console/Getopt.php';
+
+$GLOBALS['_System_temp_files'] = array();
+
+/**
+* System offers cross plattform compatible system functions
+*
+* Static functions for different operations. Should work under
+* Unix and Windows. The names and usage has been taken from its respectively
+* GNU commands. The functions will return (bool) false on error and will
+* trigger the error with the PHP trigger_error() function (you can silence
+* the error by prefixing a '@' sign after the function call).
+*
+* Documentation on this class you can find in:
+* http://pear.php.net/manual/
+*
+* Example usage:
+* if (!@System::rm('-r file1 dir1')) {
+* print "could not delete file1 or dir1";
+* }
+*
+* In case you need to to pass file names with spaces,
+* pass the params as an array:
+*
+* System::rm(array('-r', $file1, $dir1));
+*
+* @package System
+* @author Tomas V.V.Cox <[email protected]>
+* @version $Revision: 1.36 $
+* @access public
+* @see http://pear.php.net/manual/
+*/
+class System
+{
+ /**
+ * returns the commandline arguments of a function
+ *
+ * @param string $argv the commandline
+ * @param string $short_options the allowed option short-tags
+ * @param string $long_options the allowed option long-tags
+ * @return array the given options and there values
+ * @access private
+ */
+ function _parseArgs($argv, $short_options, $long_options = null)
+ {
+ if (!is_array($argv) && $argv !== null) {
+ $argv = preg_split('/\s+/', $argv);
+ }
+ return Console_Getopt::getopt2($argv, $short_options);
+ }
+
+ /**
+ * Output errors with PHP trigger_error(). You can silence the errors
+ * with prefixing a "@" sign to the function call: @System::mkdir(..);
+ *
+ * @param mixed $error a PEAR error or a string with the error message
+ * @return bool false
+ * @access private
+ */
+ function raiseError($error)
+ {
+ if (PEAR::isError($error)) {
+ $error = $error->getMessage();
+ }
+ trigger_error($error, E_USER_WARNING);
+ return false;
+ }
+
+ /**
+ * Creates a nested array representing the structure of a directory
+ *
+ * System::_dirToStruct('dir1', 0) =>
+ * Array
+ * (
+ * [dirs] => Array
+ * (
+ * [0] => dir1
+ * )
+ *
+ * [files] => Array
+ * (
+ * [0] => dir1/file2
+ * [1] => dir1/file3
+ * )
+ * )
+ * @param string $sPath Name of the directory
+ * @param integer $maxinst max. deep of the lookup
+ * @param integer $aktinst starting deep of the lookup
+ * @return array the structure of the dir
+ * @access private
+ */
+
+ function _dirToStruct($sPath, $maxinst, $aktinst = 0)
+ {
+ $struct = array('dirs' => array(), 'files' => array());
+ if (($dir = @opendir($sPath)) === false) {
+ System::raiseError("Could not open dir $sPath");
+ return $struct; // XXX could not open error
+ }
+ $struct['dirs'][] = $sPath; // XXX don't add if '.' or '..' ?
+ $list = array();
+ while ($file = readdir($dir)) {
+ if ($file != '.' && $file != '..') {
+ $list[] = $file;
+ }
+ }
+ closedir($dir);
+ sort($list);
+ if ($aktinst < $maxinst || $maxinst == 0) {
+ foreach($list as $val) {
+ $path = $sPath . DIRECTORY_SEPARATOR . $val;
+ if (is_dir($path)) {
+ $tmp = System::_dirToStruct($path, $maxinst, $aktinst+1);
+ $struct = array_merge_recursive($tmp, $struct);
+ } else {
+ $struct['files'][] = $path;
+ }
+ }
+ }
+ return $struct;
+ }
+
+ /**
+ * Creates a nested array representing the structure of a directory and files
+ *
+ * @param array $files Array listing files and dirs
+ * @return array
+ * @see System::_dirToStruct()
+ */
+ function _multipleToStruct($files)
+ {
+ $struct = array('dirs' => array(), 'files' => array());
+ settype($files, 'array');
+ foreach ($files as $file) {
+ if (is_dir($file)) {
+ $tmp = System::_dirToStruct($file, 0);
+ $struct = array_merge_recursive($tmp, $struct);
+ } else {
+ $struct['files'][] = $file;
+ }
+ }
+ return $struct;
+ }
+
+ /**
+ * The rm command for removing files.
+ * Supports multiple files and dirs and also recursive deletes
+ *
+ * @param string $args the arguments for rm
+ * @return mixed PEAR_Error or true for success
+ * @access public
+ */
+ function rm($args)
+ {
+ $opts = System::_parseArgs($args, 'rf'); // "f" do nothing but like it :-)
+ if (PEAR::isError($opts)) {
+ return System::raiseError($opts);
+ }
+ foreach($opts[0] as $opt) {
+ if ($opt[0] == 'r') {
+ $do_recursive = true;
+ }
+ }
+ $ret = true;
+ if (isset($do_recursive)) {
+ $struct = System::_multipleToStruct($opts[1]);
+ foreach($struct['files'] as $file) {
+ if (!@unlink($file)) {
+ $ret = false;
+ }
+ }
+ foreach($struct['dirs'] as $dir) {
+ if (!@rmdir($dir)) {
+ $ret = false;
+ }
+ }
+ } else {
+ foreach ($opts[1] as $file) {
+ $delete = (is_dir($file)) ? 'rmdir' : 'unlink';
+ if (!@$delete($file)) {
+ $ret = false;
+ }
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * Make directories. Note that we use call_user_func('mkdir') to avoid
+ * a problem with ZE2 calling System::mkDir instead of the native PHP func.
+ *
+ * @param string $args the name of the director(y|ies) to create
+ * @return bool True for success
+ * @access public
+ */
+ function mkDir($args)
+ {
+ $opts = System::_parseArgs($args, 'pm:');
+ if (PEAR::isError($opts)) {
+ return System::raiseError($opts);
+ }
+ $mode = 0777; // default mode
+ foreach($opts[0] as $opt) {
+ if ($opt[0] == 'p') {
+ $create_parents = true;
+ } elseif($opt[0] == 'm') {
+ // if the mode is clearly an octal number (starts with 0)
+ // convert it to decimal
+ if (strlen($opt[1]) && $opt[1]{0} == '0') {
+ $opt[1] = octdec($opt[1]);
+ } else {
+ // convert to int
+ $opt[1] += 0;
+ }
+ $mode = $opt[1];
+ }
+ }
+ $ret = true;
+ if (isset($create_parents)) {
+ foreach($opts[1] as $dir) {
+ $dirstack = array();
+ while (!@is_dir($dir) && $dir != DIRECTORY_SEPARATOR) {
+ array_unshift($dirstack, $dir);
+ $dir = dirname($dir);
+ }
+ while ($newdir = array_shift($dirstack)) {
+ if (!call_user_func('mkdir', $newdir, $mode)) {
+ $ret = false;
+ }
+ }
+ }
+ } else {
+ foreach($opts[1] as $dir) {
+ if (!@is_dir($dir) && !call_user_func('mkdir', $dir, $mode)) {
+ $ret = false;
+ }
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * Concatenate files
+ *
+ * Usage:
+ * 1) $var = System::cat('sample.txt test.txt');
+ * 2) System::cat('sample.txt test.txt > final.txt');
+ * 3) System::cat('sample.txt test.txt >> final.txt');
+ *
+ * Note: as the class use fopen, urls should work also (test that)
+ *
+ * @param string $args the arguments
+ * @return boolean true on success
+ * @access public
+ */
+ function &cat($args)
+ {
+ $ret = null;
+ $files = array();
+ if (!is_array($args)) {
+ $args = preg_split('/\s+/', $args);
+ }
+ for($i=0; $i < count($args); $i++) {
+ if ($args[$i] == '>') {
+ $mode = 'wb';
+ $outputfile = $args[$i+1];
+ break;
+ } elseif ($args[$i] == '>>') {
+ $mode = 'ab+';
+ $outputfile = $args[$i+1];
+ break;
+ } else {
+ $files[] = $args[$i];
+ }
+ }
+ if (isset($mode)) {
+ if (!$outputfd = fopen($outputfile, $mode)) {
+ $err = System::raiseError("Could not open $outputfile");
+ return $err;
+ }
+ $ret = true;
+ }
+ foreach ($files as $file) {
+ if (!$fd = fopen($file, 'r')) {
+ System::raiseError("Could not open $file");
+ continue;
+ }
+ while ($cont = fread($fd, 2048)) {
+ if (isset($outputfd)) {
+ fwrite($outputfd, $cont);
+ } else {
+ $ret .= $cont;
+ }
+ }
+ fclose($fd);
+ }
+ if (@is_resource($outputfd)) {
+ fclose($outputfd);
+ }
+ return $ret;
+ }
+
+ /**
+ * Creates temporary files or directories. This function will remove
+ * the created files when the scripts finish its execution.
+ *
+ * Usage:
+ * 1) $tempfile = System::mktemp("prefix");
+ * 2) $tempdir = System::mktemp("-d prefix");
+ * 3) $tempfile = System::mktemp();
+ * 4) $tempfile = System::mktemp("-t /var/tmp prefix");
+ *
+ * prefix -> The string that will be prepended to the temp name
+ * (defaults to "tmp").
+ * -d -> A temporary dir will be created instead of a file.
+ * -t -> The target dir where the temporary (file|dir) will be created. If
+ * this param is missing by default the env vars TMP on Windows or
+ * TMPDIR in Unix will be used. If these vars are also missing
+ * c:\windows\temp or /tmp will be used.
+ *
+ * @param string $args The arguments
+ * @return mixed the full path of the created (file|dir) or false
+ * @see System::tmpdir()
+ * @access public
+ */
+ function mktemp($args = null)
+ {
+ static $first_time = true;
+ $opts = System::_parseArgs($args, 't:d');
+ if (PEAR::isError($opts)) {
+ return System::raiseError($opts);
+ }
+ foreach($opts[0] as $opt) {
+ if($opt[0] == 'd') {
+ $tmp_is_dir = true;
+ } elseif($opt[0] == 't') {
+ $tmpdir = $opt[1];
+ }
+ }
+ $prefix = (isset($opts[1][0])) ? $opts[1][0] : 'tmp';
+ if (!isset($tmpdir)) {
+ $tmpdir = System::tmpdir();
+ }
+ if (!System::mkDir("-p $tmpdir")) {
+ return false;
+ }
+ $tmp = tempnam($tmpdir, $prefix);
+ if (isset($tmp_is_dir)) {
+ unlink($tmp); // be careful possible race condition here
+ if (!call_user_func('mkdir', $tmp, 0700)) {
+ return System::raiseError("Unable to create temporary directory $tmpdir");
+ }
+ }
+ $GLOBALS['_System_temp_files'][] = $tmp;
+ if ($first_time) {
+ PEAR::registerShutdownFunc(array('System', '_removeTmpFiles'));
+ $first_time = false;
+ }
+ return $tmp;
+ }
+
+ /**
+ * Remove temporary files created my mkTemp. This function is executed
+ * at script shutdown time
+ *
+ * @access private
+ */
+ function _removeTmpFiles()
+ {
+ if (count($GLOBALS['_System_temp_files'])) {
+ $delete = $GLOBALS['_System_temp_files'];
+ array_unshift($delete, '-r');
+ System::rm($delete);
+ }
+ }
+
+ /**
+ * Get the path of the temporal directory set in the system
+ * by looking in its environments variables.
+ * Note: php.ini-recommended removes the "E" from the variables_order setting,
+ * making unavaible the $_ENV array, that s why we do tests with _ENV
+ *
+ * @return string The temporal directory on the system
+ */
+ function tmpdir()
+ {
+ if (OS_WINDOWS) {
+ if ($var = isset($_ENV['TEMP']) ? $_ENV['TEMP'] : getenv('TEMP')) {
+ return $var;
+ }
+ if ($var = isset($_ENV['TMP']) ? $_ENV['TMP'] : getenv('TMP')) {
+ return $var;
+ }
+ if ($var = isset($_ENV['windir']) ? $_ENV['windir'] : getenv('windir')) {
+ return $var;
+ }
+ return getenv('SystemRoot') . '\temp';
+ }
+ if ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR')) {
+ return $var;
+ }
+ return '/tmp';
+ }
+
+ /**
+ * The "which" command (show the full path of a command)
+ *
+ * @param string $program The command to search for
+ * @return mixed A string with the full path or false if not found
+ * @author Stig Bakken <[email protected]>
+ */
+ function which($program, $fallback = false)
+ {
+ // is_executable() is not available on windows
+ if (OS_WINDOWS) {
+ $pear_is_executable = 'is_file';
+ } else {
+ $pear_is_executable = 'is_executable';
+ }
+
+ // full path given
+ if (basename($program) != $program) {
+ return (@$pear_is_executable($program)) ? $program : $fallback;
+ }
+
+ // XXX FIXME honor safe mode
+ $path_delim = OS_WINDOWS ? ';' : ':';
+ $exe_suffixes = OS_WINDOWS ? array('.exe','.bat','.cmd','.com') : array('');
+ $path_elements = explode($path_delim, getenv('PATH'));
+ foreach ($exe_suffixes as $suff) {
+ foreach ($path_elements as $dir) {
+ $file = $dir . DIRECTORY_SEPARATOR . $program . $suff;
+ if (@is_file($file) && @$pear_is_executable($file)) {
+ return $file;
+ }
+ }
+ }
+ return $fallback;
+ }
+
+ /**
+ * The "find" command
+ *
+ * Usage:
+ *
+ * System::find($dir);
+ * System::find("$dir -type d");
+ * System::find("$dir -type f");
+ * System::find("$dir -name *.php");
+ * System::find("$dir -name *.php -name *.htm*");
+ * System::find("$dir -maxdepth 1");
+ *
+ * Params implmented:
+ * $dir -> Start the search at this directory
+ * -type d -> return only directories
+ * -type f -> return only files
+ * -maxdepth <n> -> max depth of recursion
+ * -name <pattern> -> search pattern (bash style). Multiple -name param allowed
+ *
+ * @param mixed Either array or string with the command line
+ * @return array Array of found files
+ *
+ */
+ function find($args)
+ {
+ if (!is_array($args)) {
+ $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
+ }
+ $dir = array_shift($args);
+ $patterns = array();
+ $depth = 0;
+ $do_files = $do_dirs = true;
+ for ($i = 0; $i < count($args); $i++) {
+ switch ($args[$i]) {
+ case '-type':
+ if (in_array($args[$i+1], array('d', 'f'))) {
+ if ($args[$i+1] == 'd') {
+ $do_files = false;
+ } else {
+ $do_dirs = false;
+ }
+ }
+ $i++;
+ break;
+ case '-name':
+ $patterns[] = "(" . preg_replace(array('/\./', '/\*/'),
+ array('\.', '.*'),
+ $args[$i+1])
+ . ")";
+ $i++;
+ break;
+ case '-maxdepth':
+ $depth = $args[$i+1];
+ break;
+ }
+ }
+ $path = System::_dirToStruct($dir, $depth);
+ if ($do_files && $do_dirs) {
+ $files = array_merge($path['files'], $path['dirs']);
+ } elseif ($do_dirs) {
+ $files = $path['dirs'];
+ } else {
+ $files = $path['files'];
+ }
+ if (count($patterns)) {
+ $patterns = implode('|', $patterns);
+ $ret = array();
+ for ($i = 0; $i < count($files); $i++) {
+ if (preg_match("#^$patterns\$#", $files[$i])) {
+ $ret[] = $files[$i];
+ }
+ }
+ return $ret;
+ }
+ return $files;
+ }
+}
+?>
diff --git a/pearlib/XML/Parser.php b/pearlib/XML/Parser.php
new file mode 100644
index 00000000..54db9fae
--- /dev/null
+++ b/pearlib/XML/Parser.php
@@ -0,0 +1,670 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <[email protected]> |
+// | Tomas V.V.Cox <[email protected]> |
+// | Stephan Schmidt <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Parser.php,v 1.15 2004/10/04 16:56:49 schst Exp $
+
+/**
+ * XML Parser class.
+ *
+ * This is an XML parser based on PHP's "xml" extension,
+ * based on the bundled expat library.
+ *
+ * @category XML
+ * @package XML_Parser
+ * @author Stig Bakken <[email protected]>
+ * @author Tomas V.V.Cox <[email protected]>
+ * @author Stephan Schmidt <[email protected]>
+ */
+
+/**
+ * uses PEAR's error handling
+ */
+require_once 'PEAR.php';
+
+/**
+ * resource could not be created
+ */
+define('XML_PARSER_ERROR_NO_RESOURCE', 200);
+
+/**
+ * unsupported mode
+ */
+define('XML_PARSER_ERROR_UNSUPPORTED_MODE', 201);
+
+/**
+ * invalid encoding was given
+ */
+define('XML_PARSER_ERROR_INVALID_ENCODING', 202);
+
+/**
+ * specified file could not be read
+ */
+define('XML_PARSER_ERROR_FILE_NOT_READABLE', 203);
+
+/**
+ * invalid input
+ */
+define('XML_PARSER_ERROR_INVALID_INPUT', 204);
+
+/**
+ * remote file cannot be retrieved in safe mode
+ */
+define('XML_PARSER_ERROR_REMOTE', 205);
+
+/**
+ * XML Parser class.
+ *
+ * This is an XML parser based on PHP's "xml" extension,
+ * based on the bundled expat library.
+ *
+ * Notes:
+ * - It requires PHP 4.0.4pl1 or greater
+ * - From revision 1.17, the function names used by the 'func' mode
+ * are in the format "xmltag_$elem", for example: use "xmltag_name"
+ * to handle the <name></name> tags of your xml file.
+ *
+ * @category XML
+ * @package XML_Parser
+ * @author Stig Bakken <[email protected]>
+ * @author Tomas V.V.Cox <[email protected]>
+ * @author Stephan Schmidt <[email protected]>
+ * @todo create XML_Parser_Namespace to parse documents with namespaces
+ * @todo create XML_Parser_Pull
+ * @todo Tests that need to be made:
+ * - mixing character encodings
+ * - a test using all expat handlers
+ * - options (folding, output charset)
+ * - different parsing modes
+ */
+class XML_Parser extends PEAR
+{
+ // {{{ properties
+
+ /**
+ * XML parser handle
+ *
+ * @var resource
+ * @see xml_parser_create()
+ */
+ var $parser;
+
+ /**
+ * File handle if parsing from a file
+ *
+ * @var resource
+ */
+ var $fp;
+
+ /**
+ * Whether to do case folding
+ *
+ * If set to true, all tag and attribute names will
+ * be converted to UPPER CASE.
+ *
+ * @var boolean
+ */
+ var $folding = true;
+
+ /**
+ * Mode of operation, one of "event" or "func"
+ *
+ * @var string
+ */
+ var $mode;
+
+ /**
+ * Mapping from expat handler function to class method.
+ *
+ * @var array
+ */
+ var $handler = array(
+ 'character_data_handler' => 'cdataHandler',
+ 'default_handler' => 'defaultHandler',
+ 'processing_instruction_handler' => 'piHandler',
+ 'unparsed_entity_decl_handler' => 'unparsedHandler',
+ 'notation_decl_handler' => 'notationHandler',
+ 'external_entity_ref_handler' => 'entityrefHandler'
+ );
+
+ /**
+ * source encoding
+ *
+ * @var string
+ */
+ var $srcenc;
+
+ /**
+ * target encoding
+ *
+ * @var string
+ */
+ var $tgtenc;
+
+ /**
+ * handler object
+ *
+ * @var object
+ */
+ var $_handlerObj;
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * Creates an XML parser.
+ *
+ * This is needed for PHP4 compatibility, it will
+ * call the constructor, when a new instance is created.
+ *
+ * @param string $srcenc source charset encoding, use NULL (default) to use
+ * whatever the document specifies
+ * @param string $mode how this parser object should work, "event" for
+ * startelement/endelement-type events, "func"
+ * to have it call functions named after elements
+ * @param string $tgenc a valid target encoding
+ */
+ function XML_Parser($srcenc = null, $mode = 'event', $tgtenc = null)
+ {
+ XML_Parser::__construct($srcenc, $mode, $tgtenc);
+ }
+ // }}}
+
+ /**
+ * PHP5 constructor
+ *
+ * @param string $srcenc source charset encoding, use NULL (default) to use
+ * whatever the document specifies
+ * @param string $mode how this parser object should work, "event" for
+ * startelement/endelement-type events, "func"
+ * to have it call functions named after elements
+ * @param string $tgenc a valid target encoding
+ */
+ function __construct($srcenc = null, $mode = 'event', $tgtenc = null)
+ {
+ $this->PEAR('XML_Parser_Error');
+
+ $this->mode = $mode;
+ $this->srcenc = $srcenc;
+ $this->tgtenc = $tgtenc;
+ }
+ // }}}
+
+ /**
+ * Sets the mode of the parser.
+ *
+ * Possible modes are:
+ * - func
+ * - event
+ *
+ * You can set the mode using the second parameter
+ * in the constructor.
+ *
+ * This method is only needed, when switching to a new
+ * mode at a later point.
+ *
+ * @access public
+ * @param string mode, either 'func' or 'event'
+ * @return boolean|object true on success, PEAR_Error otherwise
+ */
+ function setMode($mode)
+ {
+ if ($mode != 'func' && $mode != 'event') {
+ $this->raiseError('Unsupported mode given', XML_PARSER_ERROR_UNSUPPORTED_MODE);
+ }
+
+ $this->mode = $mode;
+ return true;
+ }
+
+ /**
+ * Sets the object, that will handle the XML events
+ *
+ * This allows you to create a handler object independent of the
+ * parser object that you are using and easily switch the underlying
+ * parser.
+ *
+ * If no object will be set, XML_Parser assumes that you
+ * extend this class and handle the events in $this.
+ *
+ * @access public
+ * @param object object to handle the events
+ * @return boolean will always return true
+ * @since v1.2.0beta3
+ */
+ function setHandlerObj(&$obj)
+ {
+ $this->_handlerObj = &$obj;
+ return true;
+ }
+
+ /**
+ * Init the element handlers
+ *
+ * @access private
+ */
+ function _initHandlers()
+ {
+ if (!is_resource($this->parser)) {
+ return false;
+ }
+
+ if (!is_object($this->_handlerObj)) {
+ $this->_handlerObj = &$this;
+ }
+ switch ($this->mode) {
+
+ case 'func':
+ xml_set_object($this->parser, $this);
+ xml_set_element_handler($this->parser, 'funcStartHandler', 'funcEndHandler');
+ break;
+
+ case 'event':
+ xml_set_object($this->parser, $this->_handlerObj);
+ xml_set_element_handler($this->parser, 'startHandler', 'endHandler');
+ break;
+ default:
+ return $this->raiseError('Unsupported mode given', XML_PARSER_ERROR_UNSUPPORTED_MODE);
+ break;
+ }
+
+
+ /**
+ * set additional handlers for character data, entities, etc.
+ */
+ foreach ($this->handler as $xml_func => $method) {
+ if (method_exists($this->_handlerObj, $method)) {
+ $xml_func = 'xml_set_' . $xml_func;
+ $xml_func($this->parser, $method);
+ }
+ }
+ }
+
+ // {{{ _create()
+
+ /**
+ * create the XML parser resource
+ *
+ * Has been moved from the constructor to avoid
+ * problems with object references.
+ *
+ * Furthermore it allows us returning an error
+ * if something fails.
+ *
+ * @access private
+ * @return boolean|object true on success, PEAR_Error otherwise
+ *
+ * @see xml_parser_create
+ */
+ function _create()
+ {
+ if ($this->srcenc === null) {
+ $xp = @xml_parser_create();
+ } else {
+ $xp = @xml_parser_create($this->srcenc);
+ }
+ if (is_resource($xp)) {
+ if ($this->tgtenc !== null) {
+ if (!@xml_parser_set_option($xp, XML_OPTION_TARGET_ENCODING,
+ $this->tgtenc)) {
+ return $this->raiseError('invalid target encoding', XML_PARSER_ERROR_INVALID_ENCODING);
+ }
+ }
+ $this->parser = $xp;
+ $result = $this->_initHandlers($this->mode);
+ if ($this->isError($result)) {
+ return $result;
+ }
+ xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, $this->folding);
+
+ return true;
+ }
+ return $this->raiseError('Unable to create XML parser resource.', XML_PARSER_ERROR_NO_RESOURCE);
+ }
+
+ // }}}
+ // {{{ reset()
+
+ /**
+ * Reset the parser.
+ *
+ * This allows you to use one parser instance
+ * to parse multiple XML documents.
+ *
+ * @access public
+ * @return boolean|object true on success, PEAR_Error otherwise
+ */
+ function reset()
+ {
+ $result = $this->_create();
+ if ($this->isError( $result )) {
+ return $result;
+ }
+ }
+
+ // }}}
+ // {{{ setInputFile()
+
+ /**
+ * Sets the input xml file to be parsed
+ *
+ * @param string Filename (full path)
+ * @return resource fopen handle of the given file
+ * @throws XML_Parser_Error
+ * @see setInput(), setInputString(), parse()
+ * @access public
+ */
+ function setInputFile($file)
+ {
+ /**
+ * check, if file is a remote file
+ */
+ if (eregi('^(http|ftp)://', substr($file, 0, 10))) {
+ if (!ini_get('safe_mode')) {
+ ini_set('allow_url_fopen', 1);
+ } else {
+ return $this->raiseError('Remote files cannot be parsed, as safe mode is enabled.', XML_PARSER_ERROR_REMOTE);
+ }
+ }
+
+ $fp = @fopen($file, 'rb');
+ if (is_resource($fp)) {
+ $this->fp = $fp;
+ return $fp;
+ }
+ return $this->raiseError('File could not be opened.', XML_PARSER_ERROR_FILE_NOT_READABLE);
+ }
+
+ // }}}
+ // {{{ setInputString()
+
+ /**
+ * XML_Parser::setInputString()
+ *
+ * Sets the xml input from a string
+ *
+ * @param string $data a string containing the XML document
+ * @return null
+ **/
+ function setInputString($data)
+ {
+ $this->fp = $data;
+ return null;
+ }
+
+ // }}}
+ // {{{ setInput()
+
+ /**
+ * Sets the file handle to use with parse().
+ *
+ * You should use setInputFile() or setInputString() if you
+ * pass a string
+ *
+ * @param mixed $fp Can be either a resource returned from fopen(),
+ * a URL, a local filename or a string.
+ * @access public
+ * @see parse()
+ * @uses setInputString(), setInputFile()
+ */
+ function setInput($fp)
+ {
+ if (is_resource($fp)) {
+ $this->fp = $fp;
+ return true;
+ }
+ // see if it's an absolute URL (has a scheme at the beginning)
+ elseif (eregi('^[a-z]+://', substr($fp, 0, 10))) {
+ return $this->setInputFile($fp);
+ }
+ // see if it's a local file
+ elseif (file_exists($fp)) {
+ return $this->setInputFile($fp);
+ }
+ // it must be a string
+ else {
+ $this->fp = $fp;
+ return true;
+ }
+
+ return $this->raiseError('Illegal input format', XML_PARSER_ERROR_INVALID_INPUT);
+ }
+
+ // }}}
+ // {{{ parse()
+
+ /**
+ * Central parsing function.
+ *
+ * @return true|object PEAR error returns true on success, or a PEAR_Error otherwise
+ * @access public
+ */
+ function parse()
+ {
+ /**
+ * reset the parser
+ */
+ $result = $this->reset();
+ if ($this->isError($result)) {
+ return $result;
+ }
+ // if $this->fp was fopened previously
+ if (is_resource($this->fp)) {
+
+ while ($data = fread($this->fp, 4096)) {
+ if (!$this->_parseString($data, feof($this->fp))) {
+ return $this->raiseError();
+ }
+ }
+ // otherwise, $this->fp must be a string
+ } else {
+ if (!$this->_parseString($this->fp, true)) {
+ return $this->raiseError();
+ }
+ }
+ $this->free();
+
+ return true;
+ }
+
+ /**
+ * XML_Parser::_parseString()
+ *
+ * @param string $data
+ * @param boolean $eof
+ * @return bool
+ * @access private
+ * @see parseString()
+ **/
+ function _parseString($data, $eof = false)
+ {
+ return xml_parse($this->parser, $data, $eof);
+ }
+
+ // }}}
+ // {{{ parseString()
+
+ /**
+ * XML_Parser::parseString()
+ *
+ * Parses a string.
+ *
+ * @param string $data XML data
+ * @param boolean $eof If set and TRUE, data is the last piece of data sent in this parser
+ * @throws XML_Parser_Error
+ * @return Pear Error|true true on success or a PEAR Error
+ * @see _parseString()
+ */
+ function parseString($data, $eof = false)
+ {
+ if (!isset($this->parser) || !is_resource($this->parser)) {
+ $this->reset();
+ }
+
+ if (!$this->_parseString($data, $eof)) {
+ return $this->raiseError();
+ }
+
+ if ($eof === true) {
+ $this->free();
+ }
+ return true;
+ }
+
+ /**
+ * XML_Parser::free()
+ *
+ * Free the internal resources associated with the parser
+ *
+ * @return null
+ **/
+ function free()
+ {
+ if (is_resource($this->parser)) {
+ xml_parser_free($this->parser);
+ unset( $this->parser );
+ }
+ if (isset($this->fp) && is_resource($this->fp)) {
+ fclose($this->fp);
+ }
+ unset($this->fp);
+ return null;
+ }
+
+ /**
+ * XML_Parser::raiseError()
+ *
+ * Trows a XML_Parser_Error and free's the internal resources
+ *
+ * @param string $msg the error message
+ * @param integer $ecode the error message code
+ * @return XML_Parser_Error
+ **/
+ function raiseError($msg = null, $ecode = 0)
+ {
+ $msg = !is_null($msg) ? $msg : $this->parser;
+ $err = &new XML_Parser_Error($msg, $ecode);
+ $this->free();
+ return parent::raiseError($err);
+ }
+
+ // }}}
+ // {{{ funcStartHandler()
+
+ function funcStartHandler($xp, $elem, $attribs)
+ {
+ $func = 'xmltag_' . $elem;
+ if (method_exists($this->_handlerObj, $func)) {
+ call_user_func(array(&$this->_handlerObj, $func), $xp, $elem, $attribs);
+ }
+ }
+
+ // }}}
+ // {{{ funcEndHandler()
+
+ function funcEndHandler($xp, $elem)
+ {
+ $func = 'xmltag_' . $elem . '_';
+ if (method_exists($this->_handlerObj, $func)) {
+ call_user_func(array(&$this->_handlerObj, $func), $xp, $elem);
+ }
+ }
+
+ // }}}
+ // {{{ startHandler()
+
+ /**
+ *
+ * @abstract
+ */
+ function startHandler($xp, $elem, &$attribs)
+ {
+ return NULL;
+ }
+
+ // }}}
+ // {{{ endHandler()
+
+ /**
+ *
+ * @abstract
+ */
+ function endHandler($xp, $elem)
+ {
+ return NULL;
+ }
+
+
+ // }}}me
+}
+
+/**
+ * error class, replaces PEAR_Error
+ *
+ * An instance of this class will be returned
+ * if an error occurs inside XML_Parser.
+ *
+ * There are three advantages over using the standard PEAR_Error:
+ * - All messages will be prefixed
+ * - check for XML_Parser error, using is_a( $error, 'XML_Parser_Error' )
+ * - messages can be generated from the xml_parser resource
+ *
+ * @package XML_Parser
+ * @access public
+ * @see PEAR_Error
+ */
+class XML_Parser_Error extends PEAR_Error
+{
+ // {{{ properties
+
+ /**
+ * prefix for all messages
+ *
+ * @var string
+ */
+ var $error_message_prefix = 'XML_Parser: ';
+
+ // }}}
+ // {{{ constructor()
+ /**
+ * construct a new error instance
+ *
+ * You may either pass a message or an xml_parser resource as first
+ * parameter. If a resource has been passed, the last error that
+ * happened will be retrieved and returned.
+ *
+ * @access public
+ * @param string|resource message or parser resource
+ * @param integer error code
+ * @param integer error handling
+ * @param integer error level
+ */
+ function XML_Parser_Error($msgorparser = 'unknown error', $code = 0, $mode = PEAR_ERROR_RETURN, $level = E_USER_NOTICE)
+ {
+ if (is_resource($msgorparser)) {
+ $code = xml_get_error_code($msgorparser);
+ $msgorparser = sprintf('%s at XML input line %d',
+ xml_error_string($code),
+ xml_get_current_line_number($msgorparser));
+ }
+ $this->PEAR_Error($msgorparser, $code, $mode, $level);
+ }
+ // }}}
+}
+?>
diff --git a/pearlib/XML/Parser/Simple.php b/pearlib/XML/Parser/Simple.php
new file mode 100644
index 00000000..47724712
--- /dev/null
+++ b/pearlib/XML/Parser/Simple.php
@@ -0,0 +1,279 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stephan Schmidt <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Simple.php,v 1.3 2004/05/25 13:26:42 schst Exp $
+
+/**
+ * Simple XML parser class.
+ *
+ * This class is a simplified version of XML_Parser.
+ * In most XML applications the real action is executed,
+ * when a closing tag is found.
+ *
+ * XML_Parser_Simple allows you to just implement one callback
+ * for each tag that will receive the tag with its attributes
+ * and CData
+ *
+ * @category XML
+ * @package XML_Parser
+ * @author Stephan Schmidt <[email protected]>
+ */
+
+/**
+ * built on XML_Parser
+ */
+require_once 'XML/Parser.php';
+
+/**
+ * Simple XML parser class.
+ *
+ * This class is a simplified version of XML_Parser.
+ * In most XML applications the real action is executed,
+ * when a closing tag is found.
+ *
+ * XML_Parser_Simple allows you to just implement one callback
+ * for each tag that will receive the tag with its attributes
+ * and CData.
+ *
+ * <code>
+ * require_once '../Parser/Simple.php';
+ *
+ * class myParser extends XML_Parser_Simple
+ * {
+ * function myParser()
+ * {
+ * $this->XML_Parser_Simple();
+ * }
+ *
+ * function handleElement($name, $attribs, $data)
+ * {
+ * printf('handle %s<br>', $name);
+ * }
+ * }
+ *
+ * $p = &new myParser();
+ *
+ * $result = $p->setInputFile('myDoc.xml');
+ * $result = $p->parse();
+ * </code>
+ *
+ * @category XML
+ * @package XML_Parser
+ * @author Stephan Schmidt <[email protected]>
+ */
+class XML_Parser_Simple extends XML_Parser
+{
+ /**
+ * element stack
+ *
+ * @access private
+ * @var array
+ */
+ var $_elStack = array();
+
+ /**
+ * all character data
+ *
+ * @access private
+ * @var array
+ */
+ var $_data = array();
+
+ /**
+ * element depth
+ *
+ * @access private
+ * @var integer
+ */
+ var $_depth = 0;
+
+ /**
+ * Creates an XML parser.
+ *
+ * This is needed for PHP4 compatibility, it will
+ * call the constructor, when a new instance is created.
+ *
+ * @param string $srcenc source charset encoding, use NULL (default) to use
+ * whatever the document specifies
+ * @param string $mode how this parser object should work, "event" for
+ * handleElement(), "func" to have it call functions
+ * named after elements (handleElement_$name())
+ * @param string $tgenc a valid target encoding
+ */
+ function XML_Parser_Simple($srcenc = null, $mode = 'event', $tgtenc = null)
+ {
+ $this->XML_Parser($srcenc, $mode, $tgtenc);
+ }
+
+ /**
+ * inits the handlers
+ *
+ * @access private
+ */
+ function _initHandlers()
+ {
+ if (!is_object($this->_handlerObj)) {
+ $this->_handlerObj = &$this;
+ }
+
+ if ($this->mode != 'func' && $this->mode != 'event') {
+ return $this->raiseError('Unsupported mode given', XML_PARSER_ERROR_UNSUPPORTED_MODE);
+ }
+ xml_set_object($this->parser, $this);
+
+ xml_set_element_handler($this->parser, 'startHandler', 'endHandler');
+
+ /**
+ * set additional handlers for character data, entities, etc.
+ */
+ foreach ($this->handler as $xml_func => $method) {
+ if (method_exists($this, $method)) {
+ $xml_func = 'xml_set_' . $xml_func;
+ $xml_func($this->parser, $method);
+ }
+ }
+ }
+
+ /**
+ * Reset the parser.
+ *
+ * This allows you to use one parser instance
+ * to parse multiple XML documents.
+ *
+ * @access public
+ * @return boolean|object true on success, PEAR_Error otherwise
+ */
+ function reset()
+ {
+ $this->_elStack = array();
+ $this->_data = array();
+ $this->_depth = 0;
+
+ $result = $this->_create();
+ if ($this->isError( $result )) {
+ return $result;
+ }
+ }
+
+ /**
+ * start handler
+ *
+ * Pushes attributes and tagname onto a stack
+ *
+ * @access private
+ * @final
+ * @param resource xml parser resource
+ * @param string element name
+ * @param array attributes
+ */
+ function startHandler($xp, $elem, &$attribs)
+ {
+ array_push($this->_elStack, array(
+ 'name' => $elem,
+ 'attribs' => $attribs
+ )
+ );
+ $this->_depth++;
+ $this->_data[$this->_depth] = '';
+ }
+
+ /**
+ * end handler
+ *
+ * Pulls attributes and tagname from a stack
+ *
+ * @access private
+ * @final
+ * @param resource xml parser resource
+ * @param string element name
+ */
+ function endHandler($xp, $elem)
+ {
+ $el = array_pop($this->_elStack);
+ $data = $this->_data[$this->_depth];
+ $this->_depth--;
+
+ switch ($this->mode) {
+ case 'event':
+ $this->_handlerObj->handleElement($el['name'], $el['attribs'], $data);
+ break;
+ case 'func':
+ $func = 'handleElement_' . $elem;
+ if (method_exists($this->_handlerObj, $func)) {
+ call_user_func(array(&$this->_handlerObj, $func), $el['name'], $el['attribs'], $data);
+ }
+ break;
+ }
+ }
+
+ /**
+ * handle character data
+ *
+ * @access private
+ * @final
+ * @param resource xml parser resource
+ * @param string data
+ */
+ function cdataHandler($xp, $data)
+ {
+ $this->_data[$this->_depth] .= $data;
+ }
+
+ /**
+ * handle a tag
+ *
+ * Implement this in your parser
+ *
+ * @access public
+ * @abstract
+ * @param string element name
+ * @param array attributes
+ * @param string character data
+ */
+ function handleElement($name, $attribs, $data)
+ {
+ }
+
+ /**
+ * get the current tag depth
+ *
+ * The root tag is in depth 0.
+ *
+ * @access public
+ * @return integer
+ */
+ function getCurrentDepth()
+ {
+ return $this->_depth;
+ }
+
+ /**
+ * add some string to the current ddata.
+ *
+ * This is commonly needed, when a document is parsed recursively.
+ *
+ * @access public
+ * @param string data to add
+ * @return void
+ */
+ function addToData( $data )
+ {
+ $this->_data[$this->_depth] .= $data;
+ }
+}
+?>
diff --git a/pearlib/XML/RPC.php b/pearlib/XML/RPC.php
new file mode 100644
index 00000000..15b06716
--- /dev/null
+++ b/pearlib/XML/RPC.php
@@ -0,0 +1,1189 @@
+<?php
+// /* vim: set expandtab tabstop=4 shiftwidth=4: */
+// by Edd Dumbill (C) 1999-2001
+// $Id: RPC.php,v 1.21 2004/03/15 13:51:44 pajoye Exp $
+
+// License is granted to use or modify this software ("XML-RPC for PHP")
+// for commercial or non-commercial use provided the copyright of the author
+// is preserved in any distributed or derivative work.
+
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR
+// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Adapted to PEAR standards by Stig S�her Bakken <[email protected]> and
+// Martin Jansen <[email protected]>
+// /* $id$ */
+
+if (!function_exists('xml_parser_create')) {
+// Win 32 fix. From: "Leo West" <[email protected]>
+ if ($WINDIR) {
+ dl("php_xml.dll");
+ } else {
+ dl("xml.so");
+ }
+}
+
+define('XML_RPC_ERROR_INVALID_TYPE', 101);
+define('XML_RPC_ERROR_NON_NUMERIC_FOUND', 102);
+define('XML_RPC_ERROR_CONNECTION_FAILED', 103);
+define('XML_RPC_ERROR_ALREADY_INITIALIZED', 104);
+
+$GLOBALS['XML_RPC_I4'] = "i4";
+$GLOBALS['XML_RPC_Int'] = "int";
+$GLOBALS['XML_RPC_Boolean'] = "boolean";
+$GLOBALS['XML_RPC_Double'] = "double";
+$GLOBALS['XML_RPC_String'] = "string";
+$GLOBALS['XML_RPC_DateTime'] = "dateTime.iso8601";
+$GLOBALS['XML_RPC_Base64'] = "base64";
+$GLOBALS['XML_RPC_Array'] = "array";
+$GLOBALS['XML_RPC_Struct'] = "struct";
+
+$GLOBALS['XML_RPC_Types'] = array($GLOBALS['XML_RPC_I4'] => 1,
+ $GLOBALS['XML_RPC_Int'] => 1,
+ $GLOBALS['XML_RPC_Boolean'] => 1,
+ $GLOBALS['XML_RPC_String'] => 1,
+ $GLOBALS['XML_RPC_Double'] => 1,
+ $GLOBALS['XML_RPC_DateTime'] => 1,
+ $GLOBALS['XML_RPC_Base64'] => 1,
+ $GLOBALS['XML_RPC_Array'] => 2,
+ $GLOBALS['XML_RPC_Struct'] => 3);
+
+$GLOBALS['XML_RPC_entities'] = array("quot" => '"',
+ "amp" => "&",
+ "lt" => "<",
+ "gt" => ">",
+ "apos" => "'");
+
+$GLOBALS['XML_RPC_err']["unknown_method"] = 1;
+$GLOBALS['XML_RPC_str']["unknown_method"] = "Unknown method";
+$GLOBALS['XML_RPC_err']["invalid_return"] = 2;
+$GLOBALS['XML_RPC_str']["invalid_return"] = "Invalid return payload: enabling debugging to examine incoming payload";
+$GLOBALS['XML_RPC_err']["incorrect_params"] = 3;
+$GLOBALS['XML_RPC_str']["incorrect_params"] = "Incorrect parameters passed to method";
+$GLOBALS['XML_RPC_err']["introspect_unknown"] = 4;
+$GLOBALS['XML_RPC_str']["introspect_unknown"] = "Can't introspect: method unknown";
+$GLOBALS['XML_RPC_err']["http_error"] = 5;
+$GLOBALS['XML_RPC_str']["http_error"] = "Didn't receive 200 OK from remote server.";
+
+$GLOBALS['XML_RPC_defencoding'] = "UTF-8";
+
+// let user errors start at 800
+$GLOBALS['XML_RPC_erruser'] = 800;
+
+// let XML parse errors start at 100
+$GLOBALS['XML_RPC_errxml'] = 100;
+
+// formulate backslashes for escaping regexp
+$GLOBALS['XML_RPC_backslash'] = chr(92) . chr(92);
+
+$GLOBALS['XML_RPC_twoslash'] = $GLOBALS['XML_RPC_backslash'] . $GLOBALS['XML_RPC_backslash'];
+$GLOBALS['XML_RPC_twoslash'] = "2SLS";
+
+// used to store state during parsing
+// quick explanation of components:
+// st - used to build up a string for evaluation
+// ac - used to accumulate values
+// qt - used to decide if quotes are needed for evaluation
+// cm - used to denote struct or array (comma needed)
+// isf - used to indicate a fault
+// lv - used to indicate "looking for a value": implements
+// the logic to allow values with no types to be strings
+// params - used to store parameters in method calls
+// method - used to store method name
+
+$GLOBALS['XML_RPC_xh'] = array();
+
+function XML_RPC_entity_decode($string)
+{
+ $top = split("&", $string);
+ $op = "";
+ $i = 0;
+ while($i < sizeof($top)) {
+ if (ereg("^([#a-zA-Z0-9]+);", $top[$i], $regs)) {
+ $op .= ereg_replace("^[#a-zA-Z0-9]+;",
+ XML_RPC_lookup_entity($regs[1]),
+ $top[$i]);
+ } else {
+ if ($i == 0) {
+ $op = $top[$i];
+ } else {
+ $op .= "&" . $top[$i];
+ }
+ }
+
+ $i++;
+ }
+ return $op;
+}
+
+
+function XML_RPC_lookup_entity($ent)
+{
+ global $XML_RPC_entities;
+
+ if ($XML_RPC_entities[strtolower($ent)]) {
+ return $XML_RPC_entities[strtolower($ent)];
+ }
+
+ if (ereg("^#([0-9]+)$", $ent, $regs)) {
+ return chr($regs[1]);
+ }
+
+ return "?";
+}
+
+
+function XML_RPC_se($parser, $name, $attrs)
+{
+ global $XML_RPC_xh, $XML_RPC_DateTime, $XML_RPC_String;
+
+ switch ($name) {
+ case "STRUCT":
+ case "ARRAY":
+ $XML_RPC_xh[$parser]['st'] .= "array(";
+ $XML_RPC_xh[$parser]['cm']++;
+ // this last line turns quoting off
+ // this means if we get an empty array we'll
+ // simply get a bit of whitespace in the eval
+ $XML_RPC_xh[$parser]['qt'] = 0;
+ break;
+
+ case "NAME":
+ $XML_RPC_xh[$parser]['st'] .= "'";
+ $XML_RPC_xh[$parser]['ac'] = "";
+ break;
+
+ case "FAULT":
+ $XML_RPC_xh[$parser]['isf'] = 1;
+ break;
+
+ case "PARAM":
+ $XML_RPC_xh[$parser]['st'] = "";
+ break;
+
+ case "VALUE":
+ $XML_RPC_xh[$parser]['st'] .= "new XML_RPC_Value(";
+ $XML_RPC_xh[$parser]['lv'] = 1;
+ $XML_RPC_xh[$parser]['vt'] = $XML_RPC_String;
+ $XML_RPC_xh[$parser]['ac'] = "";
+ $XML_RPC_xh[$parser]['qt'] = 0;
+ // look for a value: if this is still 1 by the
+ // time we reach the first data segment then the type is string
+ // by implication and we need to add in a quote
+ break;
+
+ case "I4":
+ case "INT":
+ case "STRING":
+ case "BOOLEAN":
+ case "DOUBLE":
+ case "DATETIME.ISO8601":
+ case "BASE64":
+ $XML_RPC_xh[$parser]['ac'] = ""; // reset the accumulator
+
+ if ($name == "DATETIME.ISO8601" || $name == "STRING") {
+ $XML_RPC_xh[$parser]['qt'] = 1;
+
+ if ($name == "DATETIME.ISO8601") {
+ $XML_RPC_xh[$parser]['vt'] = $XML_RPC_DateTime;
+ }
+
+ } elseif ($name == "BASE64") {
+ $XML_RPC_xh[$parser]['qt'] = 2;
+ } else {
+ // No quoting is required here -- but
+ // at the end of the element we must check
+ // for data format errors.
+ $XML_RPC_xh[$parser]['qt'] = 0;
+ }
+ break;
+
+ case "MEMBER":
+ $XML_RPC_xh[$parser]['ac'] = "";
+ break;
+
+ default:
+ break;
+ }
+
+ if ($name!="VALUE") {
+ $XML_RPC_xh[$parser]['lv'] = 0;
+ }
+}
+
+
+function XML_RPC_ee($parser, $name)
+{
+ global $XML_RPC_xh,$XML_RPC_Types,$XML_RPC_String;
+
+ switch ($name) {
+ case "STRUCT":
+ case "ARRAY":
+ if ($XML_RPC_xh[$parser]['cm'] && substr($XML_RPC_xh[$parser]['st'], -1) == ',') {
+ $XML_RPC_xh[$parser]['st'] = substr($XML_RPC_xh[$parser]['st'],0,-1);
+ }
+
+ $XML_RPC_xh[$parser]['st'] .= ")";
+ $XML_RPC_xh[$parser]['vt'] = strtolower($name);
+ $XML_RPC_xh[$parser]['cm']--;
+ break;
+
+ case "NAME":
+ $XML_RPC_xh[$parser]['st'] .= $XML_RPC_xh[$parser]['ac'] . "' => ";
+ break;
+
+ case "BOOLEAN":
+ // special case here: we translate boolean 1 or 0 into PHP
+ // constants true or false
+ if ($XML_RPC_xh[$parser]['ac'] == '1') {
+ $XML_RPC_xh[$parser]['ac'] = "true";
+ } else {
+ $XML_RPC_xh[$parser]['ac'] = "false";
+ }
+
+ $XML_RPC_xh[$parser]['vt'] = strtolower($name);
+ // Drop through intentionally.
+
+ case "I4":
+ case "INT":
+ case "STRING":
+ case "DOUBLE":
+ case "DATETIME.ISO8601":
+ case "BASE64":
+ if ($XML_RPC_xh[$parser]['qt'] == 1) {
+ // we use double quotes rather than single so backslashification works OK
+ $XML_RPC_xh[$parser]['st'] .= "\"" . $XML_RPC_xh[$parser]['ac'] . "\"";
+ } elseif ($XML_RPC_xh[$parser]['qt'] == 2) {
+ $XML_RPC_xh[$parser]['st'] .= "base64_decode('" . $XML_RPC_xh[$parser]['ac'] . "')";
+ } elseif ($name=="BOOLEAN") {
+ $XML_RPC_xh[$parser]['st'] .= $XML_RPC_xh[$parser]['ac'];
+ } else {
+ // we have an I4, INT or a DOUBLE
+ // we must check that only 0123456789-.<space> are characters here
+ if (!ereg("^\-?[0123456789 \t\.]+$", $XML_RPC_xh[$parser]['ac'])) {
+ $this->raiseError("Non-numeric value recieved in INT or DOUBLE", XML_RPC_ERROR_NON_NUMERIC_FOUND);
+ $XML_RPC_xh[$parser]['st'] .= "ERROR_NON_NUMERIC_FOUND";
+ } else {
+ // it's ok, add it on
+ $XML_RPC_xh[$parser]['st'] .= $XML_RPC_xh[$parser]['ac'];
+ }
+ }
+
+ $XML_RPC_xh[$parser]['ac'] = "";
+ $XML_RPC_xh[$parser]['qt'] = 0;
+ $XML_RPC_xh[$parser]['lv'] = 3; // indicate we've found a value
+ break;
+
+ case "VALUE":
+ // deal with a string value
+ if (strlen($XML_RPC_xh[$parser]['ac']) > 0 &&
+ $XML_RPC_xh[$parser]['vt'] == $XML_RPC_String) {
+
+ $XML_RPC_xh[$parser]['st'] .= "\"" . $XML_RPC_xh[$parser]['ac'] . "\"";
+ }
+
+ // This if () detects if no scalar was inside <VALUE></VALUE>
+ // and pads an empty "".
+ if ($XML_RPC_xh[$parser]['st'][strlen($XML_RPC_xh[$parser]['st'])-1] == '(') {
+ $XML_RPC_xh[$parser]['st'] .= '""';
+ }
+ $XML_RPC_xh[$parser]['st'] .= ", '" . $XML_RPC_xh[$parser]['vt'] . "')";
+ if ($XML_RPC_xh[$parser]['cm']) {
+ $XML_RPC_xh[$parser]['st'] .= ",";
+ }
+ break;
+
+ case "MEMBER":
+ $XML_RPC_xh[$parser]['ac'] = "";
+ $XML_RPC_xh[$parser]['qt'] = 0;
+ break;
+
+ case "DATA":
+ $XML_RPC_xh[$parser]['ac'] = "";
+ $XML_RPC_xh[$parser]['qt'] = 0;
+ break;
+
+ case "PARAM":
+ $XML_RPC_xh[$parser]['params'][] = $XML_RPC_xh[$parser]['st'];
+ break;
+
+ case "METHODNAME":
+ $XML_RPC_xh[$parser]['method'] = ereg_replace("^[\n\r\t ]+", "", $XML_RPC_xh[$parser]['ac']);
+ break;
+
+ case "BOOLEAN":
+ // special case here: we translate boolean 1 or 0 into PHP
+ // constants true or false
+ if ($XML_RPC_xh[$parser]['ac'] == '1') {
+ $XML_RPC_xh[$parser]['ac'] = "true";
+ } else {
+ $XML_RPC_xh[$parser]['ac'] = "false";
+ }
+
+ $XML_RPC_xh[$parser]['vt'] = strtolower($name);
+ break;
+
+ default:
+ break;
+ }
+
+ // if it's a valid type name, set the type
+ if (isset($XML_RPC_Types[strtolower($name)])) {
+ $XML_RPC_xh[$parser]['vt'] = strtolower($name);
+ }
+}
+
+
+function XML_RPC_cd($parser, $data)
+{
+ global $XML_RPC_xh, $XML_RPC_backslash;
+
+ if ($XML_RPC_xh[$parser]['lv'] != 3) {
+ // "lookforvalue==3" means that we've found an entire value
+ // and should discard any further character data
+
+ if ($XML_RPC_xh[$parser]['lv'] == 1) {
+ // if we've found text and we're just in a <value> then
+ // turn quoting on, as this will be a string
+ $XML_RPC_xh[$parser]['qt'] = 1;
+ // and say we've found a value
+ $XML_RPC_xh[$parser]['lv'] = 2;
+ }
+
+ // replace characters that eval would
+ // do special things with
+ if (isset($XML_RPC_xh[$parser]['ac'])) {
+ $XML_RPC_xh[$parser]['ac'] .= str_replace('$', '\$',
+ str_replace('"', '\"', str_replace(chr(92),
+ $XML_RPC_backslash, $data)));
+ } else {
+ $XML_RPC_xh[$parser]['ac'] = '';
+ }
+ }
+}
+
+
+function XML_RPC_dh($parser, $data)
+{
+ global $XML_RPC_xh;
+
+ if (substr($data, 0, 1) == "&" && substr($data, -1, 1) == ";") {
+ if ($XML_RPC_xh[$parser]['lv'] == 1) {
+ $XML_RPC_xh[$parser]['qt'] = 1;
+ $XML_RPC_xh[$parser]['lv'] = 2;
+ }
+ $XML_RPC_xh[$parser]['ac'] .= str_replace('$', '\$',
+ str_replace('"', '\"', str_replace(chr(92),
+ $XML_RPC_backslash, $data)));
+ }
+}
+
+/**
+ * Base class
+ *
+ * This class provides common functions for all of the XML_RPC classes.
+ */
+class XML_RPC_Base {
+ function raiseError($msg, $code)
+ {
+ include_once 'PEAR.php';
+ PEAR::raiseError(get_class($this) . ": " . $msg, $code);
+ }
+}
+
+class XML_RPC_Client extends XML_RPC_Base {
+ var $path;
+ var $server;
+ var $port;
+ var $errno;
+ var $errstring;
+ var $debug = 0;
+ var $username = "";
+ var $password = "";
+
+ function XML_RPC_Client($path, $server, $port = 80,
+ $proxy = '', $proxy_port = 8080,
+ $proxy_user = '', $proxy_pass = '')
+ {
+ $this->port = $port;
+ $this->server = $server;
+ $this->path = $path;
+ $this->proxy = $proxy;
+ $this->proxy_port = $proxy_port;
+ $this->proxy_user = $proxy_user;
+ $this->proxy_pass = $proxy_pass;
+ }
+
+ function setDebug($in)
+ {
+ if ($in) {
+ $this->debug = 1;
+ } else {
+ $this->debug = 0;
+ }
+ }
+
+ function setCredentials($u, $p)
+ {
+ $this->username = $u;
+ $this->password = $p;
+ }
+
+ function send($msg, $timeout = 0)
+ {
+ // where msg is an xmlrpcmsg
+ $msg->debug = $this->debug;
+ return $this->sendPayloadHTTP10($msg, $this->server, $this->port,
+ $timeout, $this->username,
+ $this->password);
+ }
+
+ function sendPayloadHTTP10($msg, $server, $port, $timeout=0,
+ $username = "", $password = "")
+ {
+ // If we're using a proxy open a socket to the proxy server instead to the xml-rpc server
+ if ($this->proxy){
+ if ($timeout > 0) {
+ $fp = fsockopen($this->proxy, $this->proxy_port, $this->errno, $this->errstr, $timeout);
+ } else {
+ $fp = fsockopen($this->proxy, $this->proxy_port, $this->errno, $this->errstr);
+ }
+ } else {
+ if ($timeout > 0) {
+ $fp = fsockopen($server, $port, $this->errno, $this->errstr, $timeout);
+ } else {
+ $fp = fsockopen($server, $port, $this->errno, $this->errstr);
+ }
+ }
+
+ if (!$fp && $this->proxy) {
+ $this->raiseError(
+ "Connection to proxy server " . $this->proxy . ":" . $this->proxy_port . " failed",
+ XML_RPC_ERROR_CONNECTION_FAILED);
+ } elseif (!$fp) {
+ $this->raiseError(
+ "Connection to RPC server " . $this->server . " failed",
+ XML_RPC_ERROR_CONNECTION_FAILED);
+ }
+
+ // Only create the payload if it was not created previously
+ if (empty($msg->payload)) {
+ $msg->createPayload();
+ }
+
+ // thanks to Grant Rauscher <[email protected]> for this
+ $credentials = "";
+ if ($username != "") {
+ $credentials = "Authorization: Basic " .
+ base64_encode($username . ":" . $password) . "\r\n";
+ }
+
+
+ if ($this->proxy) {
+ $op = "POST http://" . $this->server;
+
+ if ($this->proxy_port) {
+ $op .= ":" . $this->port;
+ }
+ } else {
+ $op = "POST ";
+ }
+
+ $op .= $this->path. " HTTP/1.0\r\n" .
+ "User-Agent: PEAR XML_RPC\r\n" .
+ "Host: " . $this->server . "\r\n";
+ if ($this->proxy && $this->proxy_user != '') {
+ $op .= 'Proxy-Authorization: Basic ' .
+ base64_encode($this->proxy_user . ':' . $this->proxy_pass) .
+ "\r\n";
+ }
+ $op .= $credentials .
+ "Content-Type: text/xml\r\n" .
+ "Content-Length: " . strlen($msg->payload) . "\r\n\r\n" .
+ $msg->payload;
+
+ if (!fputs($fp, $op, strlen($op))) {
+ $this->errstr = "Write error";
+ return 0;
+ }
+ $resp = $msg->parseResponseFile($fp);
+ fclose($fp);
+ return $resp;
+ }
+}
+
+
+class XML_RPC_Response extends XML_RPC_Base
+{
+ var $xv;
+ var $fn;
+ var $fs;
+ var $hdrs;
+
+ function XML_RPC_Response($val, $fcode = 0, $fstr = "")
+ {
+ if ($fcode != 0) {
+ $this->fn = $fcode;
+ $this->fs = htmlspecialchars($fstr);
+ } else {
+ $this->xv = $val;
+ }
+ }
+
+ function faultCode()
+ {
+ if (isset($this->fn)) {
+ return $this->fn;
+ } else {
+ return 0;
+ }
+ }
+
+ function faultString()
+ {
+ return $this->fs;
+ }
+
+ function value()
+ {
+ return $this->xv;
+ }
+
+ function serialize()
+ {
+ $rs = "<methodResponse>\n";
+ if ($this->fn) {
+ $rs .= "<fault>
+ <value>
+ <struct>
+ <member>
+ <name>faultCode</name>
+ <value><int>" . $this->fn . "</int></value>
+ </member>
+ <member>
+ <name>faultString</name>
+ <value><string>" . $this->fs . "</string></value>
+ </member>
+ </struct>
+ </value>
+</fault>";
+ } else {
+ $rs .= "<params>\n<param>\n" . $this->xv->serialize() .
+ "</param>\n</params>";
+ }
+ $rs .= "\n</methodResponse>";
+ return $rs;
+ }
+}
+
+
+class XML_RPC_Message extends XML_RPC_Base
+{
+ var $payload;
+ var $methodname;
+ var $params = array();
+ var $debug = 0;
+
+ function XML_RPC_Message($meth, $pars = 0)
+ {
+ $this->methodname = $meth;
+ if (is_array($pars) && sizeof($pars)>0) {
+ for($i = 0; $i < sizeof($pars); $i++) {
+ $this->addParam($pars[$i]);
+ }
+ }
+ }
+
+ function xml_header()
+ {
+ return "<?xml version=\"1.0\"?>\n<methodCall>\n";
+ }
+
+ function xml_footer()
+ {
+ return "</methodCall>\n";
+ }
+
+ function createPayload()
+ {
+ $this->payload = $this->xml_header();
+ $this->payload .= "<methodName>" . $this->methodname . "</methodName>\n";
+ $this->payload .= "<params>\n";
+ for($i = 0; $i < sizeof($this->params); $i++) {
+ $p = $this->params[$i];
+ $this->payload .= "<param>\n" . $p->serialize() . "</param>\n";
+ }
+ $this->payload .= "</params>\n";
+ $this->payload .= $this->xml_footer();
+ $this->payload = str_replace("\n", "\r\n", $this->payload);
+ }
+
+ function method($meth = "")
+ {
+ if ($meth != "") {
+ $this->methodname = $meth;
+ }
+ return $this->methodname;
+ }
+
+ function serialize()
+ {
+ $this->createPayload();
+ return $this->payload;
+ }
+
+ function addParam($par)
+ {
+ $this->params[] = $par;
+ }
+
+ function getParam($i)
+ {
+ return $this->params[$i];
+ }
+
+ function getNumParams()
+ {
+ return sizeof($this->params);
+ }
+
+ function parseResponseFile($fp)
+ {
+ $ipd = "";
+
+ while($data = fread($fp, 32768)) {
+ $ipd .= $data;
+ }
+ return $this->parseResponse($ipd);
+ }
+
+ function parseResponse($data = "")
+ {
+ global $XML_RPC_xh,$XML_RPC_err,$XML_RPC_str;
+ global $XML_RPC_defencoding;
+
+ $parser = xml_parser_create($XML_RPC_defencoding);
+
+ $XML_RPC_xh[$parser] = array();
+
+ $XML_RPC_xh[$parser]['st'] = "";
+ $XML_RPC_xh[$parser]['cm'] = 0;
+ $XML_RPC_xh[$parser]['isf'] = 0;
+ $XML_RPC_xh[$parser]['ac'] = "";
+ $XML_RPC_xh[$parser]['qt'] = "";
+
+ xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
+ xml_set_element_handler($parser, "XML_RPC_se", "XML_RPC_ee");
+ xml_set_character_data_handler($parser, "XML_RPC_cd");
+ xml_set_default_handler($parser, "XML_RPC_dh");
+ $xmlrpc_value = new XML_RPC_Value;
+
+ $hdrfnd = 0;
+ if ($this->debug) {
+ print "<PRE>---GOT---\n";
+ print isset($_SERVER['SERVER_PROTOCOL']) ? htmlspecialchars($data) : $data;
+ print "\n---END---\n</PRE>";
+ }
+
+ // see if we got an HTTP 200 OK, else bomb
+ // but only do this if we're using the HTTP protocol.
+ if (ereg("^HTTP",$data) &&
+ !ereg("^HTTP/[0-9\.]+ 200 ", $data)) {
+ $errstr = substr($data, 0, strpos($data, "\n")-1);
+ error_log("HTTP error, got response: " . $errstr);
+ $r = new XML_RPC_Response(0, $XML_RPC_err["http_error"],
+ $XML_RPC_str["http_error"] . " (" .
+ $errstr . ")");
+ xml_parser_free($parser);
+ return $r;
+ }
+ // gotta get rid of headers here
+
+
+ if ((!$hdrfnd) && ($brpos = strpos($data,"\r\n\r\n"))) {
+ $XML_RPC_xh[$parser]['ha'] = substr($data, 0, $brpos);
+ $data = substr($data, $brpos + 4);
+ $hdrfnd = 1;
+ }
+
+ // be tolerant of junk after methodResponse (e.g. javascript automatically inserted by free hosts)
+ // thanks to Luca Mariano <[email protected]>
+ $data = substr($data, 0, strpos($data, "</methodResponse>") + 17);
+
+ if (!xml_parse($parser, $data, sizeof($data))) {
+ // thanks to Peter Kocks <[email protected]>
+ if ((xml_get_current_line_number($parser)) == 1) {
+ $errstr = "XML error at line 1, check URL";
+ } else {
+ $errstr = sprintf("XML error: %s at line %d",
+ xml_error_string(xml_get_error_code($parser)),
+ xml_get_current_line_number($parser));
+ }
+ error_log($errstr);
+ $r = new XML_RPC_Response(0, $XML_RPC_err["invalid_return"],
+ $XML_RPC_str["invalid_return"]);
+ xml_parser_free($parser);
+ return $r;
+ }
+ xml_parser_free($parser);
+ if ($this->debug) {
+ print "<PRE>---EVALING---[" .
+ strlen($XML_RPC_xh[$parser]['st']) . " chars]---\n" .
+ htmlspecialchars($XML_RPC_xh[$parser]['st']) . ";\n---END---</PRE>";
+ }
+ if (strlen($XML_RPC_xh[$parser]['st']) == 0) {
+ // then something odd has happened
+ // and it's time to generate a client side error
+ // indicating something odd went on
+ $r = new XML_RPC_Response(0, $XML_RPC_err["invalid_return"],
+ $XML_RPC_str["invalid_return"]);
+ } else {
+ eval('$v=' . $XML_RPC_xh[$parser]['st'] . '; $allOK=1;');
+ if ($XML_RPC_xh[$parser]['isf']) {
+ $f = $v->structmem("faultCode");
+ $fs = $v->structmem("faultString");
+ $r = new XML_RPC_Response($v, $f->scalarval(),
+ $fs->scalarval());
+ } else {
+ $r = new XML_RPC_Response($v);
+ }
+ }
+ $r->hdrs = split("\r?\n", $XML_RPC_xh[$parser]['ha'][1]);
+ return $r;
+ }
+
+}
+
+
+class XML_RPC_Value extends XML_RPC_Base
+{
+ var $me = array();
+ var $mytype = 0;
+
+ function XML_RPC_Value($val = -1, $type = "")
+ {
+ global $XML_RPC_Types;
+ $this->me = array();
+ $this->mytype = 0;
+ if ($val != -1 || $type != "") {
+ if ($type == "") {
+ $type="string";
+ }
+ if ($XML_RPC_Types[$type] == 1) {
+ $this->addScalar($val,$type);
+ } elseif ($XML_RPC_Types[$type] == 2) {
+ $this->addArray($val);
+ } elseif ($XML_RPC_Types[$type] == 3) {
+ $this->addStruct($val);
+ }
+ }
+ }
+
+ function addScalar($val, $type = "string")
+ {
+ global $XML_RPC_Types, $XML_RPC_Boolean;
+
+ if ($this->mytype == 1) {
+ $this->raiseError("Scalar can have only one value", XML_RPC_ERROR_INVALID_TYPE);
+ return 0;
+ }
+ $typeof = $XML_RPC_Types[$type];
+ if ($typeof != 1) {
+ $this->raiseError("Not a scalar type (${typeof})", XML_RPC_ERROR_INVALID_TYPE);
+ return 0;
+ }
+
+ if ($type == $XML_RPC_Boolean) {
+ if (strcasecmp($val,"true") == 0 ||
+ $val == 1 ||
+ ($val == true &&
+ strcasecmp($val,"false"))) {
+
+ $val = 1;
+ } else {
+ $val = 0;
+ }
+ }
+
+ if ($this->mytype == 2) {
+ // we're adding to an array here
+ $ar = $this->me["array"];
+ $ar[] = new XML_RPC_Value($val, $type);
+ $this->me["array"] = $ar;
+ } else {
+ // a scalar, so set the value and remember we're scalar
+ $this->me[$type] = $val;
+ $this->mytype = $typeof;
+ }
+ return 1;
+ }
+
+ function addArray($vals)
+ {
+ global $XML_RPC_Types;
+ if ($this->mytype != 0) {
+ $this->raiseError(
+ "Already initialized as a [" . $this->kindOf() . "]",
+ XML_RPC_ERROR_ALREADY_INITIALIZED);
+ return 0;
+ }
+ $this->mytype = $XML_RPC_Types["array"];
+ $this->me["array"] = $vals;
+ return 1;
+ }
+
+ function addStruct($vals)
+ {
+ global $XML_RPC_Types;
+ if ($this->mytype != 0) {
+ $this->raiseError(
+ "Already initialized as a [" . $this->kindOf() . "]",
+ XML_RPC_ERROR_ALREADY_INITIALIZED);
+ return 0;
+ }
+ $this->mytype = $XML_RPC_Types["struct"];
+ $this->me["struct"] = $vals;
+ return 1;
+ }
+
+ function dump($ar)
+ {
+ reset($ar);
+ while (list( $key, $val ) = each($ar)) {
+ echo "$key => $val<br>";
+ if ($key == 'array') {
+ while ( list( $key2, $val2 ) = each( $val ) ) {
+ echo "-- $key2 => $val2<br>";
+ }
+ }
+ }
+ }
+
+ function kindOf()
+ {
+ switch ($this->mytype) {
+ case 3:
+ return "struct";
+ break;
+ case 2:
+ return "array";
+ break;
+ case 1:
+ return "scalar";
+ break;
+ default:
+ return "undef";
+ }
+ }
+
+ function serializedata($typ, $val)
+ {
+ $rs = "";
+ global $XML_RPC_Types, $XML_RPC_Base64, $XML_RPC_String, $XML_RPC_Boolean;
+ switch ($XML_RPC_Types[$typ]) {
+ case 3:
+ // struct
+ $rs .= "<struct>\n";
+ reset($val);
+ while(list($key2, $val2) = each($val)) {
+ $rs .= "<member><name>${key2}</name>\n";
+ $rs .= $this->serializeval($val2);
+ $rs .= "</member>\n";
+ }
+ $rs .= "</struct>";
+ break;
+ case 2:
+ // array
+ $rs .= "<array>\n<data>\n";
+ for($i = 0; $i < sizeof($val); $i++) {
+ $rs .= $this->serializeval($val[$i]);
+ }
+ $rs .= "</data>\n</array>";
+ break;
+ case 1:
+ switch ($typ) {
+ case $XML_RPC_Base64:
+ $rs .= "<${typ}>" . base64_encode($val) . "</${typ}>";
+ break;
+ case $XML_RPC_Boolean:
+ $rs .= "<${typ}>" . ($val ? "1" : "0") . "</${typ}>";
+ break;
+ case $XML_RPC_String:
+ $rs .= "<${typ}>" . htmlspecialchars($val). "</${typ}>";
+ break;
+ default:
+ $rs .= "<${typ}>${val}</${typ}>";
+ }
+ break;
+ default:
+ break;
+ }
+ return $rs;
+ }
+
+ function serialize()
+ {
+ return $this->serializeval($this);
+ }
+
+ function serializeval($o)
+ {
+ $rs = "";
+ $ar = $o->me;
+ reset($ar);
+ list($typ, $val) = each($ar);
+ $rs .= "<value>";
+ $rs .= $this->serializedata($typ, $val);
+ $rs .= "</value>\n";
+ return $rs;
+ }
+
+ function structmem($m)
+ {
+ $nv = $this->me["struct"][$m];
+ return $nv;
+ }
+
+ function structreset()
+ {
+ reset($this->me["struct"]);
+ }
+
+ function structeach()
+ {
+ return each($this->me["struct"]);
+ }
+
+ function getval() {
+ // UNSTABLE
+ global $XML_RPC_BOOLEAN, $XML_RPC_Base64;
+
+ reset($this->me);
+ list($a,$b) = each($this->me);
+
+ // contributed by I Sofer, 2001-03-24
+ // add support for nested arrays to scalarval
+ // i've created a new method here, so as to
+ // preserve back compatibility
+
+ if (is_array($b)) {
+ foreach ($b as $id => $cont) {
+ $b[$id] = $cont->scalarval();
+ }
+ }
+
+ // add support for structures directly encoding php objects
+ if (is_object($b)) {
+ $t = get_object_vars($b);
+ foreach ($t as $id => $cont) {
+ $t[$id] = $cont->scalarval();
+ }
+ foreach ($t as $id => $cont) {
+ eval('$b->'.$id.' = $cont;');
+ }
+ }
+
+ // end contrib
+ return $b;
+ }
+
+ function scalarval()
+ {
+ global $XML_RPC_Boolean, $XML_RPC_Base64;
+ reset($this->me);
+ list($a,$b) = each($this->me);
+ return $b;
+ }
+
+ function scalartyp()
+ {
+ global $XML_RPC_I4, $XML_RPC_Int;
+ reset($this->me);
+ list($a,$b) = each($this->me);
+ if ($a == $XML_RPC_I4) {
+ $a = $XML_RPC_Int;
+ }
+ return $a;
+ }
+
+ function arraymem($m)
+ {
+ $nv = $this->me["array"][$m];
+ return $nv;
+ }
+
+ function arraysize()
+ {
+ reset($this->me);
+ list($a,$b) = each($this->me);
+ return sizeof($b);
+ }
+}
+
+
+/**
+ * date helpers
+ */
+function XML_RPC_iso8601_encode($timet, $utc = 0) {
+ // return an ISO8601 encoded string
+ // really, timezones ought to be supported
+ // but the XML-RPC spec says:
+ //
+ // "Don't assume a timezone. It should be specified by the server in its
+ // documentation what assumptions it makes about timezones."
+ //
+ // these routines always assume localtime unless
+ // $utc is set to 1, in which case UTC is assumed
+ // and an adjustment for locale is made when encoding
+ if (!$utc) {
+ $t = strftime("%Y%m%dT%H:%M:%S", $timet);
+ } else {
+ if (function_exists("gmstrftime")) {
+ // gmstrftime doesn't exist in some versions
+ // of PHP
+ $t = gmstrftime("%Y%m%dT%H:%M:%S", $timet);
+ } else {
+ $t = strftime("%Y%m%dT%H:%M:%S", $timet - date("Z"));
+ }
+ }
+
+ return $t;
+}
+
+function XML_RPC_iso8601_decode($idate, $utc = 0) {
+ // return a timet in the localtime, or UTC
+ $t = 0;
+ if (ereg("([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})", $idate, $regs)) {
+
+ if ($utc) {
+ $t = gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
+ } else {
+ $t = mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
+ }
+ }
+
+ return $t;
+}
+
+/**
+ * Takes a message in PHP XML_RPC object format and translates it into native PHP types.
+ *
+ * @author Dan Libby <[email protected]>
+ **/
+function XML_RPC_decode($XML_RPC_val) {
+ $kind = $XML_RPC_val->kindOf();
+
+ if ($kind == "scalar") {
+ return $XML_RPC_val->scalarval();
+
+ } elseif ($kind == "array") {
+ $size = $XML_RPC_val->arraysize();
+ $arr = array();
+
+ for($i = 0; $i < $size; $i++) {
+ $arr[] = XML_RPC_decode($XML_RPC_val->arraymem($i));
+ }
+ return $arr;
+
+ } elseif ($kind == "struct") {
+ $XML_RPC_val->structreset();
+ $arr = array();
+
+ while(list($key,$value) = $XML_RPC_val->structeach()) {
+ $arr[$key] = XML_RPC_decode($value);
+ }
+ return $arr;
+ }
+}
+
+/**
+ * Takes native php types and encodes them into XML_RPC PHP object format.
+ *
+ * Feature creep -- could support more types via optional type argument.
+ *
+ * @author Dan Libby <[email protected]>
+ **/
+function XML_RPC_encode($php_val) {
+ global $XML_RPC_Boolean;
+ global $XML_RPC_Int;
+ global $XML_RPC_Double;
+ global $XML_RPC_String;
+ global $XML_RPC_Array;
+ global $XML_RPC_Struct;
+
+ $type = gettype($php_val);
+ $XML_RPC_val = new XML_RPC_Value;
+
+ switch ($type) {
+ case "array":
+ $keys = array_keys($php_val);
+ $count = count($php_val);
+ $firstkey = $keys[0];
+ $lastkey = $keys[$count - 1];
+ if ($firstkey === 0 && is_int($lastkey) && ($lastkey + 1) == $count) {
+ $is_continuous = true;
+ $expected = 0;
+ foreach ($keys as $actual) {
+ if ($actual != $expected) {
+ $is_continuous = false;
+ break;
+ }
+ $expected++;
+ }
+
+ if ($is_continuous) {
+ $arr = array();
+ foreach ($php_val as $k => $v) {
+ $arr[$k] = XML_RPC_encode($v);
+ }
+ $XML_RPC_val->addArray($arr);
+ break;
+ }
+ }
+ // fall though if not numerical and continuous
+ case "object":
+ $arr = array();
+ foreach ($php_val as $k => $v) {
+ $arr[$k] = XML_RPC_encode($v);
+ }
+ $XML_RPC_val->addStruct($arr);
+ break;
+
+ case "integer":
+ $XML_RPC_val->addScalar($php_val, $XML_RPC_Int);
+ break;
+
+ case "double":
+ $XML_RPC_val->addScalar($php_val, $XML_RPC_Double);
+ break;
+
+ case "string":
+ case "NULL":
+ $XML_RPC_val->addScalar($php_val, $XML_RPC_String);
+ break;
+
+ // <G_Giunta_2001-02-29>
+ // Add support for encoding/decoding of booleans, since they are supported in PHP
+ case "boolean":
+ $XML_RPC_val->addScalar($php_val, $XML_RPC_Boolean);
+ break;
+ // </G_Giunta_2001-02-29>
+
+ case "unknown type":
+ default:
+ $XML_RPC_val = false;
+ break;
+ }
+ return $XML_RPC_val;
+}
+
+?>
diff --git a/pearlib/XML/RPC/Server.php b/pearlib/XML/RPC/Server.php
new file mode 100644
index 00000000..05e8ca73
--- /dev/null
+++ b/pearlib/XML/RPC/Server.php
@@ -0,0 +1,310 @@
+<?php
+// /* vim: set expandtab tabstop=4 shiftwidth=4: */
+// by Edd Dumbill (C) 1999,2000
+
+// License is granted to use or modify this software ("XML-RPC for PHP")
+// for commercial or non-commercial use provided the copyright of the author
+// is preserved in any distributed or derivative work.
+
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR
+// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Adapted to PEAR standards by Stig S�her Bakken <[email protected]>
+// /* $Id: Server.php,v 1.6 2004/03/15 13:58:39 pajoye Exp $ */
+
+require_once "XML/RPC.php";
+
+// listMethods: either a string, or nothing
+$GLOBALS['XML_RPC_Server_listMethods_sig'] =
+ array(array($GLOBALS['XML_RPC_Array'], $GLOBALS['XML_RPC_String']),
+ array($GLOBALS['XML_RPC_Array']));
+$GLOBALS['XML_RPC_Server_listMethods_doc'] =
+ 'This method lists all the methods that the XML-RPC server knows how to dispatch';
+
+function XML_RPC_Server_listMethods($server, $m)
+{
+ global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap;
+ $v = new XML_RPC_Value();
+ $dmap = $server->dmap;
+ $outAr = array();
+ for (reset($dmap); list($key, $val) = each($dmap); ) {
+ $outAr[] = new XML_RPC_Value($key, "string");
+ }
+ $dmap = $XML_RPC_Server_dmap;
+ for (reset($dmap); list($key, $val) = each($dmap); ) {
+ $outAr[] = new XML_RPC_Value($key, "string");
+ }
+ $v->addArray($outAr);
+ return new XML_RPC_Response($v);
+}
+
+$GLOBALS['XML_RPC_Server_methodSignature_sig'] =
+ array(array($GLOBALS['XML_RPC_Array'], $GLOBALS['XML_RPC_String']));
+$GLOBALS['XML_RPC_Server_methodSignature_doc'] =
+ 'Returns an array of known signatures (an array of arrays) for the method name passed. If no signatures are known, returns a none-array (test for type != array to detect missing signature)';
+
+function XML_RPC_Server_methodSignature($server, $m)
+{
+ global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap;
+
+ $methName = $m->getParam(0);
+ $methName = $methName->scalarval();
+ if (ereg("^system\.", $methName)) {
+ $dmap = $XML_RPC_Server_dmap;
+ $sysCall = 1;
+ } else {
+ $dmap = $server->dmap;
+ $sysCall = 0;
+ }
+ // print "<!-- ${methName} -->\n";
+ if (isset($dmap[$methName])) {
+ if ($dmap[$methName]["signature"]) {
+ $sigs = array();
+ $thesigs = $dmap[$methName]["signature"];
+ for ($i = 0; $i < sizeof($thesigs); $i++) {
+ $cursig = array();
+ $inSig = $thesigs[$i];
+ for ($j = 0; $j < sizeof($inSig); $j++) {
+ $cursig[] = new XML_RPC_Value($inSig[$j], "string");
+ }
+ $sigs[] = new XML_RPC_Value($cursig, "array");
+ }
+ $r = new XML_RPC_Response(new XML_RPC_Value($sigs, "array"));
+ } else {
+ $r = new XML_RPC_Response(new XML_RPC_Value("undef", "string"));
+ }
+ } else {
+ $r = new XML_RPC_Response(0, $XML_RPC_err["introspect_unknown"],
+ $XML_RPC_str["introspect_unknown"]);
+ }
+ return $r;
+}
+
+$GLOBALS['XML_RPC_Server_methodHelp_sig'] =
+ array(array($GLOBALS['XML_RPC_String'], $GLOBALS['XML_RPC_String']));
+$GLOBALS['XML_RPC_Server_methodHelp_doc'] =
+ 'Returns help text if defined for the method passed, otherwise returns an empty string';
+
+function XML_RPC_Server_methodHelp($server, $m)
+{
+ global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap;
+
+ $methName = $m->getParam(0);
+ $methName = $methName->scalarval();
+ if (ereg("^system\.", $methName)) {
+ $dmap = $XML_RPC_Server_dmap;
+ $sysCall = 1;
+ } else {
+ $dmap = $server->dmap;
+ $sysCall = 0;
+ }
+ // print "<!-- ${methName} -->\n";
+ if (isset($dmap[$methName])) {
+ if ($dmap[$methName]["docstring"]) {
+ $r = new XML_RPC_Response(new XML_RPC_Value($dmap[$methName]["docstring"]), "string");
+ } else {
+ $r = new XML_RPC_Response(new XML_RPC_Value("", "string"));
+ }
+ } else {
+ $r = new XML_RPC_Response(0, $XML_RPC_err["introspect_unknown"],
+ $XML_RPC_str["introspect_unknown"]);
+ }
+ return $r;
+}
+
+$GLOBALS['XML_RPC_Server_dmap'] = array(
+ "system.listMethods" =>
+ array("function" => "XML_RPC_Server_listMethods",
+ "signature" => $GLOBALS['XML_RPC_Server_listMethods_sig'],
+ "docstring" => $GLOBALS['XML_RPC_Server_listMethods_doc']),
+
+ "system.methodHelp" =>
+ array("function" => "XML_RPC_Server_methodHelp",
+ "signature" => $GLOBALS['XML_RPC_Server_methodHelp_sig'],
+ "docstring" => $GLOBALS['XML_RPC_Server_methodHelp_doc']),
+
+ "system.methodSignature" =>
+ array("function" => "XML_RPC_Server_methodSignature",
+ "signature" => $GLOBALS['XML_RPC_Server_methodSignature_sig'],
+ "docstring" => $GLOBALS['XML_RPC_Server_methodSignature_doc'])
+);
+
+$GLOBALS['XML_RPC_Server_debuginfo'] = "";
+
+function XML_RPC_Server_debugmsg($m)
+{
+ global $XML_RPC_Server_debuginfo;
+ $XML_RPC_Server_debuginfo = $XML_RPC_Server_debuginfo . $m . "\n";
+}
+
+class XML_RPC_Server
+{
+ var $dmap = array();
+
+ function XML_RPC_Server($dispMap, $serviceNow = 1)
+ {
+ global $HTTP_RAW_POST_DATA;
+ // dispMap is a despatch array of methods
+ // mapped to function names and signatures
+ // if a method
+ // doesn't appear in the map then an unknown
+ // method error is generated
+ $this->dmap = $dispMap;
+ if ($serviceNow) {
+ $this->service();
+ }
+ }
+
+ function serializeDebug()
+ {
+ global $XML_RPC_Server_debuginfo;
+ if ($XML_RPC_Server_debuginfo != "")
+ return "<!-- DEBUG INFO:\n\n" . $XML_RPC_Server_debuginfo . "\n-->\n";
+ else
+ return "";
+ }
+
+ function service()
+ {
+ $r = $this->parseRequest();
+ $payload = "<?xml version=\"1.0\"?>\n" .
+ $this->serializeDebug() .
+ $r->serialize();
+ header('Content-Length: ' . strlen($payload));
+ header('Content-Type: text/xml');
+ print $payload;
+ }
+
+ function verifySignature($in, $sig)
+ {
+ for ($i = 0; $i < sizeof($sig); $i++) {
+ // check each possible signature in turn
+ $cursig = $sig[$i];
+ if (sizeof($cursig) == $in->getNumParams() + 1) {
+ $itsOK = 1;
+ for ($n = 0; $n < $in->getNumParams(); $n++) {
+ $p = $in->getParam($n);
+ // print "<!-- $p -->\n";
+ if ($p->kindOf() == "scalar") {
+ $pt = $p->scalartyp();
+ } else {
+ $pt = $p->kindOf();
+ }
+ // $n+1 as first type of sig is return type
+ if ($pt != $cursig[$n+1]) {
+ $itsOK = 0;
+ $pno = $n+1;
+ $wanted = $cursig[$n+1];
+ $got = $pt;
+ break;
+ }
+ }
+ if ($itsOK)
+ return array(1);
+ }
+ }
+ return array(0, "Wanted ${wanted}, got ${got} at param ${pno})");
+ }
+
+ function parseRequest($data = "")
+ {
+ global $XML_RPC_xh,$HTTP_RAW_POST_DATA;
+ global $XML_RPC_err, $XML_RPC_str, $XML_RPC_errxml,
+ $XML_RPC_defencoding, $XML_RPC_Server_dmap;
+
+ if ($data == "") {
+ $data = $HTTP_RAW_POST_DATA;
+ }
+ $parser = xml_parser_create($XML_RPC_defencoding);
+
+ $XML_RPC_xh[$parser] = array();
+ $XML_RPC_xh[$parser]['st'] = "";
+ $XML_RPC_xh[$parser]['cm'] = 0;
+ $XML_RPC_xh[$parser]['isf'] = 0;
+ $XML_RPC_xh[$parser]['params'] = array();
+ $XML_RPC_xh[$parser]['method'] = "";
+
+ $plist = '';
+
+ // decompose incoming XML into request structure
+
+ xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
+ xml_set_element_handler($parser, "XML_RPC_se", "XML_RPC_ee");
+ xml_set_character_data_handler($parser, "XML_RPC_cd");
+ xml_set_default_handler($parser, "XML_RPC_dh");
+ if (!xml_parse($parser, $data, 1)) {
+ // return XML error as a faultCode
+ $r = new XML_RPC_Response(0,
+ $XML_RPC_errxml+xml_get_error_code($parser),
+ sprintf("XML error: %s at line %d",
+ xml_error_string(xml_get_error_code($parser)),
+ xml_get_current_line_number($parser)));
+ xml_parser_free($parser);
+ } else {
+ xml_parser_free($parser);
+ $m = new XML_RPC_Message($XML_RPC_xh[$parser]['method']);
+ // now add parameters in
+ for ($i = 0; $i < sizeof($XML_RPC_xh[$parser]['params']); $i++) {
+ // print "<!-- " . $XML_RPC_xh[$parser]['params'][$i]. "-->\n";
+ $plist .= "$i - " . $XML_RPC_xh[$parser]['params'][$i] . " \n";
+ eval('$m->addParam(' . $XML_RPC_xh[$parser]['params'][$i] . ");");
+ }
+ XML_RPC_Server_debugmsg($plist);
+ // now to deal with the method
+ $methName = $XML_RPC_xh[$parser]['method'];
+ if (ereg("^system\.", $methName)) {
+ $dmap = $XML_RPC_Server_dmap;
+ $sysCall = 1;
+ } else {
+ $dmap = $this->dmap;
+ $sysCall = 0;
+ }
+ if (isset($dmap[$methName]['function'])) {
+ // dispatch if exists
+ if (isset($dmap[$methName]['signature'])) {
+ $sr = $this->verifySignature($m,
+ $dmap[$methName]['signature'] );
+ }
+ if ( (!isset($dmap[$methName]['signature'])) || $sr[0]) {
+ // if no signature or correct signature
+ if ($sysCall) {
+ eval('$r=' . $dmap[$methName]['function'] . '($this, $m);');
+ } else {
+ eval('$r=' . $dmap[$methName]['function'] . '($m);');
+ }
+ } else {
+ $r = new XML_RPC_Response(0, $XML_RPC_err["incorrect_params"],
+ $XML_RPC_str["incorrect_params"] .
+ ": " . $sr[1]);
+ }
+ } else {
+ // else prepare error response
+ $r = new XML_RPC_Response(0, $XML_RPC_err["unknown_method"],
+ $XML_RPC_str["unknown_method"]);
+ }
+ }
+ return $r;
+ }
+
+ function echoInput() {
+ global $HTTP_RAW_POST_DATA;
+
+ // a debugging routine: just echos back the input
+ // packet as a string value
+
+ $r = new XML_RPC_Response;
+ $r->xv = new XML_RPC_Value("'Aha said I: '" . $HTTP_RAW_POST_DATA, "string");
+ print $r->serialize();
+ }
+}
+
+?>
diff --git a/pearlib/XML/Serializer.php b/pearlib/XML/Serializer.php
new file mode 100644
index 00000000..f798d5b0
--- /dev/null
+++ b/pearlib/XML/Serializer.php
@@ -0,0 +1,508 @@
+<?PHP
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2002 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stephan Schmidt <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Serializer.php,v 1.14 2003/10/26 19:38:21 schst Exp $
+
+/**
+ * uses PEAR error management
+ */
+require_once 'PEAR.php';
+
+/**
+ * uses XML_Util to create XML tags
+ */
+require_once 'XML/Util.php';
+
+/**
+ * error code for no serialization done
+ */
+define("XML_SERIALIZER_ERROR_NO_SERIALIZATION", 51);
+
+/**
+ * XML_Serializer
+ * class that serializes various structures into an XML document
+ *
+ * this class can be used in two modes:
+ *
+ * 1. create an XML document from an array or object that is processed by other
+ * applications. That means, you can create a RDF document from an array in the
+ * following format:
+ *
+ * $data = array(
+ * "channel" => array(
+ * "title" => "Example RDF channel",
+ * "link" => "http://www.php-tools.de",
+ * "image" => array(
+ * "title" => "Example image",
+ * "url" => "http://www.php-tools.de/image.gif",
+ * "link" => "http://www.php-tools.de"
+ * ),
+ * array(
+ * "title" => "Example item",
+ * "link" => "http://example.com"
+ * ),
+ * array(
+ * "title" => "Another Example item",
+ * "link" => "http://example.org"
+ * )
+ * )
+ * );
+ *
+ * to create a RDF document from this array do the following:
+ *
+ * require_once 'XML/Serializer.php';
+ *
+ * $options = array(
+ * "indent" => "\t", // indent with tabs
+ * "linebreak" => "\n", // use UNIX line breaks
+ * "rootName" => "rdf:RDF", // root tag
+ * "defaultTagName" => "item" // tag for values with numeric keys
+ * );
+ *
+ * $serializer = new XML_Serializer($options);
+ * $rdf = $serializer->serialize($data);
+ *
+ * You will get a complete XML document that can be processed like any RDF document.
+ *
+ *
+ * 2. this classes can be used to serialize any data structure in a way that it can
+ * later be unserialized again.
+ * XML_Serializer will store the type of the value and additional meta information
+ * in attributes of the surrounding tag. This meat information can later be used
+ * to restore the original data structure in PHP. If you want XML_Serializer
+ * to add meta information to the tags, add
+ *
+ * "typeHints" => true
+ *
+ * to the options array in the constructor.
+ *
+ * Future versions of this package will include an XML_Unserializer, that does
+ * the unserialization automatically for you.
+ *
+ * @category XML
+ * @package XML_Serializer
+ * @version 0.9.1
+ * @author Stephan Schmidt <[email protected]>
+ * @uses XML_Util
+ */
+class XML_Serializer extends PEAR {
+
+ /**
+ * default options for the serialization
+ * @access private
+ * @var array $_defaultOptions
+ */
+ var $_defaultOptions = array(
+ "indent" => "", // string used for indentation
+ "linebreak" => "\n", // string used for newlines
+ "typeHints" => false, // automatically add type hin attributes
+ "addDecl" => false, // add an XML declaration
+ "defaultTagName" => "XML_Serializer_Tag", // tag used for indexed arrays or invalid names
+ "keyAttribute" => "_originalKey", // attribute where original key is stored
+ "typeAttribute" => "_type", // attribute for type (only if typeHints => true)
+ "classAttribute" => "_class", // attribute for class of objects (only if typeHints => true)
+ "scalarAsAttributes" => false, // scalar values (strings, ints,..) will be serialized as attribute
+ "prependAttributes" => "", // prepend string for attributes
+ "indentAttributes" => false, // indent the attributes, if set to '_auto', it will indent attributes so they all start at the same column
+ "mode" => 'default', // use 'simplexml' to use parent name as tagname if transforming an indexed array
+ "addDoctype" => false, // add a doctype declaration
+ "doctype" => null, // supply a string or an array with id and uri ({@see XML_Util::getDoctypeDeclaration()}
+ "rootAttributes" => array() // attributes of the root tag
+ );
+
+ /**
+ * options for the serialization
+ * @access private
+ * @var array $options
+ */
+ var $options = array();
+
+ /**
+ * current tag depth
+ * @var integer $_tagDepth
+ */
+ var $_tagDepth = 0;
+
+ /**
+ * serilialized representation of the data
+ * @var string $_serializedData
+ */
+ var $_serializedData = null;
+
+ /**
+ * constructor
+ *
+ * @access public
+ * @param mixed $options array containing options for the serialization
+ */
+ function XML_Serializer( $options = null )
+ {
+ $this->PEAR();
+ if (is_array($options)) {
+ $this->options = array_merge($this->_defaultOptions, $options);
+ } else {
+ $this->options = $this->_defaultOptions;
+ }
+ }
+
+ /**
+ * return API version
+ *
+ * @access public
+ * @static
+ * @return string $version API version
+ */
+ function apiVersion()
+ {
+ return "0.9";
+ }
+
+ /**
+ * reset all options to default options
+ *
+ * @access public
+ * @see setOption(), XML_Unserializer()
+ */
+ function resetOptions()
+ {
+ $this->options = $this->_defaultOptions;
+ }
+
+ /**
+ * set an option
+ *
+ * You can use this method if you do not want to set all options in the constructor
+ *
+ * @access public
+ * @see resetOption(), XML_Serializer()
+ */
+ function setOption($name, $value)
+ {
+ $this->options[$name] = $value;
+ }
+
+ /**
+ * serialize data
+ *
+ * @access public
+ * @param mixed $data data to serialize
+ * @return boolean true on success, pear error on failure
+ */
+ function serialize($data, $options = null)
+ {
+ // if options have been specified, use them instead
+ // of the previously defined ones
+ if (is_array($options)) {
+ $optionsBak = $this->options;
+ if (isset($options["overrideOptions"]) && $options["overrideOptions"] == true) {
+ $this->options = array_merge($this->_defaultOptions, $options);
+ } else {
+ $this->options = array_merge($this->options, $options);
+ }
+ }
+ else {
+ $optionsBak = null;
+ }
+
+ // maintain BC
+ if (isset($this->options["tagName"])) {
+ $this->options["rootName"] = $this->options["tagName"];
+ }
+
+ // start depth is zero
+ $this->_tagDepth = 0;
+
+ $this->_serializedData = "";
+ // serialize an array
+ if (is_array($data)) {
+ if (isset($this->options["rootName"])) {
+ $tagName = $this->options["rootName"];
+ } else {
+ $tagName = "array";
+ }
+
+ $this->_serializedData .= $this->_serializeArray($data, $tagName, $this->options["rootAttributes"]);
+ }
+ // serialize an object
+ elseif (is_object($data)) {
+ if (isset($this->options["rootName"])) {
+ $tagName = $this->options["rootName"];
+ } else {
+ $tagName = get_class($data);
+ }
+ $this->_serializedData .= $this->_serializeObject($data, $tagName, $this->options["rootAttributes"]);
+ }
+
+ // add doctype declaration
+ if ($this->options["addDoctype"] === true) {
+ $this->_serializedData = XML_Util::getDoctypeDeclaration($tagName, $this->options["doctype"])
+ . $this->options["linebreak"]
+ . $this->_serializedData;
+ }
+
+ // build xml declaration
+ if ($this->options["addDecl"]) {
+ $atts = array();
+ if (isset($this->options["encoding"]) ) {
+ $encoding = $this->options["encoding"];
+ } else {
+ $encoding = null;
+ }
+ $this->_serializedData = XML_Util::getXMLDeclaration("1.0", $encoding)
+ . $this->options["linebreak"]
+ . $this->_serializedData;
+ }
+
+
+ if ($optionsBak !== null) {
+ $this->options = $optionsBak;
+ }
+
+ return true;
+ }
+
+ /**
+ * get the result of the serialization
+ *
+ * @access public
+ * @return string $serializedData
+ */
+ function getSerializedData()
+ {
+ if ($this->_serializedData == null ) {
+ return $this->raiseError("No serialized data available. Use XML_Serializer::serialize() first.", XML_SERIALIZER_ERROR_NO_SERIALIZATION);
+ }
+ return $this->_serializedData;
+ }
+
+ /**
+ * serialize any value
+ *
+ * This method checks for the type of the value and calls the appropriate method
+ *
+ * @access private
+ * @param mixed $value
+ * @param string $tagName
+ * @param array $attributes
+ * @return string
+ */
+ function _serializeValue($value, $tagName = null, $attributes = array())
+ {
+ if (is_array($value)) {
+ $xml = $this->_serializeArray($value, $tagName, $attributes);
+ } elseif (is_object($value)) {
+ $xml = $this->_serializeObject($value, $tagName);
+ } else {
+ $tag = array(
+ "qname" => $tagName,
+ "attributes" => $attributes,
+ "content" => $value
+ );
+ $xml = $this->_createXMLTag($tag);
+ }
+ return $xml;
+ }
+
+ /**
+ * serialize an array
+ *
+ * @access private
+ * @param array $array array to serialize
+ * @param string $tagName name of the root tag
+ * @param array $attributes attributes for the root tag
+ * @return string $string serialized data
+ * @uses XML_Util::isValidName() to check, whether key has to be substituted
+ */
+ function _serializeArray(&$array, $tagName = null, $attributes = array())
+ {
+ if (is_array($array) && $this->options["mode"] == "simplexml") {
+ $indexed = true;
+ foreach ($array as $key => $val) {
+ if (!is_int($key)) {
+ $indexed = false;
+ break;
+ }
+ }
+
+ if ($indexed) {
+ $string = "";
+ foreach ($array as $key => $val) {
+ $string .= $this->_serializeValue( $val, $tagName, $attributes);
+
+ $string .= $this->options["linebreak"];
+ // do indentation
+ if ($this->options["indent"]!==null && $this->_tagDepth>0) {
+ $string .= str_repeat($this->options["indent"], $this->_tagDepth);
+ }
+ }
+ return rtrim($string);
+ }
+ }
+
+ $this->_tagDepth++;
+
+ if ($this->options["scalarAsAttributes"] === true) {
+ foreach ($array as $key => $value) {
+ if (is_scalar($value) && (XML_Util::isValidName($key) === true)) {
+ unset($array[$key]);
+ $attributes[$this->options["prependAttributes"].$key] = $value;
+ }
+ }
+ }
+
+ // check for empty array => create empty tag
+ if (empty($array)) {
+
+ }
+
+ $tmp = $this->options["linebreak"];
+ foreach ($array as $key => $value) {
+ // do indentation
+ if ($this->options["indent"]!==null && $this->_tagDepth>0) {
+ $tmp .= str_repeat($this->options["indent"], $this->_tagDepth);
+ }
+
+ // copy key
+ $origKey = $key;
+ // key cannot be used as tagname => use default tag
+ $valid = XML_Util::isValidName($key);
+ if (PEAR::isError($valid)) {
+ $key = $this->options["defaultTagName"];
+ }
+ $atts = array();
+ if ($this->options["typeHints"] === true) {
+ $atts[$this->options["typeAttribute"]] = gettype($value);
+ if ($key !== $origKey) {
+ $atts[$this->options["keyAttribute"]] = (string)$origKey;
+ }
+
+ }
+
+ $tmp .= $this->_createXMLTag(array(
+ "qname" => $key,
+ "attributes" => $atts,
+ "content" => $value )
+ );
+ $tmp .= $this->options["linebreak"];
+ }
+
+ $this->_tagDepth--;
+ if ($this->options["indent"]!==null && $this->_tagDepth>0) {
+ $tmp .= str_repeat($this->options["indent"], $this->_tagDepth);
+ }
+
+ if (trim($tmp) === '') {
+ $tmp = null;
+ }
+
+ $tag = array(
+ "qname" => $tagName,
+ "content" => $tmp,
+ "attributes" => $attributes
+ );
+ if ($this->options["typeHints"] === true) {
+ if (!isset($tag["attributes"][$this->options["typeAttribute"]])) {
+ $tag["attributes"][$this->options["typeAttribute"]] = "array";
+ }
+ }
+
+ $string = $this->_createXMLTag($tag, false);
+ return $string;
+ }
+
+ /**
+ * serialize an object
+ *
+ * @access private
+ * @param object $object object to serialize
+ * @return string $string serialized data
+ */
+ function _serializeObject(&$object, $tagName = null, $attributes = array())
+ {
+ // check for magic function
+ if (method_exists($object, "__sleep")) {
+ $object->__sleep();
+ }
+
+ $tmp = $this->options["linebreak"];
+ $properties = get_object_vars($object);
+ if (empty($tagName)) {
+ $tagName = get_class($object);
+ }
+
+ // typehints activated?
+ if ($this->options["typeHints"] === true) {
+ $attributes[$this->options["typeAttribute"]] = "object";
+ $attributes[$this->options["classAttribute"]] = get_class($object);
+ }
+
+ $string = $this->_serializeArray($properties, $tagName, $attributes);
+ return $string;
+ }
+
+ /**
+ * create a tag from an array
+ * this method awaits an array in the following format
+ * array(
+ * "qname" => $tagName,
+ * "attributes" => array(),
+ * "content" => $content, // optional
+ * "namespace" => $namespace // optional
+ * "namespaceUri" => $namespaceUri // optional
+ * )
+ *
+ * @access private
+ * @param array $tag tag definition
+ * @param boolean $replaceEntities whether to replace XML entities in content or not
+ * @return string $string XML tag
+ */
+ function _createXMLTag( $tag, $replaceEntities = true )
+ {
+ if ($this->options["indentAttributes"] !== false) {
+ $multiline = true;
+ $indent = str_repeat($this->options["indent"], $this->_tagDepth);
+
+ if ($this->options["indentAttributes"] == "_auto") {
+ $indent .= str_repeat(" ", (strlen($tag["qname"])+2));
+
+ } else {
+ $indent .= $this->options["indentAttributes"];
+ }
+ } else {
+ $multiline = false;
+ $indent = false;
+ }
+
+ if ((string)$tag["content"] == '') {
+ $tag["content"] = '';
+ }
+
+ if (is_scalar($tag["content"])) {
+ $tag = XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $this->options["linebreak"]);
+ } elseif (is_array($tag["content"])) {
+ $tag = $this->_serializeArray($tag["content"], $tag["qname"], $tag["attributes"]);
+ } elseif (is_object($tag["content"])) {
+ $tag = $this->_serializeObject($tag["content"], $tag["qname"], $tag["attributes"]);
+ } elseif (is_resource($tag["content"])) {
+ settype($tag["content"], "string");
+ $tag = XML_Util::createTagFromArray($tag, $replaceEntities);
+ }
+ return $tag;
+ }
+}
+?> \ No newline at end of file
diff --git a/pearlib/XML/Unserializer.php b/pearlib/XML/Unserializer.php
new file mode 100644
index 00000000..d7fadbd9
--- /dev/null
+++ b/pearlib/XML/Unserializer.php
@@ -0,0 +1,491 @@
+<?PHP
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2002 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stephan Schmidt <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Unserializer.php,v 1.12 2003/10/26 19:38:21 schst Exp $
+
+/**
+ * uses PEAR error managemt
+ */
+require_once 'PEAR.php';
+
+/**
+ * uses XML_Parser to unserialize document
+ */
+require_once 'XML/Parser.php';
+
+/**
+ * error code for no serialization done
+ */
+define("XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION", 151);
+
+/**
+ * XML_Unserializer
+ *
+ * class to unserialize XML documents that have been created with
+ * XML_Serializer. To unserialize an XML document you have to add
+ * type hints to the XML_Serializer options.
+ *
+ * If no type hints are available, XML_Unserializer will guess how
+ * the tags should be treated, that means complex structures will be
+ * arrays and tags with only CData in them will be strings.
+ *
+ * <code>
+ * require_once 'XML/Unserializer.php';
+ *
+ * // be careful to always use the ampersand in front of the new operator
+ * $unserializer = &new XML_Unserializer();
+ *
+ * $unserializer->unserialize($xml);
+ *
+ * $data = $unserializer->getUnserializedData();
+ * <code>
+ *
+ * Possible options for the Unserializer are:
+ *
+ * 1. complexTypes => array|object
+ * This is needed, when unserializing XML files w/o type hints. If set to
+ * 'array' (default), all nested tags will be arrays, if set to 'object'
+ * all nested tags will be objects, that means you have read access like:
+ *
+ * <code>
+ * require_once 'XML/Unserializer.php';
+ * $options = array('complexType' => 'object');
+ * $unserializer = &new XML_Unserializer($options);
+ *
+ * $unserializer->unserialize('http://pear.php.net/rss.php');
+ *
+ * $rss = $unserializer->getUnserializedData();
+ * echo $rss->channel->item[3]->title;
+ * </code>
+ *
+ * 2. keyAttribute
+ * This lets you specify an attribute inside your tags, that are used as key
+ * for associative arrays or object properties.
+ * You will need this if you have XML that looks like this:
+ *
+ * <users>
+ * <user handle="schst">Stephan Schmidt</user>
+ * <user handle="ssb">Stig S. Bakken</user>
+ * </users>
+ *
+ * Then you can use:
+ * <code>
+ * require_once 'XML/Unserializer.php';
+ * $options = array('keyAttribute' => 'handle');
+ * $unserializer = &new XML_Unserializer($options);
+ *
+ * $unserializer->unserialize($xml, false);
+ *
+ * $users = $unserializer->getUnserializedData();
+ * </code>
+ *
+ * @category XML
+ * @package XML_Serializer
+ * @version 0.9.1
+ * @author Stephan Schmidt <[email protected]>
+ * @uses XML_Parser
+ */
+class XML_Unserializer extends XML_Parser {
+
+ /**
+ * default options for the serialization
+ * @access private
+ * @var array $_defaultOptions
+ */
+ var $_defaultOptions = array(
+ "complexType" => "array", // complex types will be converted to arrays, if no type hint is given
+ "keyAttribute" => "_originalKey", // get array key/property name from this attribute
+ "typeAttribute" => "_type", // get type from this attribute
+ "classAttribute" => "_class", // get class from this attribute (if not given, use tag name)
+ "parseAttributes" => false, // parse the attributes of the tag into an array
+ "attributesArray" => false, // parse them into sperate array (specify name of array here)
+ "prependAttributes" => "", // prepend attribute names with this string
+ "contentName" => "_content", // put cdata found in a tag that has been converted to a complex type in this key
+ "tagMap" => array() // use this to map tagnames
+ );
+
+ /**
+ * actual options for the serialization
+ * @access private
+ * @var array $options
+ */
+ var $options = array();
+
+ /**
+ * do not use case folding
+ * @var boolean $folding
+ */
+ var $folding = false;
+
+ /**
+ * unserilialized data
+ * @var string $_serializedData
+ */
+ var $_unserializedData = null;
+
+ /**
+ * name of the root tag
+ * @var string $_root
+ */
+ var $_root = null;
+
+ /**
+ * stack for all data that is found
+ * @var array $_dataStack
+ */
+ var $_dataStack = array();
+
+ /**
+ * stack for all values that are generated
+ * @var array $_valStack
+ */
+ var $_valStack = array();
+
+ /**
+ * current tag depth
+ * @var int $_depth
+ */
+ var $_depth = 0;
+
+ /**
+ * constructor
+ *
+ * @access public
+ * @param mixed $options array containing options for the serialization
+ */
+ function XML_Unserializer($options = null)
+ {
+ if (is_array($options)) {
+ $this->options = array_merge($this->_defaultOptions, $options);
+ } else {
+ $this->options = $this->_defaultOptions;
+ }
+ }
+
+ /**
+ * return API version
+ *
+ * @access public
+ * @static
+ * @return string $version API version
+ */
+ function apiVersion()
+ {
+ return "0.9";
+ }
+
+ /**
+ * reset all options to default options
+ *
+ * @access public
+ * @see setOption(), XML_Unserializer()
+ */
+ function resetOptions()
+ {
+ $this->options = $this->_defaultOptions;
+ }
+
+ /**
+ * set an option
+ *
+ * You can use this method if you do not want to set all options in the constructor
+ *
+ * @access public
+ * @see resetOption(), XML_Unserializer()
+ */
+ function setOption($name, $value)
+ {
+ $this->options[$name] = $value;
+ }
+
+ /**
+ * unserialize data
+ *
+ * @access public
+ * @param mixed $data data to unserialize (string, filename or resource)
+ * @param boolean $isFile string should be treated as a file
+ * @param array $options
+ * @return boolean $success
+ */
+ function unserialize($data, $isFile = false, $options = null)
+ {
+ // reset parser and properties
+ $this->XML_Parser(null,"event");
+ $this->_unserializedData = null;
+ $this->_root = null;
+
+ // if options have been specified, use them instead
+ // of the previously defined ones
+ if (is_array($options)) {
+ $optionsBak = $this->options;
+ if (isset($options["overrideOptions"]) && $options["overrideOptions"] == true) {
+ $this->options = array_merge($this->_defaultOptions, $options);
+ } else {
+ $this->options = array_merge($this->options, $options);
+ }
+ }
+ else {
+ $optionsBak = null;
+ }
+
+ $this->_valStack = array();
+ $this->_dataStack = array();
+ $this->_depth = 0;
+
+ if (is_string($data)) {
+ if ($isFile) {
+ $result = $this->setInputFile($data);
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+ $result = $this->parse();
+ } else {
+ $result = $this->parseString($data,true);
+ }
+ } else {
+ $this->setInput($data);
+ $result = $this->parse();
+ }
+
+ if ($optionsBak !== null) {
+ $this->options = $optionsBak;
+ }
+
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+
+ return true;
+ }
+
+ /**
+ * get the result of the serialization
+ *
+ * @access public
+ * @return string $serializedData
+ */
+ function getUnserializedData()
+ {
+ if ($this->_root === null ) {
+ return $this->raiseError("No unserialized data available. Use XML_Unserializer::unserialize() first.", XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION);
+ }
+ return $this->_unserializedData;
+ }
+
+ /**
+ * get the name of the root tag
+ *
+ * @access public
+ * @return string $rootName
+ */
+ function getRootName()
+ {
+ if ($this->_root === null ) {
+ return $this->raiseError("No unserialized data available. Use XML_Unserializer::unserialize() first.", XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION);
+ }
+ return $this->_root;
+ }
+
+ /**
+ * Start element handler for XML parser
+ *
+ * @access private
+ * @param object $parser XML parser object
+ * @param string $element XML element
+ * @param array $attribs attributes of XML tag
+ * @return void
+ */
+ function startHandler($parser, $element, $attribs)
+ {
+ if (isset($attribs[$this->options["typeAttribute"]])) {
+ $type = $attribs[$this->options["typeAttribute"]];
+ } else {
+ $type = "string";
+ }
+
+ $this->_depth++;
+ $this->_dataStack[$this->_depth] = null;
+
+ $val = array(
+ "name" => $element,
+ "value" => null,
+ "type" => $type,
+ "childrenKeys" => array(),
+ "aggregKeys" => array()
+ );
+
+ if ($this->options["parseAttributes"] == true && (count($attribs) > 0)) {
+ $val["children"] = array();
+ $val["type"] = $this->options["complexType"];
+
+ if ($this->options["attributesArray"] != false) {
+ $val["children"][$this->options["attributesArray"]] = $attribs;
+ } else {
+ foreach ($attribs as $attrib => $value) {
+ $val["children"][$this->options["prependAttributes"].$attrib] = $value;
+ }
+ }
+ }
+
+ if (isset($attribs[$this->options["keyAttribute"]])) {
+ $val["name"] = $attribs[$this->options["keyAttribute"]];
+ }
+
+ if (isset($attribs[$this->options["classAttribute"]])) {
+ $val["class"] = $attribs[$this->options["classAttribute"]];
+ }
+
+ array_push($this->_valStack, $val);
+ }
+
+ /**
+ * End element handler for XML parser
+ *
+ * @access private
+ * @param object XML parser object
+ * @param string
+ * @return void
+ */
+ function endHandler($parser, $element)
+ {
+ $value = array_pop($this->_valStack);
+ $data = trim($this->_dataStack[$this->_depth]);
+
+ // adjust type of the value
+ switch(strtolower($value["type"])) {
+ /*
+ * unserialize an object
+ */
+ case "object":
+ $classname = $value["class"];
+ if (is_array($this->options["tagMap"]) && isset($this->options["tagMap"][$classname])) {
+ $classname = $this->options["tagMap"][$classname];
+ }
+
+ // instantiate the class
+ if (class_exists($classname)) {
+ $value["value"] = &new $classname;
+ } else {
+ $value["value"] = &new stdClass;
+ }
+ if ($data !== '') {
+ $value["children"][$this->options["contentName"]] = $data;
+ }
+
+ // set properties
+ foreach($value["children"] as $prop => $propVal) {
+ // check whether there is a special method to set this property
+ $setMethod = "set".$prop;
+ if (method_exists($value["value"], $setMethod)) {
+ call_user_func(array(&$value["value"], $setMethod), $propVal);
+ } else {
+ $value["value"]->$prop = $propVal;
+ }
+ }
+ // check for magic function
+ if (method_exists($value["value"], "__wakeup")) {
+ $value["value"]->__wakeup();
+ }
+ break;
+
+ /*
+ * unserialize an array
+ */
+ case "array":
+ if ($data !== '') {
+ $value["children"][$this->options["contentName"]] = $data;
+ }
+
+ $value["value"] = $value["children"];
+ break;
+
+ /*
+ * unserialize a null value
+ */
+ case "null":
+ $data = null;
+ break;
+
+ /*
+ * unserialize a resource => this is not possible :-(
+ */
+ case "resource":
+ $value["value"] = $data;
+ break;
+
+ /*
+ * unserialize any scalar value
+ */
+ default:
+ settype($data, $value["type"]);
+ $value["value"] = $data;
+ break;
+ }
+ $parent = array_pop($this->_valStack);
+ if ($parent === null) {
+ $this->_unserializedData = &$value["value"];
+ $this->_root = &$value["name"];
+ return true;
+ } else {
+ // parent has to be an array
+ if (!isset($parent["children"]) || !is_array($parent["children"])) {
+ $parent["children"] = array();
+ if (!in_array($parent["type"], array("array", "object"))) {
+ $parent["type"] = $this->options["complexType"];
+ if ($this->options["complexType"] == "object") {
+ $parent["class"] = $parent["name"];
+ }
+ }
+ }
+
+ if (!empty($value["name"])) {
+ // there already has been a tag with this name
+ if (in_array($value["name"], $parent["childrenKeys"])) {
+ // no aggregate has been created for this tag
+ if (!in_array($value["name"], $parent["aggregKeys"])) {
+ $parent["children"][$value["name"]] = array($parent["children"][$value["name"]]);
+ array_push($parent["aggregKeys"], $value["name"]);
+ }
+ array_push($parent["children"][$value["name"]], $value["value"]);
+ } else {
+ $parent["children"][$value["name"]] = &$value["value"];
+ array_push($parent["childrenKeys"], $value["name"]);
+ }
+ } else {
+ array_push($parent["children"],$value["value"]);
+ }
+ array_push($this->_valStack, $parent);
+ }
+
+ $this->_depth--;
+ }
+
+ /**
+ * Handler for character data
+ *
+ * @access private
+ * @param object XML parser object
+ * @param string CDATA
+ * @return void
+ */
+ function cdataHandler($parser, $cdata)
+ {
+ $this->_dataStack[$this->_depth] .= $cdata;
+ }
+}
+?> \ No newline at end of file
diff --git a/pearlib/XML/Util.php b/pearlib/XML/Util.php
new file mode 100644
index 00000000..0bd24cc3
--- /dev/null
+++ b/pearlib/XML/Util.php
@@ -0,0 +1,700 @@
+<?PHP
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2002 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stephan Schmidt <[email protected]> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Util.php,v 1.20 2004/06/27 18:15:13 schst Exp $
+
+/**
+ * error code for invalid chars in XML name
+ */
+define("XML_UTIL_ERROR_INVALID_CHARS", 51);
+
+/**
+ * error code for invalid chars in XML name
+ */
+define("XML_UTIL_ERROR_INVALID_START", 52);
+
+/**
+ * error code for non-scalar tag content
+ */
+define("XML_UTIL_ERROR_NON_SCALAR_CONTENT", 60);
+
+/**
+ * error code for missing tag name
+ */
+define("XML_UTIL_ERROR_NO_TAG_NAME", 61);
+
+/**
+ * replace XML entities
+ */
+define("XML_UTIL_REPLACE_ENTITIES", 1);
+
+/**
+ * embedd content in a CData Section
+ */
+define("XML_UTIL_CDATA_SECTION", 2);
+
+/**
+ * do not replace entitites
+ */
+define("XML_UTIL_ENTITIES_NONE", 0);
+
+/**
+ * replace all XML entitites
+ * This setting will replace <, >, ", ' and &
+ */
+define("XML_UTIL_ENTITIES_XML", 1);
+
+/**
+ * replace only required XML entitites
+ * This setting will replace <, " and &
+ */
+define("XML_UTIL_ENTITIES_XML_REQUIRED", 2);
+
+/**
+ * replace HTML entitites
+ * @link http://www.php.net/htmlentities
+ */
+define("XML_UTIL_ENTITIES_HTML", 3);
+
+/**
+ * utility class for working with XML documents
+ *
+ * @category XML
+ * @package XML_Util
+ * @version 0.6.0
+ * @author Stephan Schmidt <[email protected]>
+ */
+class XML_Util {
+
+ /**
+ * return API version
+ *
+ * @access public
+ * @static
+ * @return string $version API version
+ */
+ function apiVersion()
+ {
+ return "1.0";
+ }
+
+ /**
+ * replace XML entities
+ *
+ * With the optional second parameter, you may select, which
+ * entities should be replaced.
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // replace XML entites:
+ * $string = XML_Util::replaceEntities("This string contains < & >.");
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string string where XML special chars should be replaced
+ * @param integer setting for entities in attribute values (one of XML_UTIL_ENTITIES_XML, XML_UTIL_ENTITIES_XML_REQUIRED, XML_UTIL_ENTITIES_HTML)
+ * @return string string with replaced chars
+ * @see reverseEntities()
+ */
+ function replaceEntities($string, $replaceEntities = XML_UTIL_ENTITIES_XML)
+ {
+ switch ($replaceEntities) {
+ case XML_UTIL_ENTITIES_XML:
+ return strtr($string,array(
+ '&' => '&amp;',
+ '>' => '&gt;',
+ '<' => '&lt;',
+ '"' => '&quot;',
+ '\'' => '&apos;' ));
+ break;
+ case XML_UTIL_ENTITIES_XML_REQUIRED:
+ return strtr($string,array(
+ '&' => '&amp;',
+ '<' => '&lt;',
+ '"' => '&quot;' ));
+ break;
+ case XML_UTIL_ENTITIES_HTML:
+ return htmlspecialchars($string);
+ break;
+ }
+ return $string;
+ }
+
+ /**
+ * reverse XML entities
+ *
+ * With the optional second parameter, you may select, which
+ * entities should be reversed.
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // reverse XML entites:
+ * $string = XML_Util::reverseEntities("This string contains &lt; &amp; &gt;.");
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string string where XML special chars should be replaced
+ * @param integer setting for entities in attribute values (one of XML_UTIL_ENTITIES_XML, XML_UTIL_ENTITIES_XML_REQUIRED, XML_UTIL_ENTITIES_HTML)
+ * @return string string with replaced chars
+ * @see replaceEntities()
+ */
+ function reverseEntities($string, $replaceEntities = XML_UTIL_ENTITIES_XML)
+ {
+ switch ($replaceEntities) {
+ case XML_UTIL_ENTITIES_XML:
+ return strtr($string,array(
+ '&amp;' => '&',
+ '&gt;' => '>',
+ '&lt;' => '<',
+ '&quot;' => '"',
+ '&apos;' => '\'' ));
+ break;
+ case XML_UTIL_ENTITIES_XML_REQUIRED:
+ return strtr($string,array(
+ '&amp;' => '&',
+ '&lt;' => '<',
+ '&quot;' => '"' ));
+ break;
+ case XML_UTIL_ENTITIES_HTML:
+ $arr = array_flip(get_html_translation_table(HTML_SPECIALCHARS));
+ return strtr($string, $arr);
+ break;
+ }
+ return $string;
+ }
+
+ /**
+ * build an xml declaration
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // get an XML declaration:
+ * $xmlDecl = XML_Util::getXMLDeclaration("1.0", "UTF-8", true);
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string $version xml version
+ * @param string $encoding character encoding
+ * @param boolean $standAlone document is standalone (or not)
+ * @return string $decl xml declaration
+ * @uses XML_Util::attributesToString() to serialize the attributes of the XML declaration
+ */
+ function getXMLDeclaration($version = "1.0", $encoding = null, $standalone = null)
+ {
+ $attributes = array(
+ "version" => $version,
+ );
+ // add encoding
+ if ($encoding !== null) {
+ $attributes["encoding"] = $encoding;
+ }
+ // add standalone, if specified
+ if ($standalone !== null) {
+ $attributes["standalone"] = $standalone ? "yes" : "no";
+ }
+
+ return sprintf("<?xml%s?>", XML_Util::attributesToString($attributes, false));
+ }
+
+ /**
+ * build a document type declaration
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // get a doctype declaration:
+ * $xmlDecl = XML_Util::getDocTypeDeclaration("rootTag","myDocType.dtd");
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string $root name of the root tag
+ * @param string $uri uri of the doctype definition (or array with uri and public id)
+ * @param string $internalDtd internal dtd entries
+ * @return string $decl doctype declaration
+ * @since 0.2
+ */
+ function getDocTypeDeclaration($root, $uri = null, $internalDtd = null)
+ {
+ if (is_array($uri)) {
+ $ref = sprintf( ' PUBLIC "%s" "%s"', $uri["id"], $uri["uri"] );
+ } elseif (!empty($uri)) {
+ $ref = sprintf( ' SYSTEM "%s"', $uri );
+ } else {
+ $ref = "";
+ }
+
+ if (empty($internalDtd)) {
+ return sprintf("<!DOCTYPE %s%s>", $root, $ref);
+ } else {
+ return sprintf("<!DOCTYPE %s%s [\n%s\n]>", $root, $ref, $internalDtd);
+ }
+ }
+
+ /**
+ * create string representation of an attribute list
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // build an attribute string
+ * $att = array(
+ * "foo" => "bar",
+ * "argh" => "tomato"
+ * );
+ *
+ * $attList = XML_Util::attributesToString($att);
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param array $attributes attribute array
+ * @param boolean|array $sort sort attribute list alphabetically, may also be an assoc array containing the keys 'sort', 'multiline', 'indent', 'linebreak' and 'entities'
+ * @param boolean $multiline use linebreaks, if more than one attribute is given
+ * @param string $indent string used for indentation of multiline attributes
+ * @param string $linebreak string used for linebreaks of multiline attributes
+ * @param integer $entities setting for entities in attribute values (one of XML_UTIL_ENTITIES_NONE, XML_UTIL_ENTITIES_XML, XML_UTIL_ENTITIES_XML_REQUIRED, XML_UTIL_ENTITIES_HTML)
+ * @return string string representation of the attributes
+ * @uses XML_Util::replaceEntities() to replace XML entities in attribute values
+ * @todo allow sort also to be an options array
+ */
+ function attributesToString($attributes, $sort = true, $multiline = false, $indent = ' ', $linebreak = "\n", $entities = XML_UTIL_ENTITIES_XML)
+ {
+ /**
+ * second parameter may be an array
+ */
+ if (is_array($sort)) {
+ if (isset($sort['multiline'])) {
+ $multiline = $sort['multiline'];
+ }
+ if (isset($sort['indent'])) {
+ $indent = $sort['indent'];
+ }
+ if (isset($sort['linebreak'])) {
+ $multiline = $sort['linebreak'];
+ }
+ if (isset($sort['entities'])) {
+ $entities = $sort['entities'];
+ }
+ if (isset($sort['sort'])) {
+ $sort = $sort['sort'];
+ } else {
+ $sort = true;
+ }
+ }
+ $string = '';
+ if (is_array($attributes) && !empty($attributes)) {
+ if ($sort) {
+ ksort($attributes);
+ }
+ if( !$multiline || count($attributes) == 1) {
+ foreach ($attributes as $key => $value) {
+ if ($entities != XML_UTIL_ENTITIES_NONE) {
+ $value = XML_Util::replaceEntities($value, $entities);
+ }
+ $string .= ' '.$key.'="'.$value.'"';
+ }
+ } else {
+ $first = true;
+ foreach ($attributes as $key => $value) {
+ if ($entities != XML_UTIL_ENTITIES_NONE) {
+ $value = XML_Util::replaceEntities($value, $entities);
+ }
+ if ($first) {
+ $string .= " ".$key.'="'.$value.'"';
+ $first = false;
+ } else {
+ $string .= $linebreak.$indent.$key.'="'.$value.'"';
+ }
+ }
+ }
+ }
+ return $string;
+ }
+
+ /**
+ * create a tag
+ *
+ * This method will call XML_Util::createTagFromArray(), which
+ * is more flexible.
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // create an XML tag:
+ * $tag = XML_Util::createTag("myNs:myTag", array("foo" => "bar"), "This is inside the tag", "http://www.w3c.org/myNs#");
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string $qname qualified tagname (including namespace)
+ * @param array $attributes array containg attributes
+ * @param mixed $content
+ * @param string $namespaceUri URI of the namespace
+ * @param integer $replaceEntities whether to replace XML special chars in content, embedd it in a CData section or none of both
+ * @param boolean $multiline whether to create a multiline tag where each attribute gets written to a single line
+ * @param string $indent string used to indent attributes (_auto indents attributes so they start at the same column)
+ * @param string $linebreak string used for linebreaks
+ * @return string $string XML tag
+ * @see XML_Util::createTagFromArray()
+ * @uses XML_Util::createTagFromArray() to create the tag
+ */
+ function createTag($qname, $attributes = array(), $content = null, $namespaceUri = null, $replaceEntities = XML_UTIL_REPLACE_ENTITIES, $multiline = false, $indent = "_auto", $linebreak = "\n")
+ {
+ $tag = array(
+ "qname" => $qname,
+ "attributes" => $attributes
+ );
+
+ // add tag content
+ if ($content !== null) {
+ $tag["content"] = $content;
+ }
+
+ // add namespace Uri
+ if ($namespaceUri !== null) {
+ $tag["namespaceUri"] = $namespaceUri;
+ }
+
+ return XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $linebreak);
+ }
+
+ /**
+ * create a tag from an array
+ * this method awaits an array in the following format
+ * <pre>
+ * array(
+ * "qname" => $qname // qualified name of the tag
+ * "namespace" => $namespace // namespace prefix (optional, if qname is specified or no namespace)
+ * "localpart" => $localpart, // local part of the tagname (optional, if qname is specified)
+ * "attributes" => array(), // array containing all attributes (optional)
+ * "content" => $content, // tag content (optional)
+ * "namespaceUri" => $namespaceUri // namespaceUri for the given namespace (optional)
+ * )
+ * </pre>
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * $tag = array(
+ * "qname" => "foo:bar",
+ * "namespaceUri" => "http://foo.com",
+ * "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ),
+ * "content" => "I'm inside the tag",
+ * );
+ * // creating a tag with qualified name and namespaceUri
+ * $string = XML_Util::createTagFromArray($tag);
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param array $tag tag definition
+ * @param integer $replaceEntities whether to replace XML special chars in content, embedd it in a CData section or none of both
+ * @param boolean $multiline whether to create a multiline tag where each attribute gets written to a single line
+ * @param string $indent string used to indent attributes (_auto indents attributes so they start at the same column)
+ * @param string $linebreak string used for linebreaks
+ * @return string $string XML tag
+ * @see XML_Util::createTag()
+ * @uses XML_Util::attributesToString() to serialize the attributes of the tag
+ * @uses XML_Util::splitQualifiedName() to get local part and namespace of a qualified name
+ */
+ function createTagFromArray($tag, $replaceEntities = XML_UTIL_REPLACE_ENTITIES, $multiline = false, $indent = "_auto", $linebreak = "\n" )
+ {
+ if (isset($tag["content"]) && !is_scalar($tag["content"])) {
+ return XML_Util::raiseError( "Supplied non-scalar value as tag content", XML_UTIL_ERROR_NON_SCALAR_CONTENT );
+ }
+
+ if (!isset($tag['qname']) && !isset($tag['localPart'])) {
+ return XML_Util::raiseError( 'You must either supply a qualified name (qname) or local tag name (localPart).', XML_UTIL_ERROR_NO_TAG_NAME );
+ }
+
+ // if no attributes hav been set, use empty attributes
+ if (!isset($tag["attributes"]) || !is_array($tag["attributes"])) {
+ $tag["attributes"] = array();
+ }
+
+ // qualified name is not given
+ if (!isset($tag["qname"])) {
+ // check for namespace
+ if (isset($tag["namespace"]) && !empty($tag["namespace"])) {
+ $tag["qname"] = $tag["namespace"].":".$tag["localPart"];
+ } else {
+ $tag["qname"] = $tag["localPart"];
+ }
+ // namespace URI is set, but no namespace
+ } elseif (isset($tag["namespaceUri"]) && !isset($tag["namespace"])) {
+ $parts = XML_Util::splitQualifiedName($tag["qname"]);
+ $tag["localPart"] = $parts["localPart"];
+ if (isset($parts["namespace"])) {
+ $tag["namespace"] = $parts["namespace"];
+ }
+ }
+
+ if (isset($tag["namespaceUri"]) && !empty($tag["namespaceUri"])) {
+ // is a namespace given
+ if (isset($tag["namespace"]) && !empty($tag["namespace"])) {
+ $tag["attributes"]["xmlns:".$tag["namespace"]] = $tag["namespaceUri"];
+ } else {
+ // define this Uri as the default namespace
+ $tag["attributes"]["xmlns"] = $tag["namespaceUri"];
+ }
+ }
+
+ // check for multiline attributes
+ if ($multiline === true) {
+ if ($indent === "_auto") {
+ $indent = str_repeat(" ", (strlen($tag["qname"])+2));
+ }
+ }
+
+ // create attribute list
+ $attList = XML_Util::attributesToString($tag["attributes"], true, $multiline, $indent, $linebreak );
+ if (!isset($tag["content"]) || (string)$tag["content"] == '') {
+ $tag = sprintf("<%s%s />", $tag["qname"], $attList);
+ } else {
+ if ($replaceEntities == XML_UTIL_REPLACE_ENTITIES) {
+ $tag["content"] = XML_Util::replaceEntities($tag["content"]);
+ } elseif ($replaceEntities == XML_UTIL_CDATA_SECTION) {
+ $tag["content"] = XML_Util::createCDataSection($tag["content"]);
+ }
+ $tag = sprintf("<%s%s>%s</%s>", $tag["qname"], $attList, $tag["content"], $tag["qname"] );
+ }
+ return $tag;
+ }
+
+ /**
+ * create a start element
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // create an XML start element:
+ * $tag = XML_Util::createStartElement("myNs:myTag", array("foo" => "bar") ,"http://www.w3c.org/myNs#");
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string $qname qualified tagname (including namespace)
+ * @param array $attributes array containg attributes
+ * @param string $namespaceUri URI of the namespace
+ * @param boolean $multiline whether to create a multiline tag where each attribute gets written to a single line
+ * @param string $indent string used to indent attributes (_auto indents attributes so they start at the same column)
+ * @param string $linebreak string used for linebreaks
+ * @return string $string XML start element
+ * @see XML_Util::createEndElement(), XML_Util::createTag()
+ */
+ function createStartElement($qname, $attributes = array(), $namespaceUri = null, $multiline = false, $indent = '_auto', $linebreak = "\n")
+ {
+ // if no attributes hav been set, use empty attributes
+ if (!isset($attributes) || !is_array($attributes)) {
+ $attributes = array();
+ }
+
+ if ($namespaceUri != null) {
+ $parts = XML_Util::splitQualifiedName($qname);
+ }
+
+ // check for multiline attributes
+ if ($multiline === true) {
+ if ($indent === "_auto") {
+ $indent = str_repeat(" ", (strlen($qname)+2));
+ }
+ }
+
+ if ($namespaceUri != null) {
+ // is a namespace given
+ if (isset($parts["namespace"]) && !empty($parts["namespace"])) {
+ $attributes["xmlns:".$parts["namespace"]] = $namespaceUri;
+ } else {
+ // define this Uri as the default namespace
+ $attributes["xmlns"] = $namespaceUri;
+ }
+ }
+
+ // create attribute list
+ $attList = XML_Util::attributesToString($attributes, true, $multiline, $indent, $linebreak);
+ $element = sprintf("<%s%s>", $qname, $attList);
+ return $element;
+ }
+
+ /**
+ * create an end element
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // create an XML start element:
+ * $tag = XML_Util::createEndElement("myNs:myTag");
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string $qname qualified tagname (including namespace)
+ * @return string $string XML end element
+ * @see XML_Util::createStartElement(), XML_Util::createTag()
+ */
+ function createEndElement($qname)
+ {
+ $element = sprintf("</%s>", $qname);
+ return $element;
+ }
+
+ /**
+ * create an XML comment
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // create an XML start element:
+ * $tag = XML_Util::createComment("I am a comment");
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string $content content of the comment
+ * @return string $comment XML comment
+ */
+ function createComment($content)
+ {
+ $comment = sprintf("<!-- %s -->", $content);
+ return $comment;
+ }
+
+ /**
+ * create a CData section
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // create a CData section
+ * $tag = XML_Util::createCDataSection("I am content.");
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string $data data of the CData section
+ * @return string $string CData section with content
+ */
+ function createCDataSection($data)
+ {
+ return sprintf("<![CDATA[%s]]>", $data);
+ }
+
+ /**
+ * split qualified name and return namespace and local part
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // split qualified tag
+ * $parts = XML_Util::splitQualifiedName("xslt:stylesheet");
+ * </code>
+ * the returned array will contain two elements:
+ * <pre>
+ * array(
+ * "namespace" => "xslt",
+ * "localPart" => "stylesheet"
+ * );
+ * </pre>
+ *
+ * @access public
+ * @static
+ * @param string $qname qualified tag name
+ * @param string $defaultNs default namespace (optional)
+ * @return array $parts array containing namespace and local part
+ */
+ function splitQualifiedName($qname, $defaultNs = null)
+ {
+ if (strstr($qname, ':')) {
+ $tmp = explode(":", $qname);
+ return array(
+ "namespace" => $tmp[0],
+ "localPart" => $tmp[1]
+ );
+ }
+ return array(
+ "namespace" => $defaultNs,
+ "localPart" => $qname
+ );
+ }
+
+ /**
+ * check, whether string is valid XML name
+ *
+ * <p>XML names are used for tagname, attribute names and various
+ * other, lesser known entities.</p>
+ * <p>An XML name may only consist of alphanumeric characters,
+ * dashes, undescores and periods, and has to start with a letter
+ * or an underscore.
+ * </p>
+ *
+ * <code>
+ * require_once 'XML/Util.php';
+ *
+ * // verify tag name
+ * $result = XML_Util::isValidName("invalidTag?");
+ * if (XML_Util::isError($result)) {
+ * print "Invalid XML name: " . $result->getMessage();
+ * }
+ * </code>
+ *
+ * @access public
+ * @static
+ * @param string $string string that should be checked
+ * @return mixed $valid true, if string is a valid XML name, PEAR error otherwise
+ * @todo support for other charsets
+ */
+ function isValidName($string)
+ {
+ // check for invalid chars
+ if (!preg_match("/^[[:alnum:]_\-.]$/", $string{0})) {
+ return XML_Util::raiseError( "XML names may only start with letter or underscore", XML_UTIL_ERROR_INVALID_START );
+ }
+
+ // check for invalid chars
+ if (!preg_match("/^([a-zA-Z_]([a-zA-Z0-9_\-\.]*)?:)?[a-zA-Z_]([a-zA-Z0-9_\-\.]+)?$/", $string)) {
+ return XML_Util::raiseError( "XML names may only contain alphanumeric chars, period, hyphen, colon and underscores", XML_UTIL_ERROR_INVALID_CHARS );
+ }
+ // XML name is valid
+ return true;
+ }
+
+ /**
+ * replacement for XML_Util::raiseError
+ *
+ * Avoids the necessity to always require
+ * PEAR.php
+ *
+ * @access public
+ * @param string error message
+ * @param integer error code
+ * @return object PEAR_Error
+ */
+ function raiseError($msg, $code)
+ {
+ require_once 'PEAR.php';
+ return PEAR::raiseError($msg, $code);
+ }
+}
+?> \ No newline at end of file
diff --git a/pearlib/pearcmd.php b/pearlib/pearcmd.php
new file mode 100644
index 00000000..dc550190
--- /dev/null
+++ b/pearlib/pearcmd.php
@@ -0,0 +1,313 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available through the world-wide-web at the following url: |
+// | http://www.php.net/license/3_0.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | [email protected] so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Bakken <[email protected]> |
+// | Tomas V.V.Cox <[email protected]> |
+// | |
+// +----------------------------------------------------------------------+
+//
+// $Id: pearcmd.php,v 1.13.2.1 2004/10/27 05:58:21 cellog Exp $
+
+ob_end_clean();
+/**
+ * @nodep Gtk
+ */
+ini_set('allow_url_fopen', true);
+if (!ini_get('safe_mode')) {
+ @set_time_limit(0);
+}
+ob_implicit_flush(true);
+ini_set('track_errors', true);
+ini_set('html_errors', false);
+ini_set('magic_quotes_runtime', false);
+set_error_handler('error_handler');
+
+$pear_package_version = "1.3.3";
+
+require_once 'PEAR.php';
+require_once 'PEAR/Config.php';
+require_once 'PEAR/Command.php';
+require_once 'Console/Getopt.php';
+
+PEAR_Command::setFrontendType('CLI');
+$all_commands = PEAR_Command::getCommands();
+
+$argv = Console_Getopt::readPHPArgv();
+/* $progname = basename($argv[0]); */
+$progname = 'pear';
+if (in_array('getopt2', get_class_methods('Console_Getopt'))) {
+ array_shift($argv);
+ $options = Console_Getopt::getopt2($argv, "c:C:d:D:Gh?sSqu:vV");
+} else {
+ $options = Console_Getopt::getopt($argv, "c:C:d:D:Gh?sSqu:vV");
+}
+if (PEAR::isError($options)) {
+ usage($options);
+}
+
+$opts = $options[0];
+
+$fetype = 'CLI';
+if ($progname == 'gpear' || $progname == 'pear-gtk') {
+ $fetype = 'Gtk';
+} else {
+ foreach ($opts as $opt) {
+ if ($opt[0] == 'G') {
+ $fetype = 'Gtk';
+ }
+ }
+}
+PEAR_Command::setFrontendType($fetype);
+$ui = &PEAR_Command::getFrontendObject();
+PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, array($ui, "displayFatalError"));
+if (ini_get('safe_mode')) {
+ $ui->outputData('WARNING: running in safe mode requires that all files created ' .
+ 'be the same uid as the current script. PHP reports this script is uid: ' .
+ @getmyuid() . ', and current user is: ' . @get_current_user());
+}
+
+$pear_user_config = '';
+$pear_system_config = '';
+$store_user_config = false;
+$store_system_config = false;
+$verbose = 1;
+
+foreach ($opts as $opt) {
+ switch ($opt[0]) {
+ case 'c':
+ $pear_user_config = $opt[1];
+ break;
+ case 'C':
+ $pear_system_config = $opt[1];
+ break;
+ }
+}
+
+$config = &PEAR_Config::singleton($pear_user_config, $pear_system_config);
+$verbose = $config->get("verbose");
+$cmdopts = array();
+
+foreach ($opts as $opt) {
+ $param = !empty($opt[1]) ? $opt[1] : true;
+ switch ($opt[0]) {
+ case 'd':
+ list($key, $value) = explode('=', $param);
+ $config->set($key, $value, 'user');
+ break;
+ case 'D':
+ list($key, $value) = explode('=', $param);
+ $config->set($key, $value, 'system');
+ break;
+ case 's':
+ $store_user_config = true;
+ break;
+ case 'S':
+ $store_system_config = true;
+ break;
+ case 'u':
+ $config->remove($param, 'user');
+ break;
+ case 'v':
+ $config->set('verbose', $config->get('verbose') + 1);
+ break;
+ case 'q':
+ $config->set('verbose', $config->get('verbose') - 1);
+ break;
+ case 'V':
+ usage(null, 'version');
+ default:
+ // all non pear params goes to the command
+ $cmdopts[$opt[0]] = $param;
+ break;
+ }
+}
+
+if ($store_system_config) {
+ $config->store('system');
+}
+
+if ($store_user_config) {
+ $config->store('user');
+}
+
+$command = (isset($options[1][0])) ? $options[1][0] : null;
+
+if (empty($command) && ($store_user_config || $store_system_config)) {
+ exit;
+}
+
+if ($fetype == 'Gtk') {
+ Gtk::main();
+} else do {
+ if ($command == 'help') {
+ usage(null, @$options[1][1]);
+ }
+
+ PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+ $cmd = PEAR_Command::factory($command, $config);
+ PEAR::popErrorHandling();
+ if (PEAR::isError($cmd)) {
+ usage(null, @$options[1][1]);
+ }
+
+ $short_args = $long_args = null;
+ PEAR_Command::getGetoptArgs($command, $short_args, $long_args);
+ if (in_array('getopt2', get_class_methods('Console_Getopt'))) {
+ array_shift($options[1]);
+ $tmp = Console_Getopt::getopt2($options[1], $short_args, $long_args);
+ } else {
+ $tmp = Console_Getopt::getopt($options[1], $short_args, $long_args);
+ }
+ if (PEAR::isError($tmp)) {
+ break;
+ }
+ list($tmpopt, $params) = $tmp;
+ $opts = array();
+ foreach ($tmpopt as $foo => $tmp2) {
+ list($opt, $value) = $tmp2;
+ if ($value === null) {
+ $value = true; // options without args
+ }
+ if (strlen($opt) == 1) {
+ $cmdoptions = $cmd->getOptions($command);
+ foreach ($cmdoptions as $o => $d) {
+ if (@$d['shortopt'] == $opt) {
+ $opts[$o] = $value;
+ }
+ }
+ } else {
+ if (substr($opt, 0, 2) == '--') {
+ $opts[substr($opt, 2)] = $value;
+ }
+ }
+ }
+ $ok = $cmd->run($command, $opts, $params);
+ if ($ok === false) {
+ PEAR::raiseError("unknown command `$command'");
+ }
+} while (false);
+
+// {{{ usage()
+
+function usage($error = null, $helpsubject = null)
+{
+ global $progname, $all_commands;
+ $stderr = fopen('php://stderr', 'w');
+ if (PEAR::isError($error)) {
+ fputs($stderr, $error->getMessage() . "\n");
+ } elseif ($error !== null) {
+ fputs($stderr, "$error\n");
+ }
+ if ($helpsubject != null) {
+ $put = cmdHelp($helpsubject);
+ } else {
+ $put =
+ "Usage: $progname [options] command [command-options] <parameters>\n".
+ "Type \"$progname help options\" to list all options.\n".
+ "Type \"$progname help <command>\" to get the help for the specified command.\n".
+ "Commands:\n";
+ $maxlen = max(array_map("strlen", $all_commands));
+ $formatstr = "%-{$maxlen}s %s\n";
+ ksort($all_commands);
+ foreach ($all_commands as $cmd => $class) {
+ $put .= sprintf($formatstr, $cmd, PEAR_Command::getDescription($cmd));
+ }
+ }
+ fputs($stderr, "$put\n");
+ fclose($stderr);
+ exit;
+}
+
+function cmdHelp($command)
+{
+ global $progname, $all_commands, $config;
+ if ($command == "options") {
+ return
+ "Options:\n".
+ " -v increase verbosity level (default 1)\n".
+ " -q be quiet, decrease verbosity level\n".
+ " -c file find user configuration in `file'\n".
+ " -C file find system configuration in `file'\n".
+ " -d foo=bar set user config variable `foo' to `bar'\n".
+ " -D foo=bar set system config variable `foo' to `bar'\n".
+ " -G start in graphical (Gtk) mode\n".
+ " -s store user configuration\n".
+ " -S store system configuration\n".
+ " -u foo unset `foo' in the user configuration\n".
+ " -h, -? display help/usage (this message)\n".
+ " -V version information\n";
+ } elseif ($command == "shortcuts") {
+ $sc = PEAR_Command::getShortcuts();
+ $ret = "Shortcuts:\n";
+ foreach ($sc as $s => $c) {
+ $ret .= sprintf(" %-8s %s\n", $s, $c);
+ }
+ return $ret;
+
+ } elseif ($command == "version") {
+ return "PEAR Version: ".$GLOBALS['pear_package_version'].
+ "\nPHP Version: ".phpversion().
+ "\nZend Engine Version: ".zend_version().
+ "\nRunning on: ".php_uname();
+
+ } elseif ($help = PEAR_Command::getHelp($command)) {
+ if (is_string($help)) {
+ return "$progname $command [options] $help\n";
+ }
+ if ($help[1] === null) {
+ return "$progname $command $help[0]";
+ } else {
+ return "$progname $command [options] $help[0]\n$help[1]";
+ }
+ }
+ return "Command '$command' is not valid, try 'pear help'";
+}
+
+// }}}
+
+function error_handler($errno, $errmsg, $file, $line, $vars) {
+ if ((defined('E_STRICT') && $errno & E_STRICT) || !error_reporting()) {
+ return; // @silenced error
+ }
+ $errortype = array (
+ E_ERROR => "Error",
+ E_WARNING => "Warning",
+ E_PARSE => "Parsing Error",
+ E_NOTICE => "Notice",
+ E_CORE_ERROR => "Core Error",
+ E_CORE_WARNING => "Core Warning",
+ E_COMPILE_ERROR => "Compile Error",
+ E_COMPILE_WARNING => "Compile Warning",
+ E_USER_ERROR => "User Error",
+ E_USER_WARNING => "User Warning",
+ E_USER_NOTICE => "User Notice"
+ );
+ $prefix = $errortype[$errno];
+ $file = basename($file);
+ print "\n$prefix: $errmsg in $file on line $line\n";
+}
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * mode: php
+ * End:
+ */
+// vim600:syn=php
+
+?>