From 0a9efdd6d41d4398c3a1676b4a779e6eea84dba9 Mon Sep 17 00:00:00 2001 From: YuCheng Hu Date: Sat, 12 Jun 2010 20:12:52 +0000 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=88=B0=20trunk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit +YUCHENG HU+ git-svn-id: https://svn.code.sf.net/p/hawebs/svn@219 a2543c7e-f6e9-4f8a-8bff-1ffc34733512 --- .../trunk/libraries/domit/domitBanner.gif | Bin 0 -> 4285 bytes oss/joomla/trunk/libraries/domit/index.html | 1 + oss/joomla/trunk/libraries/domit/license.txt | 482 +++ .../libraries/domit/php_file_utilities.php | 50 + .../domit/php_http_client_generic.php | 761 ++++ .../domit/php_http_client_include.php | 4 + .../libraries/domit/php_http_connector.php | 77 + .../libraries/domit/php_http_exceptions.php | 113 + .../trunk/libraries/domit/php_http_proxy.php | 78 + .../domit/php_http_server_generic.php | 193 + .../domit/php_http_server_include.php | 4 + .../libraries/domit/php_http_status_codes.php | 50 + .../trunk/libraries/domit/php_text_cache.php | 192 + oss/joomla/trunk/libraries/domit/timer.php | 43 + .../trunk/libraries/domit/xml_domit_cache.php | 74 + .../libraries/domit/xml_domit_doctor.php | 94 + .../domit/xml_domit_getelementsbypath.php | 278 ++ .../libraries/domit/xml_domit_include.php | 17 + .../domit/xml_domit_lite_include.php | 15 + .../libraries/domit/xml_domit_lite_parser.php | 1892 +++++++++ .../libraries/domit/xml_domit_nodemaps.php | 457 ++ .../libraries/domit/xml_domit_nodetools.php | 235 ++ .../domit/xml_domit_parseattributes.php | 144 + .../libraries/domit/xml_domit_parser.php | 3679 +++++++++++++++++ .../trunk/libraries/domit/xml_domit_rss.php | 1121 +++++ .../libraries/domit/xml_domit_rss_lite.php | 244 ++ .../libraries/domit/xml_domit_rss_shared.php | 1025 +++++ .../libraries/domit/xml_domit_shared.php | 267 ++ .../libraries/domit/xml_domit_utilities.php | 384 ++ .../trunk/libraries/domit/xml_domit_xpath.php | 1048 +++++ .../libraries/domit/xml_saxy_lite_parser.php | 243 ++ .../trunk/libraries/domit/xml_saxy_parser.php | 847 ++++ .../trunk/libraries/domit/xml_saxy_shared.php | 294 ++ oss/joomla/trunk/libraries/geshi/geshi.php | 2832 +++++++++++++ .../trunk/libraries/geshi/geshi/css.php | 208 + .../trunk/libraries/geshi/geshi/diff.php | 185 + .../libraries/geshi/geshi/html4strict.php | 256 ++ .../trunk/libraries/geshi/geshi/index.html | 1 + .../trunk/libraries/geshi/geshi/ini.php | 125 + .../libraries/geshi/geshi/javascript.php | 146 + .../trunk/libraries/geshi/geshi/mysql.php | 183 + .../trunk/libraries/geshi/geshi/php-brief.php | 162 + .../trunk/libraries/geshi/geshi/php.php | 356 ++ .../trunk/libraries/geshi/geshi/sql.php | 137 + .../trunk/libraries/geshi/geshi/xml.php | 147 + oss/joomla/trunk/libraries/geshi/index.html | 1 + 46 files changed, 19145 insertions(+) create mode 100644 oss/joomla/trunk/libraries/domit/domitBanner.gif create mode 100644 oss/joomla/trunk/libraries/domit/index.html create mode 100644 oss/joomla/trunk/libraries/domit/license.txt create mode 100644 oss/joomla/trunk/libraries/domit/php_file_utilities.php create mode 100644 oss/joomla/trunk/libraries/domit/php_http_client_generic.php create mode 100644 oss/joomla/trunk/libraries/domit/php_http_client_include.php create mode 100644 oss/joomla/trunk/libraries/domit/php_http_connector.php create mode 100644 oss/joomla/trunk/libraries/domit/php_http_exceptions.php create mode 100644 oss/joomla/trunk/libraries/domit/php_http_proxy.php create mode 100644 oss/joomla/trunk/libraries/domit/php_http_server_generic.php create mode 100644 oss/joomla/trunk/libraries/domit/php_http_server_include.php create mode 100644 oss/joomla/trunk/libraries/domit/php_http_status_codes.php create mode 100644 oss/joomla/trunk/libraries/domit/php_text_cache.php create mode 100644 oss/joomla/trunk/libraries/domit/timer.php create mode 100644 oss/joomla/trunk/libraries/domit/xml_domit_cache.php create mode 100644 oss/joomla/trunk/libraries/domit/xml_domit_doctor.php create mode 100644 oss/joomla/trunk/libraries/domit/xml_domit_getelementsbypath.php create mode 100644 oss/joomla/trunk/libraries/domit/xml_domit_include.php create mode 100644 oss/joomla/trunk/libraries/domit/xml_domit_lite_include.php create mode 100644 oss/joomla/trunk/libraries/domit/xml_domit_lite_parser.php create mode 100644 oss/joomla/trunk/libraries/domit/xml_domit_nodemaps.php create mode 100644 oss/joomla/trunk/libraries/domit/xml_domit_nodetools.php create mode 100644 oss/joomla/trunk/libraries/domit/xml_domit_parseattributes.php create mode 100644 oss/joomla/trunk/libraries/domit/xml_domit_parser.php create mode 100644 oss/joomla/trunk/libraries/domit/xml_domit_rss.php create mode 100644 oss/joomla/trunk/libraries/domit/xml_domit_rss_lite.php create mode 100644 oss/joomla/trunk/libraries/domit/xml_domit_rss_shared.php create mode 100644 oss/joomla/trunk/libraries/domit/xml_domit_shared.php create mode 100644 oss/joomla/trunk/libraries/domit/xml_domit_utilities.php create mode 100644 oss/joomla/trunk/libraries/domit/xml_domit_xpath.php create mode 100644 oss/joomla/trunk/libraries/domit/xml_saxy_lite_parser.php create mode 100644 oss/joomla/trunk/libraries/domit/xml_saxy_parser.php create mode 100644 oss/joomla/trunk/libraries/domit/xml_saxy_shared.php create mode 100644 oss/joomla/trunk/libraries/geshi/geshi.php create mode 100644 oss/joomla/trunk/libraries/geshi/geshi/css.php create mode 100644 oss/joomla/trunk/libraries/geshi/geshi/diff.php create mode 100644 oss/joomla/trunk/libraries/geshi/geshi/html4strict.php create mode 100644 oss/joomla/trunk/libraries/geshi/geshi/index.html create mode 100644 oss/joomla/trunk/libraries/geshi/geshi/ini.php create mode 100644 oss/joomla/trunk/libraries/geshi/geshi/javascript.php create mode 100644 oss/joomla/trunk/libraries/geshi/geshi/mysql.php create mode 100644 oss/joomla/trunk/libraries/geshi/geshi/php-brief.php create mode 100644 oss/joomla/trunk/libraries/geshi/geshi/php.php create mode 100644 oss/joomla/trunk/libraries/geshi/geshi/sql.php create mode 100644 oss/joomla/trunk/libraries/geshi/geshi/xml.php create mode 100644 oss/joomla/trunk/libraries/geshi/index.html diff --git a/oss/joomla/trunk/libraries/domit/domitBanner.gif b/oss/joomla/trunk/libraries/domit/domitBanner.gif new file mode 100644 index 0000000000000000000000000000000000000000..e87579012ce649d3a3420703f43e890a83e935d7 GIT binary patch literal 4285 zcmdT`=U-C?!@VoXO;|U~U>RWr2mu2Evc#Jp8*4yDP{4p72#8_HQ1K=~L_iHo5di}t zpaRB?J1j-58fT$ajSJCQpJ=tBw&m&b{ul50aDKn@?R+^SG(5z|H?f9eE?c&2admZda&mHXbY!#Hn>TN+sHo`a>EUoV zEEa3ircKGo$9p}SEr_?*4EZ$WMrH=bxNU7q@|_p*s((-5-F9+`}glRH#c{5bQBd8X*8O> zd-ryCcVE4F^}&M&J9qBfwryKiSJ$6^{<&(^s)mM!)vH%mRaK>=q&$EA{PX9}FJ8Qm z$z(tO{PVqg_jc{tb@1T9*4EbT+qXwXMz*!J9XWEu*VlLH(xnp<6ZZD@EiEklA)ea60Uc7iQ zIy!pMqD9A#AAkAsWlT&AkH?#xoqhJ~nNTS7@$q^0?%nzG=WpG*b?n%&@bK_+=gvu` z(!+-j^ZER{cke!Y`0(V(lPg!QOh`xw2ng`=^9v3R_V)Iknwkm;32AC-I&tE}*x1;O z8#e|92Hw7X+uz^6b?a7vKrl2k6dD@3d-v{Ne)(l&WaQ}4qpx1QI(_=|lP6EqYW4K= z^nwKoR;*a@=+UFTzP?MBE*&~_Xl7<6FfefO;>FjlUAuGV&ccNYqoSgQhllO#>@Huv z?B(TESy}1t?;jKtbo=)0<;$0cg@xU`dGqPhr`NAvkByB5{vX2TEAoG?IsfDTWdcX% z02s9hQ!0jKs1ZA9LfJXG&5|6}=A+P))H2*#uNT=v(E=Aiyasm7=S= z1Z$Goz~ef*tnG@lCq{#4`YFjRTz7P7Enb!`D&^y&B!pseVdneKA_>JJP(WY+DACI^ zaZ(UKR;<*R<0`cUl*5OtT}=nluE})G%mSTI0RdBWi~BHjP{h*xbl)x|HX^$bS$9Hy z{MWtou$s29+HWTV7KGi|&oeR&E6i6veL!+SCvUZHBmtVy+BxfG9m~G_0gcF4-M)13 z`xottoc~yj1RFqG89rgSh)*{05o&^U&FktXtn@iz04Nw@5@bZ_uKU9d6&-Ojb>d1Z z7WK(_u&F)wU)QW5QK=3lAFk2ma-~MA!#YP0w||srFf~t63G0W6RA%$L`ByF7{DvWm zBqbNr|G`f&Mkcp8J=ru9xAs-@<90Ab&$e0}yh&8$Jl&GM+x!V=$W;R{m7&BT-F_bl zNnfZ2stqq(BepPE8dZnq$05g#m{wm_R;=NPZgTh^}=muO*X|`kUDp&6hltuji}<=?nUm&FBAa z{ScC2pKx0EW0kX(Fj*;29WxE$c9K>s1rug@B*dyk+1{zq;{k@@ZP-;CM|(Q{2pZpk zfJ-dI&&?9U<}ejC&WBI@>ewd2Es)p`yhmX^h7KEJ3o={vFWD41V;=U$P=I(%Q#c4p$!WmcN$=?gi7iue0N%QpUJZ2hel6*>$& z<)zSjONUPCg!>cFyG_A&{?QY^M&3A@=jDbNptMNhn=cB4PcGL6hB@Q zIAlv+h^C_3aFYEdrZXl4Z5othc0VBO2l-5I5l8PQfW56#0wf7wYsL|ZpBf;~#f&mY zMFh~J)1{B9Ad5*NvUZ}9nx$gMm?9d&l#m-p9*0#fF4n8{kQlt$1r?Dn25!`^+UlsA zDl@t>g3W%En!o(X#j8)^G^mD2!}vt^}|=jByDCkxSVH z8HR{TLJ1UONi^>UeEkxG|7siH+OA!dXQ=mIta*=m{91cCS%I~x?MVGEpx@a=7F>9`cT572zH zeNpr-!lV+QESB>e^qy};8K3(?Kl8!e*YEfEeLlp&569YRD|=UbJ}kU>IR4D_xas28 z{%dIP?peX*Zw~;|jP{t4C=K+3rxa*vQMIDle;a3*OrFH&F~KVOj3!_6Oh#pnQIlKJ zs7_8J7bu4 zlZ=E_CdDa9CUb2f9baJs4JG-0D#ekQ>Y!?SQLq0%_|lS@v29TZO3{m_<%*Y(iUjZv z??8&tL|*MYIq3by`Rt7W%NmXxoEJ_E{ZqfZ_T-};y-kwPKo1-m@~<+;KZ6%!;~RG6 zbbTmo_IK+e2uhTOHZh)is}N+~&33x@X2)=LaYyAq_raTQMaaO#-|Q|4hV*9Ep=pz< z#{s%$#>!@apc&;`NS64)j=&erk2)r(X(zI?fl}nNbLIduH2`^?uVH zg^a>tZfA->ogxJQdq`#8pGVJuVc)aVAFEPN<-SOV29r-3l_)D7O1yARC+$5LO@MFcgcEm!Q{= zV}#TkRp}wK5|l3n4b_T=ljXLzf4d1eRo{XWCYGG#gkwmzj{ZuES!kd6(E*NEJEVoN zyi*7Z)1dv&Z~kPfAX%Xj6T}HHxk)7%2vPy}`2_dJPRZ^^ie0L|GRA(zNvRJDJ$eBt zr6U~E7k#AI%n}d`%*;2u1fa?>ljD4FS=0zAk~>20OC#GEry6Izqa|`xO@<1Qu|!4a zW(na^{W19;>3UGLShC;$V;Xzqux>RFG+2ilE#mUtnJ@b+FdS>^sk@<@rs(sE8rDy= znZz=g2(*M4>^gFZtK)oIW{iW_#I?Dnm#r(Bwl~ldFrLo5squKf1$9sv&O}oqq93bv z^23aay|rM)y#~Zu4p}PL!+RkAW1H#sHtT*U(Q^-BX+X!!oRm)$NIqQ7h0tcmKnp&& z3N?>K7^dd0QX(7$V{Xp^Pu#Dwm!S5Q=nNBIJ^?Jd;^nLi?~rqh`^*#gosj1;IO#cRo7Tv8rRcE>CJ{XLv4VA8YYptKfg`lYdM?870`kQ3 z&44<4jr;SE!k;IhDYV9OA#A3tePmM?n2-bKGhFbEzdqZsIJEZ7%sd8n3G+dP^&OBj z28Akjg@w@_L|N-j$2O!^ARkse0^>8%tP;AcavsXHsd9*rr`yIS7*h4i>VF>&uy8{7 zO2n3+q~U-8nU#sb7JFgzilxpPr$UuOAz*b%<&dm)Nai*sk9Z}|iJ@Jh#!|e|R@(r` zL-W-R)>B?NaZoJ*BN*9E4V&VCsRY^8nC2)>dodjE`x^0AQbWb0BpgiRA`~&2_OEOr zTlb_T9q>m4jwuR+%7p|PK){6pZ1Ku{b3^7nkQqr#@h16r;CQp8Z82rS+n zkZtOldk_`_37#LDT)Ln|a-ak@3N*AT;R@pqpeq$L(-Ejt-9i=Yd1>f>IpIfzX zUibIl14%F9SQy0MQlgJ%tewsfVU!=`4UgQ{zcGba0&wM@8L6x_De-G^sB84Ebgw+u zo&3!w&97&5Ye3|=0MCa#Gtnr+yf@@cPu_}DK;;uBM_L_v^{Mqm-vUg_Sl$@2SOhI+ zQmHuP&4rOxjolV42~i;XRb=N8GL=9hM2Q<8By4ej)~$v!wK^MX>MJ687YR^*??v8R z=yx#DT$z<6cj`4Qk8l IK@jl&FF6xz5dZ)H literal 0 HcmV?d00001 diff --git a/oss/joomla/trunk/libraries/domit/index.html b/oss/joomla/trunk/libraries/domit/index.html new file mode 100644 index 00000000..fa6d84e8 --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/oss/joomla/trunk/libraries/domit/license.txt b/oss/joomla/trunk/libraries/domit/license.txt new file mode 100644 index 00000000..532c8911 --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/license.txt @@ -0,0 +1,482 @@ +DOMIT! is a non-validating, but lightweight and fast DOM parser for PHP +DOMIT! RSS is a DOM based RSS parser for PHP + +By John Heinstein +jheinstein@engageinteractive.com +johnkarl@nbnet.nb.ca + +Copyright (C) 2004 John Heinstein +http://www.engageinteractive.com/domit/ +http://www.engageinteractive.com/domitrss/ +All rights reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + + + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/oss/joomla/trunk/libraries/domit/php_file_utilities.php b/oss/joomla/trunk/libraries/domit/php_file_utilities.php new file mode 100644 index 00000000..b881564d --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/php_file_utilities.php @@ -0,0 +1,50 @@ + \ No newline at end of file diff --git a/oss/joomla/trunk/libraries/domit/php_http_client_generic.php b/oss/joomla/trunk/libraries/domit/php_http_client_generic.php new file mode 100644 index 00000000..bfbf76bb --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/php_http_client_generic.php @@ -0,0 +1,761 @@ + +* @link http://www.engageinteractive.com/php_http_tools/ PHP HTTP Tools Home Page +* PHP HTTP Tools are Free Software +**/ + +if (!defined('PHP_HTTP_TOOLS_INCLUDE_PATH')) { + define('PHP_HTTP_TOOLS_INCLUDE_PATH', (dirname(__FILE__) . "/")); +} + +/** end-of-line character sequence as defined in HTTP spec */ +define ('CRLF', "\r\n"); +/** carriage return character */ +define ('CR', "\r"); +/** line feed character */ +define ('LF', "\n"); + +//http read states for client +/** beginning read state */ +define('HTTP_READ_STATE_BEGIN', 1); +/** state when reading headers */ +define('HTTP_READ_STATE_HEADERS', 2); +/** state when reading body of message */ +define('HTTP_READ_STATE_BODY', 3); + +require_once(PHP_HTTP_TOOLS_INCLUDE_PATH . 'php_http_exceptions.php'); + +/** + +* An HTTP Request class +* +* @package php-http-tools +* @author John Heinstein +*/ +class php_http_request { + /** @var object A reference to the headers object */ + var $headers = null; + /** @var string The requested method, e.g. GET, POST, HEAD */ + var $requestMethod = 'POST'; + /** @var string The requested path */ + var $requestPath = ''; + /** @var string The requested protocol */ + var $protocol = 'HTTP'; + /** @var string The version of the requested protocol */ + var $protocolVersion= '1.1'; + + /** + * Returns the headers object + * @return object The headers object + */ + function &getHeaders() { + return $this->headers; + } //getHeaders + + /** + * Sets the header to the specified value + * @param string The header name + * @param string The header value + * @param boolean True if multiple headers with the same name are allowed + */ + function setHeader($name, $value, $allowMultipleHeaders = false) { + $this->headers->setHeader($name, $value, $allowMultipleHeaders); + } //setHeader + + /** + * Default method for setting headers; meant to be overridden in subclasses + */ + function setHeaders() { + //you will want to override this method + $this->setHeader('User-Agent', 'PHP-HTTP-Client(Generic)/0.1'); + $this->setHeader('Connection', 'Close'); + } //setHeaders + + /** + * Sets the request method, e.g., GET + * @param string The name of the request method + * @return boolean True if the version number is valid + */ + function setRequestMethod($method) { + $method = strtoupper($method); + + switch ($method) { + case 'POST': + case 'GET': + case 'HEAD': + case 'PUT': + $this->requestMethod = $method; + return true; + break; + } + + return false; + } //setRequestMethod + + /** + * Sets the request path, e.g., http://www.engageinteractive.com/domit/test.xml + * @param string The request path + */ + function setRequestPath($path) { + $this->requestPath = $path; + } //setRequestPath + + /** + * Sets the version number of the protocol + * @param string The version number + * @return boolean True if the version number is valid + */ + function setProtocolVersion($version) { + if (($version == '1.0') || ($version == '1.1')) { + $this->protocolVersion = $version; + return true; + } + + return false; + } //setProtocolVersion + + /** + * Specifies a user name and password for basic authentication + * @param string The user name + * @param string The password + */ + function setAuthorization($user, $password) { + $encodedChallengeResponse = 'Basic ' . base64_encode($this->user . ':' . $this->password); + $this->setHeader('Authorization', $encodedChallengeResponse); + } //setAuthorization +} //php_http_request + + +class php_http_client_generic extends php_http_request { + /** @var object A reference to the connection object */ + var $connection; + /** @var string True if response headers are to be generated as an object */ + var $responseHeadersAsObject = false; + /** @var object The http response */ + var $response = null; + /** @var string A list of event names that can be fired by the client */ + var $events = array('onRequest' => null, 'onRead' => null, + 'onResponse' => null, 'onResponseHeaders' => null, + 'onResponseBody' => null); + + /** + * HTTP Client constructor + * @param string The client connection host name, with or without its protocol prefix + * @param string The client connection path, not including the host name + * @param int The port to establish the client connection on + * @param int The timeout value for the client connection + */ + function php_http_client_generic($host = '', $path = '/', $port = 80, $timeout = 0) { + $this->connection = new php_http_connection($host, $path, $port, $timeout); + $this->headers = new php_http_headers(); + $this->requestPath = $path; + $this->response = new php_http_response(); + $this->setHeaders(); + } //php_http_client_generic + + /** + * Specifies that the response headers array should be generated + * @param boolean True if the response headers array should be built + */ + function generateResponseHeadersAsObject($responseHeadersAsObject) { + $this->responseHeadersAsObject = $responseHeadersAsObject; + + if ($responseHeadersAsObject) { + $this->response->headers = new php_http_headers(); + } + } //generateResponseHeadersAsObject + + /** + * Fires an http event that has been registered + * @param string The name of the event, e.g., onRead + * @param string The data to be passed to the event + */ + function fireEvent($target, $data) { + if ($this->events[$target] != null) { + call_user_func($this->events[$target], $data); + } + } //fireEvent + + /** + * Sets which http events are to be fired + * @param string The http event option to be set + * @param string True if the event is to be fired + * @param object A reference to a custom handler for the http event data + */ + function setHTTPEvent($option, $truthVal, $customHandler = null) { + if ($customHandler != null) { + $handler =& $customHandler; + } + else { + $handler = array(&$this, 'defaultHTTPEventHandler'); + } + + switch($option) { + case 'onRequest': + case 'onRead': + case 'onResponse': + case 'onResponseHeaders': + case 'onResponseBody': + $truthVal ? ($this->events[$option] =& $handler) : ($this->events[$option] = null); + break; + } + } //setHTTPEvent + + /** + * Evaluates whether the specified http event option is active + * @param string The http event option to evaluate + * @return boolean True if the specified option is active + */ + function getHTTPEvent($option) { + switch($option) { + case 'onRequest': + case 'onRead': + case 'onResponse': + case 'onResponseHeaders': + case 'onResponseBody': + return ($this->events[$option] != null); + break; + } + } //getHTTPEvent + + /** + * The default http event handler; fired if no custom handler has been registered + * @param string The event data + */ + function defaultHTTPEventHandler($data) { + $this->printHTML($data); + } //defaultHTTPEventHandler + + /** + * Prints the data to the browser as preformatted, htmlentified output + * @param string The data to be printed + */ + function printHTML($html) { + print('
' . htmlentities($html)  . '
'); + } //printHTML + + /** + * Establishes a client connection + */ + function connect() { + if (!$this->headers->headerExists('Host')) { + $this->setHeader('Host', $this->connection->host); + } + + return $this->connection->connect(); + } //connect + + /** + * Disconnects the current client connection if one exists + */ + function disconnect() { + return $this->connection->disconnect(); + } //disconnect + + /** + * Evaluated whether the current client is connected + * @return boolean True if a connection exists + */ + function isConnected() { + return $this->connection->isOpen(); + } //isConnected + + /** + * Performs an HTTP GET + * @param string The target url + * @return object An HTTP response object + */ + function &get($url) { + $this->setRequestMethod('GET'); + $this->setRequestPath($url); + + $this->get_custom($url); + + $this->connect(); + + $result = $this->send(''); + + return $result; + } //get + + /** + * Handler for customizing the HTTP GET call + * @param string The target url + */ + function get_custom($url) { + //do nothing; meant to be overridden + } //get_custom + + /** + * Performs an HTTP POST + * @param string The posted data + * @return object An HTTP response object + */ + function &post($data) { + $this->setRequestMethod('POST'); + $this->setHeader('Content-Type', 'text/html'); + $this->post_custom($data); + + $this->connect(); + + return $this->send($data); + } //post + + /** + * Handler for customizing the HTTP POST call + * @param string The post data + */ + function post_custom($data) { + //do nothing; meant to be overridden + } //post_custom + + /** + * Performs an HTTP HEAD + * @param string The target url + * @return object An HTTP response object + */ + function &head($url) { + $this->setRequestMethod('HEAD'); + $this->head_custom($url); + + $this->connect(); + + return $this->send(''); + } //head + + /** + * Handler for customizing the HTTP HEAD call + * @param string The target url + */ + function head_custom($url) { + //do nothing; meant to be overridden + } //head_custom + + /** + * Sends data through the client connection + * @param string The message to be sent + * @return string The http response + */ + function send($message) { + $conn =& $this->connection; + + if ($conn->isOpen()) { + //build header info + $request = $this->requestMethod . ' ' . $this->requestPath . ' ' . $this->protocol . + '/' . $this->protocolVersion . CRLF; + $request .= $this->headers->toString() . CRLF; + $request .= $message; + + //init variables + $response = $headers = $body = ''; + $readState = HTTP_READ_STATE_BEGIN; + + $this->fireEvent('onRequest', $request); + + //send request + $connResource =& $conn->connection; + fputs ($connResource, $request); + + //read response + while (!feof($connResource)) { + $data = fgets($connResource, 4096); + $this->fireEvent('onRead', $data); + + switch ($readState) { + case HTTP_READ_STATE_BEGIN: + $this->response->statusLine = $data; + $readState = HTTP_READ_STATE_HEADERS; + break; + + case HTTP_READ_STATE_HEADERS: + if (trim($data) == '') { //end of headers is signalled by a blank line + $readState = HTTP_READ_STATE_BODY; + } + else { + if ($this->responseHeadersAsObject) { + $this->response->setUnformattedHeader($data); + } + else { + $this->response->headers .= $data; + } + } + break; + + case HTTP_READ_STATE_BODY: + $this->response->message .= $data; + break; + } + } + + $this->normalizeResponseIfChunked(); + + $headerString = is_object($this->response->headers) ? + $this->response->headers->toString() : $this->response->headers; + + $this->fireEvent('onResponseHeaders', $headerString); + $this->fireEvent('onResponseBody', $this->response->message); + + $this->fireEvent('onResponse', $this->response->headers . $this->response->message); + + return $this->response; + } + else { + HTTPExceptions::raiseException(HTTP_SOCKET_CONNECTION_ERR, ('HTTP Transport Error - Unable to establish connection to host ' . + $conn->host)); + } + } //send + + /** + * Determines if response data is transfer encoding chunked, then decodes + */ + function normalizeResponseIfChunked() { + if (($this->protocolVersion = '1.1') && (!$this->response->isResponseChunkDecoded)) { + if ($this->responseHeadersAsObject) { + if ($this->response->headers->headerExists('Transfer-Encoding') && + ($this->response->headers->getHeader('Transfer-Encoding') == 'chunked')) { + $this->response->message = $this->decodeChunkedData($this->response->getResponse()); + $this->response->isResponseChunkDecoded = true; + } + } + else { + if ((strpos($this->response->headers, 'Transfer-Encoding') !== false) && + (strpos($this->response->headers, 'chunked') !== false)){ + $this->response->message = $this->decodeChunkedData($this->response->getResponse()); + $this->response->isResponseChunkDecoded = true; + } + } + } + } //normalizeResponseIfChunked + + /** + * Decodes data if transfer encoding chunked + * @param string The encoded data + * @return string The decoded data + */ + function decodeChunkedData($data) { + $chunkStart = $chunkEnd = strpos($data, CRLF) + 2; + $chunkLengthInHex = substr($data, 0, $chunkEnd); + $chunkLength = hexdec(trim($chunkLengthInHex)); + + $decodedData = ''; + + while ($chunkLength > 0) { + $chunkEnd = strpos($data, CRLF, ($chunkStart + $chunkLength)); + + if (!$chunkEnd) { + //if the trailing CRLF is missing, return all the remaining data + $decodedData .= substr($data, $chunkStart); + break; + } + + $decodedData .= substr($data, $chunkStart, ($chunkEnd - $chunkStart)); + $chunkStart = $chunkEnd + 2; + $chunkEnd = strpos($data, CRLF, $chunkStart) + 2; + + if (!$chunkEnd) break; + + $chunkLengthInHex = substr($data, $chunkStart, ($chunkEnd - $chunkStart)); + $chunkLength = hexdec(trim($chunkLengthInHex)); + $chunkStart = $chunkEnd; + } + + return $decodedData; + } //decodeChunkedData +} //php_http_client_generic + + +/** +* An HTTP Connection class +* +* @package php-http-tools +* @author John Heinstein +*/ +class php_http_connection { + /** @var object A reference to the current connection */ + var $connection = null; + /** @var string The host of the connection */ + var $host; + /** @var string The path of the connection */ + var $path; + /** @var int The port of the connection */ + var $port; + /** @var int The timeout value for the connection */ + var $timeout; + /** @var int The error number of the connection */ + var $errorNumber = 0; + /** @var string The error string of the connection */ + var $errorString = ''; + + /** + * HTTP Connection constructor + * @param string The connection host name, with or without its protocol prefix + * @param string The connection path, not including the host name + * @param int The port to establish the client connection on + * @param int The timeout value for the client connection + */ + function php_http_connection($host = '', $path = '/', $port = 80, $timeout = 0) { + $this->host = $this->formatHost($host); + $this->path = $this->formatPath($path); + $this->port = $port; + $this->timeout = $timeout; + } //php_http_connection + + /** + * Formats a host string by stripping off the http:// prefix + * @param string The host name + * @return string The formatted host name + */ + function formatHost($hostString) { + $hasProtocol = (substr(strtoupper($hostString), 0, 7) == 'HTTP://'); + + if ($hasProtocol) { + $hostString = substr($hostString, 7); + } + + return $hostString; + } //formatHost + + /** + * Formats a path string + * @param string The path + * @return string The formatted path + */ + function formatPath($pathString) { + if (($pathString == '') || ($pathString == null)) { + $pathString = '/'; + } + + return $pathString; + } //formatPath + + /** + * Establishes a socket connection + * @return boolean True if the connection was successful + */ + function connect() { + if ($this->timeout == 0) { + $this->connection = @fsockopen($this->host, $this->port, $errorNumber, $errorString); + } + else { + $this->connection = @fsockopen($this->host, $this->port, $errorNumber, $errorString, $this->timeout); + } + + $this->errorNumber = $errorNumber; + $this->errorString = $errorString; + + return is_resource($this->connection); + } //connect + + /** + * Determines whether the connection is still open + * @return boolean True if the connection is still open + */ + function isOpen() { + return (is_resource($this->connection) && (!feof($this->connection))); + } //isOpen + + /** + * Disconnects the current connection + * @return boolean True if the connection has been disconnected + */ + function disconnect() { + fclose($this->connection); + $this->connection = null; + return true; + } //disconnect +} //php_http_connection + +/** +* An HTTP Headers class +* +* @package php-http-tools +* @author John Heinstein +*/ +class php_http_headers { + /** @var object An array of headers */ + var $headers; + + /** + * HTTP Headers constructor + */ + function php_http_headers() { + $this->headers = array(); + } //php_http_headers + + /** + * Returns the specified header value + * @param string The header name + * @return mixed The header value, or an array of header values + */ + function &getHeader($name) { + if ($this->headerExists($name)) { + return $this->headers[$name]; + } + + return false; + } //getHeader + + /** + * Sets the named header to the specified value + * @param string The header name + * @param string The header value + * @param boolean True if multiple headers with the same name are allowed + */ + function setHeader($name, $value, $allowMultipleHeaders = false) { + if ($allowMultipleHeaders) { + if (isset($this->headers[$name])) { + if (is_array($this->headers[$name])) { + $this->headers[$name][count($this->headers)] = $value; + } + else { + $tempVal = $this->headers[$name]; + $this->headers[$name] = array($tempVal, $value); + } + } + else { + $this->headers[$name] = array(); + $this->headers[$name][0] = $value; + } + } + else { + $this->headers[$name] = $value; + } + } //setHeader + + /** + * Determines whether the specified header exists + * @param string The header name + * @return boolean True if the specified header exists + */ + function headerExists($name) { + return isset($this->headers[$name]); + } //headerExists + + /** + * Removes the specified header + * @param string The header name + * @return boolean True if the specified header has been removed + */ + function removeHeader($name) { + if ($this->headerExists($name)) { + unset($this->headers[$name]); + return true; + } + + return false; + } //removeHeader + + /** + * Returns a reference to the headers array + * @return array The headers array + */ + function getHeaders() { + return $this->headers; + } //getHeaders + + /** + * Returns a list of existing headers names + * @return array A list of existing header names + */ + function getHeaderList() { + return array_keys($this->headers); + } //getHeaderList + + /** + * Returns a string representation of the headers array + * @return string A string representation of the headers array + */ + function toString() { + $retString = ''; + + foreach ($this->headers as $key => $value) { + if (is_array($value)) { + foreach ($value as $key2 => $value2) { + $retString .= $key . ': ' . $value2 . CRLF; + } + } + else { + $retString .= $key . ': ' . $value . CRLF; + } + } + + return $retString; + } //toString +} //php_http_headers + +/** +* An HTTP Response class +* +* @package php-http-tools +* @author John Heinstein +*/ +class php_http_response { + /** @var string Response number */ + var $statusLine = ''; + /** @var mixed Response headers, either as object or string */ + var $headers = ''; + /** @var string Response message */ + var $message = ''; + /** @var boolean True if the chunked transfer-encoding of the response has been decoded */ + var $isResponseChunkDecoded = false; + + /** + * Returns a reference to the headers array + * @return array The headers array + */ + function getResponse() { + return $this->message; + } //getResponse + + /** + * Returns the response status line + * @return string The response status line + */ + function getStatusLine() { + return $this->statusLine; + } //getStatusLine + + /** + * Returns the response status code + * @return int The response status code + */ + function getStatusCode() { + $statusArray = split(' ', $this->statusLine); + + if (count($statusArray > 1)) { + return intval($statusArray[1], 10); + } + + return -1; + } //getStatusCode + + /** + * Returns a reference to the headers array + * @return array The headers array + */ + function &getHeaders() { + return $this->headers; + } //getHeaders + + /** + * Converts a header string into a key value pair and sets header + */ + function setUnformattedHeader($headerString) { + $colonIndex = strpos($headerString, ':'); + + if ($colonIndex !== false) { + $key = trim(substr($headerString, 0, $colonIndex)); + $value = trim(substr($headerString, ($colonIndex + 1))); + $this->headers->setHeader($key, $value, true); + } + } //setUnformattedHeader +} //php_http_response + +?> \ No newline at end of file diff --git a/oss/joomla/trunk/libraries/domit/php_http_client_include.php b/oss/joomla/trunk/libraries/domit/php_http_client_include.php new file mode 100644 index 00000000..5cc3b34c --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/php_http_client_include.php @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/oss/joomla/trunk/libraries/domit/php_http_connector.php b/oss/joomla/trunk/libraries/domit/php_http_connector.php new file mode 100644 index 00000000..de23c546 --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/php_http_connector.php @@ -0,0 +1,77 @@ + +* @link http://www.engageinteractive.com/php_http_tools/ PHP HTTP Tools Home Page +* PHP HTTP Tools are Free Software +**/ + +if (!defined('PHP_HTTP_TOOLS_INCLUDE_PATH')) { + define('PHP_HTTP_TOOLS_INCLUDE_PATH', (dirname(__FILE__) . "/")); +} + +/** +* A helper class for establishing HTTP connections +* +* @package php-http-tools +* @author John Heinstein +*/ +class php_http_connector { + /** @var object A reference to a http connection or proxy, if one is required */ + var $httpConnection = null; + + /** + * Specifies the parameters of the http conection used to obtain the xml data + * @param string The ip address or domain name of the connection + * @param string The path of the connection + * @param int The port that the connection is listening on + * @param int The timeout value for the connection + * @param string The user name, if authentication is required + * @param string The password, if authentication is required + */ + function setConnection($host, $path = '/', $port = 80, $timeout = 0, $user = null, $password = null) { + require_once(PHP_HTTP_TOOLS_INCLUDE_PATH . 'php_http_client_generic.php'); + + $this->httpConnection = new php_http_client_generic($host, $path, $port, $timeout, $user, $password); + } //setConnection + + /** + * Specifies basic authentication for an http connection + * @param string The user name + * @param string The password + */ + function setAuthorization($user, $password) { + $this->httpConnection->setAuthorization($user, $password); + } //setAuthorization + + /** + * Specifies that a proxy is to be used to obtain the data + * @param string The ip address or domain name of the proxy + * @param string The path to the proxy + * @param int The port that the proxy is listening on + * @param int The timeout value for the connection + * @param string The user name, if authentication is required + * @param string The password, if authentication is required + */ + function setProxyConnection($host, $path = '/', $port = 80, $timeout = 0, $user = null, $password = null) { + require_once(PHP_HTTP_TOOLS_INCLUDE_PATH . 'php_http_proxy.php'); + + $this->httpConnection = new php_http_proxy($host, $path, $port, $timeout, $user, $password); + } //setProxyConnection + + /** + * Specifies basic authentication for the proxy + * @param string The user name + * @param string The password + */ + function setProxyAuthorization($user, $password) { + $this->httpConnection->setProxyAuthorization($user, $password); + } //setProxyAuthorization +} //php_http_connector + +?> \ No newline at end of file diff --git a/oss/joomla/trunk/libraries/domit/php_http_exceptions.php b/oss/joomla/trunk/libraries/domit/php_http_exceptions.php new file mode 100644 index 00000000..b982cd12 --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/php_http_exceptions.php @@ -0,0 +1,113 @@ + +* @link http://www.engageinteractive.com/php_http_tools/ PHP HTTP Tools Home Page +* PHP HTTP Tools are Free Software +**/ + +/** socket connection error */ +define('HTTP_SOCKET_CONNECTION_ERR', 1); +/** http transport error */ +define('HTTP_TRANSPORT_ERR', 2); + +//HTTPExceptions Error Modes +/** continue on error */ +define('HTTP_ONERROR_CONTINUE', 1); +/** die on error */ +define('HTTP_ONERROR_DIE', 2); + +/** +* @global object Reference to custom error handler for HTTP Exception class +*/ +$GLOBALS['HTTP_Exception_errorHandler'] = null; +/** +* @global int Error mode; specifies whether to die on error or simply return +*/ +//$GLOBALS['HTTP_Exception_mode'] = HTTP_ONERROR_RETURN; +// fixes bug identified here: sarahk.pcpropertymanager.com/blog/using-domit-rss/225/ +$GLOBALS['HTTP_Exception_mode'] = 1; +/** +* @global string Log file for errors +*/ +$GLOBALS['HTTP_Exception_log'] = null; + +/** +* An HTTP Exceptions class (not yet implemented) +* +* @package php-http-tools +* @author John Heinstein +*/ +class HTTPExceptions { + function raiseException($errorNum, $errorString) { + //die('HTTP Exception: ' . $errorNum . "\n " . $errorString); + + if ($GLOBALS['HTTP_Exception_errorHandler'] != null) { + call_user_func($GLOBALS['HTTP_Exception_errorHandler'], $errorNum, $errorString); + } + else { + $errorMessageText = $errorNum . ' ' . $errorString; + $errorMessage = 'Error: ' . $errorMessageText; + + if ((!isset($GLOBALS['HTTP_ERROR_FORMATTING_HTML'])) || + ($GLOBALS['HTTP_ERROR_FORMATTING_HTML'] == true)) { + $errorMessage = "

" . $errorMessage . "

"; + } + + //log error to file + if ((isset($GLOBALS['HTTP_Exception_log'])) && + ($GLOBALS['HTTP_Exception_log'] != null)) { + require_once(PHP_HTTP_TOOLS_INCLUDE_PATH . 'php_file_utilities.php'); + $logItem = "\n" . date('Y-m-d H:i:s') . ' HTTP Error ' . $errorMessageText; + php_file_utilities::putDataToFile($GLOBALS['HTTP_Exception_log'], + $logItem, 'a'); + } + + switch ($GLOBALS['HTTP_Exception_mode']) { + case HTTP_ONERROR_CONTINUE: + return; + break; + + case HTTP_ONERROR_DIE: + die($errorMessage); + break; + } + } + } //raiseException + + /** + * custom handler for HTTP errors + * @param object A reference to the custom error handler + */ + function setErrorHandler($method) { + $GLOBALS['HTTP_Exception_errorHandler'] =& $method; + } //setErrorHandler + + /** + * Set error mode + * @param int The HTTP error mode + */ + function setErrorMode($mode) { + $GLOBALS['HTTP_Exception_mode'] = $mode; + } //setErrorMode + + /** + * Set error mode + * @param boolean True if errors should be logged + * @param string Absolute or relative path to log file + */ + function setErrorLog($doLogErrors, $logfile) { + if ($doLogErrors) { + $GLOBALS['HTTP_Exception_log'] = $logfile; + } + else { + $GLOBALS['HTTP_Exception_log'] = null; + } + } //setErrorLog +} //HTTPExceptions + +?> \ No newline at end of file diff --git a/oss/joomla/trunk/libraries/domit/php_http_proxy.php b/oss/joomla/trunk/libraries/domit/php_http_proxy.php new file mode 100644 index 00000000..5f01f783 --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/php_http_proxy.php @@ -0,0 +1,78 @@ + +* @link http://www.engageinteractive.com/php_http_tools/ PHP HTTP Tools Home Page +* PHP HTTP Tools are Free Software +**/ +if (!defined('PHP_HTTP_TOOLS_INCLUDE_PATH')) { + define('PHP_HTTP_TOOLS_INCLUDE_PATH', (dirname(__FILE__) . "/")); +} + +require_once(PHP_HTTP_TOOLS_INCLUDE_PATH . 'php_http_client_generic.php'); + +/** +* An HTTP Proxy class +* +* @package php-http-tools +* @author John Heinstein +*/ +class php_http_proxy extends php_http_client_generic { + + /** + * HTTP Proxy constructor + * @param string The client connection host name, with or without its protocol prefix + * @param string The client connection path, not including the host name + * @param int The port to establish the client connection on + * @param int The timeout value for the client connection + */ + function php_http_proxy($host, $path = '/', $port = 80, $timeout = 0) { + $this->php_http_client_generic($host, $path, $port, $timeout); + $this->setHeaders(); + } //php_http_proxy + + /** + * Sets the proxy timeout to the specified value + * @param int The timeout value for the client connection + */ + function setTimeout($timeout) { + $this->timeout = $timeout; + } //setTimeout + + /** + * Sets the proxy headers + */ + function setHeaders() { + $this->setHeader('User-Agent', 'PHP-HTTP-Proxy-Client/0.1'); + $this->setHeader('Connection', 'Close'); + } //setHeaders + + /** + * Specifies a user name and password for basic proxy authentication + * @param string The user name for proxy authentication + * @param string The password for proxy authentication + */ + function setProxyAuthorization($user, $password) { + $encodedChallengeResponse = 'Basic ' . base64_encode($this->user . ':' . $this->password); + $this->setHeader('Proxy-Authorization', $encodedChallengeResponse); + } //setProxyAuthorization + + /** + * Handler for customizing the HTTP GET call + * @param string The target url + */ + function get_custom($filename) { + $url = $this->connection->formatHost($filename); + $sep = strpos($url, '/'); + $targetHost = substr($url, 0, $sep); + + $this->setHeader('Host', $targetHost); + } //get_custom +} //php_http_proxy + +?> \ No newline at end of file diff --git a/oss/joomla/trunk/libraries/domit/php_http_server_generic.php b/oss/joomla/trunk/libraries/domit/php_http_server_generic.php new file mode 100644 index 00000000..c1602628 --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/php_http_server_generic.php @@ -0,0 +1,193 @@ + null, 'onResponse' => null, + 'onGet' => null, 'onHead' => null, + 'onPost' => null, 'onPut' => null); + + function php_http_server_generic() { + //require_once(PHP_HTTP_TOOLS_INCLUDE_PATH . 'php_http_status_codes.php'); + //$this->httpStatusCodes =&new php_http_status_codes(); + } //php_http_server_generic + + function &getHeaders() { + $headers = headers_list(); + $response = ''; + + if (count($headers) > 0) { + foreach ($headers as $key => $value) { + $response .= $value . CRLF; + } + } + + return $response; + } //getHeaders + + function setProtocolVersion($version) { + if (($version == '1.0') || ($version == '1.1')) { + $$this->protocolVersion = $version; + return true; + } + + return false; + } //setProtocolVersion + + function setHeader($name, $value) { + header($name . ': ' . $value); + } //setHeader + + function setHeaders() { + //you will want to override this method + $this->setHeader('Content-Type', 'text/html'); + $this->setHeader('Server', 'PHP HTTP Server (Generic)/0.1'); + } //setHeaders + + function fireEvent($target, $data) { + if ($this->events[$target] != null) { + call_user_func($this->events[$target], $data); + } + } //fireEvent + + function fireHTTPEvent($target, $data = null) { + if ($this->events[$target] == null) { + //if no handler is assigned, + //delegate the event to the default handler + $this->setHTTPEvent($target); + } + + call_user_func($this->events[$target], $data); + } //fireHTTPEvent + + function setHTTPEvent($option, $customHandler = null) { + if ($customHandler != null) { + $handler =& $customHandler; + } + else { + $handler = array(&$this, 'defaultHTTPEventHandler'); + } + + switch($option) { + case 'onGet': + case 'onHead': + case 'onPost': + case 'onPut': + $this->events[$option] =& $handler; + break; + } + } //setHTTPServerEvent + + function defaultHTTPHandler() { + //will add functionality for this later + //work with subclasses for the time being + } //defaultHTTPHandler + + function setDebug($option, $truthVal, $customHandler = null) { + if ($customHandler != null) { + $handler =& $customHandler; + } + else { + $handler = array(&$this, 'defaultDebugHandler'); + } + + switch($option) { + case 'onRequest': + case 'onResponse': + $truthVal ? ($this->events[$option] =& $handler) : + ($this->events[$option] = null); + break; + } + } //setDebug + + function getDebug($option) { + switch($option) { + case 'onRequest': + case 'onResponse': + return ($this->events[$option] != null); + break; + } + } //getDebug + + function defaultDebugHandler($data) { + //just write to a log file, since can't display in a browser + $this->writeDebug($data); + } //defaultDebugHandler + + function writeDebug($data) { + $filename = 'debug_' . time() . '.txt'; + $fileHandle = fopen($fileName, 'a'); + fwrite($fileHandle, $data); + fclose($fileHandle); + } //writeDebug + + function receive() { + global $HTTP_SERVER_VARS; + $requestMethod = strToUpper($HTTP_SERVER_VARS['REQUEST_METHOD']); + + switch ($requestMethod) { + case 'GET': + $this->fireHTTPEvent('onGet'); + break; + + case 'HEAD': + $this->fireHTTPEvent('onHead'); + break; + + case 'POST': + $this->fireHTTPEvent('onPost'); + break; + + case 'PUT': + $this->fireHTTPEvent('onPut'); + break; + } + } //receive + + function respond($response) { + //build header info + //$response = $this->protocol . '/' . $this->protocolVersion . ' ' . + //$this->statusCode . ' ' . $this->httpStatusCodes->getCodeString($this->statusCode) . CRLF; + + if (!headers_sent()) { //avoid generating an error when in debug mode + $this->setHeader('Date', "date('r')"); + $this->setHeader('Content-Length', strlen($response)); + $this->setHeader('Connection', 'Close'); + } + + echo $response; + } //respond +} //php_http_server_generic + + +//To invoke the server, do: +//$httpServer = new php_http_server_generic(); //or instance of a subclass +//$httpServer->receive(); + +?> \ No newline at end of file diff --git a/oss/joomla/trunk/libraries/domit/php_http_server_include.php b/oss/joomla/trunk/libraries/domit/php_http_server_include.php new file mode 100644 index 00000000..994416e4 --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/php_http_server_include.php @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/oss/joomla/trunk/libraries/domit/php_http_status_codes.php b/oss/joomla/trunk/libraries/domit/php_http_status_codes.php new file mode 100644 index 00000000..4afe38af --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/php_http_status_codes.php @@ -0,0 +1,50 @@ +codes = array( + 200 => 'OK', + 201 => 'CREATED', + 202 => 'Accepted', + 203 => 'Partial Information', + 204 => 'No Response', + 301 => 'Moved', + 302 => 'Found', + 303 => 'Method', + 304 => 'Not Modified', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'PaymentRequired', + 403 => 'Forbidden', + 404 => 'Not found', + 500 => 'Internal Error', + 501 => 'Not implemented', + 502 => 'Service temporarily overloaded', + 503 => 'Gateway timeout'); + } //php_http_status_codes + + function getCodes() { + return $this->codes; + } //getCodes + + function getCodeString($code) { + return $this->codes[$code]; + } //getCodeString +} //class php_http_status_codes + +?> \ No newline at end of file diff --git a/oss/joomla/trunk/libraries/domit/php_text_cache.php b/oss/joomla/trunk/libraries/domit/php_text_cache.php new file mode 100644 index 00000000..f51ed366 --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/php_text_cache.php @@ -0,0 +1,192 @@ + +* @link http://www.engageinteractive.com/php_text_cache/ PHP Text Cache Home Page +* PHP Text Cache is Free Software +**/ + +if (!defined('PHP_TEXT_CACHE_INCLUDE_PATH')) { + define('PHP_TEXT_CACHE_INCLUDE_PATH', (dirname(__FILE__) . "/")); +} + +require_once(PHP_TEXT_CACHE_INCLUDE_PATH . 'php_http_connector.php'); + +/** +* A simple caching class for saving/retrieving local copies of url data +* +* @package php_text_cache +* @author John Heinstein +*/ +class php_text_cache extends php_http_connector { + /** @var string The directory in which cached files are stored */ + var $cacheDir; + /** @var int The amount time of time to wait before a cached file should be updated */ + var $cacheTime; + /** @var boolean True if an HTTP client should be used to establish the connection */ + var $doUseHTTPClient; + /** @var int Time in seconds to disconnect after attempting an http connection */ + var $httpTimeout; + + /** + * Constructor + * @param string Directory in which to store the cache files + * @param int Expiry time for cache file (-1 signifies no expiry limit) + * @param int Time in seconds to disconnect after attempting an http connection + */ + function php_text_cache($cacheDir = './', $cacheTime = -1, $timeout = 0) { + $this->cacheDir = $cacheDir; + $this->cacheTime = $cacheTime; + $this->timeout = $timeout; + } //php_text_cache + + /** + * Specifies the default timeout value for connecting to a host + * @param int The number of seconds to timeout when attempting to connect to a server + */ + function setTimeout($timeout) { + $this->timeout = $timeout; + } //setTimeout + + /** + * Gets data from an url, or its cache file + * + * @param string The url of the data + * @return string The data at the specified url + */ + function getData($url) { + $cacheFile = $this->getCacheFileName($url); + + if (is_file($cacheFile)) { + $fileStats = stat($cacheFile); + $lastChangeTime = $fileStats[9]; //mtime + $currTime = time(); + + if (($this->cacheTime != -1) && ($currTime - $lastChangeTime) > $this->cacheTime) { //get data from url + return $this->fromURL($url, $cacheFile); + } + else { //get data from file + return $this->fromCache($cacheFile); + } + } + else { + return $this->fromURL($url, $cacheFile); + } + } //getData + + /** + * Given an url, returns the path to the cache file + * + * Uses an md5 hash of the url. This can be + * overridden if a different approach is required + * + * @param string The url of the data + * @return string The cache file name + */ + function getCacheFileName($url) { + return ($this->cacheDir . md5($url)); + } //getCacheFileName + + /** + * Establishes a connection, given an url + * @param string The url of the data + */ + function establishConnection($url) { + require_once(PHP_TEXT_CACHE_INCLUDE_PATH . 'php_http_client_generic.php'); + + $host = php_http_connection::formatHost($url); + $host = substr($host, 0, strpos($host, '/')); + + $this->setConnection($host, '/', 80, $this->timeout); + } //establishConnection + + /** + * Specifies whether an HTTP client should be used to establish a connection + * @param boolean True if an HTTP client is to be used to establish the connection + */ + function useHTTPClient($truthVal) { + // fixes bug identified here: sarahk.pcpropertymanager.com/blog/using-domit-rss/225/ + //$this->doUseHTTPClient = truthVal; + $this->doUseHTTPClient = $truthVal; + } //useHTTPClient + + /** + * Gets data from an url and caches a copy of the data + * @param string The url for the data + * @param string The cache file path + * @return string The contents of the url + */ + function fromURL($url, $cacheFile) { + $fileContents = ''; + + if ($this->httpConnection != null) { + $response =& $this->httpConnection->get($url); + + if ($response != null) { + $fileContents = $response->getResponse(); + } + } + else if ($this->doUseHTTPClient) { + $this->establishConnection($url); + $response =& $this->httpConnection->get($url); + + if ($response != null) { + $fileContents = $response->getResponse(); + } + } + else { + $fileContents = $this->fromFile($url); + } + + //if file is empty, might need to establish an + //http connection to get the data + if (($fileContents == '') && !$this->doUseHTTPClient) { + $this->establishConnection($url); + $response =& $this->httpConnection->get($url); + + if ($response != null) { + $fileContents = $response->getResponse(); + } + } + + if ($fileContents != '') { + require_once(PHP_TEXT_CACHE_INCLUDE_PATH . 'php_file_utilities.php'); + php_file_utilities::putDataToFile($cacheFile, $fileContents, 'w'); + } + + return $fileContents; + } //fromURL + + /** + * Get text from cache file + * @param string The file path + * @return string The text contained in the file, or an empty string + */ + function fromCache($cacheFile) { + return $this->fromFile($cacheFile); + } //fromCache + + /** + * Get text from an url or file + * @param string The url or file path + * @return string The text contained in the url or file, or an empty string + */ + function fromFile($filename) { + if (function_exists('file_get_contents')) { + return @file_get_contents($filename); + } + else { + require_once(PHP_TEXT_CACHE_INCLUDE_PATH . 'php_file_utilities.php'); + $fileContents =& php_file_utilities::getDataFromFile($filename, 'r'); + return $fileContents; + } + + return ''; + } //fromFile +} //php_text_cache + +?> \ No newline at end of file diff --git a/oss/joomla/trunk/libraries/domit/timer.php b/oss/joomla/trunk/libraries/domit/timer.php new file mode 100644 index 00000000..97ae81da --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/timer.php @@ -0,0 +1,43 @@ +startTime = microtime(); + } //start + + function stop() { + $this->stopTime = microtime(); + } //stop + + function getTime() { + return $this->elapsed($this->startTime, $this->stopTime); + } //getTime + + function elapsed($a, $b) { + list($a_micro, $a_int) = explode(' ',$a); + list($b_micro, $b_int) = explode(' ',$b); + + if ($a_int > $b_int) { + return ($a_int - $b_int) + ($a_micro - $b_micro); + } + else if ($a_int == $b_int) { + if ($a_micro > $b_micro) { + return ($a_int - $b_int) + ($a_micro - $b_micro); + } + else if ($a_micro<$b_micro) { + return ($b_int - $a_int) + ($b_micro - $a_micro); + } + else { + return 0; + } + } + else { // $a_int < $b_int + return ($b_int - $a_int) + ($b_micro - $a_micro); + } + } //elapsed +} //Timer + +?> \ No newline at end of file diff --git a/oss/joomla/trunk/libraries/domit/xml_domit_cache.php b/oss/joomla/trunk/libraries/domit/xml_domit_cache.php new file mode 100644 index 00000000..dc6a1ff4 --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/xml_domit_cache.php @@ -0,0 +1,74 @@ + +* @link http://www.engageinteractive.com/domit/ DOMIT! Home Page +* DOMIT! is Free Software +**/ + +/** Extension for cache files */ +define ('DOMIT_FILE_EXTENSION_CACHE', 'dch'); + +/** +* A simple caching mechanism for a DOMIT_Document +*/ +class DOMIT_cache { + /** + * Serializes and caches the specified DOMIT! document + * @param string The name of the xml file to be saved + * @param Object A reference to the document to be saved + * @param string The write attributes for the saved document ('w' or 'wb') + */ + function toCache($xmlFileName, &$doc, $writeAttributes = 'w') { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_utilities.php'); + require_once(DOMIT_INCLUDE_PATH . 'php_file_utilities.php'); + + $name = DOMIT_Utilities::removeExtension($xmlFileName) . '.' . DOMIT_FILE_EXTENSION_CACHE; + php_file_utilities::putDataToFile($name, serialize($doc), $writeAttributes); + + return (file_exists($name) && is_writable($name)); + } //toCache + + /** + * Unserializes a cached DOMIT! document + * @param string The name of the xml file to be retrieved + * @return Object The retrieved document + */ + function &fromCache($xmlFileName) { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_utilities.php'); + require_once(DOMIT_INCLUDE_PATH . 'php_file_utilities.php'); + + $name = DOMIT_Utilities::removeExtension($xmlFileName) . '.' . DOMIT_FILE_EXTENSION_CACHE; + $fileContents =& php_file_utilities::getDataFromFile($name, 'r'); + $newxmldoc =& unserialize($fileContents); + + return $newxmldoc; + } //fromCache + + /** + * Determines whether a cached version of the specified document exists + * @param string The name of the xml file to be retrieved + * @return boolean True if a cache of the specified document exists + */ + function cacheExists($xmlFileName) { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_utilities.php'); + + $name = DOMIT_Utilities::removeExtension($xmlFileName) . '.' . DOMIT_FILE_EXTENSION_CACHE; + return file_exists($name); + } //xmlFileName + + /** + * Removes a cache of the specified document + * @param string The name of the xml file to be retrieved + * @return boolean True if a cache has been removed + */ + function removeFromCache($xmlFileName) { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_utilities.php'); + + $name = DOMIT_Utilities::removeExtension($xmlFileName) . '.' . DOMIT_FILE_EXTENSION_CACHE; + return unlink($name); + } //removeFromCache +} //DOMIT_cache +?> diff --git a/oss/joomla/trunk/libraries/domit/xml_domit_doctor.php b/oss/joomla/trunk/libraries/domit/xml_domit_doctor.php new file mode 100644 index 00000000..2b31b91d --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/xml_domit_doctor.php @@ -0,0 +1,94 @@ + +* @link http://www.engageinteractive.com/domit/ DOMIT! Home Page +* DOMIT! is Free Software +**/ + +/** +* A (static) class containing utilities for repairing malformed XML +* +* @package domit-xmlparser +* @author John Heinstein +*/ +class domit_doctor { + + /** + * Looks for illegal ampersands and converts them to entities + * @param string The xml text to be repaired + * @return string The repaired xml text + */ + function fixAmpersands($xmlText) { + $xmlText = trim($xmlText); + $startIndex = -1; + $processing = true; + $illegalChar = '&'; + + while ($processing) { + $startIndex = strpos($xmlText, $illegalChar, ($startIndex + 1)); + + if ($startIndex !== false) { + $xmlText = domit_doctor::evaluateCharacter($xmlText, + $illegalChar, ($startIndex + 1)); + } + else { + $processing = false; + } + } + + return $xmlText; + } //fixAmpersands + + /** + * Evaluates whether an ampersand should be converted to an entity, and performs the conversion + * @param string The xml text + * @param string The (ampersand) character + * @param int The character index immediately following the ampersand in question + * @return string The repaired xml text + */ + function evaluateCharacter($xmlText, $illegalChar, $startIndex) { + $total = strlen($xmlText); + $searchingForCDATASection = false; + + for ($i = $startIndex; $i < $total; $i++) { + $currChar = substr($xmlText, $i, 1); + + if (!$searchingForCDATASection) { + switch ($currChar) { + case ' ': + case "'": + case '"': + case "\n": + case "\r": + case "\t": + case '&': + case "]": + $searchingForCDATASection = true; + break; + case ";": + return $xmlText; + break; + } + } + else { + switch ($currChar) { + case '<': + case '>': + return (substr_replace($xmlText, '&', + ($startIndex - 1) , 1)); + break; + case "]": + return $xmlText; + break; + } + } + } + + return $xmlText; + } //evaluateCharacter +} //domit_doctor +?> diff --git a/oss/joomla/trunk/libraries/domit/xml_domit_getelementsbypath.php b/oss/joomla/trunk/libraries/domit/xml_domit_getelementsbypath.php new file mode 100644 index 00000000..7473dcd5 --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/xml_domit_getelementsbypath.php @@ -0,0 +1,278 @@ + +* @link http://www.engageinteractive.com/domit/ DOMIT! Home Page +* DOMIT! is Free Software +**/ + +if (!defined('DOMIT_INCLUDE_PATH')) { + define('DOMIT_INCLUDE_PATH', (dirname(__FILE__) . "/")); +} + +/** Separator for elements path */ +define('GET_ELEMENTS_BY_PATH_SEPARATOR', '/'); +/** Constant for an absolute path search (starting at the document root) */ +define('GET_ELEMENTS_BY_PATH_SEARCH_ABSOLUTE', 0); +/** Constant for a relative path search (starting at the level of the calling node) */ +define('GET_ELEMENTS_BY_PATH_SEARCH_RELATIVE', 1); +/** Constant for a variable path search (finds all matches, regardless of place in the hierarchy) */ +define('GET_ELEMENTS_BY_PATH_SEARCH_VARIABLE', 2); + +/** +* getElementsByPath is a simple utility for path-based access to nodes in a DOMIT! document. +*/ +class DOMIT_GetElementsByPath { + /** @var Object The node from which the search is called */ + var $callingNode; + /** @var int The type of search to be performed, i.e., relative, absolute, or variable */ + var $searchType; + /** @var Object The node that is the current parent of the search */ + var $contextNode; + /** @var array An array containing a series of path segments for which to search */ + var $arPathSegments = array(); + /** @var Object A DOMIT_NodeList of matching nodes */ + var $nodeList; + /** @var Object The index of the current node of the search */ + var $targetIndex; + /** @var Object if true, the search will be aborted once the first match is found */ + var $abortSearch = false; + + /** + * Constructor - creates an empty DOMIT_NodeList to store matching nodes + */ + function DOMIT_GetElementsByPath() { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_nodemaps.php'); + $this->nodeList = new DOMIT_NodeList(); + } //DOMIT_GetElementsByPath + + /** + * Parses the supplied "path"-based pattern + * @param Object The node from which the search is called + * @param string The pattern + * @param int The node level of the current search + * @return Object The NodeList containing matching nodes + */ + function &parsePattern(&$node, $pattern, $nodeIndex = 0) { + $this->callingNode =& $node; + $pattern = trim($pattern); + + $this->determineSearchType($pattern); + $this->setContextNode(); + $this->splitPattern($pattern); + + $this->targetIndex = $nodeIndex; + $totalSegments = count($this->arPathSegments); + + if ($totalSegments > 0) { + if ($this->searchType == GET_ELEMENTS_BY_PATH_SEARCH_VARIABLE) { + $arContextNodes =& $this->contextNode->ownerDocument->getElementsByTagName($this->arPathSegments[0]); + $totalContextNodes = $arContextNodes->getLength(); + + for ($i = 0; $i < $totalContextNodes; $i++) { + $this->selectNamedChild($arContextNodes->item($i), 1); + } + } + else { + if ($this->searchType == GET_ELEMENTS_BY_PATH_SEARCH_ABSOLUTE) { + if ($this->contextNode->nodeName == $this->arPathSegments[0]) { + if (count($this->arPathSegments) == 1) { + $this->nodeList->appendNode($this->contextNode); + } + else { + $this->selectNamedChild($this->contextNode, 1); + } + } + } + else if ($this->searchType == GET_ELEMENTS_BY_PATH_SEARCH_RELATIVE) { + $this->selectNamedChild($this->contextNode, 0); + } + } + } + + if ($nodeIndex > 0) { + if ($nodeIndex <= $this->nodeList->getLength()) { + return $this->nodeList->item(($nodeIndex - 1)); + } + else { + $null = null; + return $null; + } + } + + return $this->nodeList; + } //parsePattern + + /** + * Determines the type of search to be performed: absolute, relative, or variable + * @param string The pattern + */ + function determineSearchType($pattern) { + $firstChar = $pattern{0}; + + if ($firstChar != GET_ELEMENTS_BY_PATH_SEPARATOR) { + //relative path + $this->searchType = GET_ELEMENTS_BY_PATH_SEARCH_RELATIVE; + } + else { + $secondChar = $pattern{1}; + + if ($secondChar != GET_ELEMENTS_BY_PATH_SEPARATOR) { + //absolute path + $this->searchType = GET_ELEMENTS_BY_PATH_SEARCH_ABSOLUTE; + } + else { + //variable path + $this->searchType = GET_ELEMENTS_BY_PATH_SEARCH_VARIABLE; + } + } + } //determineSearchType + + + /** + * Sets the context node, i.e., the node from which the search begins + */ + function setContextNode() { + switch($this->searchType) { + case GET_ELEMENTS_BY_PATH_SEARCH_ABSOLUTE: + $this->contextNode =& $this->callingNode->ownerDocument->documentElement; + break; + + case GET_ELEMENTS_BY_PATH_SEARCH_RELATIVE: + if ($this->callingNode->uid != $this->callingNode->ownerDocument->uid) { + $this->contextNode =& $this->callingNode; + } + else { + $this->contextNode =& $this->callingNode->ownerDocument->documentElement; + } + break; + + case GET_ELEMENTS_BY_PATH_SEARCH_VARIABLE: + $this->contextNode =& $this->callingNode->ownerDocument->documentElement; + break; + } + } //setContextNode + + /** + * Splits the supplied pattern into searchable segments + * @param string The pattern + */ + function splitPattern($pattern) { + switch($this->searchType) { + case GET_ELEMENTS_BY_PATH_SEARCH_ABSOLUTE: + $lastIndex = 1; + break; + + case GET_ELEMENTS_BY_PATH_SEARCH_RELATIVE: + $lastIndex = 0; + break; + + case GET_ELEMENTS_BY_PATH_SEARCH_VARIABLE: + $lastIndex = 2; + break; + } + + $this->arPathSegments = explode(GET_ELEMENTS_BY_PATH_SEPARATOR, substr($pattern, $lastIndex)); + } //splitPattern + + /** + * Matches the current path segment against the child nodes of the current context node + * @param Object The context node + * @param int The index in the arPathSegments array of the current path segment + */ + function selectNamedChild(&$node, $pIndex) { + if (!$this->abortSearch) { + if ($pIndex < count($this->arPathSegments)) { //not at last path segment + $name = $this->arPathSegments[$pIndex]; + $numChildren = $node->childCount; + + for ($i = 0; $i < $numChildren; $i++) { + $currentChild =& $node->childNodes[$i]; + + if ($currentChild->nodeName == $name) { + $this->selectNamedChild($currentChild, ($pIndex + 1)); + } + } + } + else { + $this->nodeList->appendNode($node); + + if ($this->targetIndex == $this->nodeList->getLength()) { + $this->abortSearch = true; + } + } + } + } //selectNamedChild +} //DOMIT_GetElementsByPath + + + +/** +* getElementsByAttributePath is a temporary utility requested by a DOMIT! user for path-based access to attributes in a DOMIT! document. +* This class may be absent from future versions of DOMIT! +*/ +class DOMIT_GetElementsByAttributePath { + /** @var Object A DOMIT_NodeList of matching attribute nodes */ + var $nodeList; + + /** + * Constructor - creates an empty DOMIT_NodeList to store matching nodes + */ + function DOMIT_GetElementsByAttributePath() { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_nodemaps.php'); + $this->nodeList = new DOMIT_NodeList(); + } //DOMIT_GetElementsByAttributePath + + /** + * Matches the current path segment against the child nodes of the current context node + * @param Object The context node + * @param string The pattern + * @param int The index of the current path segment + */ + function &parsePattern(&$node, $pattern, $nodeIndex = 0) { + $beginSquareBrackets = strpos($pattern, '['); + + if ($beginSquareBrackets != 0) { + $path = substr($pattern, 0, $beginSquareBrackets); + + $attrPattern = substr($pattern, (strpos($pattern, '@') + 1)); + $attrPattern = substr($attrPattern, 0, strpos($attrPattern, ')')); + + $commaIndex = strpos($attrPattern, ','); + $key = trim(substr($attrPattern, 0, $commaIndex)); + $value = trim(substr($attrPattern, ($commaIndex + 1))); + $value = substr($value, 1, (strlen($value) - 2)); + + $gebp = new DOMIT_GetElementsByPath(); + $myResponse =& $gebp->parsePattern($node, $path); + + $total = $myResponse->getLength(); + + for ($i = 0; $i < $total; $i++) { + $currNode =& $myResponse->item($i); + + if ($currNode->hasAttribute($key)) { + if ($currNode->getAttribute($key) == $value) { + $this->nodeList->appendNode($currNode); + } + } + } + } + + if ($nodeIndex == 0) { + return $this->nodeList; + } + else { + if ($nodeIndex <= $this->nodeList->getLength()) { + return $this->nodeList->item(($nodeIndex - 1)); + } + else { + $this->nodeList = new DOMIT_NodeList(); + return $this->nodeList; + } + } + } //parsePattern +} //DOMIT_GetElementsByAttributePath +?> \ No newline at end of file diff --git a/oss/joomla/trunk/libraries/domit/xml_domit_include.php b/oss/joomla/trunk/libraries/domit/xml_domit_include.php new file mode 100644 index 00000000..9bb14d25 --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/xml_domit_include.php @@ -0,0 +1,17 @@ + +* @link http://www.engageinteractive.com/domit/ DOMIT! Home Page +* DOMIT! is Free Software +**/ + +/** The file system path to the domit library */ + +if (!defined( 'DOMIT_INCLUDE_PATH' )) { + define('DOMIT_INCLUDE_PATH', (dirname(__FILE__) . "/")); +} +require_once(DOMIT_INCLUDE_PATH . 'xml_domit_parser.php'); +?> diff --git a/oss/joomla/trunk/libraries/domit/xml_domit_lite_include.php b/oss/joomla/trunk/libraries/domit/xml_domit_lite_include.php new file mode 100644 index 00000000..8dab5130 --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/xml_domit_lite_include.php @@ -0,0 +1,15 @@ + +* @link http://www.engageinteractive.com/domit/ DOMIT! Home Page +* DOMIT! is Free Software +**/ + +/** The file system path to the domit library */ +define('DOMIT_INCLUDE_PATH', (dirname(__FILE__) . "/")); +require_once(DOMIT_INCLUDE_PATH . 'xml_domit_lite_parser.php'); +?> diff --git a/oss/joomla/trunk/libraries/domit/xml_domit_lite_parser.php b/oss/joomla/trunk/libraries/domit/xml_domit_lite_parser.php new file mode 100644 index 00000000..49908ab6 --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/xml_domit_lite_parser.php @@ -0,0 +1,1892 @@ + +* @link http://www.engageinteractive.com/domit/ DOMIT! Home Page +* DOMIT! is Free Software +**/ + +if (!defined('DOMIT_INCLUDE_PATH')) { + define('DOMIT_INCLUDE_PATH', (dirname(__FILE__) . "/")); +} + +/** current version of DOMIT! Lite */ +define ('DOMIT_LITE_VERSION', '1.01'); + +/** +*@global array Flipped version of $definedEntities array, to allow two-way conversion of entities +* +* Made global so that Attr nodes, which have no ownerDocument property, can access the array +*/ +$GLOBALS['DOMIT_defined_entities_flip'] = array(); + +require_once(DOMIT_INCLUDE_PATH . 'xml_domit_shared.php'); + +/** +* The base class of all DOMIT node types +* +* @package domit-xmlparser +* @subpackage domit-xmlparser-lite +* @author John Heinstein +*/ +class DOMIT_Lite_Node { + /** @var string The name of the node, varies according to node type */ + var $nodeName = null; + /** @var string The value of the node, varies according to node type */ + var $nodeValue = null; + /** @var int The type of node, e.g. CDataSection */ + var $nodeType = null; + /** @var Object A reference to the parent of the current node */ + var $parentNode = null; + /** @var Array An array of child node references */ + var $childNodes = null; + /** @var Object A reference to the first node in the childNodes list */ + var $firstChild = null; + /** @var Object A reference to the last node in the childNodes list */ + var $lastChild = null; + /** @var Object A reference to the node prior to the current node in its parents childNodes list */ + var $previousSibling = null; + /** @var Object A reference to the node after the current node in its parents childNodes list */ + var $nextSibling = null; + /** @var Array An array of attribute key / value pairs */ + var $attributes = null; + /** @var Object A reference to the Document node */ + var $ownerDocument = null; + /** @var string The unique node id */ + var $uid; + /** @var int The number of children of the current node */ + var $childCount = 0; + + /** + * Raises error if abstract class is directly instantiated + */ + function DOMIT_Lite_Node() { + DOMIT_DOMException::raiseException(DOMIT_ABSTRACT_CLASS_INSTANTIATION_ERR, + 'Cannot instantiate abstract class DOMIT_Lite_Node'); + } //DOMIT_Lite_Node + + /** + * DOMIT_Lite_Node constructor, assigns a uid + */ + function _constructor() { + global $uidFactory; + $this->uid = $uidFactory->generateUID(); + } //_constructor + + /** + * Appends a node to the childNodes list of the current node + * @abstract + * @param Object The node to be appended + * @return Object The appended node + */ + function &appendChild(&$child) { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Method appendChild cannot be called by class ' . get_class($this))); + } //appendChild + + /** + * Inserts a node to the childNodes list of the current node + * @abstract + * @param Object The node to be inserted + * @param Object The node before which the insertion is to occur + * @return Object The inserted node + */ + function &insertBefore(&$newChild, &$refChild) { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Method insertBefore cannot be called by class ' . get_class($this))); + } //insertBefore + + /** + * Replaces a node with another + * @abstract + * @param Object The new node + * @param Object The old node + * @return Object The new node + */ + function &replaceChild(&$newChild, &$oldChild) { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Method replaceChild cannot be called by class ' . get_class($this))); + } //replaceChild + + /** + * Removes a node from the childNodes list of the current node + * @abstract + * @param Object The node to be removed + * @return Object The removed node + */ + function &removeChild(&$oldChild) { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Method removeChild cannot be called by class ' . get_class($this))); + } //removeChild + + /** + * Returns the index of the specified node in a childNodes list + * @param Array The childNodes array to be searched + * @param Object The node targeted by the search + * @return int The index of the target node, or -1 if not found + */ + function getChildNodeIndex(&$arr, &$child) { + $index = -1; + $total = count($arr); + + for ($i = 0; $i < $total; $i++) { + if ($child->uid == $arr[$i]->uid) { + $index = $i; + break; + } + } + + return $index; + } //getChildNodeIndex + + /** + * Determines whether a node has any children + * @return boolean True if any child nodes are present + */ + function hasChildNodes() { + return ($this->childCount > 0); + } //hasChildNodes + + /** + * Copies a node and/or its children + * @abstract + * @param boolean True if all child nodes are also to be cloned + * @return Object A copy of the node and/or its children + */ + function &cloneNode($deep = false) { + DOMIT_DOMException::raiseException(DOMIT_ABSTRACT_METHOD_INVOCATION_ERR, + 'Cannot invoke abstract method DOMIT_Lite_Node->cloneNode($deep). Must provide an overridden method in your subclass.'); + } //cloneNode + + /** + * Adds elements with the specified tag name to a NodeList collection + * @param Object The NodeList collection + * @param string The tag name of matching elements + */ + function getNamedElements(&$nodeList, $tagName) { + //Implemented in DOMIT_Lite_Element. + //Needs to be here though! This is called against all nodes in the document. + } //getNamedElements + + /** + * Sets the ownerDocument property of a node to the containing DOMIT_Document + * @param Object A reference to the document element of the DOMIT_Document + */ + function setOwnerDocument(&$rootNode) { + if ($rootNode->ownerDocument == null) { + unset($this->ownerDocument); + $this->ownerDocument = null; + } + else { + $this->ownerDocument =& $rootNode->ownerDocument; + } + + $total = $this->childCount; + + for ($i = 0; $i < $total; $i++) { + $this->childNodes[$i]->setOwnerDocument($rootNode); + } + } //setOwnerDocument + + /** + * Tests whether a value is null, and if so, returns a default value + * @param mixed The value to be tested + * @param mixed The default value + * @return mixed The specified value, or the default value if null + */ + function &nvl(&$value,$default) { + if (is_null($value)) return $default; + return $value; + } //nvl + + /** + * Retrieves an element or DOMIT_NodeList of elements corresponding to an Xpath-like expression. + * @abstract + * @param string The query pattern + * @param int If a single node is to be returned (rather than the entire NodeList) the index of that node + * @return mixed A NodeList or single node that matches the pattern + */ + function &getElementsByPath($pattern, $nodeIndex = 0) { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Method getElementsByPath cannot be called by class ' . get_class($this))); + } //getElementsByPath + + /** + * Returns the concatented text of the current node and its children + * @return string The concatented text of the current node and its children + */ + function getText() { + return $this->nodeValue; + } //getText + + /** + * Formats a string for presentation as HTML + * @param string The string to be formatted + * @param boolean True if the string is to be sent directly to output + * @return string The HTML formatted string + */ + function forHTML($str, $doPrint = false) { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_utilities.php'); + return DOMIT_Utilities::forHTML($str, $doPrint); + } //forHTML + + /** + * Generates an array representation of the node and its children + * @abstract + * @return Array A representation of the node and its children + */ + function toArray() { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Method toArray cannot be called by class ' . get_class($this))); + } //toArray + + /** + * A node event that can be set to fire upon document loading, used for node initialization + * @abstract + */ + function onLoad() { + //you can override this method if you subclass any of the + //DOMIT_Lite_Nodes. It's a way of performing + //initialization of your subclass as soon as the document + //has been loaded (as opposed to as soon as the current node + //has been instantiated). + } //onLoad + + /** + * Clears previousSibling, nextSibling, and parentNode references from a node that has been removed + */ + function clearReferences() { + if ($this->previousSibling != null) { + unset($this->previousSibling); + $this->previousSibling = null; + } + if ($this->nextSibling != null) { + unset($this->nextSibling); + $this->nextSibling = null; + } + if ($this->parentNode != null) { + unset($this->parentNode); + $this->parentNode = null; + } + } //clearReferences + + /** + * Generates a normalized (formatted for readability) representation of the node and its children + * @param boolean True if HTML readable output is desired + * @param boolean True if illegal xml characters in text nodes and attributes should be converted to entities + * @return string The formatted string representation + */ + function toNormalizedString($htmlSafe = false, $subEntities = false) { + //require this file for generating a normalized (readable) xml string representation + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_utilities.php'); + global $DOMIT_defined_entities_flip; + + $result = DOMIT_Utilities::toNormalizedString($this, $subEntities, $DOMIT_defined_entities_flip); + + if ($htmlSafe) $result = $this->forHTML($result); + + return $result; + } //toNormalizedString +} //DOMIT_Lite_Node + + +/** +* A parent class for nodes which possess child nodes +* +* @package domit-xmlparser +* @subpackage domit-xmlparser-lite +* @author John Heinstein +*/ +class DOMIT_Lite_ChildNodes_Interface extends DOMIT_Lite_Node { + /** + * Raises error if abstract class is directly instantiated + */ + function DOMIT_Lite_ChildNodes_Interface() { + DOMIT_DOMException::raiseException(DOMIT_ABSTRACT_CLASS_INSTANTIATION_ERR, + 'Cannot instantiate abstract class DOMIT_Lite_ChildNodes_Interface'); + } //DOMIT_Lite_ChildNodes_Interface + + /** + * Appends a node to the childNodes list of the current node + * @param Object The node to be appended + * @return Object The appended node + */ + /** + * Appends a node to the childNodes list of the current node + * @param Object The node to be appended + * @return Object The appended node + */ + function &appendChild(&$child) { + if (!($this->hasChildNodes())) { + $this->childNodes[0] =& $child; + $this->firstChild =& $child; + } + else { + //remove $child if it already exists + $index = $this->getChildNodeIndex($this->childNodes, $child); + + if ($index != -1) { + $this->removeChild($child); + } + + //append child + $numNodes = $this->childCount; + //BB: was bug auto-created wrong childnodes[-1]: added IF + if ($numNodes>0) $prevSibling =& $this->childNodes[($numNodes - 1)]; + + $this->childNodes[$numNodes] =& $child; + + //set next and previous relationships + //BB: added this line and the else part to finish correcting bug + if (isset($prevSibling)) { + $child->previousSibling =& $prevSibling; + $prevSibling->nextSibling =& $child; + } else { + unset($child->previousSibling); + $child->previousSibling = null; + $this->firstChild =& $child; + } + } + + $this->lastChild =& $child; + $child->parentNode =& $this; + + unset($child->nextSibling); + $child->nextSibling = null; + + $child->setOwnerDocument($this); + $this->childCount++; + + return $child; + } //appendChild + + /** + * Inserts a node to the childNodes list of the current node + * @param Object The node to be inserted + * @param Object The node before which the insertion is to occur + * @return Object The inserted node + */ + function &insertBefore(&$newChild, &$refChild) { + if (($refChild->nodeType == DOMIT_DOCUMENT_NODE) || + ($refChild->parentNode == null)) { + + DOMIT_DOMException::raiseException(DOMIT_NOT_FOUND_ERR, + 'Reference child not present in the child nodes list.'); + } + + //if reference child is also the node to be inserted + //leave the document as is and don't raise an exception + if ($refChild->uid == $newChild->uid) { + return $newChild; + } + + //remove $newChild if it already exists + $index = $this->getChildNodeIndex($this->childNodes, $newChild); + if ($index != -1) { + $this->removeChild($newChild); + } + + //find index of $refChild in childNodes + $index = $this->getChildNodeIndex($this->childNodes, $refChild); + + if ($index != -1) { + //reset sibling chain + if ($refChild->previousSibling != null) { + $refChild->previousSibling->nextSibling =& $newChild; + $newChild->previousSibling =& $refChild->previousSibling; + } + else { + $this->firstChild =& $newChild; + + if ($newChild->previousSibling != null) { + unset($newChild->previousSibling); + $newChild->previousSibling = null; + } + } + + $newChild->parentNode =& $refChild->parentNode; + $newChild->nextSibling =& $refChild; + $refChild->previousSibling =& $newChild; + + //add node to childNodes + $i = $this->childCount; + + while ($i >= 0) { + if ($i > $index) { + $this->childNodes[$i] =& $this->childNodes[($i - 1)]; + } + else if ($i == $index) { + $this->childNodes[$i] =& $newChild; + } + $i--; + } + + $this->childCount++; + } + else { + $this->appendChild($newChild); + } + + $newChild->setOwnerDocument($this); + + return $newChild; + } //insertBefore + + /** + * Replaces a node with another + * @param Object The new node + * @param Object The old node + * @return Object The new node + */ + function &replaceChild(&$newChild, &$oldChild) { + if ($this->hasChildNodes()) { + //remove $newChild if it already exists + $index = $this->getChildNodeIndex($this->childNodes, $newChild); + if ($index != -1) { + $this->removeChild($newChild); + } + + //find index of $oldChild in childNodes + $index = $this->getChildNodeIndex($this->childNodes, $oldChild); + + if ($index != -1) { + $newChild->ownerDocument =& $oldChild->ownerDocument; + $newChild->parentNode =& $oldChild->parentNode; + + //reset sibling chain + if ($oldChild->previousSibling == null) { + unset($newChild->previousSibling); + $newChild->previousSibling = null; + } + else { + $oldChild->previousSibling->nextSibling =& $newChild; + $newChild->previousSibling =& $oldChild->previousSibling; + } + + if ($oldChild->nextSibling == null) { + unset($newChild->nextSibling); + $newChild->nextSibling = null; + } + else { + $oldChild->nextSibling->previousSibling =& $newChild; + $newChild->nextSibling =& $oldChild->nextSibling; + } + + $this->childNodes[$index] =& $newChild; + + if ($index == 0) $this->firstChild =& $newChild; + if ($index == ($this->childCount - 1)) $this->lastChild =& $newChild; + + $newChild->setOwnerDocument($this); + + return $newChild; + } + } + + DOMIT_DOMException::raiseException(DOMIT_NOT_FOUND_ERR, + ('Reference node for replaceChild not found.')); + } //replaceChild + + /** + * Removes a node from the childNodes list of the current node + * @param Object The node to be removed + * @return Object The removed node + */ + function &removeChild(&$oldChild) { + if ($this->hasChildNodes()) { + //find index of $oldChild in childNodes + $index = $this->getChildNodeIndex($this->childNodes, $oldChild); + + if ($index != -1) { + //reset sibling chain + if (($oldChild->previousSibling != null) && ($oldChild->nextSibling != null)) { + $oldChild->previousSibling->nextSibling =& $oldChild->nextSibling; + $oldChild->nextSibling->previousSibling =& $oldChild->previousSibling; + } + else if (($oldChild->previousSibling != null) && ($oldChild->nextSibling == null)) { + $this->lastChild =& $oldChild->previousSibling; + unset($oldChild->previousSibling->nextSibling); + $oldChild->previousSibling->nextSibling = null; + } + else if (($oldChild->previousSibling == null) && ($oldChild->nextSibling != null)) { + unset($oldChild->nextSibling->previousSibling); + $oldChild->nextSibling->previousSibling = null; + $this->firstChild =& $oldChild->nextSibling; + } + else if (($oldChild->previousSibling == null) && ($oldChild->nextSibling == null)) { + unset($this->firstChild); + $this->firstChild = null; + unset($this->lastChild); + $this->lastChild = null; + } + + $total = $this->childCount; + + //remove node from childNodes + for ($i = 0; $i < $total; $i++) { + if ($i == ($total - 1)) { + array_splice($this->childNodes, $i, 1); + } + else if ($i >= $index) { + $this->childNodes[$i] =& $this->childNodes[($i + 1)]; + } + } + + $this->childCount--; + + $oldChild->clearReferences(); + return $oldChild; + } + } + + DOMIT_DOMException::raiseException(DOMIT_NOT_FOUND_ERR, + ('Target node for removeChild not found.')); + } //removeChild + + /** + * Searches the element tree for an element with the specified attribute name and value. + * @param string The value of the attribute + * @param string The name of the attribute + * @param boolean True if the first found node is to be returned as a node instead of a nodelist + * @param boolean True if uid is to be considered an attribute + * @return object A NodeList of found elements, or null + */ + function &getElementsByAttribute($attrName = 'id', $attrValue = '', + $returnFirstFoundNode = false, $treatUIDAsAttribute = false) { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_nodemaps.php'); + + $nodelist = new DOMIT_NodeList(); + + switch ($this->nodeType) { + case DOMIT_ELEMENT_NODE: + $this->_getElementsByAttribute($nodelist, $attrName, $attrValue, + $returnFirstFoundNode, $treatUIDAsAttribute); + break; + + case DOMIT_DOCUMENT_NODE: + if ($this->documentElement != null) { + $this->documentElement->_getElementsByAttribute($nodelist, + $attrName, $attrValue, $returnFirstFoundNode, $treatUIDAsAttribute); + } + break; + } + + if ($returnFirstFoundNode) { + if ($nodelist->getLength() > 0) { + return $nodelist->item(0); + } + $null = null; + return $null; + } + + return $nodelist; + } //getElementsByAttribute + + /** + * Searches the element tree for an element with the specified attribute name and value. + * @param object The node list of found elements + * @param string The value of the attribute + * @param string The name of the attribute + * @param boolean True if the first found node is to be returned as a node instead of a nodelist + * @param boolean True if uid is to be considered an attribute + * @param boolean True the node has been found + */ + function _getElementsByAttribute(&$nodelist, $attrName, $attrValue, + $returnFirstFoundNode, $treatUIDAsAttribute, $foundNode = false) { + if (!($foundNode && $returnFirstFoundNode)) { + if (($this->getAttribute($attrName) == $attrValue) || + ($treatUIDAsAttribute && ($attrName == 'uid') && ($this->uid == $attrValue))) { + $nodelist->appendNode($this); + $foundNode = true; + if ($returnFirstFoundNode) return; + } + + $total = $this->childCount; + + for ($i = 0; $i < $total; $i++) { + $currNode =& $this->childNodes[$i]; + + if ($currNode->nodeType == DOMIT_ELEMENT_NODE) { + $currNode->_getElementsByAttribute($nodelist, + $attrName, $attrValue, $returnFirstFoundNode, + $treatUIDAsAttribute, $foundNode); + } + } + } + } //_getElementsByAttribute +} //DOMIT_Lite_ChildNodes_Interface + +/** +* A class representing the DOM Document +* +* @package domit-xmlparser +* @subpackage domit-xmlparser-lite +* @author John Heinstein +*/ +class DOMIT_Lite_Document extends DOMIT_Lite_ChildNodes_Interface { + /** @var string The xml declaration text */ + var $xmlDeclaration; + /** @var string The doctype text */ + var $doctype; + /** @var Object A reference to the root node of the DOM document */ + var $documentElement; + /** @var string The parser used to process the DOM document, either "EXPAT" or "SAXY_LITE" */ + var $parser; + /** @var Object A reference to the DOMIT_DOMImplementation object */ + var $implementation; + /** @var Array User defined translation table for XML entities */ + var $definedEntities = array(); + /** @var boolean If true, loadXML or parseXML will attempt to detect and repair invalid xml */ + var $doResolveErrors = false; + /** @var boolean True if whitespace is to be preserved during parsing */ + var $preserveWhitespace = false; + /** @var boolean If true, elements tags will be rendered to string as rather than */ + var $doExpandEmptyElementTags = false; + /** @var array A list of exceptions to the empty element expansion rule */ + var $expandEmptyElementExceptions = array(); + /** @var int The error code returned by the SAX parser */ + var $errorCode = 0; + /** @var string The error string returned by the SAX parser */ + var $errorString = ''; + /** @var object A reference to a http connection or proxy server, if one is required */ + var $httpConnection = null; + /** @var boolean True if php_http_client_generic is to be used instead of PHP get_file_contents to retrieve xml data */ + var $doUseHTTPClient = false; + + /** + * DOM Document constructor + */ + function DOMIT_Lite_Document() { + $this->_constructor(); + $this->xmlDeclaration = ''; + $this->doctype = ''; + $this->documentElement = null; + $this->nodeType = DOMIT_DOCUMENT_NODE; + $this->nodeName = '#document'; + $this->ownerDocument =& $this; + $this->parser = ''; + $this->implementation = new DOMIT_DOMImplementation(); + } //DOMIT_Lite_Document + + /** + * Specifies whether DOMIT! Lite will try to fix invalid XML before parsing begins + * @param boolean True if errors are to be resolved + */ + function resolveErrors($truthVal) { + $this->doResolveErrors = $truthVal; + } //resolveErrors + + /** + * Specifies the parameters of the http conection used to obtain the xml data + * @param string The ip address or domain name of the connection + * @param string The path of the connection + * @param int The port that the connection is listening on + * @param int The timeout value for the connection + * @param string The user name, if authentication is required + * @param string The password, if authentication is required + */ + function setConnection($host, $path = '/', $port = 80, $timeout = 0, $user = null, $password = null) { + require_once(DOMIT_INCLUDE_PATH . 'php_http_client_generic.php'); + + $this->httpConnection = new php_http_client_generic($host, $path, $port, $timeout, $user, $password); + } //setConnection + + /** + * Specifies whether DOMIT! preserves whitespace when parsing + * @param boolean True if whitespace is to be preserved + */ + function preserveWhitespace($truthVal) { + $this->preserveWhitespace = $truthVal; + } //preserveWhitespace + + /** + * Specifies basic authentication for an http connection + * @param string The user name + * @param string The password + */ + function setAuthorization($user, $password) { + $this->httpConnection->setAuthorization($user, $password); + } //setAuthorization + + /** + * Specifies that a proxy is to be used to obtain the xml data + * @param string The ip address or domain name of the proxy + * @param string The path to the proxy + * @param int The port that the proxy is listening on + * @param int The timeout value for the connection + * @param string The user name, if authentication is required + * @param string The password, if authentication is required + */ + function setProxyConnection($host, $path = '/', $port = 80, $timeout = 0, $user = null, $password = null) { + require_once(DOMIT_INCLUDE_PATH . 'php_http_proxy.php'); + + $this->httpConnection = new php_http_proxy($host, $path, $port, $timeout, $user, $password); + } //setProxyConnection + + /** + * Specifies basic authentication for the proxy + * @param string The user name + * @param string The password + */ + function setProxyAuthorization($user, $password) { + $this->httpConnection->setProxyAuthorization($user, $password); + } //setProxyAuthorization + + /** + * Specifies whether an HTTP client should be used to establish a connection + * @param boolean True if an HTTP client is to be used to establish the connection + */ + function useHTTPClient($truthVal) { + $this->doUseHTTPClient = $truthVal; + } //useHTTPClient + + /** + * Returns the error code from the underlying SAX parser + * @return int The error code + */ + function getErrorCode() { + return $this->errorCode; + } //getErrorCode + + /** + * Returns the error string from the underlying SAX parser + * @return string The error string + */ + function getErrorString() { + return $this->errorString; + } //getErrorString + + /** + * Specifies whether elements tags will be rendered to string as rather than + * @param boolean True if the expanded form is to be used + * @param mixed An array of tag names that should be excepted from expandEmptyElements rule (optional) + */ + function expandEmptyElementTags($truthVal, $expandEmptyElementExceptions = false) { + $this->doExpandEmptyElementTags = $truthVal; + + if (is_array($expandEmptyElementExceptions)) { + $this->expandEmptyElementExceptions = $expandEmptyElementExceptions; + } + } //expandEmptyElementTags + + /** + * Set the specified node as document element + * @param Object The node that is to become document element + * @return Object The new document element + */ + function &setDocumentElement(&$node) { + if ($node->nodeType == DOMIT_ELEMENT_NODE) { + if ($this->documentElement == null) { + parent::appendChild($node); + } + else { + parent::replaceChild($node, $this->documentElement); + } + + $this->documentElement =& $node; + } + else { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Cannot add a node of type ' . get_class($node) . ' as a Document Element.')); + } + + return $node; + } //setDocumentElement + + /** + * Appends a node to the childNodes list of the current node + * @param Object The node to be appended + * @return Object The appended node + */ + function &appendChild(&$node) { + if ($node->nodeType == DOMIT_ELEMENT_NODE) { + if ($this->documentElement == null) { + parent::appendChild($node); + $this->setDocumentElement($node); + } + else { + //error thrown if documentElement already exists! + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Cannot have more than one root node (documentElement) in a DOMIT_Document.')); + } + } + else { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Cannot add a node of type ' . get_class($node) . ' to a DOMIT_Document.')); + } + + return $node; + } //appendChild + + /** + * Replaces a node with another + * @param Object The new node + * @param Object The old node + * @return Object The new node + */ + function &replaceChild(&$newChild, &$oldChild) { + if (($this->documentElement != null) && ($oldChild->uid == $this->documentElement->uid)) { + if ($node->nodeType == DOMIT_ELEMENT_NODE) { + //replace documentElement with new node + $this->setDocumentElement($newChild); + } + else { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Cannot replace Document Element with a node of class ' . get_class($newChild))); + } + } + else { + if ($node->nodeType == DOMIT_ELEMENT_NODE) { + if ($this->documentElement != null) { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Cannot have more than one root node (documentElement) in a DOMIT_Document.')); + } + else { + parent::replaceChild($newChild, $oldChild); + } + } + else { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Nodes of class ' . get_class($newChild) . ' cannot be children of a DOMIT_Document.')); + } + } + + return $newChild; + } //replaceChild + + /** + * Inserts a node to the childNodes list of the current node + * @param Object The node to be inserted + * @param Object The node before which the insertion is to occur + * @return Object The inserted node + */ + function &insertBefore(&$newChild, &$refChild) { + $type = $newChild->nodeType; + + if ($type == DOMIT_ELEMENT_NODE) { + if ($this->documentElement == null) { + parent::insertBefore($newChild, $refChild); + $this->setDocumentElement($newChild); + } + else { + //error thrown if documentElement already exists! + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Cannot have more than one root node (documentElement) in a DOMIT_Document.')); + } + } + else { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Cannot insert a node of type ' . get_class($newChild) . ' to a DOMIT_Document.')); + } + + return $newChild; + } //insertBefore + + /** + * Removes a node from the childNodes list of the current node + * @param Object The node to be removed + * @return Object The removed node + */ + function &removeChild(&$oldChild) { + if (($this->documentElement != null) && ($oldChild->uid == $this->documentElement->uid)) { + parent::removeChild($oldChild); + $this->documentElement = null; + } + else { + parent::removeChild($oldChild); + } + + $oldChild->clearReferences(); + return $oldChild; + } //removeChild + + /** + * Creates a new DOMIT_Lite_Element node + * @param string The tag name of the element + * @return Object The new element + */ + function &createElement($tagName) { + $node = new DOMIT_Lite_Element($tagName); + $node->ownerDocument = $this; + + $reference =& $node; + return $node; + } //createElement + + /** + * Creates a new DOMIT_Text node + * @param string The text of the node + * @return Object The new text node + */ + function &createTextNode($data) { + $node = new DOMIT_Lite_TextNode($data); + $node->ownerDocument = $this; + + $reference =& $node; + return $reference; + } //createTextNode + + /** + * Creates a new DOMIT_Lite_CDATASection node + * @param string The text of the CDATASection + * @return Object The new CDATASection node + */ + function &createCDATASection($data) { + $node = new DOMIT_Lite_CDATASection($data); + $node->ownerDocument = $this; + + return $node; + } //createCDATASection + + /** + * Retrieves a NodeList of child elements with the specified tag name + * @param string The matching element tag name + * @return Object A NodeList of found elements + */ + function &getElementsByTagName($tagName) { + $nodeList = new DOMIT_NodeList(); + + if ($this->documentElement != null) { + $this->documentElement->getNamedElements($nodeList, $tagName); + } + + return $nodeList; + } //getElementsByTagName + + /** + * Retrieves an element or DOMIT_NodeList of elements corresponding to an Xpath-like expression. + * @param string The query pattern + * @param int If a single node is to be returned (rather than the entire NodeList) the index of that node + * @return mixed A NodeList or single node that matches the pattern + */ + function &getElementsByPath($pattern, $nodeIndex = 0) { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_getelementsbypath.php'); + + $gebp = new DOMIT_GetElementsByPath(); + $myResponse =& $gebp->parsePattern($this, $pattern, $nodeIndex); + + return $myResponse; + } //getElementsByPath + + /** + * Parses an xml string; first encodes string as UTF-8 + * @param string The xml text to be parsed + * @param boolean True if SAXY is to be used instead of Expat + * @param boolean False if CDATA Section are to be generated as Text nodes + * @param boolean True if onLoad is to be called on each node after parsing + * @return boolean True if parsing is successful + */ + function parseXML_utf8($xmlText, $useSAXY = true, $preserveCDATA = true, $fireLoadEvent = false) { + return $this->parseXML(utf8_encode($xmlText), $useSAXY, $preserveCDATA, $fireLoadEvent); + } //parseXML_utf8 + + /** + * Parses an xml string + * @param string The xml text to be parsed + * @param boolean True if SAXY is to be used instead of Expat + * @param boolean False if CDATA Section are to be generated as Text nodes + * @param boolean True if onLoad is to be called on each node after parsing + * @return boolean True if parsing is successful + */ + function parseXML($xmlText, $useSAXY = true, $preserveCDATA = true, $fireLoadEvent = false) { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_utilities.php'); + + if ($this->doResolveErrors) { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_doctor.php'); + $xmlText = DOMIT_Doctor::fixAmpersands($xmlText); + } + + if (DOMIT_Utilities::validateXML($xmlText)) { + $domParser = new DOMIT_Lite_Parser(); + + if ($useSAXY || (!function_exists('xml_parser_create'))) { + //use SAXY parser to populate xml tree + $this->parser = 'SAXY_LITE'; + $success = $domParser->parseSAXY($this, $xmlText, $preserveCDATA, $this->definedEntities); + } + else { + //use Expat parser to populate xml tree + $this->parser = 'EXPAT'; + $success = $domParser->parse($this, $xmlText, $preserveCDATA); + } + + if ($fireLoadEvent && ($this->documentElement != null)) $this->load($this->documentElement); + + return $success; + } + + return false; + } //parseXML + + /** + * Parses an xml file; first encodes text as UTF-8 + * @param string The xml file to be parsed + * @param boolean True if SAXY is to be used instead of Expat + * @param boolean False if CDATA Section are to be generated as Text nodes + * @param boolean True if onLoad is to be called on each node after parsing + * @return boolean True if parsing is successful + */ + function loadXML_utf8($filename, $useSAXY = true, $preserveCDATA = true, $fireLoadEvent = false) { + $xmlText = $this->getTextFromFile($filename); + return $this->parseXML_utf8($xmlText, $useSAXY, $preserveCDATA, $fireLoadEvent); + } //loadXML_utf8 + + /** + * Parses an xml file + * @param string The xml file to be parsed + * @param boolean True if SAXY is to be used instead of Expat + * @param boolean False if CDATA Section are to be generated as Text nodes + * @param boolean True if onLoad is to be called on each node after parsing + * @return boolean True if parsing is successful + */ + function loadXML($filename, $useSAXY = true, $preserveCDATA = true, $fireLoadEvent = false) { + $xmlText = $this->getTextFromFile($filename); + return $this->parseXML($xmlText, $useSAXY, $preserveCDATA, $fireLoadEvent); + } //loadXML + + /** + * Establishes a connection, given an url + * @param string The url of the data + */ + function establishConnection($url) { + require_once(DOMIT_INCLUDE_PATH . 'php_http_client_generic.php'); + + $host = php_http_connection::formatHost($url); + $host = substr($host, 0, strpos($host, '/')); + + $this->setConnection($host); + } //establishConnection + + /** + * Retrieves text from a file + * @param string The file path + * @return string The text contained in the file + */ + function getTextFromFile($filename) { + if ($this->doUseHTTPClient && (substr($filename, 0, 5) == 'http:')) { + $this->establishConnection($filename); + } + + if ($this->httpConnection != null) { + $response =& $this->httpConnection->get($filename); + $this->httpConnection->disconnect(); + return $response->getResponse(); + } + else if (function_exists('file_get_contents')) { + //if (file_exists($filename)) { + return file_get_contents($filename); + //} + } + else { + require_once(DOMIT_INCLUDE_PATH . 'php_file_utilities.php'); + + $fileContents =& php_file_utilities::getDataFromFile($filename, 'r'); + return $fileContents; + } + + return ''; + } //getTextFromFile + + /** + * Saves the current DOM document as an xml file; first encodes text as UTF-8 + * @param string The path of the xml file + * @param boolean True if xml text is to be normalized before saving + * @return boolean True if save is successful + */ + function saveXML_utf8($filename, $normalized=false) { + if ($normalized) { + $stringRep = $this->toNormalizedString(false, true); //param 2 is $subEntities + } + else { + $stringRep = $this->toString(false, true); + } + + return $this->saveTextToFile($filename, utf8_encode($stringRep)); + } //saveXML_utf8 + + /** + * Saves the current DOM document as an xml file + * @param string The path of the xml file + * @param boolean True if xml text is to be normalized before saving + * @return boolean True if save is successful + */ + function saveXML($filename, $normalized=false) { + if ($normalized) { + $stringRep = $this->toNormalizedString(false, true); + } + else { + $stringRep = $this->toString(false, true); + } + if ($this->xmlDeclaration) { + $stringRep = $this->xmlDeclaration . "\n" . $stringRep; + } + return $this->saveTextToFile($filename, $stringRep); + } //saveXML + + /** + * Saves text to a file + * @param string The file path + * @param string The text to be saved + * @return boolean True if the save is successful + */ + function saveTextToFile($filename, $text) { + if (function_exists('file_put_contents')) { + file_put_contents($filename, $text); + } + else { + require_once(DOMIT_INCLUDE_PATH . 'php_file_utilities.php'); + php_file_utilities::putDataToFile($filename, $text, 'w'); + } + + return (file_exists($filename) && is_writable($filename)); + } //saveTextToFile + + /** + * Indicates the SAX parser used to parse the current document + * @return string Either "SAXY_LITE" or "EXPAT" + */ + function parsedBy() { + return $this->parser; + } //parsedBy + + /** + * Returns the concatented text of the current node and its children + * @return string The concatented text of the current node and its children + */ + function getText() { + if ($this->documentElement != null) { + $root =& $this->documentElement; + return $root->getText(); + } + + return ''; + } //getText + + /** + * Returns the doctype text + * @return string The doctype text, or an emty string + */ + function getDocType() { + return $this->doctype; + } //getDocType + + /** + * Returns the xml declaration text + * @return mixed The xml declaration text, or an empty string + */ + function getXMLDeclaration() { + return $this->xmlDeclaration; + } //getXMLDeclaration + + /** + * Returns the xml declaration text + * @return mixed The xml declaration text, or an empty string + */ + function setXMLDeclaration( $decl ) { + $this->xmlDeclaration = $decl; + } //setXMLDeclaration + + /** + * Returns a reference to the DOMIT_DOMImplementation object + * @return Object A reference to the DOMIT_DOMImplementation object + */ + function &getDOMImplementation() { + return $this->implementation; + } //getDOMImplementation + + /** + * Manages the firing of the onLoad() event + * @param Object The parent node of the current recursion + */ + function load(&$contextNode) { + $total = $contextNode->childCount; + + for ($i = 0; $i < $total; $i++) { + $currNode =& $contextNode->childNodes[$i]; + $currNode->ownerDocument->load($currNode); + } + + $contextNode->onLoad(); + } //load + + /** + * Returns the current version of DOMIT! Lite + * @return Object The current version of DOMIT! Lite + */ + function getVersion() { + return DOMIT_LITE_VERSION; + } //getVersion + + /** + * Appends an array of entity mappings to the existing translation table + * + * Intended mainly to facilitate the conversion of non-ASCII entities into equivalent characters + * + * @param array A list of entity mappings in the format: array('&' => '&'); + */ + function appendEntityTranslationTable($table) { + $this->definedEntities = $table; + + global $DOMIT_defined_entities_flip; + $DOMIT_defined_entities_flip = array_flip($table); + } //appendEntityTranslationTable + + /** + * Generates an array representation of the node and its children + * @return Array A representation of the node and its children + */ + function toArray() { + $arReturn = array($this->nodeName => array()); + $total = $this->childCount; + + for ($i = 0; $i < $total; $i++) { + $arReturn[$this->nodeName][$i] = $this->childNodes[$i]->toArray(); + } + + return $arReturn; + } //toArray + + /** + * Copies a node and/or its children + * @param boolean True if all child nodes are also to be cloned + * @return Object A copy of the node and/or its children + */ + function &cloneNode($deep = false) { + $className = get_class($this); + $clone = new $className($this->nodeName); + + if ($deep) { + $total = $this->childCount; + + for ($i = 0; $i < $total; $i++) { + $currentChild =& $this->childNodes[$i]; + $clone->appendChild($currentChild->cloneNode($deep)); + } + } + + return $clone; + } //cloneNode + + /** + * Generates a string representation of the node and its children + * @param boolean True if HTML readable output is desired + * @param boolean True if illegal xml characters in text nodes and attributes should be converted to entities + * @return string The string representation + */ + function toString($htmlSafe = false, $subEntities = false) { + $result = ''; + $total = $this->childCount; + + for ($i = 0; $i < $total; $i++) { + $result .= $this->childNodes[$i]->toString(false, $subEntities); + } + + if ($htmlSafe) $result = $this->forHTML($result); + + return $result; + } //toString +} //DOMIT_Lite_Document + +/** +* A class representing the DOM Element +* +* @package domit-xmlparser +* @subpackage domit-xmlparser-lite +* @author John Heinstein +*/ +class DOMIT_Lite_Element extends DOMIT_Lite_ChildNodes_Interface { + /** + * DOM Element constructor + * @param string The tag name of the element + */ + function DOMIT_Lite_Element($tagName) { + $this->_constructor(); + $this->nodeType = DOMIT_ELEMENT_NODE; + $this->nodeName = $tagName; + $this->attributes = array(); + $this->childNodes = array(); + } //DOMIT_Lite_Element + + /** + * Returns the tag name of the element + * @return string The tag name of the element + */ + function getTagName() { + return $this->nodeName; + } //getTagName + + /** + * Adds elements with the specified tag name to a NodeList collection + * @param Object The NodeList collection + * @param string The tag name of matching elements + */ + function getNamedElements(&$nodeList, $tagName) { + if (($this->nodeName == $tagName) || ($tagName == '*')) { + $nodeList->appendNode($this); + } + + $total = $this->childCount; + + for ($i = 0; $i < $total; $i++) { + $this->childNodes[$i]->getNamedElements($nodeList, $tagName); + } + } //getNamedElements + + /** + * Returns the concatented text of the current node and its children + * @return string The concatented text of the current node and its children + */ + function getText() { + $text = ''; + $numChildren = $this->childCount; + + for ($i = 0; $i < $numChildren; $i++) { + $child =& $this->childNodes[$i]; + $text .= $child->getText(); + } + + return $text; + } //getText + + /** + * If a child text node exists, sets the nodeValue to $data. A child text node is created if none exists + * @param string The text data of the node + */ + function setText($data) { + switch ($this->childCount) { + case 1: + if ($this->firstChild->nodeType == DOMIT_TEXT_NODE) { + $this->firstChild->setText($data); + } + break; + + case 0: + $childTextNode =& $this->ownerDocument->createTextNode($data); + $this->appendChild($childTextNode); + break; + + default: + //do nothing. Maybe throw error??? + } + } //setText + + /** + * Retrieves a NodeList of child elements with the specified tag name + * @param string The matching element tag name + * @return Object A NodeList of found elements + */ + function &getElementsByTagName($tagName) { + $nodeList = new DOMIT_NodeList(); + $this->getNamedElements($nodeList, $tagName); + + return $nodeList; + } //getElementsByTagName + + /** + * Retrieves an element or DOMIT_NodeList of elements corresponding to an Xpath-like expression. + * @param string The query pattern + * @param int If a single node is to be returned (rather than the entire NodeList) the index of that node + * @return mixed A NodeList or single node that matches the pattern + */ + function &getElementsByPath($pattern, $nodeIndex = 0) { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_getelementsbypath.php'); + + $gebp = new DOMIT_GetElementsByPath(); + $myResponse = $gebp->parsePattern($this, $pattern, $nodeIndex); + + return $myResponse; + } //getElementsByPath + + /** + * Gets the value of the specified attribute, if it exists + * @param string The attribute name + * @return string The attribute value + */ + function getAttribute($name) { + if ($this->hasAttribute($name)) { + return $this->attributes[$name]; + } + else { + /* + DOMIT_DOMException::raiseException(DOMIT_NOT_FOUND_ERR, + ('No attribute named ' . $name . 'exists.')); + */ + // Joomla! hack + return null; + } + } //getAttribute + + /** + * Sets the value of the specified attribute; creates a new attribute if one doesn't exist + * @param string The attribute name + * @param string The desired attribute value + */ + function setAttribute($name, $value) { + $this->attributes[$name] = $value; + } //setAttribute + + /** + * Removes the specified attribute + * @param string The name of the attribute to be removed + */ + function removeAttribute($name) { + if ($this->hasAttribute($name)) { + unset($this->attributes[$name]); + } + } //removeAttribute + + /** + * Determines whether an attribute with the specified name exists + * @param string The name of the attribute + * @return boolean True if the attribute exists + */ + function hasAttribute($name) { + return isset($this->attributes[$name]); + } //hasAttribute + + /** + * Collapses adjacent text nodes in entire element subtree + */ + function normalize() { + if ($this->hasChildNodes()) { + $currNode =& $this->childNodes[0]; + + while ($currNode->nextSibling != null) { + $nextNode =& $currNode->nextSibling; + + if (($currNode->nodeType == DOMIT_TEXT_NODE) && + ($nextNode->nodeType == DOMIT_TEXT_NODE)) { + $currNode->nodeValue .= $nextNode->nodeValue; + $this->removeChild($nextNode); + } + else { + $currNode->normalize(); + } + + if ($currNode->nextSibling != null) { + $currNode =& $currNode->nextSibling; + } + } + } + } //normalize + + /** + * Generates an array representation of the node and its children + * @return Array A representation of the node and its children + */ + function toArray() { + $arReturn = array($this->nodeName => array("attributes" => $this->attributes)); + $total = $this->childCount; + + for ($i = 0; $i < $total; $i++) { + $arReturn[$this->nodeName][$i] = $this->childNodes[$i]->toArray(); + } + + return $arReturn; + } //toArray + + /** + * Copies a node and/or its children + * @param boolean True if all child nodes are also to be cloned + * @return Object A copy of the node and/or its children + */ + function &cloneNode($deep = false) { + $className = get_class($this); + $clone = new $className($this->nodeName); + + $clone->attributes = $this->attributes; + + if ($deep) { + $total = $this->childCount; + + for ($i = 0; $i < $total; $i++) { + $currentChild =& $this->childNodes[$i]; + $clone->appendChild($currentChild->cloneNode($deep)); + } + } + + return $clone; + } //cloneNode + + /** + * Generates a string representation of the node and its children + * @param boolean True if HTML readable output is desired + * @param boolean True if illegal xml characters in text nodes and attributes should be converted to entities + * @return string The string representation + */ + function toString($htmlSafe = false, $subEntities = false) { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_utilities.php'); + global $DOMIT_defined_entities_flip; + + $result = '<' . $this->nodeName; + + //get attributes + foreach ($this->attributes as $key => $value) { + $result .= ' ' . $key . '="'; + $result .= $subEntities ? DOMIT_Utilities::convertEntities($value, + $DOMIT_defined_entities_flip) : $value; + $result .= '"'; + } + + //get children + $myNodes =& $this->childNodes; + $total = count($myNodes); + + if ($total != 0) { + $result .= '>'; + + for ($i = 0; $i < $total; $i++) { + $child =& $myNodes[$i]; + $result .= $child->toString(false, $subEntities); + } + + $result .= 'nodeName . '>'; + } + else { + if ($this->ownerDocument->doExpandEmptyElementTags) { + if (in_array($this->nodeName, $this->ownerDocument->expandEmptyElementExceptions)) { + $result .= ' />'; + } + else { + $result .= '>nodeName . '>'; + } + } + else { + if (in_array($this->nodeName, $this->ownerDocument->expandEmptyElementExceptions)) { + $result .= '>nodeName . '>'; + } + else { + $result .= ' />'; + } + } + } + + if ($htmlSafe) $result = $this->forHTML($result); + + return $result; + } //toString +} //DOMIT_Lite_Element + +/** +* A class representing the DOM Text Node +* +* @package domit-xmlparser +* @subpackage domit-xmlparser-lite +* @author John Heinstein +*/ +class DOMIT_Lite_TextNode extends DOMIT_Lite_Node { + /** + * DOM Text Node constructor + * @param string The text of the node + */ + function DOMIT_Lite_TextNode($data) { + $this->_constructor(); + $this->nodeType = DOMIT_TEXT_NODE; + $this->nodeName = '#text'; + $this->setText($data); + } //DOMIT_Lite_TextNode + + /** + * Returns the text contained in the current node + * @return string The text of the current node + */ + function getText() { + return $this->nodeValue; + } //getText + + /** + * Sets the text contained in the current node to $data. + * @param string The text data of the node + */ + function setText($data) { + $this->nodeValue = $data; + } //setText + + /** + * Generates an array representation of the node and its children + * @return Array A representation of the node and its children + */ + function toArray() { + return $this->toString(); + } //toArray + + /** + * Copies a node and/or its children + * @param boolean True if all child nodes are also to be cloned + * @return Object A copy of the node and/or its children + */ + function &cloneNode($deep = false) { + $className = get_class($this); + $clone = new $className($this->nodeValue); + + return $clone; + } //cloneNode + + /** + * Generates a string representation of the node and its children + * @param boolean True if HTML readable output is desired + * @param boolean True if illegal xml characters should be converted to entities + * @return string The string representation + */ + function toString($htmlSafe = false, $subEntities = false) { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_utilities.php'); + global $DOMIT_defined_entities_flip; + + $result = $subEntities ? DOMIT_Utilities::convertEntities($this->nodeValue, + $DOMIT_defined_entities_flip) : $this->nodeValue; + + if ($htmlSafe) $result = $this->forHTML($result); + + return $result; + } //toString +} //DOMIT_Lite_TextNode + +/** +* A class representing the DOM CDATA Section +* +* @package domit-xmlparser +* @subpackage domit-xmlparser-lite +* @author John Heinstein +*/ +class DOMIT_Lite_CDATASection extends DOMIT_Lite_TextNode { + /** + * DOM CDATA Section node constructor + * @param string The text of the node + */ + function DOMIT_Lite_CDATASection($data) { + $this->_constructor(); + $this->nodeType = DOMIT_CDATA_SECTION_NODE; + $this->nodeName = '#cdata-section'; + $this->setText($data); + } //DOMIT_Lite_CDATASection + + /** + * Generates a string representation of the node and its children + * @param boolean True if HTML readable output is desired + * @param boolean True if illegal xml characters should be converted to entities + * @return string The string representation + */ + function toString($htmlSafe = false, $subEntities = false) { + $result = '", "]]>", $this->nodeValue) : + $this->nodeValue; + $result .= ']]>'; + + if ($htmlSafe) $result = $this->forHTML($result); + + return $result; + } //toString +} //DOMIT_Lite_CDATASection + +/** +* Manages the generation of a DOMIT! document from SAX events +* +* @package domit-xmlparser +* @subpackage domit-xmlparser-lite +* @author John Heinstein +*/ +class DOMIT_Lite_Parser { + /** @var Object A reference to the resulting xmldoc */ + var $xmlDoc = null; + /** @var Object A reference to the current node in the parsing process */ + var $currentNode = null; + /** @var Object A reference to the last child in the parsing process */ + var $lastChild = null; + /** @var boolean True if currently parsing a CDATA Section */ + var $inCDATASection = false; //flag for Expat + /** @var boolean True if currently parsing a Text node */ + var $inTextNode = false; + /** @var boolean True is CDATA Section nodes are not to be converted into Text nodes */ + var $preserveCDATA; + /** @var string A container for holding the currently parsed text data */ + var $parseContainer = ''; + /** @var string The current docutype text */ + var $parseItem = ''; + + /** + * Parses xml text using Expat + * @param Object A reference to the DOM document that the xml is to be parsed into + * @param string The text to be parsed + * @param boolean True if CDATA Section nodes are not to be converted into Text nodes + * @return boolean True if the parsing is successful + */ + function parse (&$myXMLDoc, $xmlText, $preserveCDATA = true) { + $this->xmlDoc =& $myXMLDoc; + $this->lastChild =& $this->xmlDoc; + + $this->preserveCDATA = $preserveCDATA; + + //create instance of expat parser (should be included in php distro) + if (version_compare(phpversion(), '5.0', '<=')) { + $parser = xml_parser_create(''); + } + else { + $parser = xml_parser_create(); + } + + //set handlers for SAX events + xml_set_object($parser, $this); + xml_set_element_handler($parser, 'startElement', 'endElement'); + xml_set_character_data_handler($parser, 'dataElement'); + xml_set_default_handler($parser, 'defaultDataElement'); + xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); + + if (!$this->xmlDoc->preserveWhitespace) { + xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); + } + else { + xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 0); + } + + //parse out whitespace - (XML_OPTION_SKIP_WHITE = 1 does not + //seem to work consistently across versions of PHP and Expat + if (!$this->xmlDoc->preserveWhitespace) { + $xmlText = eregi_replace('>' . "[[:space:]]+" . '<' , '><', $xmlText); + } + + $success = xml_parse($parser, $xmlText); + + $this->xmlDoc->errorCode = xml_get_error_code($parser); + $this->xmlDoc->errorString = xml_error_string($this->xmlDoc->errorCode); + + xml_parser_free($parser); + + return $success; + } //parse + + /** + * Parses xml text using SAXY + * @param Object A reference to the DOM document that the xml is to be parsed into + * @param string The text to be parsed + * @param boolean True if CDATA Section nodes are not to be converted into Text nodes + * @return boolean True if the parsing is successful + */ + function parseSAXY(&$myXMLDoc, $xmlText, $preserveCDATA, $definedEntities) { + require_once(DOMIT_INCLUDE_PATH . 'xml_saxy_lite_parser.php'); + + $this->xmlDoc =& $myXMLDoc; + $this->lastChild =& $this->xmlDoc; + + //create instance of SAXY parser + $parser = new SAXY_Lite_Parser(); + $parser->appendEntityTranslationTable($definedEntities); + + //not yet implemented in SAXY!!! + $parser->preserveWhitespace = $this->xmlDoc->preserveWhitespace; + + $parser->xml_set_element_handler(array(&$this, 'startElement'), array(&$this, 'endElement')); + $parser->xml_set_character_data_handler(array(&$this, 'dataElement')); + + if ($preserveCDATA) { + $parser->xml_set_cdata_section_handler(array(&$this, 'cdataElement')); + } + + $success = $parser->parse($xmlText); + + $this->xmlDoc->errorCode = $parser->xml_get_error_code(); + $this->xmlDoc->errorString = $parser->xml_error_string($this->xmlDoc->errorCode); + + return $success; + } //parseSAXY + + /** + * Generates and appends a new text node from the parseContainer text + */ + function dumpTextNode() { + //traps for mixed content + $currentNode =& $this->xmlDoc->createTextNode($this->parseContainer); + $this->lastChild->appendChild($currentNode); + $this->inTextNode = false; + $this->parseContainer = ''; + } //dumpTextNode + + /** + * Catches a start element event and processes the data + * @param Object A reference to the current SAX parser + * @param string The tag name of the current element + * @param Array An array of the element attributes + */ + function startElement(&$parser, $name, $attrs) { + if ($this->inTextNode) { + $this->dumpTextNode(); + } + + $currentNode =& $this->xmlDoc->createElement($name); + $currentNode->attributes = $attrs; + $this->lastChild->appendChild($currentNode); + $this->lastChild =& $currentNode; + } //startElement + + /** + * Catches an end element event and processes the data + * @param Object A reference to the current SAX parser + * @param string The tag name of the current element + */ + function endElement(&$parser, $name) { + if ($this->inTextNode) { + $this->dumpTextNode(); + } + + $this->lastChild =& $this->lastChild->parentNode; + } //endElement + + /** + * Catches a data event and processes the text + * @param Object A reference to the current SAX parser + * @param string The current text data + */ + function dataElement(&$parser, $data) { + if (!$this->inCDATASection) $this->inTextNode = true; + + $this->parseContainer .= $data; + } //dataElement + + /** + * Catches a CDATA Section event and processes the text + * @param Object A reference to the current SAX parser + * @param string The current text data + */ + function cdataElement(&$parser, $data) { + $currentNode =& $this->xmlDoc->createCDATASection($data); + + $this->lastChild->appendChild($currentNode); + } //cdataElement + + /** + * Catches a default data event and processes the data + * @param Object A reference to the current SAX parser + * @param string The current data + */ + function defaultDataElement(&$parser, $data) { + if (strlen($data) > 2){ + $pre = strtoupper(substr($data, 0, 3)); + + switch ($pre) { + case 'preserveCDATA) { + $this->inCDATASection = true; + } + break; + case ']]>': //cdata remnant - ignore + if ($this->preserveCDATA) { + $currentNode =& $this->xmlDoc->createCDATASection($this->parseContainer); + $this->lastChild->appendChild($currentNode); + $this->inCDATASection = false; + $this->parseContainer = ''; + } + else { + $this->dumpTextNode(); + } + + break; + } + } + } //defaultDataElement +} //DOMIT_Lite_Parser + +?> diff --git a/oss/joomla/trunk/libraries/domit/xml_domit_nodemaps.php b/oss/joomla/trunk/libraries/domit/xml_domit_nodemaps.php new file mode 100644 index 00000000..5ab837ab --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/xml_domit_nodemaps.php @@ -0,0 +1,457 @@ + +* @link http://www.engageinteractive.com/domit/ DOMIT! Home Page +* DOMIT! is Free Software +**/ + +if (!defined('DOMIT_INCLUDE_PATH')) { + define('DOMIT_INCLUDE_PATH', (dirname(__FILE__) . "/")); +} + +/** +* A DOM NodeList implementation +* +* @package domit-xmlparser +* @author John Heinstein +*/ +class DOMIT_NodeList { + /** @var Array A container for the nodes in the list */ + var $arNodeList = array(); + + /** + * Return the node at the specified index + * @param int The index of the requested node + * @return Object A reference to the requested node, or null + */ + function &item($index) { + if ($index < $this->getLength()) { + return $this->arNodeList[$index]; + } + + $null = null; + return $null; + } //item + + /** + * Returns the number of nodes in the list + * @return int The number of nodes in the list + */ + function getLength() { + return count($this->arNodeList); + } //getLength + + /** + * Appends a node to the list + * @return Object The appended node + */ + function &appendNode(&$node) { + $this->arNodeList[] =& $node; + return $node; + } //appendNode + + /** + * Removes the specified node from the list + * @param Object A reference to the node to be removed + * @return Object A reference to the removed node + */ + function &removeNode(&$node) { + $total = $this->getLength(); + $returnNode = null; + $found = false; + + for ($i = 0; $i < $total; $i++) { + if (!$found) { + if ($node->uid == $this->arNodeList[$i]->uid) { + $found = true; + $returnNode=& $node; + } + } + + if ($found) { + if ($i == ($total - 1)) { + unset($this->arNodeList[$i]); + } + else { + $this->arNodeList[$i] =& $this->arNodeList[($i + 1)]; + } + } + } + + return $returnNode; + } //$removeNode + + /** + * Formats a string for presentation as HTML + * @param string The string to be formatted + * @param boolean True if the string is to be sent directly to output + * @return string The HTML formatted string + */ + function forHTML($str, $doPrint = false) { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_utilities.php'); + return DOMIT_Utilities::forHTML($str, $doPrint); + } //forHTML + + /** + * Generates an array representation of the node and its children + * @return Array A representation of the node and its children + */ + function toArray() { + return $this->arNodeList; + } //toArray + + /** + * Copies a node and/or its children + * @param boolean True if all child nodes are also to be cloned + * @return Object A copy of the node and/or its children + */ + function &createClone($deep = false) { + $className = get_class($this); + $clone = new $className(); + + foreach ($this->arNodeList as $key => $value) { + $currNode =& $this->arNodeList[$key]; + $clone->arNodeList[$key] =& $currNode->cloneNode($deep); + } + + return $clone; + } //createClone + + /** + * Generates a string representation of the node and its children + * @param boolean True if HTML readable output is desired + * @param boolean True if illegal xml characters in text nodes and attributes should be converted to entities + * @return string The string representation + */ + function toString($htmlSafe = false, $subEntities=false) { + $result = ''; + + foreach ($this->arNodeList as $key => $value) { + $currNode =& $this->arNodeList[$key]; + $result .= $currNode->toString(false, $subEntities); + } + + if ($htmlSafe) $result = $this->forHTML($result); + + return $result; + } //toString + + /** + * Generates a normalized (formatted for readability) representation of the node collection + * @param boolean True if HTML readable output is desired + * @param boolean True if illegal xml characters in text nodes and attributes should be converted to entities + * @return string The string representation + */ + function toNormalizedString($htmlSafe = false, $subEntities=false) { + $result = ''; + + foreach ($this->arNodeList as $key => $value) { + $currNode =& $this->arNodeList[$key]; + $result .= $currNode->toNormalizedString(false, $subEntities); + } + + if ($htmlSafe) $result = $this->forHTML($result); + + return $result; + } //toNormalizedString +} //DOMIT_NodeList + +/** +* A DOM NamedNodeMap implementation +* +* @package domit-xmlparser +* @author John Heinstein +*/ +class DOMIT_NamedNodeMap { + /** @var Array A container for the nodes in the map */ + var $arNodeMap = array(); + /** @var Array A numerical index to the keys of the mapped nodes */ + var $indexedNodeMap = array(); + /** @var boolean True if the list has been modified and $indexedNodeMap needs reindexing */ + var $isDirty = true; + + /** + * Gets a node with the specifed name + * @param string The name of the node + * @return mixed A reference to the requested node, or null + */ + function &getNamedItem($name) { + if (isset($this->arNodeMap[$name])) { + return $this->arNodeMap[$name]; + } + + $null = null; + return $null; + } //getNamedItem + + /** + * Reindexes the numerical index for the named node map + */ + function reindexNodeMap() { + $this->indexedNodeMap = array(); + + foreach ($this->arNodeMap as $key => $value) { + $this->indexedNodeMap[] = $key; + } + + $this->isDirty = false; + } //reindexNodeMap + + /** + * Assigns a node to the list + * @param Object A reference to the node to be assigned + * @return Object A reference to the assigned node + */ + function &setNamedItem(&$arg) { + $returnNode = null; + + if (isset($this->arNodeMap[$arg->nodeName])) { + $returnNode =& $this->arNodeMap[$arg->nodeName]; + } + else { + $this->isDirty = true; + } + + $this->arNodeMap[$arg->nodeName] =& $arg; + return $returnNode; + } //setNamedItem + + /** + * Removes a node from the list, by name + * @param string The name of the node to be removed + * @return mixed A reference to the removed node, or null + */ + function &removeNamedItem($name) { + $returnNode = null; + + if (isset($this->arNodeMap[$name])) { + $returnNode =& $this->arNodeMap[$name]; + unset($this->arNodeMap[$name]); + $this->isDirty = true; + } + + return $returnNode; + } //removeNamedItem + + /** + * Gets a node with the specifed name, taking into account namespaces + * @param string The namespaceURI of the node + * @param string The localName of the node + * @return mixed A reference to the requested node, or null + */ + function &getNamedItemNS($namespaceURI, $localName) { + $key = $this->getKeyNS($namespaceURI, $localName); + + //check for explicit namespaces + if (isset($this->arNodeMap[$key])) { + return $this->arNodeMap[$key]; + } + + //check for implicit namespaces too + //URI will be on element, but not attribute + if (isset($this->arNodeMap[$localName])) { + //get element namespace + $firstAttr =& $this->item(1); + $ownerElem =& $firstAttr->ownerElement; + + if ($namespaceURI == $ownerElem->namespaceURI) { + return $this->arNodeMap[$localName]; + } + } + + $null = null; + return $null; + } //getNamedItemNS + + /** + * Assigns a node to the list, using its namespaceURI and localName + * @param Object A reference to the node to be assigned + * @return Object A reference to the assigned node + */ + function &setNamedItemNS(&$arg) { + $returnNode = null; + $key = $this->getKeyNS($arg->namespaceURI, $arg->localName); + + if (isset($this->arNodeMap[$key])) { + $returnNode =& $this->arNodeMap[$key]; + } + else { + $this->isDirty = true; + } + + $this->arNodeMap[$key] =& $arg; + return $returnNode; + } //setNamedItemNS + + /** + * Removes a node from the list, by name, by local name and namespace URI + * @param string The namespaceURI of the node to be removed + * @param string The localName of the node to be removed + * @return mixed A reference to the removed node, or null + */ + function &removeNamedItemNS($namespaceURI, $localName) { + $returnNode = null; + $key = $this->getKeyNS($namespaceURI, $localName); + + if (isset($this->arNodeMap[$key])) { + $returnNode =& $this->arNodeMap[$key]; + unset($this->arNodeMap[$key]); + $this->isDirty = true; + } + + return $returnNode; + } //removeNamedItemNS + + /** + * Returns the key of the NamedNodeMap, given the namespaceURI and localName + * @param string The namespaceURI of the node to be removed + * @param string The localName of the node to be removed + * @return string The key of the NamedNodeMap + */ + function getKeyNS($namespaceURI, $localName) { + if ($namespaceURI != '') { + return $namespaceURI . ":" . $localName; + } + + return $localName; + } //getKeyNS + + /** + * Return the node at the specified index + * @param int The index of the requested node + * @return mixed A reference to the requested node, or null + */ + function &item($index) { + if ($this->isDirty) $this->reindexNodeMap(); + return $this->arNodeMap[$this->indexedNodeMap[$index]]; + } //item + + /** + * Returns the number of nodes in the map + * @return int The number of nodes in the map + */ + function getLength() { + return count($this->arNodeMap); + } //getLength + + /** + * Formats a string for presentation as HTML + * @param string The string to be formatted + * @param boolean True if the string is to be sent directly to output + * @return string The HTML formatted string + */ + function forHTML($str, $doPrint = false) { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_utilities.php'); + return DOMIT_Utilities::forHTML($str, $doPrint); + } //forHTML + + /** + * Generates an array representation of the node and its children + * @return Array A representation of the node and its children + */ + function toArray() { + return $this->arNodeMap; + } //toArray + + /** + * Copies a node and/or its children + * @param boolean True if all child nodes are also to be cloned + * @return Object A copy of the node and/or its children + */ + function &createClone($deep = false) { + $className = get_class($this); + $clone = new $className(); + + foreach ($this->arNodeMap as $key => $value) { + $currNode =& $this->arNodeMap[$key]; + $clone->arNodeMap[$key] =& $currNode->cloneNode($deep); + } + + return $clone; + } //createClone + + /** + * Generates a string representation of the node and its children + * @param boolean True if HTML readable output is desired + * @param boolean True if illegal xml characters in text nodes and attributes should be converted to entities + * @return string The string representation + */ + function toString($htmlSafe = false, $subEntities=false) { + $result = ''; + + foreach ($this->arNodeMap as $key => $value) { + $currNode =& $this->arNodeMap[$key]; + $result .= $currNode->toString(false, $subEntities); + } + + if ($htmlSafe) $result = $this->forHTML($result); + + return $result; + } //toString + + /** + * Generates a normalized (formatted for readability) representation of the node collection + * @param boolean True if HTML readable output is desired + * @param boolean True if illegal xml characters in text nodes and attributes should be converted to entities + * @return string The string representation + */ + function toNormalizedString($htmlSafe = false, $subEntities=false) { + $result = ''; + + foreach ($this->arNodeMap as $key => $value) { + $currNode =& $this->arNodeMap[$key]; + $result .= $currNode->toNormalizedString(false, $subEntities); + } + + if ($htmlSafe) $result = $this->forHTML($result); + + return $result; + } //toNormalizedString +} //DOMIT_NamedNodeMap + +/** +* A NamedNodeMap with specialized funtionality for Attribute nodes +* +* @package domit-xmlparser +* @author John Heinstein +*/ +class DOMIT_NamedNodeMap_Attr extends DOMIT_NamedNodeMap { + /** + * Generates an array representation of the node and its children + * @return Array A representation of the node and its children + */ + function toArray() { + $arReturn = array(); + + foreach ($this->arNodeMap as $key => $value) { + $arReturn[$key] = $this->arNodeMap[$key]->getValue(); + } + + return $arReturn; + } //toArray + + /** + * Generates a string representation of the node and its children + * @param boolean True if HTML readable output is desired + * @param boolean True if illegal xml characters in text nodes and attributes should be converted to entities + * @return string The string representation + */ + function toString($htmlSafe = false, $subEntities=false) { + $result = ''; + + foreach ($this->arNodeMap as $key => $value) { + $currNode =& $this->arNodeMap[$key]; + $result .= $currNode->toString(false, $subEntities); + } + + if ($htmlSafe) $result = $this->forHTML($result); + + return $result; + } //toString +} //DOMIT_NamedNodeMap_Attr + +?> \ No newline at end of file diff --git a/oss/joomla/trunk/libraries/domit/xml_domit_nodetools.php b/oss/joomla/trunk/libraries/domit/xml_domit_nodetools.php new file mode 100644 index 00000000..8b34c58a --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/xml_domit_nodetools.php @@ -0,0 +1,235 @@ + +* @link http://www.engageinteractive.com/domit/ DOMIT! Home Page +* DOMIT! is Free Software +**/ + +/** attribute parse state, just before parsing an attribute */ +define('DOMIT_ATTRIBUTEPARSER_STATE_ATTR_NONE', 0); +/** attribute parse state, parsing an attribute key */ +define('DOMIT_ATTRIBUTEPARSER_STATE_ATTR_KEY', 1); +/** attribute parse state, parsing an attribute value */ +define('DOMIT_ATTRIBUTEPARSER_STATE_ATTR_VALUE', 2); + +/** +*@global Array Translation table for predefined XML entities +*/ +$GLOBALS['DOMIT_PREDEFINED_ENTITIES'] = array('&' => '&', '<' => '<', '>' => '>', + '"' => '"', "'" => '''); + + /** +* A class of miscellaneous XML helper methods +* +* @package domit-xmlparser +* @author John Heinstein +*/ +class nodetools { + /** + * Parses the attributes string into an array of key / value pairs + * @param string The attribute text + * @return Array An array of key / value pairs + */ + function parseAttributes($attrText, $convertEntities = true, $definedEntities = null) { + $attrText = trim($attrText); + $attrArray = array(); + $maybeEntity = false; + + $total = strlen($attrText); + $keyDump = ''; + $valueDump = ''; + $currentState = DOMIT_ATTRIBUTEPARSER_STATE_ATTR_NONE; + $quoteType = ''; + + if ($definedEntities == null) $defineEntities = array(); + + for ($i = 0; $i < $total; $i++) { +// $currentChar = $attrText{$i}; + $currentChar = substr($attrText, $i, 1); + + if ($currentState == DOMIT_ATTRIBUTEPARSER_STATE_ATTR_NONE) { + if (trim($currentChar != '')) { + $currentState = DOMIT_ATTRIBUTEPARSER_STATE_ATTR_KEY; + } + } + + switch ($currentChar) { + case "\t": + if ($currentState == DOMIT_ATTRIBUTEPARSER_STATE_ATTR_VALUE) { + $valueDump .= $currentChar; + } + else { + $currentChar = ''; + } + break; + + case "\x0B": //vertical tab + case "\n": + case "\r": + $currentChar = ''; + break; + + case '=': + if ($currentState == DOMIT_ATTRIBUTEPARSER_STATE_ATTR_VALUE) { + $valueDump .= $currentChar; + } + else { + $currentState = DOMIT_ATTRIBUTEPARSER_STATE_ATTR_VALUE; + $quoteType = ''; + $maybeEntity = false; + } + break; + + case '"': + if ($currentState == DOMIT_ATTRIBUTEPARSER_STATE_ATTR_VALUE) { + if ($quoteType == '') { + $quoteType = '"'; + } + else { + if ($quoteType == $currentChar) { + if ($convertEntities && $maybeEntity) { + $valueDump = strtr($valueDump, DOMIT_PREDEFINED_ENTITIES); + $valueDump = strtr($valueDump, $definedEntities); + } + + $attrArray[trim($keyDump)] = $valueDump; + $keyDump = $valueDump = $quoteType = ''; + $currentState = DOMIT_ATTRIBUTEPARSER_STATE_ATTR_NONE; + } + else { + $valueDump .= $currentChar; + } + } + } + break; + + case "'": + if ($currentState == DOMIT_ATTRIBUTEPARSER_STATE_ATTR_VALUE) { + if ($quoteType == '') { + $quoteType = "'"; + } + else { + if ($quoteType == $currentChar) { + if ($convertEntities && $maybeEntity) { + $valueDump = strtr($valueDump, $predefinedEntities); + $valueDump = strtr($valueDump, $definedEntities); + } + + $attrArray[trim($keyDump)] = $valueDump; + $keyDump = $valueDump = $quoteType = ''; + $currentState = DOMIT_ATTRIBUTEPARSER_STATE_ATTR_NONE; + } + else { + $valueDump .= $currentChar; + } + } + } + break; + + case '&': + //might be an entity + $maybeEntity = true; + $valueDump .= $currentChar; + break; + + default: + if ($currentState == DOMIT_ATTRIBUTEPARSER_STATE_ATTR_KEY) { + $keyDump .= $currentChar; + } + else { + $valueDump .= $currentChar; + } + } + } + + return $attrArray; + } //parseAttributes + + /** + * Move a node to the previous index in the childNodes array + * @param Object The node to be moved + */ + function moveUp(&$node) { + if (($node->previousSibling != null) && ($node->parentNode != null)) { + $parent =& $node->parentNode; + $previous =& $node->previousSibling; + + $node =& $parent->removeChild($node); + $parent->insertBefore($node, $previous); + } + } //moveUp + + /** + * Move a node to the next index in the childNodes array + * @param Object The node to be moved + */ + function moveDown(&$node) { + if (($node->nextSibling != null) && ($node->parentNode != null)) { + $parent =& $node->parentNode; + + if ($node->nextSibling->nextSibling == null) { + $node =& $parent->removeChild($node); + $parent->appendChild($node); + } + else { + $insertionPoint =& $node->nextSibling->nextSibling; + $node =& $parent->removeChild($node); + $parent->insertBefore($node, $insertionPoint); + } + } + } //moveDown + + /** + * Checks if a node exists on the given path; if so, returns the node, otherwise false + * @param Object The calling node + * @param string The path + * @return mixed The found node, or false + */ + function &nodeExists(&$callingNode, $path) { + $foundNode =& $callingNode->getElementsByPath($path, 1); + + if ($foundNode == null) return false; + return $foundNode; + } //nodeExists + + /** + * Generates a heirarchy of nodes based on a path expression + * @param string The path expression + * @param string The value of a text node to be appended to the last element + * @return object The generated nodes + */ + function &fromPath(&$xmldoc, $path, $text = null) { + $pathSegments = explode('/', $path); + $parent = null; + $lastNode = null; + $total = count($pathSegments); + + for ($i = 0; $i < $total; $i++) { + if ($pathSegments[$i] != '') { + $currNode =& $xmldoc->createElement($pathSegments[$i]); + + if ($parent == null) { + $parent =& $currNode; + } + else { + $lastNode->appendChild($currNode); + } + + $lastNode =& $currNode; + } + } + + if ($text != null) { + $currNode =& $xmldoc->createTextNode($text); + $lastNode->appendChild($currNode); + } + + return $parent; + } //nodeExists +} //nodetools + +?> \ No newline at end of file diff --git a/oss/joomla/trunk/libraries/domit/xml_domit_parseattributes.php b/oss/joomla/trunk/libraries/domit/xml_domit_parseattributes.php new file mode 100644 index 00000000..43ae1163 --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/xml_domit_parseattributes.php @@ -0,0 +1,144 @@ + +* @link http://www.engageinteractive.com/domit/ DOMIT! Home Page +* DOMIT! is Free Software +**/ + +/** attribute parse state, just before parsing an attribute */ +define('DOMIT_ATTRIBUTEPARSER_STATE_ATTR_NONE', 0); +/** attribute parse state, parsing an attribute key */ +define('DOMIT_ATTRIBUTEPARSER_STATE_ATTR_KEY', 1); +/** attribute parse state, parsing an attribute value */ +define('DOMIT_ATTRIBUTEPARSER_STATE_ATTR_VALUE', 2); + +/** +*@global Array Translation table for predefined XML entities +*/ +$GLOBALS['DOMIT_PREDEFINED_ENTITIES'] = array('&' => '&', '<' => '<', '>' => '>', + '"' => '"', "'" => '''); + +/** +* Parses the attributes string into an array of key / value pairs +* @param string The attribute text +* @return Array An array of key / value pairs +*/ +function parseAttributes($attrText, $convertEntities = true, $definedEntities = null) { + $attrText = trim($attrText); + $attrArray = array(); + $maybeEntity = false; + + $total = strlen($attrText); + $keyDump = ''; + $valueDump = ''; + $currentState = DOMIT_ATTRIBUTEPARSER_STATE_ATTR_NONE; + $quoteType = ''; + + if ($definedEntities == null) $defineEntities = array(); + + for ($i = 0; $i < $total; $i++) { +// $currentChar = $attrText{$i}; + $currentChar = substr($attrText, $i, 1); + + if ($currentState == DOMIT_ATTRIBUTEPARSER_STATE_ATTR_NONE) { + if (trim($currentChar != '')) { + $currentState = DOMIT_ATTRIBUTEPARSER_STATE_ATTR_KEY; + } + } + + switch ($currentChar) { + case "\t": + if ($currentState == DOMIT_ATTRIBUTEPARSER_STATE_ATTR_VALUE) { + $valueDump .= $currentChar; + } + else { + $currentChar = ''; + } + break; + + case "\x0B": //vertical tab + case "\n": + case "\r": + $currentChar = ''; + break; + + case '=': + if ($currentState == DOMIT_ATTRIBUTEPARSER_STATE_ATTR_VALUE) { + $valueDump .= $currentChar; + } + else { + $currentState = DOMIT_ATTRIBUTEPARSER_STATE_ATTR_VALUE; + $quoteType = ''; + $maybeEntity = false; + } + break; + + case '"': + if ($currentState == DOMIT_ATTRIBUTEPARSER_STATE_ATTR_VALUE) { + if ($quoteType == '') { + $quoteType = '"'; + } + else { + if ($quoteType == $currentChar) { + if ($convertEntities && $maybeEntity) { + $valueDump = strtr($valueDump, DOMIT_PREDEFINED_ENTITIES); + $valueDump = strtr($valueDump, $definedEntities); + } + + $attrArray[trim($keyDump)] = $valueDump; + $keyDump = $valueDump = $quoteType = ''; + $currentState = DOMIT_ATTRIBUTEPARSER_STATE_ATTR_NONE; + } + else { + $valueDump .= $currentChar; + } + } + } + break; + + case "'": + if ($currentState == DOMIT_ATTRIBUTEPARSER_STATE_ATTR_VALUE) { + if ($quoteType == '') { + $quoteType = "'"; + } + else { + if ($quoteType == $currentChar) { + if ($convertEntities && $maybeEntity) { + $valueDump = strtr($valueDump, $predefinedEntities); + $valueDump = strtr($valueDump, $definedEntities); + } + + $attrArray[trim($keyDump)] = $valueDump; + $keyDump = $valueDump = $quoteType = ''; + $currentState = DOMIT_ATTRIBUTEPARSER_STATE_ATTR_NONE; + } + else { + $valueDump .= $currentChar; + } + } + } + break; + + case '&': + //might be an entity + $maybeEntity = true; + $valueDump .= $currentChar; + break; + + default: + if ($currentState == DOMIT_ATTRIBUTEPARSER_STATE_ATTR_KEY) { + $keyDump .= $currentChar; + } + else { + $valueDump .= $currentChar; + } + } + } + + return $attrArray; +} //parseAttributes +?> \ No newline at end of file diff --git a/oss/joomla/trunk/libraries/domit/xml_domit_parser.php b/oss/joomla/trunk/libraries/domit/xml_domit_parser.php new file mode 100644 index 00000000..7f715712 --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/xml_domit_parser.php @@ -0,0 +1,3679 @@ + +* @link http://www.engageinteractive.com/domit/ DOMIT! Home Page +* DOMIT! is Free Software +**/ + +if (!defined('DOMIT_INCLUDE_PATH')) { + define('DOMIT_INCLUDE_PATH', (dirname(__FILE__) . "/")); +} + +/** current version of DOMIT! */ +define ('DOMIT_VERSION', '1.01'); + +/** XML namespace URI */ +define ('DOMIT_XML_NAMESPACE', 'http://www.w3.org/xml/1998/namespace'); + +/** XMLNS namespace URI */ +define ('DOMIT_XMLNS_NAMESPACE', 'http://www.w3.org/2000/xmlns/'); + +/** +* @global array Flipped version of $definedEntities array, to allow two-way conversion of entities +* +* Made global so that Attr nodes, which have no ownerDocument property, can access the array +*/ +$GLOBALS['DOMIT_defined_entities_flip'] = array(); + +require_once(DOMIT_INCLUDE_PATH . 'xml_domit_shared.php'); + +/** +* The base class of all DOMIT node types +* +* @package domit-xmlparser +* @subpackage domit-xmlparser-main +* @author John Heinstein +*/ +class DOMIT_Node { + /** @var string The name of the node, varies according to node type */ + var $nodeName = null; + /** @var string The value of the node, varies according to node type */ + var $nodeValue = null; + /** @var int The type of node, e.g. CDataSection */ + var $nodeType = null; + /** @var Object A reference to the parent of the current node */ + var $parentNode = null; + /** @var Array An array of child node references */ + var $childNodes = null; + /** @var Object A reference to the first node in the childNodes list */ + var $firstChild = null; + /** @var Object A reference to the last node in the childNodes list */ + var $lastChild = null; + /** @var Object A reference to the node prior to the current node in its parents childNodes list */ + var $previousSibling = null; + /** @var Object A reference to the node after the current node in its parents childNodes list */ + var $nextSibling = null; + /** @var Object A NodeList of attribute nodes */ + var $attributes = null; + /** @var Object A reference to the Document node */ + var $ownerDocument = null; + /** @var String A URI that identifies the XML namespace to which the node belongs */ + var $namespaceURI = null; + /** @var String The namespace prefix for the node */ + var $prefix = null; + /** @var String The local name of the node */ + var $localName = null; + /** @var string The unique node id */ + var $uid; + /** @var int The number of children of the current node */ + var $childCount = 0; + + /** + * Raises error if abstract class is directly instantiated + */ + function DOMIT_Node() { + DOMIT_DOMException::raiseException(DOMIT_ABSTRACT_CLASS_INSTANTIATION_ERR, + 'Cannot instantiate abstract class DOMIT_Node'); + } //DOMIT_Node + + /** + * DOMIT_Node constructor, assigns a uid + */ + function _constructor() { + global $uidFactory; + $this->uid = $uidFactory->generateUID(); + } //_constructor + + /** + * Appends a node to the childNodes list of the current node + * @abstract + * @param Object The node to be appended + * @return Object The appended node + */ + function &appendChild(&$child) { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Method appendChild cannot be called by class ' . get_class($this))); + } //appendChild + + /** + * Inserts a node to the childNodes list of the current node + * @abstract + * @param Object The node to be inserted + * @param Object The node before which the insertion is to occur + * @return Object The inserted node + */ + function &insertBefore(&$newChild, &$refChild) { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Method insertBefore cannot be called by class ' . get_class($this))); + } //insertBefore + + /** + * Replaces a node with another + * @abstract + * @param Object The new node + * @param Object The old node + * @return Object The new node + */ + function &replaceChild(&$newChild, &$oldChild) { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Method replaceChild cannot be called by class ' . get_class($this))); + } //replaceChild + + /** + * Removes a node from the childNodes list of the current node + * @abstract + * @param Object The node to be removed + * @return Object The removed node + */ + function &removeChild(&$oldChild) { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Method removeChild cannot be called by class ' . get_class($this))); + } //removeChild + + /** + * Returns the index of the specified node in a childNodes list + * @param Array The childNodes array to be searched + * @param Object The node targeted by the search + * @return int The index of the target node, or -1 if not found + */ + function getChildNodeIndex(&$arr, &$child) { + $index = -1; + $total = count($arr); + + for ($i = 0; $i < $total; $i++) { + if ($child->uid == $arr[$i]->uid) { + $index = $i; + break; + } + } + + return $index; + } //getChildNodeIndex + + /** + * Determines whether a node has any children + * @return boolean True if any child nodes are present + */ + function hasChildNodes() { + return ($this->childCount > 0); + } //hasChildNodes + + /** + * Determines whether a node has any attributes + * @return boolean True if the node has attributes + */ + function hasAttributes() { + //overridden in DOMIT_Element + return false; + } //hasChildNodes + + /** + * Collapses adjacent text nodes in entire node subtree + */ + function normalize() { + if (($this->nodeType == DOMIT_DOCUMENT_NODE) && ($this->documentElement != null)) { + $this->documentElement->normalize(); + } + } //normalize + + /** + * Copies a node and/or its children + * @abstract + * @param boolean True if all child nodes are also to be cloned + * @return Object A copy of the node and/or its children + */ + function &cloneNode($deep = false) { + DOMIT_DOMException::raiseException(DOMIT_ABSTRACT_METHOD_INVOCATION_ERR, + 'Cannot invoke abstract method DOMIT_Node->cloneNode($deep). Must provide an overridden method in your subclass.'); + } //cloneNode + + /** + * Adds elements with the specified tag name to a NodeList collection + * @param Object The NodeList collection + * @param string The tag name of matching elements + */ + function getNamedElements(&$nodeList, $tagName) { + //Implemented in DOMIT_Element. + //Needs to be here though! This is called against all nodes in the document. + } //getNamedElements + + /** + * Sets the ownerDocument property of a node to the containing DOMIT_Document + * @param Object A reference to the document element of the DOMIT_Document + */ + function setOwnerDocument(&$rootNode) { + if ($rootNode->ownerDocument == null) { + unset($this->ownerDocument); + $this->ownerDocument = null; + } + else { + $this->ownerDocument =& $rootNode->ownerDocument; + } + + $total = $this->childCount; + + for ($i = 0; $i < $total; $i++) { + $this->childNodes[$i]->setOwnerDocument($rootNode); + } + } //setOwnerDocument + + /** + * Tests whether a value is null, and if so, returns a default value + * @param mixed The value to be tested + * @param mixed The default value + * @return mixed The specified value, or the default value if null + */ + function &nvl(&$value,$default) { + if (is_null($value)) return $default; + return $value; + } //nvl + + /** + * Retrieves an element or DOMIT_NodeList of elements corresponding to an Xpath-like expression. + * @abstract + * @param string The query pattern + * @param int If a single node is to be returned (rather than the entire NodeList) the index of that node + * @return mixed A NodeList or single node that matches the pattern + */ + function &getElementsByPath($pattern, $nodeIndex = 0) { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Method getElementsByPath cannot be called by class ' . get_class($this))); + } //getElementsByPath + + /** + * Retrieves an element or DOMIT_NodeList of elements corresponding to an Xpath-like attribute expression (NOT YET IMPLEMENTED!) + * @abstract + * @param string The query pattern + * @param int If a single node is to be returned (rather than the entire NodeList) the index of that node + * @return mixed A NodeList or single node that matches the pattern + */ + function &getElementsByAttributePath($pattern, $nodeIndex = 0) { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Method getElementsByAttributePath cannot be called by class ' . get_class($this))); + } //getElementsByAttributePath + + /** + * Adds all child nodes of the specified nodeType to the NodeList + * @abstract + * @param Object The NodeList collection + * @param string The nodeType of matching nodes + */ + function getTypedNodes(&$nodeList, $type) { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Method getTypedNodes cannot be called by class ' . get_class($this))); + } //getTypedNodes + + /** + * Adds all child nodes of the specified nodeValue to the NodeList + * @abstract + * @param Object The NodeList collection + * @param string The nodeValue of matching nodes + */ + function getValuedNodes(&$nodeList, $value) { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Method getValuedNodes cannot be called by class ' . get_class($this))); + } //getValuedNodes + + /** + * Returns the concatented text of the current node and its children + * @return string The concatented text of the current node and its children + */ + function getText() { + return $this->nodeValue; + } //getText + + /** + * Indicates whether the specified feature is supported by the DOM implementation and this node + * @param string The feature + * @param string The version of the DOM implementation + * @return boolean True if the specified feature is supported + */ + function isSupported($feature, $version = null) { + //don't worry about parsing based on version at this point in time; + //the only feature that is supported is 'XML'... + if (($version == '1.0') || ($version == '2.0') || ($version == null)) { + if (strtoupper($feature) == 'XML') { + return true; + } + } + + return false; + } //isSupported + + /** + * Formats a string for presentation as HTML + * @param string The string to be formatted + * @param boolean True if the string is to be sent directly to output + * @return string The HTML formatted string + */ + function forHTML($str, $doPrint = false) { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_utilities.php'); + return DOMIT_Utilities::forHTML($str, $doPrint); + } //forHTML + + /** + * Generates an array representation of the node and its children + * @abstract + * @return Array A representation of the node and its children + */ + function toArray() { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Method toArray cannot be called by class ' . get_class($this))); + } //toArray + + /** + * A node event that can be set to fire upon document loading, used for node initialization + * @abstract + */ + function onLoad() { + //you can override this method if you subclass any of the + //DOMIT_Nodes. It's a way of performing + //initialization of your subclass as soon as the document + //has been loaded (as opposed to as soon as the current node + //has been instantiated). + } //onLoad + + /** + * Clears previousSibling, nextSibling, and parentNode references from a node that has been removed + */ + function clearReferences() { + if ($this->previousSibling != null) { + unset($this->previousSibling); + $this->previousSibling = null; + } + if ($this->nextSibling != null) { + unset($this->nextSibling); + $this->nextSibling = null; + } + if ($this->parentNode != null) { + unset($this->parentNode); + $this->parentNode = null; + } + } //clearReferences + + /** + * Removes the node from the document + */ + function delete () { + if ($this->parentNode != null) { + $this->parentNode->removeChild($node); + } + } //delete + + /** + * Generates a normalized (formatted for readability) representation of the node and its children + * @param boolean True if HTML readable output is desired + * @param boolean True if illegal xml characters in text nodes and attributes should be converted to entities + * @return string The formatted string representation + */ + function toNormalizedString($htmlSafe = false, $subEntities = false) { + //require this file for generating a normalized (readable) xml string representation + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_utilities.php'); + global $DOMIT_defined_entities_flip; + + $result = DOMIT_Utilities::toNormalizedString($this, $subEntities, $DOMIT_defined_entities_flip); + + if ($htmlSafe) $result = $this->forHTML($result); + + return $result; + } //toNormalizedString +} //DOMIT_Node + +/** +* A parent class for nodes which possess child nodes +* +* @package domit-xmlparser +* @subpackage domit-xmlparser-main +* @author John Heinstein +*/ +class DOMIT_ChildNodes_Interface extends DOMIT_Node { + /** + * Raises error if abstract class is directly instantiated + */ + function DOMIT_ChildNodes_Interface() { + DOMIT_DOMException::raiseException(DOMIT_ABSTRACT_CLASS_INSTANTIATION_ERR, + 'Cannot instantiate abstract class DOMIT_ChildNodes_Interface'); + } //DOMIT_ChildNodes_Interface + + /** + * Appends a node to the childNodes list of the current node + * @param Object The node to be appended + * @return Object The appended node + */ + function &appendChild(&$child) { + if ($child->nodeType == DOMIT_ATTRIBUTE_NODE) { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Cannot add a node of type ' . get_class($child) . ' using appendChild')); + } + else if ($child->nodeType == DOMIT_DOCUMENT_FRAGMENT_NODE) { + $total = $child->childCount; + + for ($i = 0; $i < $total; $i++) { + $currChild =& $child->childNodes[$i]; + $this->appendChild($currChild); + } + } + else { + if (!($this->hasChildNodes())) { + $this->childNodes[0] =& $child; + $this->firstChild =& $child; + } + else { + //remove $child if it already exists + $index = $this->getChildNodeIndex($this->childNodes, $child); + + if ($index != -1) { + $this->removeChild($child); + } + + //append child + $numNodes = $this->childCount; + //BB: was bug auto-created wrong childnodes[-1]: added IF + if ($numNodes > 0) { + $prevSibling =& $this->childNodes[($numNodes - 1)]; + } + + $this->childNodes[$numNodes] =& $child; + + //set next and previous relationships + //BB: added this line and the else part to finish correcting bug + if (isset( $prevSibling )) { + $child->previousSibling =& $prevSibling; + $prevSibling->nextSibling =& $child; + } else { + unset( $child->previousSibling ); + $child->previousSibling = null; + $this->firstChild =& $child; + } + } + } + + $this->lastChild =& $child; + $child->parentNode =& $this; + + unset($child->nextSibling); + $child->nextSibling = null; + + $child->setOwnerDocument($this); + $this->childCount++; + + return $child; + + } //appendChild + + /** + * Inserts a node to the childNodes list of the current node + * @param Object The node to be inserted + * @param Object The node before which the insertion is to occur + * @return Object The inserted node + */ + function &insertBefore(&$newChild, &$refChild) { + if ($newChild->nodeType == DOMIT_ATTRIBUTE_NODE) { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Cannot add a node of type ' . get_class($newChild) . ' using insertBefore')); + } + + if (($refChild->nodeType == DOMIT_DOCUMENT_NODE) || + //($refChild->parentNode->nodeType == DOMIT_DOCUMENT_NODE) || + ($refChild->parentNode == null)) { + + DOMIT_DOMException::raiseException(DOMIT_NOT_FOUND_ERR, + 'Reference child not present in the child nodes list.'); + } + + //if reference child is also the node to be inserted + //leave the document as is and don't raise an exception + if ($refChild->uid == $newChild->uid) { + return $newChild; + } + + //if $newChild is a DocumentFragment, + //loop through and insert each node separately + if ($newChild->nodeType == DOMIT_DOCUMENT_FRAGMENT_NODE) { + $total = $newChild->childCount; + + for ($i = 0; $i < $total; $i++) { + $currChild =& $newChild->childNodes[$i]; + $this->insertBefore($currChild, $refChild); + } + + return $newChild; + } + + //remove $newChild if it already exists + $index = $this->getChildNodeIndex($this->childNodes, $newChild); + if ($index != -1) { + $this->removeChild($newChild); + } + + //find index of $refChild in childNodes + $index = $this->getChildNodeIndex($this->childNodes, $refChild); + + if ($index != -1) { + //reset sibling chain + if ($refChild->previousSibling != null) { + $refChild->previousSibling->nextSibling =& $newChild; + $newChild->previousSibling =& $refChild->previousSibling; + } + else { + $this->firstChild =& $newChild; + + if ($newChild->previousSibling != null) { + unset($newChild->previousSibling); + $newChild->previousSibling = null; + } + } + + $newChild->parentNode =& $refChild->parentNode; + $newChild->nextSibling =& $refChild; + $refChild->previousSibling =& $newChild; + + //add node to childNodes + $i = $this->childCount; + + while ($i >= 0) { + if ($i > $index) { + $this->childNodes[$i] =& $this->childNodes[($i - 1)]; + } + else if ($i == $index) { + $this->childNodes[$i] =& $newChild; + } + $i--; + } + + $this->childCount++; + } + else { + $this->appendChild($newChild); + } + + $newChild->setOwnerDocument($this); + return $newChild; + } //insertBefore + + /** + * Replaces a node with another + * @param Object The new node + * @param Object The old node + * @return Object The new node + */ + function &replaceChild(&$newChild, &$oldChild) { + if ($newChild->nodeType == DOMIT_ATTRIBUTE_NODE) { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Cannot add a node of type ' . get_class($newChild) . ' using replaceChild')); + } + else if ($newChild->nodeType == DOMIT_DOCUMENT_FRAGMENT_NODE) { //if $newChild is a DocumentFragment + //replace the first node then loop through and insert each node separately + $total = $newChild->childCount; + + if ($total > 0) { + $newRef =& $newChild->lastChild; + $this->replaceChild($newRef, $oldChild); + + for ($i = 0; $i < ($total - 1); $i++) { + $currChild =& $newChild->childNodes[$i]; + $this->insertBefore($currChild, $newRef); + } + } + + return $newChild; + } + else { + if ($this->hasChildNodes()) { + //remove $newChild if it already exists + $index = $this->getChildNodeIndex($this->childNodes, $newChild); + if ($index != -1) { + $this->removeChild($newChild); + } + + //find index of $oldChild in childNodes + $index = $this->getChildNodeIndex($this->childNodes, $oldChild); + + if ($index != -1) { + $newChild->ownerDocument =& $oldChild->ownerDocument; + $newChild->parentNode =& $oldChild->parentNode; + + //reset sibling chain + if ($oldChild->previousSibling == null) { + unset($newChild->previousSibling); + $newChild->previousSibling = null; + } + else { + $oldChild->previousSibling->nextSibling =& $newChild; + $newChild->previousSibling =& $oldChild->previousSibling; + } + + if ($oldChild->nextSibling == null) { + unset($newChild->nextSibling); + $newChild->nextSibling = null; + } + else { + $oldChild->nextSibling->previousSibling =& $newChild; + $newChild->nextSibling =& $oldChild->nextSibling; + } + + $this->childNodes[$index] =& $newChild; + + if ($index == 0) $this->firstChild =& $newChild; + if ($index == ($this->childCount - 1)) $this->lastChild =& $newChild; + + $newChild->setOwnerDocument($this); + return $newChild; + } + } + + DOMIT_DOMException::raiseException(DOMIT_NOT_FOUND_ERR, + ('Reference node for replaceChild not found.')); + } + } //replaceChild + + /** + * Removes a node from the childNodes list of the current node + * @param Object The node to be removed + * @return Object The removed node + */ + function &removeChild(&$oldChild) { + if ($this->hasChildNodes()) { + //find index of $oldChild in childNodes + $index = $this->getChildNodeIndex($this->childNodes, $oldChild); + + if ($index != -1) { + //reset sibling chain + if (($oldChild->previousSibling != null) && ($oldChild->nextSibling != null)) { + $oldChild->previousSibling->nextSibling =& $oldChild->nextSibling; + $oldChild->nextSibling->previousSibling =& $oldChild->previousSibling; + } + else if (($oldChild->previousSibling != null) && ($oldChild->nextSibling == null)) { + $this->lastChild =& $oldChild->previousSibling; + unset($oldChild->previousSibling->nextSibling); + $oldChild->previousSibling->nextSibling = null; + } + else if (($oldChild->previousSibling == null) && ($oldChild->nextSibling != null)) { + unset($oldChild->nextSibling->previousSibling); + $oldChild->nextSibling->previousSibling = null; + $this->firstChild =& $oldChild->nextSibling; + } + else if (($oldChild->previousSibling == null) && ($oldChild->nextSibling == null)) { + unset($this->firstChild); + $this->firstChild = null; + unset($this->lastChild); + $this->lastChild = null; + } + + $total = $this->childCount; + + //remove node from childNodes + for ($i = 0; $i < $total; $i++) { + if ($i == ($total - 1)) { + array_splice($this->childNodes, $i, 1); + } + else if ($i >= $index) { + $this->childNodes[$i] =& $this->childNodes[($i + 1)]; + } + } + + $this->childCount--; + + $oldChild->clearReferences(); + return $oldChild; + } + } + + DOMIT_DOMException::raiseException(DOMIT_NOT_FOUND_ERR, + ('Target node for removeChild not found.')); + } //removeChild + + + /** + * Searches the element tree for an element with the specified attribute name and value. + * @param string The value of the attribute + * @param string The name of the attribute + * @param boolean True if the first found node is to be returned as a node instead of a nodelist + * @param boolean True if uid is to be considered an attribute + * @return object A NodeList of found elements, or null + */ + function &getElementsByAttribute($attrName = 'id', $attrValue = '', + $returnFirstFoundNode = false, $treatUIDAsAttribute = false) { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_nodemaps.php'); + + $nodelist = new DOMIT_NodeList(); + + switch ($this->nodeType) { + case DOMIT_ELEMENT_NODE: + $this->_getElementsByAttribute($nodelist, $attrName, $attrValue, + $returnFirstFoundNode, $treatUIDAsAttribute); + break; + + case DOMIT_DOCUMENT_NODE: + if ($this->documentElement != null) { + $this->documentElement->_getElementsByAttribute($nodelist, + $attrName, $attrValue, $returnFirstFoundNode, $treatUIDAsAttribute); + } + break; + } + + if ($returnFirstFoundNode) { + if ($nodelist->getLength() > 0) { + return $nodelist->item(0); + } + else { + $null = null; + return $null; + } + } + else { + return $nodelist; + } + } //getElementsByAttribute + + /** + * Searches the element tree for an element with the specified attribute name and value. + * @param object The node list of found elements + * @param string The value of the attribute + * @param string The name of the attribute + * @param boolean True if the first found node is to be returned as a node instead of a nodelist + * @param boolean True if uid is to be considered an attribute + * @param boolean True the node has been found + */ + function _getElementsByAttribute(&$nodelist, $attrName, $attrValue, + $returnFirstFoundNode, $treatUIDAsAttribute, $foundNode = false) { + if (!($foundNode && $returnFirstFoundNode)) { + if (($this->getAttribute($attrName) == $attrValue) || + ($treatUIDAsAttribute && ($attrName == 'uid') && ($this->uid == $attrValue))) { + $nodelist->appendNode($this); + $foundNode = true; + if ($returnFirstFoundNode) return; + } + + $total = $this->childCount; + + for ($i = 0; $i < $total; $i++) { + $currNode =& $this->childNodes[$i]; + + if ($currNode->nodeType == DOMIT_ELEMENT_NODE) { + $currNode->_getElementsByAttribute($nodelist, + $attrName, $attrValue, $returnFirstFoundNode, + $treatUIDAsAttribute, $foundNode); + } + + } + } + } //_getElementsByAttribute + + /** + * Performs an XPath query + * @param string The query pattern + * @return Object A NodeList containing the found nodes + */ + function &selectNodes($pattern, $nodeIndex = 0) { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_xpath.php'); + + $xpParser = new DOMIT_XPath(); + + return $xpParser->parsePattern($this, $pattern, $nodeIndex); + } //selectNodes + + /** + * Converts the childNodes array into a NodeList object + * @return Object A NodeList containing elements of the childNodes array + */ + function &childNodesAsNodeList() { + require_once('xml_domit_nodemaps.php'); + $myNodeList = new DOMIT_NodeList(); + + $total = $this->childCount; + + for ($i = 0; $i < $total; $i++) { + $myNodeList->appendNode($this->childNodes[$i]); + } + + return $myNodeList; + } //childNodesAsNodeList +} //DOMIT_ChildNodes_Interface + +/** +* A class representing the DOM Document +* +* @package domit-xmlparser +* @subpackage domit-xmlparser-main +* @author John Heinstein +*/ +class DOMIT_Document extends DOMIT_ChildNodes_Interface { + /** @var Object The xml declaration processing instruction */ + var $xmlDeclaration; + /** @var Object A reference to a DOMIT_DocType object */ + var $doctype; + /** @var Object A reference to the root node of the DOM document */ + var $documentElement; + /** @var string The parser used to process the DOM document, either "EXPAT" or "SAXY" */ + var $parser; + /** @var Object A reference to the DOMIT_DOMImplementation object */ + var $implementation; + /** @var boolean True if the DOM document has been modifed since being parsed (NOT YET IMPLEMENTED!) */ + var $isModified; + /** @var boolean True if whitespace is to be preserved during parsing */ + var $preserveWhitespace = false; + /** @var Array User defined translation table for XML entities; passed to SAXY */ + var $definedEntities = array(); + /** @var boolean If true, loadXML or parseXML will attempt to detect and repair invalid xml */ + var $doResolveErrors = false; + /** @var boolean If true, elements tags will be rendered to string as rather than */ + var $doExpandEmptyElementTags = false; + /** @var array A list of exceptions to the empty element expansion rule */ + var $expandEmptyElementExceptions = array(); + /** @var boolean If true, namespaces will be processed */ + var $isNamespaceAware = false; + /** @var int The error code returned by the SAX parser */ + var $errorCode = 0; + /** @var string The error string returned by the SAX parser */ + var $errorString = ''; + /** @var object A reference to a http connection or proxy server, if one is required */ + var $httpConnection = null; + /** @var boolean True if php_http_client_generic is to be used instead of PHP get_file_contents to retrieve xml data */ + var $doUseHTTPClient = false; + /** @var array An array of namespacesURIs mapped to prefixes */ + var $namespaceURIMap = array(); + + /** + * DOM Document constructor + */ + function DOMIT_Document() { + $this->_constructor(); + $this->xmlDeclaration = null; + $this->doctype = null; + $this->documentElement = null; + $this->nodeType = DOMIT_DOCUMENT_NODE; + $this->nodeName = '#document'; + $this->ownerDocument =& $this; + $this->parser = ''; + $this->implementation = new DOMIT_DOMImplementation(); + } //DOMIT_Document + + /** + * Specifies whether DOMIT! will try to fix invalid XML before parsing begins + * @param boolean True if errors are to be resolved + */ + function resolveErrors($truthVal) { + $this->doResolveErrors = $truthVal; + } //resolveErrors + + /** + * Specifies whether DOMIT! processes namespace information + * @param boolean True if namespaces are to be processed + */ + function setNamespaceAwareness($truthVal) { + $this->isNamespaceAware = $truthVal; + } //setNamespaceAwareness + + /** + * Specifies whether DOMIT! preserves whitespace when parsing + * @param boolean True if whitespace is to be preserved + */ + function preserveWhitespace($truthVal) { + $this->preserveWhitespace = $truthVal; + } //preserveWhitespace + + /** + * Specifies the parameters of the http conection used to obtain the xml data + * @param string The ip address or domain name of the connection + * @param string The path of the connection + * @param int The port that the connection is listening on + * @param int The timeout value for the connection + * @param string The user name, if authentication is required + * @param string The password, if authentication is required + */ + function setConnection($host, $path = '/', $port = 80, $timeout = 0, $user = null, $password = null) { + require_once(DOMIT_INCLUDE_PATH . 'php_http_client_generic.php'); + + $this->httpConnection = new php_http_client_generic($host, $path, $port, $timeout, $user, $password); + } //setConnection + + /** + * Specifies basic authentication for an http connection + * @param string The user name + * @param string The password + */ + function setAuthorization($user, $password) { + $this->httpConnection->setAuthorization($user, $password); + } //setAuthorization + + /** + * Specifies that a proxy is to be used to obtain the xml data + * @param string The ip address or domain name of the proxy + * @param string The path to the proxy + * @param int The port that the proxy is listening on + * @param int The timeout value for the connection + * @param string The user name, if authentication is required + * @param string The password, if authentication is required + */ + function setProxyConnection($host, $path = '/', $port = 80, $timeout = 0, $user = null, $password = null) { + require_once(DOMIT_INCLUDE_PATH . 'php_http_proxy.php'); + + $this->httpConnection = new php_http_proxy($host, $path, $port, $timeout, $user, $password); + } //setProxyConnection + + /** + * Specifies basic authentication for the proxy + * @param string The user name + * @param string The password + */ + function setProxyAuthorization($user, $password) { + $this->httpConnection->setProxyAuthorization($user, $password); + } //setProxyAuthorization + + /** + * Specifies whether an HTTP client should be used to establish a connection + * @param boolean True if an HTTP client is to be used to establish the connection + */ + function useHTTPClient($truthVal) { + $this->doUseHTTPClient = $truthVal; + } //useHTTPClient + + /** + * Returns the error code from the underlying SAX parser + * @return int The error code + */ + function getErrorCode() { + return $this->errorCode; + } //getErrorCode + + /** + * Returns the error string from the underlying SAX parser + * @return string The error string + */ + function getErrorString() { + return $this->errorString; + } //getErrorString + + /** + * Specifies whether elements tags will be rendered to string as rather than + * @param boolean True if the expanded form is to be used + * @param mixed An array of tag names that should be excepted from expandEmptyElements rule (optional) + */ + function expandEmptyElementTags($truthVal, $expandEmptyElementExceptions = false) { + $this->doExpandEmptyElementTags = $truthVal; + + if (is_array($expandEmptyElementExceptions)) { + $this->expandEmptyElementExceptions = $expandEmptyElementExceptions; + } + } //expandEmptyElementTags + + /** + * Set the specified node as document element + * @param Object The node that is to become document element + * @return Object The new document element + */ + function &setDocumentElement(&$node) { + if ($node->nodeType == DOMIT_ELEMENT_NODE) { + if ($this->documentElement == null) { + parent::appendChild($node); + } + else { + parent::replaceChild($node, $this->documentElement); + } + + $this->documentElement =& $node; + } + else { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Cannot add a node of type ' . get_class($node) . ' as a Document Element.')); + } + + return $node; + } //setDocumentElement + + /** + * Imports a node (and optionally its children) from another DOM Document + * @param object A reference to the node to be imported + * @param boolean True if the children of the imported node are also to be imported + * @return object The imported node (and, if specified, its children) + */ + function &importNode(&$importedNode, $deep = true) { + $parentNode = null; + return $this->_importNode($parentNode, $importedNode, $deep); + } //importNode + + + /** + * Imports a node (and optionally its children) from another DOM Document + * @param object A reference to the parent of the node to be imported + * @param object A reference to the node to be imported + * @param boolean True if the children of the imported node are also to be imported + * @return object The imported node if it is the top level node, otherwise null + */ + function &_importNode(&$parentNode, &$sourceNode, $deep) { + $hasChildren = false; + + switch ($sourceNode->nodeType) { + case DOMIT_ELEMENT_NODE: + $hasChildren = true; + + if ($this->isNamespaceAware) { + $importedNode =& $this->createElementNS($sourceNode->namespaceURI, + ($sourceNode->prefix . ":" . $sourceNode->localName)); + } + else { + $importedNode =& $this->createElement($sourceNode->nodeName); + } + + $attrNodes =& $sourceNode->attributes; + $total = $attrNodes->getLength(); + + for ($i = 0; $i < $total; $i++) { + $attrNode =& $attrNodes->item($i); + + if ($this->isNamespaceAware) { + $importedNode->setAttributeNS($attrNode->namespaceURI, + ($attrNode->prefix . ":" . $attrNode->localName), $attrNode->nodeValue); + } + else { + $importedNode->setAttribute($attrNode->nodeName, $attrNode->nodeValue); + } + } + + break; + + case DOMIT_ATTRIBUTE_NODE: + if ($this->isNamespaceAware) { + $importedNode =& $this->createAttributeNS($sourceNode->namespaceURI, + ($sourceNode->prefix . ":" . $sourceNode->localName)); + } + else { + $importedNode =& $this->createAttribute($sourceNode->nodeValue); + } + break; + + case DOMIT_TEXT_NODE: + $importedNode =& $this->createTextNode($sourceNode->nodeValue); + break; + + case DOMIT_CDATA_SECTION_NODE: + $importedNode =& $this->createCDATASection($sourceNode->nodeValue); + break; + + case DOMIT_COMMENT_NODE: + $importedNode =& $this->createComment($sourceNode->nodeValue); + break; + + case DOMIT_DOCUMENT_FRAGMENT_NODE: + $hasChildren = true; + $importedNode =& $this->createDocumentFragment(); + break; + + case DOMIT_PROCESSING_INSTRUCTION_NODE: + $importedNode =& $this->createProcessingInstruction($sourceNode->nodeName, + $sourceNode->nodeValue); + break; + + case DOMIT_DOCUMENT_NODE: + case DOMIT_DOCUMENT_TYPE_NODE: + DOMIT_DOMException::raiseException(DOMIT_NOT_SUPPORTED_ERR, + ('Method importNode cannot be called by class ' . get_class($this))); + break; + } + + if ($hasChildren && $deep) { + $total = $sourceNode->childCount; + + for ($i = 0; $i < $total; $i++) { + $importedNode->appendChild( + $this->_importNode($importedNode, $sourceNode->childNodes[$i], $deep)); + } + } + + return $importedNode; + } //_importNode + + /** + * Appends a node to the childNodes list of the current node + * @param Object The node to be appended + * @return Object The appended node + */ + function &appendChild(&$node) { + switch ($node->nodeType) { + case DOMIT_ELEMENT_NODE: + if ($this->documentElement == null) { + parent::appendChild($node); + $this->setDocumentElement($node); + } + else { + //error thrown if documentElement already exists! + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Cannot have more than one root node (documentElement) in a DOMIT_Document.')); + } + break; + + case DOMIT_PROCESSING_INSTRUCTION_NODE: + case DOMIT_COMMENT_NODE: + parent::appendChild($node); + break; + + case DOMIT_DOCUMENT_TYPE_NODE: + if ($this->doctype == null) { + parent::appendChild($node); + } + else { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Cannot have more than one doctype node in a DOMIT_Document.')); + } + break; + + case DOMIT_DOCUMENT_FRAGMENT_NODE: + $total = $node->childCount; + + for ($i = 0; $i < $total; $i++) { + $currChild =& $node->childNodes[$i]; + $this->appendChild($currChild); + } + break; + + default: + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Cannot add a node of type ' . get_class($node) . ' to a DOMIT_Document.')); + } + + return $node; + } //appendChild + + /** + * Replaces a node with another + * @param Object The new node + * @param Object The old node + * @return Object The new node + */ + function &replaceChild(&$newChild, &$oldChild) { + if ($this->nodeType == DOMIT_DOCUMENT_FRAGMENT_NODE) { + $total = $newChild->childCount; + + if ($total > 0) { + $newRef =& $newChild->lastChild; + $this->replaceChild($newRef, $oldChild); + + for ($i = 0; $i < ($total - 1); $i++) { + $currChild =& $newChild->childNodes[$i]; + parent::insertBefore($currChild, $newRef); + } + } + } + else { + if (($this->documentElement != null) && ($oldChild->uid == $this->documentElement->uid)) { + if ($newChild->nodeType == DOMIT_ELEMENT_NODE) { + //replace documentElement with new node + $this->setDocumentElement($newChild); + } + else { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Cannot replace Document Element with a node of class ' . get_class($newChild))); + } + } + else { + switch ($newChild->nodeType) { + case DOMIT_ELEMENT_NODE: + if ($this->documentElement != null) { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Cannot have more than one root node (documentElement) in a DOMIT_Document.')); + } + else { + parent::replaceChild($newChild, $oldChild); + } + break; + + case DOMIT_PROCESSING_INSTRUCTION_NODE: + case DOMIT_COMMENT_NODE: + parent::replaceChild($newChild, $oldChild); + break; + + case DOMIT_DOCUMENT_TYPE_NODE: + if ($this->doctype != null) { + if ($this->doctype->uid == $oldChild->uid) { + parent::replaceChild($newChild, $oldChild); + } + else { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Cannot have more than one doctype node in a DOMIT_Document.')); + } + } + else { + parent::replaceChild($newChild, $oldChild); + } + break; + + default: + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Nodes of class ' . get_class($newChild) . ' cannot be children of a DOMIT_Document.')); + } + } + } + + return $newChild; + } //replaceChild + + /** + * Inserts a node to the childNodes list of the current node + * @param Object The node to be inserted + * @param Object The node before which the insertion is to occur + * @return Object The inserted node + */ + function &insertBefore(&$newChild, &$refChild) { + $type = $newChild->nodeType; + + if ($this->nodeType == DOMIT_DOCUMENT_FRAGMENT_NODE) { + $total = $newChild->childCount; + + for ($i = 0; $i < $total; $i++) { + $currChild =& $newChild->childNodes[$i]; + $this->insertBefore($currChild, $refChild); + } + } + else if ($type == DOMIT_ELEMENT_NODE) { + if ($this->documentElement == null) { + parent::insertBefore($newChild, $refChild); + $this->setDocumentElement($newChild); + } + else { + //error thrown if documentElement already exists! + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Cannot have more than one root node (documentElement) in a DOMIT_Document.')); + } + } + else if ($type == DOMIT_PROCESSING_INSTRUCTION_NODE) { + parent::insertBefore($newChild, $refChild); + } + else if ($type == DOMIT_DOCUMENT_TYPE_NODE) { + if ($this->doctype == null) { + parent::insertBefore($newChild, $refChild); + } + else { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Cannot have more than one doctype node in a DOMIT_Document.')); + } + } + else { + DOMIT_DOMException::raiseException(DOMIT_HIERARCHY_REQUEST_ERR, + ('Cannot insert a node of type ' . get_class($newChild) . ' to a DOMIT_Document.')); + } + + return $newChild; + } //insertBefore + + /** + * Removes a node from the childNodes list of the current node + * @param Object The node to be removed + * @return Object The removed node + */ + function &removeChild(&$oldChild) { + if ($this->nodeType == DOMIT_DOCUMENT_FRAGMENT_NODE) { + $total = $oldChild->childCount; + + for ($i = 0; $i < $total; $i++) { + $currChild =& $oldChild->childNodes[$i]; + $this->removeChild($currChild); + } + } + else { + if (($this->documentElement != null) && ($oldChild->uid == $this->documentElement->uid)) { + parent::removeChild($oldChild); + $this->documentElement = null; + } + else { + parent::removeChild($oldChild); + } + } + + $oldChild->clearReferences(); + return $oldChild; + } //removeChild + + + /** + * Creates a new DOMIT_DocumentFragment node + * @return Object The new document fragment node + */ + function &createDocumentFragment() { + $node = new DOMIT_DocumentFragment(); + $node->ownerDocument =& $this; + + return $node; + } //createDocumentFragment + + /** + * Creates a new DOMIT_Attr node + * @param string The name of the attribute + * @return Object The new attribute node + */ + function &createAttribute($name) { + $node = new DOMIT_Attr($name); + + return $node; + } //createAttribute + + /** + * Creates a new DOMIT_Attr node (namespace aware) + * @param string The namespaceURI of the attribute + * @param string The qualifiedName of the attribute + * @return Object The new attribute node + */ + function &createAttributeNS($namespaceURI, $qualifiedName) { + $node = new DOMIT_Attr($qualifiedName); + $node->namespaceURI = $namespaceURI; + + $colonIndex = strpos($qualifiedName, ":"); + + if ($colonIndex !== false) { + $node->prefix = substr($qualifiedName, 0, $colonIndex); + $node->localName = substr($qualifiedName, ($colonIndex + 1)); + } + else { + $node->prefix = ''; + $node->localName = $qualifiedName; + } + + return $node; + } //createAttributeNS + + /** + * Creates a new DOMIT_Element node + * @param string The tag name of the element + * @return Object The new element + */ + function &createElement($tagName) { + $node = new DOMIT_Element($tagName); + $node->ownerDocument =& $this; + + return $node; + } //createElement + + /** + * Creates a new DOMIT_Element node (namespace aware) + * @param string The namespaceURI of the element + * @param string The qualifiedName of the element + * @return Object The new element + */ + function &createElementNS($namespaceURI, $qualifiedName) { + $node = new DOMIT_Element($qualifiedName); + + $colonIndex = strpos($qualifiedName, ":"); + + if ($colonIndex !== false) { + $node->prefix = substr($qualifiedName, 0, $colonIndex); + $node->localName = substr($qualifiedName, ($colonIndex + 1)); + } + else { + $node->prefix = ''; + $node->localName = $qualifiedName; + } + + $node->namespaceURI = $namespaceURI; + + $node->ownerDocument =& $this; + + return $node; + } //createElementNS + + /** + * Creates a new DOMIT_Text node + * @param string The text of the node + * @return Object The new text node + */ + function &createTextNode($data) { + $node = new DOMIT_TextNode($data); + $node->ownerDocument =& $this; + + return $node; + } //createTextNode + + /** + * Creates a new DOMIT_CDataSection node + * @param string The text of the CDATASection + * @return Object The new CDATASection node + */ + function &createCDATASection($data) { + $node = new DOMIT_CDATASection($data); + $node->ownerDocument =& $this; + + return $node; + } //createCDATASection + + /** + * Creates a new DOMIT_Comment node + * @param string The comment text + * @return Object The new comment node + */ + function &createComment($text) { + $node = new DOMIT_Comment($text); + $node->ownerDocument =& $this; + + return $node; + } //createComment + + /** + * Creates a new DOMIT_ProcessingInstruction node + * @param string The target of the processing instruction + * @param string The data of the processing instruction + * @return Object The new processing instruction node + */ + function &createProcessingInstruction($target, $data) { + $node = new DOMIT_ProcessingInstruction($target, $data); + $node->ownerDocument =& $this; + + return $node; + } //createProcessingInstruction + + /** + * Retrieves a NodeList of child elements with the specified tag name + * @param string The matching element tag name + * @return Object A NodeList of found elements + */ + function &getElementsByTagName($tagName) { + $nodeList = new DOMIT_NodeList(); + + if ($this->documentElement != null) { + $this->documentElement->getNamedElements($nodeList, $tagName); + } + + return $nodeList; + } //getElementsByTagName + + /** + * Retrieves a NodeList of child elements with the specified namespaceURI and localName + * @param string The matching namespaceURI + * @param string The matching localName + * @return Object A NodeList of found elements + */ + function &getElementsByTagNameNS($namespaceURI, $localName) { + $nodeList = new DOMIT_NodeList(); + + if ($this->documentElement != null) { + $this->documentElement->getNamedElementsNS($nodeList, $namespaceURI, $localName); + } + + return $nodeList; + } //getElementsByTagNameNS + + /** + * Returns the element whose ID is given by elementId. + * @param string The id of the matching element + * @param boolean True if XML spec is to be strictly adhered to (only attributes xml:id are considered valid) + * @return Object The found element or null + */ + function &getElementByID($elementID, $isStrict = true) { + if ($this->isNamespaceAware) { + if ($this->documentElement != null) { + $targetAttrNode =& $this->documentElement->_getElementByID($elementID, $isStrict); + return $targetAttrNode->ownerElement; + } + + $null = null; + return $null; + } + else { + DOMIT_DOMException::raiseException(DOMIT_INVALID_ACCESS_ERR, + 'Namespace awareness must be enabled to use method getElementByID'); + } + } //getElementByID + + /** + * Retrieves an element or DOMIT_NodeList of elements corresponding to an Xpath-like expression. + * @param string The query pattern + * @param int If a single node is to be returned (rather than the entire NodeList) the index of that node + * @return mixed A NodeList or single node that matches the pattern + */ + function &getElementsByPath($pattern, $nodeIndex = 0) { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_getelementsbypath.php'); + + $gebp = new DOMIT_GetElementsByPath(); + $myResponse =& $gebp->parsePattern($this, $pattern, $nodeIndex); + + return $myResponse; + } //getElementsByPath + + /** + * Retrieves an element or DOMIT_NodeList of elements corresponding to an Xpath-like attribute expression (NOT YET IMPLEMENTED!) + * @param string The query pattern + * @param int If a single node is to be returned (rather than the entire NodeList) the index of that node + * @return mixed A NodeList or single node that matches the pattern + */ + function &getElementsByAttributePath($pattern, $nodeIndex = 0) { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_getelementsbypath.php'); + + $gabp = new DOMIT_GetElementsByAttributePath(); + $myResponse =& $gabp->parsePattern($this, $pattern, $nodeIndex); + + return $myResponse; + } //getElementsByAttributePath + + /** + * Retrieves all child nodes of the specified nodeType + * @param string The nodeType of matching nodes + * @param Object The root node of the search + * @return Object A NodeList containing found nodes + */ + function &getNodesByNodeType($type, &$contextNode) { + $nodeList = new DOMIT_NodeList(); + + if (($type == DOMIT_DOCUMENT_NODE) || ($contextNode->nodeType == DOMIT_DOCUMENT_NODE)){ + $nodeList->appendNode($this); + } + else if ($contextNode->nodeType == DOMIT_ELEMENT_NODE) { + $contextNode->getTypedNodes($nodeList, $type); + } + else if ($contextNode->uid == $this->uid) { + if ($this->documentElement != null) { + if ($type == DOMIT_ELEMENT_NODE) { + $nodeList->appendNode($this->documentElement); + } + + $this->documentElement->getTypedNodes($nodeList, $type); + } + } + + return $nodeList; + } //getNodesByNodeType + + /** + * Retrieves all child nodes of the specified nodeValue + * @param string The nodeValue of matching nodes + * @param Object The root node of the search + * @return Object A NodeList containing found nodes + */ + function &getNodesByNodeValue($value, &$contextNode) { + $nodeList = new DOMIT_NodeList(); + + if ($contextNode->uid == $this->uid) { + if ($this->nodeValue == $value) { + $nodeList->appendNode($this); + } + } + + if ($this->documentElement != null) { + $this->documentElement->getValuedNodes($nodeList, $value); + } + + return $nodeList; + } //getNodesByNodeValue + + /** + * Parses an xml string + * @param string The xml text to be parsed + * @param boolean True if SAXY is to be used instead of Expat + * @param boolean False if CDATA Section are to be generated as Text nodes + * @param boolean True if onLoad is to be called on each node after parsing + * @return boolean True if parsing is successful + */ + function parseXML($xmlText, $useSAXY = true, $preserveCDATA = true, $fireLoadEvent = false) { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_utilities.php'); + + if ($this->doResolveErrors) { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_doctor.php'); + $xmlText = DOMIT_Doctor::fixAmpersands($xmlText); + } + + if (DOMIT_Utilities::validateXML($xmlText)) { + $domParser = new DOMIT_Parser(); + + if ($useSAXY || (!function_exists('xml_parser_create'))) { + //use SAXY parser to populate xml tree + $this->parser = 'SAXY'; + $success = $domParser->parseSAXY($this, $xmlText, $preserveCDATA, $this->definedEntities); + } + else { + //use Expat parser to populate xml tree + $this->parser = 'EXPAT'; + $success = $domParser->parse($this, $xmlText, $preserveCDATA); + } + + if ($fireLoadEvent && ($this->documentElement != null)) $this->load($this->documentElement); + + return $success; + } + else { + return false; + } + } //parseXML + + /** + * Parses an xml file + * @param string The xml file to be parsed + * @param boolean True if SAXY is to be used instead of Expat + * @param boolean False if CDATA Section are to be generated as Text nodes + * @param boolean True if onLoad is to be called on each node after parsing + * @return boolean True if parsing is successful + */ + function loadXML($filename, $useSAXY = true, $preserveCDATA = true, $fireLoadEvent = false) { + $xmlText = $this->getTextFromFile($filename); + + return $this->parseXML($xmlText, $useSAXY, $preserveCDATA, $fireLoadEvent); + } //loadXML + + /** + * Establishes a connection, given an url + * @param string The url of the data + */ + function establishConnection($url) { + require_once(DOMIT_INCLUDE_PATH . 'php_http_client_generic.php'); + + $host = php_http_connection::formatHost($url); + $host = substr($host, 0, strpos($host, '/')); + + $this->setConnection($host); + } //establishConnection + + /** + * Retrieves text from a file + * @param string The file path + * @return string The text contained in the file + */ + function getTextFromFile($filename) { + if ($this->doUseHTTPClient && (substr($filename, 0, 5) == 'http:')) { + $this->establishConnection($filename); + } + + if ($this->httpConnection != null) { + $response =& $this->httpConnection->get($filename); + + $this->httpConnection->disconnect(); + return $response->getResponse(); + } + else if (function_exists('file_get_contents')) { + //if (file_exists($filename)) { + return file_get_contents($filename); + //} + } + else { + require_once(DOMIT_INCLUDE_PATH . 'php_file_utilities.php'); + + $fileContents =& php_file_utilities::getDataFromFile($filename, 'r'); + return $fileContents; + } + + return ''; + } //getTextFromFile + + /** + * Saves the current DOM document as an xml file + * @param string The path of the xml file + * @param boolean True if xml text is to be normalized before saving + * @return boolean True if save is successful + */ + function saveXML($filename, $normalized = false) { + if ($normalized) { + $stringRep = $this->toNormalizedString(false, true); + } + else { + $stringRep = $this->toString(false, true); + } + + return $this->saveTextToFile($filename, $stringRep); + } //saveXML + + /** + * Saves text to a file + * @param string The file path + * @param string The text to be saved + * @return boolean True if the save is successful + */ + function saveTextToFile($filename, $text) { + if (function_exists('file_put_contents')) { + file_put_contents($filename, $text); + } + else { + require_once(DOMIT_INCLUDE_PATH . 'php_file_utilities.php'); + php_file_utilities::putDataToFile($filename, $text, 'w'); + } + + return (file_exists($filename) && is_writable($filename)); + } //saveTextToFile + + /** + * Indicates the SAX parser used to parse the current document + * @return string Either "SAXY" or "EXPAT" + */ + function parsedBy() { + return $this->parser; + } //parsedBy + + /** + * Returns the concatented text of the current node and its children + * @return string The concatented text of the current node and its children + */ + function getText() { + if ($this->documentElement != null) { + $root =& $this->documentElement; + return $root->getText(); + } + + return ''; + } //getText + + /** + * Returns a doctype object + * @return mixed The doctype object, or null if none exists + */ + function getDocType() { + return $this->doctype; + } //getDocType + + /** + * Returns the xml declaration processing instruction + * @return mixed The xml declaration processing instruction, or null if none exists + */ + function getXMLDeclaration() { + return $this->xmlDeclaration; + } //getXMLDeclaration + + /** + * Returns a reference to the DOMIT_DOMImplementation object + * @return Object A reference to the DOMIT_DOMImplementation object + */ + function &getDOMImplementation() { + return $this->implementation; + } //getDOMImplementation + + /** + * Manages the firing of the onLoad() event + * @param Object The parent node of the current recursion + */ + function load(&$contextNode) { + $total = $contextNode->childCount; + + for ($i = 0; $i < $total; $i++) { + $currNode =& $contextNode->childNodes[$i]; + $currNode->ownerDocument->load($currNode); + } + + $contextNode->onLoad(); + } //load + + /** + * Returns the current version of DOMIT! + * @return Object The current version of DOMIT! + */ + function getVersion() { + return DOMIT_VERSION; + } //getVersion + + /** + * Appends an array of entity mappings to the existing translation table + * + * Intended mainly to facilitate the conversion of non-ASCII entities into equivalent characters + * + * @param array A list of entity mappings in the format: array('&' => '&'); + */ + function appendEntityTranslationTable($table) { + $this->definedEntities = $table; + + global $DOMIT_defined_entities_flip; + $DOMIT_defined_entities_flip = array_flip($table); + } //appendEntityTranslationTable + + /** + * Generates an array representation of the node and its children + * @return Array A representation of the node and its children + */ + function toArray() { + $arReturn = array($this->nodeName => array()); + $total = $this->childCount; + + for ($i = 0; $i < $total; $i++) { + $arReturn[$this->nodeName][$i] = $this->childNodes[$i]->toArray(); + } + + return $arReturn; + } //toArray + + /** + * Copies a node and/or its children + * @param boolean True if all child nodes are also to be cloned + * @return Object A copy of the node and/or its children + */ + function &cloneNode($deep = false) { + $className = get_class($this); + $clone = new $className($this->nodeName); + + if ($deep) { + $total = $this->childCount; + + for ($i = 0; $i < $total; $i++) { + $currentChild =& $this->childNodes[$i]; + $clone->appendChild($currentChild->cloneNode($deep)); + + if ($currentChild->nodeType == DOMIT_DOCUMENT_TYPE_NODE) { + $clone->doctype =& $clone->childNodes[$i]; + } + + if (($currentChild->nodeType == DOMIT_PROCESSING_INSTRUCTION_NODE) && + ($currentChild->getTarget() == 'xml')) { + $clone->xmlDeclaration =& $clone->childNodes[$i]; + } + } + } + + return $clone; + } //cloneNode + + /** + * Generates a string representation of the node and its children + * @param boolean True if HTML readable output is desired + * @param boolean True if illegal xml characters in text nodes and attributes should be converted to entities + * @return string The string representation + */ + function toString($htmlSafe = false, $subEntities = false) { + $result = ''; + $total = $this->childCount; + + for ($i = 0; $i < $total; $i++) { + $result .= $this->childNodes[$i]->toString(false, $subEntities); + } + + if ($htmlSafe) $result = $this->forHTML($result); + + return $result; + } //toString +} //DOMIT_Document + +/** +* A class representing the DOM Element +* +* @package domit-xmlparser +* @subpackage domit-xmlparser-main +* @author John Heinstein +*/ +class DOMIT_Element extends DOMIT_ChildNodes_Interface { + /** @var array An array of namespacesURIs mapped to prefixes */ + var $namespaceURIMap = array(); + + /** + * DOM Element constructor + * @param string The tag name of the element + */ + function DOMIT_Element($tagName) { + $this->_constructor(); + $this->nodeType = DOMIT_ELEMENT_NODE; + $this->nodeName = $tagName; + $this->attributes = new DOMIT_NamedNodeMap_Attr(); + $this->childNodes = array(); + } //DOMIT_Element + + /** + * Returns the tag name of the element + * @return string The tag name of the element + */ + function getTagName() { + return $this->nodeName; + } //getTagName + + /** + * Adds elements with the specified tag name to a NodeList collection + * @param Object The NodeList collection + * @param string The tag name of matching elements + */ + function getNamedElements(&$nodeList, $tagName) { + if (($this->nodeName == $tagName) || ($tagName == '*')) { + $nodeList->appendNode($this); + } + + $total = $this->childCount; + + for ($i = 0; $i < $total; $i++) { + $this->childNodes[$i]->getNamedElements($nodeList, $tagName); + } + } //getNamedElements + + /** + * Creates an xmlns declaration at the current element + * @param array An array of namespace declarations in the scope of the current element + */ + function declareNamespace($localname, $value) { + //namespace URI for xmlns attribute is: http://www.w3.org/2000/xmlns/ + $this->setAttributeNS(DOMIT_XMLNS_NAMESPACE, ('xmlns:' . $localname), $value); + + //add to local namespaceURI map + $this->namespaceURIMap[$value] = $localname; + } //declareNamespace + + /** + * Creates a default xmlns declaration at the current element + * @param array An array of namespace declarations in the scope of the current element + */ + function declareDefaultNamespace($value) { + //namespace URI for xmlns attribute is: http://www.w3.org/2000/xmlns/ + $this->setAttributeNS(DOMIT_XMLNS_NAMESPACE, 'xmlns', $value); + + //add to local namespaceURI map + $this->namespaceURIMap[$value] = 'xmlns'; + } //declareDefaultNamespace + + /** + * Returns an array of namespace declarations in the scope of the current element + * @return array An array of namespace declarations in the scope of the current element + */ + function &getNamespaceDeclarationsInScope() { + $nsMap = array(); + + return $this->_getNameSpaceDeclarationsInScope($nsMap); + } //getNamespacesInScope + + /** + * Returns an array of namespace declarations in the scope of the current element + * @return array An array of namespace declarations in the scope of the current element + */ + function &_getNamespaceDeclarationsInScope(&$nsMap) { + //grab local xmlns declarations if not already present + foreach ($this->namespaceURIMap as $key => $value) { + if (!isset($nsMap[$key])) { + $nsMap[$key] = $value; + } + } + + //move up the tree if not already at the top + if ($this->parentNode->uid != $this->ownerDocument->uid) { + $this->parentNode->_getNamespaceDeclarationsInScope($nsMap); + } + + return $nsMap; + } //_getNamespacesInScope + + /** + * Returns the default xmlns declaration for the current element + * @return string The default xmlns declaration for the current element + */ + function getDefaultNamespaceDeclaration() { + if (in_array('xmlns', $this->namespaceURIMap)) { + foreach ($this->namespaceURIMap as $key => $value) { + if ($value == 'xmlns') { + return $key; + } + } + } + else if ($this->parentNode->uid != $this->ownerDocument->uid) { + return $this->parentNode->getDefaultNamespaceDeclaration(); + } + else { + return ''; + } + } //getDefaultNamespaceDeclaration + + /** + * Copies all namespace declarations in scope to the namespace URI map of the current element + */ + function copyNamespaceDeclarationsLocally() { + $nsMap = $this->getNamespaceDeclarationsInScope(); + + //add xmlns declarations as attributes + foreach ($nsMap as $key => $value) { + if ($value == 'xmlns') { + $this->declareDefaultNamespace($key); + } + else { + $this->declareNamespace($value, $key); + } + } + } //copyNamespaceDeclarationsLocally + + /** + * Adds elements with the specified tag name to a NodeList collection + * @param Object The NodeList collection + * @param string The namespaceURI of matching elements + * @param string The localName of matching elements + */ + function getNamedElementsNS(&$nodeList, $namespaceURI, $localName) { + if ((($namespaceURI == $this->namespaceURI) || ($namespaceURI == '*')) && + (($localName == $this->localName) || ($localName == '*'))) { + $nodeList->appendNode($this); + } + + $total = $this->childCount; + + for ($i = 0; $i < $total; $i++) { + if ($this->childNodes[$i]->nodeType == DOMIT_ELEMENT_NODE) { + $this->childNodes[$i]->getNamedElementsNS($nodeList, $namespaceURI, $localName); + } + } + } //getNamedElementsNS + + /** + * Returns the concatented text of the current node and its children + * @return string The concatented text of the current node and its children + */ + function getText() { + $text = ''; + $numChildren = $this->childCount; + + for ($i = 0; $i < $numChildren; $i++) { + $child =& $this->childNodes[$i]; + $text .= $child->getText(); + } + + return $text; + } //getText + + /** + * If a child text node exists, sets the nodeValue to $data. A child text node is created if none exists + * @param string The text data of the node + */ + function setText($data) { + switch ($this->childCount) { + case 1: + if ($this->firstChild->nodeType == DOMIT_TEXT_NODE) { + $this->firstChild->setText($data); + } + break; + + case 0: + $childTextNode =& $this->ownerDocument->createTextNode($data); + $this->appendChild($childTextNode); + break; + + default: + //do nothing. Maybe throw error??? + } + } //setText + + /** + * Retrieves a NodeList of child elements with the specified tag name + * @param string The matching element tag name + * @return Object A NodeList of found elements + */ + function &getElementsByTagName($tagName) { + $nodeList = new DOMIT_NodeList(); + $this->getNamedElements($nodeList, $tagName); + + return $nodeList; + } //getElementsByTagName + + /** + * Retrieves a NodeList of child elements with the specified namespaceURI and localName + * @param string The namespaceURI + * @param string The localName + * @return Object A NodeList of found elements + */ + function &getElementsByTagNameNS($namespaceURI, $localName) { + $nodeList = new DOMIT_NodeList(); + $this->getNamedElementsNS($nodeList, $namespaceURI, $localName); + + return $nodeList; + } //getElementsByTagNameNS + + /** + * Returns the attribute node whose ID is given by elementId. + * @param string The id of the matching element + * @param boolean True if XML spec is to be strictly adhered to (only attributes xml:id are considered valid) + * @return Object The found attribute or null + */ + function &_getElementByID($elementID, $isStrict) { + if ($isStrict) { + $myAttrNode =& $this->getAttributeNodeNS(DOMIT_XML_NAMESPACE, 'id'); + if (($myAttrNode != null)&& ($myAttrNode->getValue() == $elementID)) return $myAttrNode; + } + else { + $myAttrNode =& $this->getAttributeNodeNS('', 'ID'); + if (($myAttrNode != null)&& ($myAttrNode->getValue() == $elementID)) return $myAttrNode; + + $myAttrNode =& $this->getAttributeNodeNS('', 'id'); + if (($myAttrNode != null)&& ($myAttrNode->getValue() == $elementID)) return $myAttrNode; + } + + $total = $this->childCount; + + for ($i = 0; $i < $total; $i++) { + if ($this->childNodes[$i]->nodeType == DOMIT_ELEMENT_NODE) { + $foundNode =& $this->childNodes[$i]->_getElementByID($elementID, $isStrict); + + if ($foundNode != null) { + return $foundNode; + } + } + } + + $null = null; + return $null; + } //_getElementByID + + /** + * Retrieves an element or DOMIT_NodeList of elements corresponding to an Xpath-like expression. + * @param string The query pattern + * @param int If a single node is to be returned (rather than the entire NodeList) the index of that node + * @return mixed A NodeList or single node that matches the pattern + */ + function &getElementsByPath($pattern, $nodeIndex = 0) { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_getelementsbypath.php'); + + $gebp = new DOMIT_GetElementsByPath(); + $myResponse =& $gebp->parsePattern($this, $pattern, $nodeIndex); + + return $myResponse; + } //getElementsByPath + + /** + * Retrieves an element or DOMIT_NodeList of elements corresponding to an Xpath-like attribute expression (NOT YET IMPLEMENTED!) + * @param string The query pattern + * @param int If a single node is to be returned (rather than the entire NodeList) the index of that node + * @return mixed A NodeList or single node that matches the pattern + */ + function &getElementsByAttributePath($pattern, $nodeIndex = 0) { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_getelementsbypath.php'); + + $gabp = new DOMIT_GetElementsByAttributePath(); + $myResponse =& $gabp->parsePattern($this, $pattern, $nodeIndex); + + return $myResponse; + } //getElementsByAttributePath + + /** + * Adds all child nodes of the specified nodeType to the NodeList + * @param Object The NodeList collection + * @param string The nodeType of matching nodes + */ + function getTypedNodes(&$nodeList, $type) { + $numChildren = $this->childCount; + + for ($i = 0; $i < $numChildren; $i++) { + $child =& $this->childNodes[$i]; + + if ($child->nodeType == $type) { + $nodeList->appendNode($child); + } + + if ($child->hasChildNodes()) { + $child->getTypedNodes($nodeList, $type); + } + } + } //getTypedNodes + + /** + * Adds all child nodes of the specified nodeValue to the NodeList + * @param Object The NodeList collection + * @param string The nodeValue of matching nodes + */ + function getValuedNodes(&$nodeList, $value) { + $numChildren = $this->childCount; + + for ($i = 0; $i < $numChildren; $i++) { + $child =& $this->childNodes[$i]; + + if ($child->nodeValue == $value) { + $nodeList->appendNode($child); + } + + if ($child->hasChildNodes()) { + $child->getValuedNodes($nodeList, $value); + } + } + } //getValuedNodes + + /** + * Gets the value of the specified attribute, if it exists + * @param string The attribute name + * @return string The attribute value + */ + function getAttribute($name) { + $returnNode =& $this->attributes->getNamedItem($name); + + if ($returnNode == null) { + return ''; + } + else { + return $returnNode->getValue(); + } + } //getAttribute + + /** + * Gets the value of the attribute with the specified namespaceURI and localName, if it exists + * @param string The namespaceURI + * @param string The localName + * @return string The attribute value + */ + function getAttributeNS($namespaceURI, $localName) { + $returnNode =& $this->attributes->getNamedItemNS($namespaceURI, $localName); + + if ($returnNode == null) { + return ''; + } + else { + return $returnNode->getValue(); + } + } //getAttributeNS + + /** + * Sets the value of the specified attribute; creates a new attribute if one doesn't exist + * @param string The attribute name + * @param string The desired attribute value + */ + function setAttribute($name, $value) { + $returnNode =& $this->attributes->getNamedItem($name); + + if ($returnNode == null) { + $newAttr = new DOMIT_Attr($name); + $newAttr->setValue($value); + $this->attributes->setNamedItem($newAttr); + } + else { + $returnNode->setValue($value); + } + } //setAttribute + + /** + * Sets the value of the specified attribute; creates a new attribute if one doesn't exist + * @param string The attribute namespaceURI + * @param string The attribute qualifiedName + * @param string The desired attribute value + */ + function setAttributeNS($namespaceURI, $qualifiedName, $value) { + //get localName + $colonIndex = strpos($qualifiedName, ":"); + + if ($colonIndex !== false) { + $localName = substr($qualifiedName, ($colonIndex + 1)); + } + else { + $localName = $qualifiedName; + } + + $returnNode =& $this->attributes->getNamedItemNS($namespaceURI, $localName); + + if ($returnNode == null) { + //create this manually in case element has no ownerDocument to reference + $newAttr = new DOMIT_Attr($qualifiedName); + $newAttr->prefix = substr($qualifiedName, 0, $colonIndex); + $newAttr->localName = $localName; + $newAttr->namespaceURI = $namespaceURI; + + $newAttr->setValue($value); + $this->attributes->setNamedItemNS($newAttr); + $newAttr->ownerElement =& $this; + } + else { + $returnNode->setValue($value); + } + } //setAttributeNS + + /** + * Removes the specified attribute + * @param string The name of the attribute to be removed + */ + function removeAttribute($name) { + $returnNode =& $this->attributes->removeNamedItem($name); + } //removeAttribute + + /** + * Removes the specified attribute + * @param string The namespaceURI of the attribute to be removed + * @param string The localName of the attribute to be removed + */ + function removeAttributeNS($namespaceURI, $localName) { + $returnNode =& $this->attributes->removeNamedItemNS($namespaceURI, $localName); + unset($returnNode->ownerElement); + $returnNode->ownerElement = null; + } //removeAttributeNS + + /** + * Determines whether an attribute with the specified name exists + * @param string The name of the attribute + * @return boolean True if the attribute exists + */ + function hasAttribute($name) { + $returnNode =& $this->attributes->getNamedItem($name); + + return ($returnNode != null); + } //hasAttribute + + /** + * Determines whether an attribute with the specified namespaceURI and localName exists + * @param string The namespaceURI of the attribute + * @param string The localName of the attribute + * @return boolean True if the attribute exists + */ + function hasAttributeNS($namespaceURI, $localName) { + $returnNode =& $this->attributes->getNamedItemNS($namespaceURI, $localName); + + return ($returnNode != null); + } //hasAttributeNS + + /** + * Determines whether the element has any atributes + * @return boolean True if the element has any atributes + */ + function hasAttributes() { + return ($this->attributes->getLength() > 0); + } //hasChildNodes + + /** + * Gets a reference to the specified attribute node + * @param string The attribute name + * @return Object A reference to the found node, or null + */ + function &getAttributeNode($name) { + $returnNode =& $this->attributes->getNamedItem($name); + return $returnNode; + } //getAttributeNode + + /** + * Gets a reference to the specified attribute node + * @param string The attribute namespaceURI + * @param string The attribute localName + * @return Object A reference to the found node, or null + */ + function &getAttributeNodeNS($namespaceURI, $localName) { + $returnNode =& $this->attributes->getNamedItemNS($namespaceURI, $localName); + return $returnNode; + } //getAttributeNodeNS + + /** + * Adds an attribute node to the current element + * @param Object The attribute node to be added + * @return Object A reference to the newly added node + */ + function &setAttributeNode(&$newAttr) { + $returnNode =& $this->attributes->setNamedItem($newAttr); + return $returnNode; + } //setAttributeNode + + /** + * Adds an attribute node to the current element (namespace aware) + * @param Object The attribute node to be added + * @return Object A reference to the newly added node + */ + function &setAttributeNodeNS(&$newAttr) { + $returnNode =& $this->attributes->setNamedItemNS($newAttr); + $newAttr->ownerElement =& $this; + return $returnNode; + } //setAttributeNodeNS + + /** + * Removes an attribute node from the current element + * @param Object The attribute node to be removed + * @return Object A reference to the removed node + */ + function &removeAttributeNode(&$oldAttr) { + $attrName = $oldAttr->getName(); + $returnNode =& $this->attributes->removeNamedItem($attrName); + + if ($returnNode == null) { + DOMIT_DOMException::raiseException(DOMIT_NOT_FOUND_ERR, + 'Target attribute not found.'); + } + else { + return $returnNode; + } + } //removeAttributeNode + + /** + * Collapses adjacent text nodes in entire element subtree + */ + function normalize() { + if ($this->hasChildNodes()) { + $currNode =& $this->childNodes[0]; + + while ($currNode->nextSibling != null) { + $nextNode =& $currNode->nextSibling; + + if (($currNode->nodeType == DOMIT_TEXT_NODE) && + ($nextNode->nodeType == DOMIT_TEXT_NODE)) { + + $currNode->nodeValue .= $nextNode->nodeValue; + $this->removeChild($nextNode); + } + else { + $currNode->normalize(); + } + + if ($currNode->nextSibling != null) { + $currNode =& $currNode->nextSibling; + } + } + } + } //normalize + + /** + * Generates an array representation of the node and its children + * @return Array A representation of the node and its children + */ + function toArray() { + $arReturn = array($this->nodeName => array("attributes" => $this->attributes->toArray())); + $total = $this->childCount; + + for ($i = 0; $i < $total; $i++) { + $arReturn[$this->nodeName][$i] = $this->childNodes[$i]->toArray(); + } + + return $arReturn; + } //toArray + + /** + * Copies a node and/or its children + * @param boolean True if all child nodes are also to be cloned + * @return Object A copy of the node and/or its children + */ + function &cloneNode($deep = false) { + $className = get_class($this); + $clone = new $className($this->nodeName); + + $clone->attributes =& $this->attributes->createClone($deep); + + if ($this->namespaceURI) { + $clone->namespaceURI = $this->namespaceURI; + $clone->localName = $this->localName; + } + + if ($deep) { + $total = $this->childCount; + + for ($i = 0; $i < $total; $i++) { + $currentChild =& $this->childNodes[$i]; + $clone->appendChild($currentChild->cloneNode($deep)); + } + } + + return $clone; + } //cloneNode + + /** + * Generates a string representation of the node and its children + * @param boolean True if HTML readable output is desired + * @param boolean True if illegal xml characters in text nodes and attributes should be converted to entities + * @return string The string representation + */ + function toString($htmlSafe = false, $subEntities = false) { + $result = '<' . $this->nodeName; + $result .= $this->attributes->toString(false, $subEntities); + + if ($this->ownerDocument->isNamespaceAware) { + foreach ($this->namespaceURIMap as $key => $value) { + $result .= ' xmlns:' . $value . '="' . $key . '"'; + } + } + + //get children + $myNodes =& $this->childNodes; + $total = count($myNodes); + + if ($total != 0) { + $result .= '>'; + + for ($i = 0; $i < $total; $i++) { + $child =& $myNodes[$i]; + $result .= $child->toString(false, $subEntities); + } + + $result .= 'nodeName . '>'; + } + else { + if ($this->ownerDocument->doExpandEmptyElementTags) { + if (in_array($this->nodeName, $this->ownerDocument->expandEmptyElementExceptions)) { + $result .= ' />'; + } + else { + $result .= '>nodeName . '>'; + } + } + else { + if (in_array($this->nodeName, $this->ownerDocument->expandEmptyElementExceptions)) { + $result .= '>nodeName . '>'; + } + else { + $result .= ' />'; + } + } + } + + if ($htmlSafe) $result = $this->forHTML($result); + + return $result; + } //toString +} //DOMIT_Element + +/** +* A parent class for Text and CDATA Section nodes +* +* @package domit-xmlparser +* @subpackage domit-xmlparser-main +* @author John Heinstein +*/ +class DOMIT_CharacterData extends DOMIT_Node { + /** + * Prevents direct instantiation of DOMIT_CharacterData class + * @abstract + */ + function DOMIT_CharacterData() { + DOMIT_DOMException::raiseException(DOMIT_ABSTRACT_CLASS_INSTANTIATION_ERR, + 'Cannot instantiate abstract class DOMIT_CharacterData'); + } //DOMIT_CharacterData + + /** + * Gets the node value of the current text node + * @return string The node value of the current text node + */ + function getData() { + return $this->nodeValue; + } //getData + + /** + * Sets the text contained in the current node to $data. + * @param string The text data of the node + */ + function setData($data) { + $this->nodeValue = $data; + } //setData + + + /** + * Gets the length of the text in the current node + * @return int The length of the text in the current node + */ + function getLength() { + return strlen($this->nodeValue); + } //getLength + + /** + * Gets a subset of the current node text + * @param int The starting point of the substring + * @param int The length of the substring + * @return string The subset of the current node text + */ + function substringData($offset, $count) { + $totalChars = $this->getLength(); + + if (($offset < 0) || (($offset + $count) > $totalChars)) { + + DOMIT_DOMException::raiseException(DOMIT_INDEX_SIZE_ERR, + 'Character Data index out of bounds.'); + + } + else { + $data = $this->getData(); + return substr($data, $offset, $count); + } + } //substringData + + /** + * Appends the specified text to the current node text + * @param string The text to be appended + */ + function appendData($arg) { + $this->setData($this->getData() . $arg); + } //appendData + + /** + * Inserts text at the sepecified offset + * @param int The insertion point + * @param string The text to be inserted + */ + function insertData($offset, $arg) { + $totalChars = $this->getLength(); + + if (($offset < 0) || ($offset > $totalChars)) { + + DOMIT_DOMException::raiseException(DOMIT_INDEX_SIZE_ERR, + 'Character Data index out of bounds.'); + + } + else { + $data = $this->getData(); + $pre = substr($data, 0, $offset); + $post = substr($data, $offset); + + $this->setData(($pre . $arg . $post)); + } + } //insertData + + /** + * Deletes a subset of the current node text + * @param int The starting point of the deletion + * @param int The length of the deletion + */ + function deleteData($offset, $count) { + $totalChars = $this->getLength(); + + if (($offset < 0) || (($offset + $count) > $totalChars)) { + + DOMIT_DOMException::raiseException(DOMIT_INDEX_SIZE_ERR, + 'Character Data index out of bounds.'); + + } + else { + $data = $this->getData(); + $pre = substr($data, 0, $offset); + $post = substr($data, ($offset + $count)); + + $this->setData(($pre . $post)); + } + } //substringData + + /** + * Replaces a subset of the current node text with the specified text + * @param int The starting point of the replacement + * @param int The length of the replacement + * @param string The replacement text + */ + function replaceData($offset, $count, $arg) { + $totalChars = $this->getLength(); + + if (($offset < 0) || (($offset + $count) > $totalChars)) { + + DOMIT_DOMException::raiseException(DOMIT_INDEX_SIZE_ERR, + 'Character Data index out of bounds.'); + + } + else { + $data = $this->getData(); + $pre = substr($data, 0, $offset); + $post = substr($data, ($offset + $count)); + + $this->setData(($pre . $arg . $post)); + } + } //replaceData +} //DOMIT_CharacterData + +/** +* A class representing the DOM Text Node +* +* @package domit-xmlparser +* @subpackage domit-xmlparser-main +* @author John Heinstein +*/ +class DOMIT_TextNode extends DOMIT_CharacterData { + /** + * DOM Text Node constructor + * @param string The text of the node + */ + function DOMIT_TextNode($data) { + $this->_constructor(); + $this->nodeType = DOMIT_TEXT_NODE; + $this->nodeName = '#text'; + $this->setText($data); + } //DOMIT_TextNode + + /** + * Returns the text contained in the current node + * @return string The text of the current node + */ + function getText() { + return $this->nodeValue; + } //getText + + /** + * Sets the text contained in the current node to $data. + * @param string The text data of the node + */ + function setText($data) { + $this->nodeValue = $data; + } //setText + + /** + * Splits a single node into multiple nodes, based on the specified offset + * @param int The offset point for the split + * @return Object The newly created text node + */ + function &splitText($offset) { + $totalChars = $this->getLength(); + + if (($offset < 0) || ($offset > $totalChars)) { + + DOMIT_DOMException::raiseException(DOMIT_INDEX_SIZE_ERR, + 'Character Data index out of bounds.'); + + } + else { + $data = $this->getData(); + $pre = substr($data, 0, $offset); + $post = substr($data, $offset); + + $this->setText($pre); + + //create new text node + $className = get_class($this); + $newTextNode = new $className($post); + $newTextNode->ownerDocument =& $this->ownerDocument; + + if ($this->parentNode->lastChild->uid == $this->uid) { + $this->parentNode->appendChild($newTextNode); + } + else { + $this->parentNode->insertBefore($newTextNode, $this); + } + + return $newTextNode; + } + } //splitText + + /** + * Generates an array representation of the node and its children + * @return Array A representation of the node and its children + */ + function toArray() { + return $this->toString(); + } //toArray + + /** + * Copies a node and/or its children + * @param boolean True if all child nodes are also to be cloned + * @return Object A copy of the node and/or its children + */ + function &cloneNode($deep = false) { + $className = get_class($this); + $clone = new $className($this->nodeValue); + + return $clone; + } //cloneNode + + /** + * Generates a string representation of the node and its children + * @param boolean True if HTML readable output is desired + * @param boolean True if illegal xml characters should be converted to entities + * @return string The string representation + */ + function toString($htmlSafe = false, $subEntities = false) { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_utilities.php'); + global $DOMIT_defined_entities_flip; + + $result = $subEntities ? DOMIT_Utilities::convertEntities($this->nodeValue, + $DOMIT_defined_entities_flip) : $this->nodeValue; + + if ($htmlSafe) $result = $this->forHTML($result); + + return $result; + } //toString +} //DOMIT_TextNode + +/** +* A class representing the DOM CDATA Section +* +* @package domit-xmlparser +* @subpackage domit-xmlparser-main +* @author John Heinstein +*/ +class DOMIT_CDATASection extends DOMIT_TextNode { + /** + * DOM CDATA Section node constructor + * @param string The text of the node + */ + function DOMIT_CDATASection($data) { + $this->_constructor(); + $this->nodeType = DOMIT_CDATA_SECTION_NODE; + $this->nodeName = '#cdata-section'; + $this->setText($data); + } //DOMIT_CDATASection + + /** + * Generates a string representation of the node and its children + * @param boolean True if HTML readable output is desired + * @param boolean True if illegal xml characters should be converted to entities + * @return string The string representation + */ + function toString($htmlSafe = false, $subEntities = false) { + $result = '", "]]>", $this->nodeValue) : + $this->nodeValue; + $result .= ']]>'; + + if ($htmlSafe) $result = $this->forHTML($result); + + return $result; + } //toString +} //DOMIT_CDATASection + +/** +* A class representing the Attr node +* +* @package domit-xmlparser +* @subpackage domit-xmlparser-main +* @author John Heinstein +*/ +class DOMIT_Attr extends DOMIT_Node { + /** @var boolean True if the attribute has been modified since parsing (NOT YET IMPLEMENTED!) */ + var $specified = false; + /** @var Object A reference to the element to which the attribute is assigned */ + var $ownerElement = null; + + /** + * DOM Attr node constructor + * @param string The name of the attribute + */ + function DOMIT_Attr($name) { + $this->_constructor(); + $this->nodeType = DOMIT_ATTRIBUTE_NODE; + $this->nodeName =$name; + } //DOMIT_Attr + + /** + * Returns the name of the attribute + * @return string The name of the attribute + */ + function getName() { + return $this->nodeName; + } //getName + + /** + * Indicates whether an attribute has been modified since parsing + * @return boolean True if the node has been modified + */ + function getSpecified() { + return $this->specified; + } //getSpecified + + /** + * Returns the value of the attribute + * @return string The value of the attribute + */ + function getValue() { + return $this->nodeValue; + } //getValue + + /** + * Sets the value of the attribute + * @param string The value of the attribute + */ + function setValue($value) { + $this->nodeValue = $value; + } //setValue + + /** + * Returns the text contained in the current node + * @return string The text of the current node + */ + function getText() { + return $this->nodeValue; + } //getText + + /** + * Sets the text contained in the current node to $data. + * @param string The text data of the node + */ + function setText($data) { + $this->nodeValue = $data; + } //setText + + /** + * Copies a node and/or its children + * @param boolean True if all child nodes are also to be cloned + * @return Object A copy of the node and/or its children + */ + function &cloneNode($deep = false) { + $className = get_class($this); + $clone = new $className($this->nodeName); + $clone->nodeValue = $this->nodeValue; + + if ($this->namespaceURI) { + $clone->namespaceURI = $this->namespaceURI; + $clone->localName = $this->localName; + } + + return $clone; + } //cloneNode + + /** + * Generates a string representation of the node and its children + * @param boolean True if HTML readable output is desired + * @param boolean True if HTML entities should be substituted + * @return string The string representation + */ + function toString($htmlSafe = false, $subEntities = false) { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_utilities.php'); + global $DOMIT_defined_entities_flip; + + $result = ' ' . $this->nodeName . '="'; + $result .= $subEntities ? DOMIT_Utilities::convertEntities($this->nodeValue, + $DOMIT_defined_entities_flip) : $this->nodeValue; + $result .= '"'; + + if ($htmlSafe) $result = $this->forHTML($result); + + return $result; + } //toString +} //DOMIT_Attr + +/** +* A class representing the DOM Document Fragment node +* +* @package domit-xmlparser +* @subpackage domit-xmlparser-main +* @author John Heinstein +*/ +class DOMIT_DocumentFragment extends DOMIT_ChildNodes_Interface { + /** + * DOM Document Fragment node constructor + */ + function DOMIT_DocumentFragment() { + $this->_constructor(); + $this->nodeType = DOMIT_DOCUMENT_FRAGMENT_NODE; + $this->nodeName ='#document-fragment'; + $this->nodeValue = null; + $this->childNodes = array(); + } //DOMIT_DocumentFragment + + /** + * Generates an array representation of the node and its children + * @return Array A representation of the node and its children + */ + function toArray() { + $arReturn = array(); + $total = $this->childCount; + + for ($i = 0; $i < $total; $i++) { + $arReturn[$i] = $this->childNodes[$i]->toArray(); + } + + return $arReturn; + } //toArray + + /** + * Copies a node and/or its children + * @param boolean True if all child nodes are also to be cloned + * @return Object A copy of the node and/or its children + */ + function &cloneNode($deep = false) { + $className = get_class($this); + $clone = new $className(); + + if ($deep) { + $total = $this->childCount; + + for ($i = 0; $i < $total; $i++) { + $currentChild =& $this->childNodes[$i]; + $clone->appendChild($currentChild->cloneNode($deep)); + } + } + + return $clone; + } //cloneNode + + /** + * Generates a string representation of the node and its children + * @param boolean True if HTML readable output is desired + * @return string The string representation + */ + function toString($htmlSafe = false, $subEntities = false) { + //get children + $result = ''; + $myNodes =& $this->childNodes; + $total = count($myNodes); + + if ($total != 0) { + for ($i = 0; $i < $total; $i++) { + $child =& $myNodes[$i]; + $result .= $child->toString(false, $subEntities); + } + } + + if ($htmlSafe) $result = $this->forHTML($result); + + return $result; + } //toString +} //DOMIT_DocumentFragment + +/** +* A class representing the DOM Comment node +* +* @package domit-xmlparser +* @subpackage domit-xmlparser-main +* @author John Heinstein +*/ +class DOMIT_Comment extends DOMIT_CharacterData { + /** + * DOM Comment node constructor + * @param string The + */ + function DOMIT_Comment($nodeValue) { + $this->_constructor(); + $this->nodeType = DOMIT_COMMENT_NODE; + $this->nodeName = '#comment'; + $this->nodeValue = $nodeValue; + } //DOMIT_Comment + + /** + * Returns the text contained in the current node + * @return string The text of the current node + */ + function getText() { + return $this->nodeValue; + } //getText + + /** + * Sets the text contained in the current node to $data. + * @param string The text data of the node + */ + function setText($data) { + $this->nodeValue = $data; + } //setText + + /** + * Generates an array representation of the node and its children + * @return Array A representation of the node and its children + */ + function toArray() { + return $this->toString(); + } //toArray + + /** + * Copies a node and/or its children + * @param boolean True if all child nodes are also to be cloned + * @return Object A copy of the node and/or its children + */ + function &cloneNode($deep = false) { + $className = get_class($this); + $clone = new $className($this->nodeValue); + + return $clone; + } //cloneNode + + /** + * Generates a string representation of the node and its children + * @param boolean True if HTML readable output is desired + * @return string The string representation + */ + function toString($htmlSafe = false) { + $result = ''; + + if ($htmlSafe) $result = $this->forHTML($result); + + return $result; + } //toString +} //DOMIT_Comment + +/** +* A class representing the DOM Processing Instruction node +* +* @package domit-xmlparser +* @subpackage domit-xmlparser-main +* @author John Heinstein +*/ +class DOMIT_ProcessingInstruction extends DOMIT_Node { + /** + * DOM Processing Instruction node constructor + * @param string The + */ + function DOMIT_ProcessingInstruction($target, $data) { + $this->_constructor(); + $this->nodeType = DOMIT_PROCESSING_INSTRUCTION_NODE; + $this->nodeName = $target; + $this->nodeValue = $data; + } //DOMIT_ProcessingInstruction + + /** + * Returns the processing instruction target + * @return string The processing instruction target + */ + function getTarget() { + return $this->nodeName; + } //getTarget + + /** + * Returns the processing instruction data + * @return string The processing instruction data + */ + function getData() { + return $this->nodeValue; + } //getData + + /** + * Returns the text contained in the current node + * @return string The text of the current node + */ + function getText() { + return ($this->nodeName . ' ' . $this->nodeValue); + } //getText + + /** + * Generates an array representation of the node and its children + * @return Array A representation of the node and its children + */ + function toArray() { + return $this->toString(); + } //toArray + + /** + * Copies a node and/or its children + * @param boolean True if all child nodes are also to be cloned + * @return Object A copy of the node and/or its children + */ + function &cloneNode($deep = false) { + $className = get_class($this); + $clone = new $className($this->nodeName, $this->nodeValue); + + return $clone; + } //cloneNode + + /** + * Generates a string representation of the node and its children + * @param boolean True if HTML readable output is desired + * @return string The string representation + */ + function toString($htmlSafe = false) { + $result = '<' . '?' . $this->nodeName . ' ' . $this->nodeValue . '?' . '>'; + + if ($htmlSafe) $result = $this->forHTML($result); + + return $result; + } //toString +} //DOMIT_ProcessingInstruction + +/** +* A class representing the DOM Document Type node +* +* @package domit-xmlparser +* @subpackage domit-xmlparser-main +* @author John Heinstein +*/ +class DOMIT_DocumentType extends DOMIT_Node { + /** @var string The doctype name */ + var $name; + /** @var Object True if the entity has been modified since parsing (NOT YET IMPLEMENTED!) */ + var $entities; + /** @var Object A NodeList of notation nodes (NOT YET IMPLEMENTED!) */ + var $notations; + /** @var Object A NodeList of elements (NOT YET IMPLEMENTED!) */ + var $elements; + /** @var string The full text of the doctype */ + var $text; + /** @var string The public identifier of the external subset */ + var $publicID; + /** @var string The system identifier of the external subset */ + var $systemID; + /** @var string The full text of internal subset */ + var $internalSubset; + + /** + * DOM Document Type node constructor + * @param string The + */ + function DOMIT_DocumentType($name, $text) { + $this->_constructor(); + $this->nodeType = DOMIT_DOCUMENT_TYPE_NODE; + $this->nodeName = $name; + $this->name = $name; + $this->entities = null; //implement later + $this->notations = null; //implement later + $this->elements = null; //implement later + $this->text = $text; + } //DOMIT_DocumentType + + /** + * Returns the text contained in the current node + * @return string The text of the current node + */ + function getText() { + return $this->text; + } //getText + + /** + * Returns the name of the doctype node + * @return string The name of the doctype node + */ + function getName() { + return $this->name; + } //getName + + /** + * Generates an array representation of the node and its children + * @return Array A representation of the node and its children + */ + function toArray() { + return $this->toString(); + } //toArray + + /** + * Copies a node and/or its children + * @param boolean True if all child nodes are also to be cloned + * @return Object A copy of the node and/or its children + */ + function &cloneNode($deep = false) { + $className = get_class($this); + $clone = new $className($this->nodeName, $this->text); + + return $clone; + } //cloneNode + + /** + * Generates a string representation of the node and its children + * @param boolean True if HTML readable output is desired + * @return string The string representation + */ + function toString($htmlSafe = false) { + $result = $this->text; + + if ($htmlSafe) $result = $this->forHTML($result); + + return $result; + } //toString +} //DOMIT_DocumentType + + +/** +* A class representing the DOM Notation node (NOT YET IMPLEMENTED!) +* +* @package domit-xmlparser +* @subpackage domit-xmlparser-main +* @author John Heinstein +*/ +class DOMIT_Notation extends DOMIT_Node { + /** + * DOM Notation node constructor (NOT YET IMPLEMENTED!) + */ + function DOMIT_Notation() { + DOMIT_DOMException::raiseException(DOMIT_NOT_SUPPORTED_ERR, + 'Cannot instantiate DOMIT_Notation class. Notation nodes not yet supported.'); + } //DOMIT_Notation +} //DOMIT_Notation + +/** +* Manages the generation of a DOMIT! document from SAX events +* +* @package domit-xmlparser +* @subpackage domit-xmlparser-main +* @author John Heinstein +*/ +class DOMIT_Parser { + /** @var Object A reference to the resulting xmldoc */ + var $xmlDoc = null; + /** @var Object A reference to the current node in the parsing process */ + var $currentNode = null; + /** @var Object A reference to the last child in the parsing process */ + var $lastChild = null; + /** @var boolean True if currently parsing a CDATA Section */ + var $inCDATASection = false; //flag for Expat + /** @var boolean True if currently parsing a Text node */ + var $inTextNode = false; + /** @var boolean True is CDATA Section nodes are not to be converted into Text nodes */ + var $preserveCDATA; + /** @var string A container for holding the currently parsed text data */ + var $parseContainer = ''; + /** @var string The current docutype text */ + var $parseItem = ''; + /** @var boolean True if waiting for an element to which to apply namespace declaration(s) */ + var $waitingForElementToDeclareNamespaces = false; + /** @var boolean True if waiting for an element to which to apply namespace declaration(s) */ + var $tempNamespaceURIMap = array(); + + /** + * Parses xml text using Expat + * @param Object A reference to the DOM document that the xml is to be parsed into + * @param string The text to be parsed + * @param boolean True if CDATA Section nodes are not to be converted into Text nodes + * @return boolean True if the parsing is successful + */ + function parse (&$myXMLDoc, $xmlText, $preserveCDATA = true) { + $this->xmlDoc =& $myXMLDoc; + $this->lastChild =& $this->xmlDoc; + + $this->preserveCDATA = $preserveCDATA; + + //create instance of expat parser (should be included in php distro) + if (version_compare(phpversion(), '5.0', '<=')) { + if ($this->xmlDoc->isNamespaceAware) { + $parser = xml_parser_create_ns(''); + } + else { + $parser = xml_parser_create(''); + } + } + else { + if ($this->xmlDoc->isNamespaceAware) { + $parser = xml_parser_create_ns(); + } + else { + $parser = xml_parser_create(); + } + } + + //set handlers for SAX events + xml_set_object($parser, $this); + xml_set_character_data_handler($parser, 'dataElement'); + xml_set_default_handler($parser, 'defaultDataElement'); + xml_set_notation_decl_handler($parser, 'notationElement'); + xml_set_processing_instruction_handler($parser, 'processingInstructionElement'); + xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); + + if (!$this->xmlDoc->preserveWhitespace) { + xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); + } + else { + xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 0); + } + + if ($this->xmlDoc->isNamespaceAware) { + xml_set_start_namespace_decl_handler($parser, 'startNamespaceDeclaration'); + xml_set_end_namespace_decl_handler($parser, 'endNamespaceDeclaration'); + xml_set_element_handler($parser, 'startElementNS', 'endElement'); + $this->xmlDoc->namespaceURIMap[DOMIT_XML_NAMESPACE] = 'xml'; + } + else { + xml_set_element_handler($parser, 'startElement', 'endElement'); + } + + //parse out whitespace - (XML_OPTION_SKIP_WHITE = 1 does not + //seem to work consistently across versions of PHP and Expat + if (!$this->xmlDoc->preserveWhitespace) { + $xmlText = eregi_replace('>' . "[[:space:]]+" . '<' , '><', $xmlText); + } + + $success = xml_parse($parser, $xmlText); + + $this->xmlDoc->errorCode = xml_get_error_code($parser); + $this->xmlDoc->errorString = xml_error_string($this->xmlDoc->errorCode); + + xml_parser_free($parser); + + return $success; + } //parse + + /** + * Parses xml text using SAXY + * @param Object A reference to the DOM document that the xml is to be parsed into + * @param string The text to be parsed + * @param boolean True if CDATA Section nodes are not to be converted into Text nodes + * @return boolean True if the parsing is successful + */ + function parseSAXY(&$myXMLDoc, $xmlText, $preserveCDATA = true, $definedEntities) { + require_once(DOMIT_INCLUDE_PATH . 'xml_saxy_parser.php'); + + $this->xmlDoc =& $myXMLDoc; + $this->lastChild =& $this->xmlDoc; + + //create instance of SAXY parser + $parser = new SAXY_Parser(); + $parser->appendEntityTranslationTable($definedEntities); + + //not yet implemented in SAXY!!! + $parser->preserveWhitespace = $this->xmlDoc->preserveWhitespace; + + if ($this->xmlDoc->isNamespaceAware) { + $parser->setNamespaceAwareness(true); + $parser->xml_set_start_namespace_decl_handler(array(&$this, 'startNamespaceDeclaration')); + $parser->xml_set_end_namespace_decl_handler(array(&$this, 'endNamespaceDeclaration')); + $parser->xml_set_element_handler(array(&$this, 'startElementNS'), array(&$this, 'endElement')); + $this->xmlDoc->namespaceURIMap[DOMIT_XML_NAMESPACE] = 'xml'; + } + else { + $parser->xml_set_element_handler(array(&$this, 'startElement'), array(&$this, 'endElement')); + } + + $parser->xml_set_character_data_handler(array(&$this, 'dataElement')); + $parser->xml_set_doctype_handler(array(&$this, 'doctypeElement')); + $parser->xml_set_comment_handler(array(&$this, 'commentElement')); + $parser->xml_set_processing_instruction_handler(array(&$this, 'processingInstructionElement')); + + if ($preserveCDATA) { + $parser->xml_set_cdata_section_handler(array(&$this, 'cdataElement')); + } + + $success = $parser->parse($xmlText); + + $this->xmlDoc->errorCode = $parser->xml_get_error_code(); + $this->xmlDoc->errorString = $parser->xml_error_string($this->xmlDoc->errorCode); + + return $success; + } //parseSAXY + + /** + * Generates and appends a new text node from the parseContainer text + */ + function dumpTextNode() { + $currentNode =& $this->xmlDoc->createTextNode($this->parseContainer); + $this->lastChild->appendChild($currentNode); + $this->inTextNode = false; + $this->parseContainer = ''; + } //dumpTextNode + + /** + * Catches a start element event and processes the data + * @param Object A reference to the current SAX parser + * @param string The tag name of the current element + * @param Array An array of the element attributes + */ + function startElement(&$parser, $name, $attrs) { + if ($this->inTextNode) { + $this->dumpTextNode(); + } + + $currentNode =& $this->xmlDoc->createElement($name); + $this->lastChild->appendChild($currentNode); + + reset ($attrs); + + while (list($key, $value) = each ($attrs)) { + $currentNode->setAttribute($key, $value); + } + + $this->lastChild =& $currentNode; + } //startElement + + /** + * Catches a start element event and processes the data + * @param Object A reference to the current SAX parser + * @param string The tag name of the current element + * @param Array An array of the element attributes + */ + function startElementNS(&$parser, $name, $attrs) { + if ($this->inTextNode) { + $this->dumpTextNode(); + } + + $colonIndex = strrpos($name, ":"); + + if ($colonIndex !== false) { + //force to lower case because Expat for some reason forces to upper case + $namespaceURI = strtolower(substr($name, 0, $colonIndex)); + $prefix = $this->xmlDoc->namespaceURIMap[$namespaceURI]; + + if ($prefix != '') { + $qualifiedName = $prefix . ":" . substr($name, ($colonIndex + 1)); + } + else { + $qualifiedName = substr($name, ($colonIndex + 1)); + } + } + else { + $namespaceURI = ''; + $qualifiedName = $name; + } + + $currentNode =& $this->xmlDoc->createElementNS($namespaceURI, $qualifiedName); + $this->lastChild->appendChild($currentNode); + + + //add attributes + reset ($attrs); + + while (list($key, $value) = each ($attrs)) { + $colonIndex = strrpos($key, ":"); + + if ($colonIndex !== false) { + //force to lower case because Expat for some reason forces to upper case + $namespaceURI = strtolower(substr($key, 0, $colonIndex)); + + $qualifiedName = $this->xmlDoc->namespaceURIMap[$namespaceURI] . + ":" . substr($key, ($colonIndex + 1));; + } + else { + //if containing element has a namespace, technically + //attribute namespace should be added here, but we'll do it elsewhere + //so toNormalizedString outputs the same things as is input + $namespaceURI = ''; + $qualifiedName = $key; + } + + $currentNode->setAttributeNS($namespaceURI, $qualifiedName, $value); + } + + //and add xmlns attributes + if ($this->waitingForElementToDeclareNamespaces) { + //map namespace declarations to element + foreach ($this->tempNamespaceURIMap as $key => $value) { + $currentNode->namespaceURIMap[$key] = $value ; + + //xmlns prefix has prefix: http://www.w3.org/2000/xmlns/ + $currentNode->setAttributeNS(DOMIT_XMLNS_NAMESPACE, ('xmlns:' . $value), $key); + } + + //reset variables + $this->tempNamespaceURIMap = array(); + $this->waitingForElementToDeclareNamespaces = false; + } + + $this->lastChild =& $currentNode; + } //startElementNS + + + /** + * Catches an end element event and processes the data + * @param Object A reference to the current SAX parser + * @param string The tag name of the current element + */ + function endElement(&$parser, $name) { + if ($this->inTextNode) { + $this->dumpTextNode(); + } + + $this->lastChild =& $this->lastChild->parentNode; + } //endElement + + /** + * Catches a data event and processes the text + * @param Object A reference to the current SAX parser + * @param string The current text data + */ + function dataElement(&$parser, $data) { + if (!$this->inCDATASection) { + $this->inTextNode = true; + } + + $this->parseContainer .= $data; + } //dataElement + + /** + * Catches a CDATA Section event and processes the text + * @param Object A reference to the current SAX parser + * @param string The current text data + */ + function cdataElement(&$parser, $data) { + $currentNode =& $this->xmlDoc->createCDATASection($data); + + $this->lastChild->appendChild($currentNode); + } //cdataElement + + /** + * Catches a default data event and processes the data + * @param Object A reference to the current SAX parser + * @param string The current data + */ + function defaultDataElement(&$parser, $data) { + if ((strlen($data) > 2) && ($this->parseItem == '')){ + $pre = strtoupper(substr($data, 0, 3)); + + switch ($pre) { + case 'processingInstructionElement($parser, 'xml', substr($data, 6, (strlen($data) - 6 - 2))); + break; + case 'xmlDoc->doctype .= "\n " . $data; + break; + case 'preserveCDATA) { + $this->inCDATASection = true; + } + break; + case 'commentElement($this, substr($data, 4, (strlen($data) - 7))); + break; + case 'parseItem = 'doctype'; + $this->parseContainer = $data; + break; + case ']]>': //cdata end tag + if ($this->preserveCDATA) { + $currentNode =& $this->xmlDoc->createCDATASection($this->parseContainer); + $this->lastChild->appendChild($currentNode); + $this->inCDATASection = false; + $this->parseContainer = ''; + } + else { + $this->dumpTextNode(); + } + break; + } + } + else { + switch ($this->parseItem) { + case 'doctype': + $this->parseContainer .= $data; + + if ($data == '>') { + $this->doctypeElement($parser, $this->parseContainer); + $this->parseContainer = ''; + $this->parseItem = ''; + } + else if ($data == '[') { + $this->parseItem = 'doctype_inline'; + } + break; + + case 'doctype_inline': + $this->parseContainer .= $data; + + if ($data == ']') { + $this->parseItem = 'doctype'; + } + else if ($data{(strlen($data) - 1)} == '>') { + $this->parseContainer .= "\n "; + } + break; + } + } + } //defaultDataElement + + /** + * Catches a doctype event and processes the data + * @param Object A reference to the current SAX parser + * @param string The current data + */ + function doctypeElement(&$parser, $data) { + $start = strpos($data, 'ownerDocument =& $this->xmlDoc; + + $this->lastChild->appendChild($currentNode); + $this->xmlDoc->doctype =& $currentNode; + } //doctypeElement + + /** + * Catches a notation node event and processes the data + * @param Object A reference to the current SAX parser + * @param string The current notation data + */ + function notationElement(&$parser, $data) { + //add to doctype string + if (($this->parseItem == 'doctype_inline') || ($this->parseItem == 'doctype')) { + $this->parseContainer .= $data; + } + } //notationElement + + /** + * Catches a comment node event and processes the data + * @param Object A reference to the current SAX parser + * @param string The comment data + */ + function commentElement(&$parser, $data) { + if ($this->inTextNode) { + $this->dumpTextNode(); + } + + $currentNode =& $this->xmlDoc->createComment($data); + $this->lastChild->appendChild($currentNode); + } //commentElement + + /** + * Catches a processing instruction node event and processes the data + * @param Object A reference to the current SAX parser + * @param string The target of the processing instruction data + * @param string The processing instruction data + */ + function processingInstructionElement(&$parser, $target, $data) { + if ($this->inTextNode) { + $this->dumpTextNode(); + } + + $currentNode =& $this->xmlDoc->createProcessingInstruction($target, $data); + $this->lastChild->appendChild($currentNode); + + if (strtolower($target) == 'xml') { + $this->xmlDoc->xmlDeclaration =& $currentNode; + } + } //processingInstructionElement + + /** + * Catches a start namespace declaration event and processes the data + * @param Object A reference to the current SAX parser + * @param string The namespace prefix + * @param string The namespace uri + */ + function startNamespaceDeclaration(&$parser, $prefix, $uri) { + //make uri lower case because Expat forces it to upper case for some reason + $this->xmlDoc->namespaceURIMap[strtolower($uri)] = $prefix; + + //set up a switch so when the target element arrives, namespaces can be mapped to it + $this->waitingForElementToDeclareNamespaces = true; + $this->tempNamespaceURIMap[strtolower($uri)] = $prefix; + } //startNamespaceDeclaration + + /** + * Catches an end namespace declaration event + * @param Object A reference to the current SAX parser + * @param string The namespace prefix + */ + function endNamespaceDeclaration(&$parser, $prefix) { + //do nothing; could remove from map, but would hardly be optimal + } //endNamespaceDeclaration + +} //DOMIT_Parser + +?> \ No newline at end of file diff --git a/oss/joomla/trunk/libraries/domit/xml_domit_rss.php b/oss/joomla/trunk/libraries/domit/xml_domit_rss.php new file mode 100644 index 00000000..ecf4550f --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/xml_domit_rss.php @@ -0,0 +1,1121 @@ + +* @link http://www.engageinteractive.com/domitrss/ DOMIT! RSS Home Page +* DOMIT! RSS is Free Software +**/ + +if (!defined('DOMIT_RSS_INCLUDE_PATH')) { + define('DOMIT_RSS_INCLUDE_PATH', (dirname(__FILE__) . DIRECTORY_SEPARATOR)); +} + +/** current version of DOMIT! RSS */ +define ('DOMIT_RSS_VERSION', '0.51'); +/** language constant */ +define('DOMIT_RSS_ELEMENT_LANGUAGE', 'language'); +/** copyright constant */ +define('DOMIT_RSS_ELEMENT_COPYRIGHT', 'copyright'); +/** managing editor constant */ +define('DOMIT_RSS_ELEMENT_MANAGINGEDITOR', 'managingeditor'); +/** webmaster constant */ +define('DOMIT_RSS_ELEMENT_WEBMASTER', 'webmaster'); +/** pubdate constant */ +define('DOMIT_RSS_ELEMENT_PUBDATE', 'pubdate'); +/** last build date constant */ +define('DOMIT_RSS_ELEMENT_LASTBUILDDATE', 'lastbuilddate'); +/** category constant */ +define('DOMIT_RSS_ELEMENT_CATEGORY', 'category'); +/** generator constant */ +define('DOMIT_RSS_ELEMENT_GENERATOR', 'generator'); +/** docs constant */ +define('DOMIT_RSS_ELEMENT_DOCS', 'docs'); +/** cloud constant */ +define('DOMIT_RSS_ELEMENT_CLOUD', 'cloud'); +/** ttl constant */ +define('DOMIT_RSS_ELEMENT_TTL', 'ttl'); +/** image constant */ +define('DOMIT_RSS_ELEMENT_IMAGE', 'image'); +/** rating constant */ +define('DOMIT_RSS_ELEMENT_RATING', 'rating'); +/** textinput constant */ +define('DOMIT_RSS_ELEMENT_TEXTINPUT', 'textinput'); +/** skiphours constant */ +define('DOMIT_RSS_ELEMENT_SKIPHOURS', 'skiphours'); +/** skipdays constant */ +define('DOMIT_RSS_ELEMENT_SKIPDAYS', 'skipdays'); +/** url constant */ +define('DOMIT_RSS_ELEMENT_URL', 'url'); +/** width constant */ +define('DOMIT_RSS_ELEMENT_WIDTH', 'width'); +/** height constant */ +define('DOMIT_RSS_ELEMENT_HEIGHT', 'height'); +/** guid constant */ +define('DOMIT_RSS_ELEMENT_GUID', 'guid'); +/** enclosure constant */ +define('DOMIT_RSS_ELEMENT_ENCLOSURE', 'enclosure'); +/** comments constant */ +define('DOMIT_RSS_ELEMENT_COMMENTS', 'comments'); +/** source constant */ +define('DOMIT_RSS_ELEMENT_SOURCE', 'source'); +/** name constant */ +define('DOMIT_RSS_ELEMENT_NAME', 'name'); +/** author constant */ +define('DOMIT_RSS_ELEMENT_AUTHOR', 'author'); + +/** domain constant */ +define('DOMIT_RSS_ATTR_DOMAIN', 'domain'); +/** port constant */ +define('DOMIT_RSS_ATTR_PORT', 'port'); +/** path constant */ +define('DOMIT_RSS_ATTR_PATH', 'path'); +/** registerProcedure constant */ +define('DOMIT_RSS_ATTR_REGISTERPROCEDURE', 'registerProcedure'); +/** protocol constant */ +define('DOMIT_RSS_ATTR_PROTOCOL', 'protocol'); +/** url constant */ +define('DOMIT_RSS_ATTR_URL', 'url'); +/** length constant */ +define('DOMIT_RSS_ATTR_LENGTH', 'length'); +/** type constant */ +define('DOMIT_RSS_ATTR_TYPE', 'type'); +/** isPermaLink constant */ +define('DOMIT_RSS_ATTR_ISPERMALINK', 'isPermaLink'); + +require_once(DOMIT_RSS_INCLUDE_PATH . 'xml_domit_rss_shared.php'); + +/** +* The base DOMIT! RSS document class +* +* @package domit-rss +* @subpackage domit-rss-main +* @author John Heinstein +*/ +class xml_domit_rss_document extends xml_domit_rss_base_document { + /** + * Constructor + * @param string Path to the rss file + * @param string Directory in which cache files are to be stored + * @param string Expiration time (in seconds) for the cache file + * @return Object A new instance of xml_domit_rss_document + */ + function xml_domit_rss_document($url = '', $cacheDir = './', $cacheTime = '3600') { + $this->parser = 'DOMIT_RSS'; + $this->xml_domit_rss_base_document($url, $cacheDir, $cacheTime); + } //xml_domit_rss_document + + /** + * Performs initialization of the RSS document + */ + function _init() { + $total = $this->node->documentElement->childCount; + $itemCounter = 0; + $channelCounter = 0; + $categoryCounter = 0; + + for ($i = 0; $i < $total; $i++) { + $currNode =& $this->node->documentElement->childNodes[$i]; + $tagName = strtolower($currNode->nodeName); + + switch ($tagName) { + case DOMIT_RSS_ELEMENT_ITEM: + $this->domit_rss_items[$itemCounter] = new xml_domit_rss_item($currNode); + $itemCounter++; + break; + case DOMIT_RSS_ELEMENT_CHANNEL: + $this->domit_rss_channels[$channelCounter] = new xml_domit_rss_channel($currNode); + $channelCounter++; + break; + case DOMIT_RSS_ELEMENT_CATEGORY: + $this->domit_rss_categories[$categoryCounter] = new xml_domit_rss_category($currNode); + $categoryCounter++; + break; + case DOMIT_RSS_ELEMENT_IMAGE: + $this->DOMIT_RSS_indexer[$tagName] = new xml_domit_rss_image($currNode); + break; + case DOMIT_RSS_ELEMENT_CLOUD: + $this->indexer[$tagName] = new xml_domit_rss_cloud($currNode); + break; + case DOMIT_RSS_ELEMENT_TEXTINPUT: + $this->indexer[$tagName] = new xml_domit_rss_textinput($currNode); + break; + case DOMIT_RSS_ELEMENT_TITLE: + case DOMIT_RSS_ELEMENT_LINK: + case DOMIT_RSS_ELEMENT_DESCRIPTION: + case DOMIT_RSS_ELEMENT_LANGUAGE: + case DOMIT_RSS_ELEMENT_COPYRIGHT: + case DOMIT_RSS_ELEMENT_MANAGINGEDITOR: + case DOMIT_RSS_ELEMENT_WEBMASTER: + case DOMIT_RSS_ELEMENT_PUBDATE: + case DOMIT_RSS_ELEMENT_LASTBUILDDATE: + case DOMIT_RSS_ELEMENT_GENERATOR: + case DOMIT_RSS_ELEMENT_DOCS: + case DOMIT_RSS_ELEMENT_TTL: + case DOMIT_RSS_ELEMENT_RATING: + case DOMIT_RSS_ELEMENT_SKIPHOURS: + case DOMIT_RSS_ELEMENT_SKIPDAYS: + $this->DOMIT_RSS_indexer[$tagName] = new xml_domit_rss_simpleelement($currNode); + break; + default: + $this->addIndexedElement($currNode); + //$this->indexer[$tagName] =& $currNode; + } + } + + if ($itemCounter != 0) { + $this->DOMIT_RSS_indexer[DOMIT_RSS_ARRAY_ITEMS] =& $this->domit_rss_items; + } + + if ($channelCounter != 0) { + $this->DOMIT_RSS_indexer[DOMIT_RSS_ARRAY_CHANNELS] =& $this->domit_rss_channels; + } + + if ($categoryCounter != 0) { + $this->DOMIT_RSS_indexer[DOMIT_RSS_ARRAY_CATEGORIES] =& $this->domit_rss_categories; + } + + $this->handleChannelElementsEmbedded(); + } //_init + + /** + * Returns the current version of DOMIT! RSS + * @return int The current version of DOMIT! RSS + */ + function getVersion() { + return DOMIT_RSS_VERSION; + } //getVersion +} //xml_domit_rss_document + + +/** +* Represents an RSS channel +* +* @package domit-rss +* @subpackage domit-rss-main +* @author John Heinstein +*/ +class xml_domit_rss_channel extends xml_domit_rss_elementindexer { + /** @var array A list of references to channel items */ + var $domit_rss_items = array(); + /** @var array A list of references to category items */ + var $domit_rss_categories = array(); + + /** + * Constructor + * @param Object A DOM node containing channel data + * @param boolean True if channel elements are siblings of the channel rather than children + */ + function xml_domit_rss_channel(&$channel, $externalElements = false) { + $this->node =& $channel; + $this->rssDefinedElements = array('title','link','description','language','copyright', + 'managingEditor','webmaster','pubDate','lastBuildDate', + 'generator','docs','cloud','ttl','image', 'rating', + 'textInput','skipHours','skipDays','domit_rss_channels', + 'domit_rss_items','domit_rss_categories'); + $this->_init(); + } //xml_domit_rss_channel + + /** + * Performs initialization of the RSS channel element + */ + function _init() { + $total = $this->node->childCount; + $itemCounter = 0; + $categoryCounter = 0; + + for ($i = 0; $i < $total; $i++) { + $currNode =& $this->node->childNodes[$i]; + $tagName = strtolower($currNode->nodeName); + + switch($tagName) { + case DOMIT_RSS_ELEMENT_ITEM: + $this->domit_rss_items[$itemCounter] = new xml_domit_rss_item($currNode); + $itemCounter++; + break; + case DOMIT_RSS_ELEMENT_CATEGORY: + $this->domit_rss_categories[$categoryCounter] = new xml_domit_rss_category($currNode); + $categoryCounter++; + break; + case DOMIT_RSS_ELEMENT_IMAGE: + $this->DOMIT_RSS_indexer[$tagName] = new xml_domit_rss_image($currNode); + break; + case DOMIT_RSS_ELEMENT_CLOUD: + $this->DOMIT_RSS_indexer[$tagName] = new xml_domit_rss_cloud($currNode); + break; + case DOMIT_RSS_ELEMENT_TEXTINPUT: + $this->DOMIT_RSS_indexer[$tagName] = new xml_domit_rss_textinput($currNode); + break; + case DOMIT_RSS_ELEMENT_SKIPHOURS: + $this->DOMIT_RSS_indexer[$tagName] = new xml_domit_rss_skiphours($currNode); + break; + case DOMIT_RSS_ELEMENT_SKIPDAYS: + $this->DOMIT_RSS_indexer[$tagName] = new xml_domit_rss_skipdays($currNode); + break; + case DOMIT_RSS_ELEMENT_TITLE: + case DOMIT_RSS_ELEMENT_LINK: + case DOMIT_RSS_ELEMENT_DESCRIPTION: + case DOMIT_RSS_ELEMENT_LANGUAGE: + case DOMIT_RSS_ELEMENT_COPYRIGHT: + case DOMIT_RSS_ELEMENT_MANAGINGEDITOR: + case DOMIT_RSS_ELEMENT_WEBMASTER: + case DOMIT_RSS_ELEMENT_PUBDATE: + case DOMIT_RSS_ELEMENT_LASTBUILDDATE: + case DOMIT_RSS_ELEMENT_GENERATOR: + case DOMIT_RSS_ELEMENT_DOCS: + case DOMIT_RSS_ELEMENT_TTL: + case DOMIT_RSS_ELEMENT_RATING: + $this->DOMIT_RSS_indexer[$tagName] = new xml_domit_rss_simpleelement($currNode); + break; + default: + $this->addIndexedElement($currNode); + //$this->DOMIT_RSS_indexer[$tagName] =& $currNode; + } + } + + if ($itemCounter != 0) { + $this->DOMIT_RSS_indexer[DOMIT_RSS_ARRAY_ITEMS] =& $this->domit_rss_items; + } + + if ($categoryCounter != 0) { + $this->DOMIT_RSS_indexer[DOMIT_RSS_ARRAY_CATEGORIES] =& $this->domit_rss_categories; + } + } //_init + + /** + * Returns the title of the channel + * @return string The title of the channel, or an empty string + */ + function getTitle() { + return $this->getElementText(DOMIT_RSS_ELEMENT_TITLE); + } //getTitle + + /** + * Returns the url of the channel + * @return string The url of the channel, or an empty string + */ + function getLink() { + return $this->getElementText(DOMIT_RSS_ELEMENT_LINK); + } //getLink + + /** + * Returns a description of the channel + * @return string A description of the channel, or an empty string + */ + function getDescription() { + return $this->getElementText(DOMIT_RSS_ELEMENT_DESCRIPTION); + } //getDescription + + /** + * Returns the language of the channel + * @return string The language of the channel, or an empty string + */ + function getLanguage() { + return $this->getElementText(DOMIT_RSS_ELEMENT_LANGUAGE); + } //getLanguage + + /** + * Returns the copyright of the channel + * @return string The copyright of the channel, or an empty string + */ + function getCopyright() { + return $this->getElementText(DOMIT_RSS_ELEMENT_COPYRIGHT); + } //getCopyright + + /** + * Returns the managing editor of the channel + * @return string The managing editor of the channel, or an empty string + */ + function getManagingEditor() { + return $this->getElementText(DOMIT_RSS_ELEMENT_MANAGINGEDITOR); + } //getManagingEditor + + /** + * Returns the webmaster of the channel + * @return string The webmaster of the channel, or an empty string + */ + function getWebMaster() { + return $this->getElementText(DOMIT_RSS_ELEMENT_WEBMASTER); + } //getWebMaster + + /** + * Returns the publication date of the channel + * @return string The publication date of the channel, or an empty string + */ + function getPubDate() { + return $this->getElementText(DOMIT_RSS_ELEMENT_PUBDATE); + } //getPubDate + + /** + * Returns the last build date of the channel + * @return string The last build date of the channel, or an empty string + */ + function getLastBuildDate() { + return $this->getElementText(DOMIT_RSS_ELEMENT_LASTBUILDDATE); + } //getLastBuildDate + + /** + * Returns the generator of the channel + * @return string The generator of the channel, or an empty string + */ + function getGenerator() { + return $this->getElementText(DOMIT_RSS_ELEMENT_GENERATOR); + } //getGenerator + + /** + * Returns the docs of the channel + * @return string The docs of the channel, or an empty string + */ + function getDocs() { + return $this->getElementText(DOMIT_RSS_ELEMENT_DOCS); + } //getDocs + + /** + * Returns the cloud of the channel + * @return object A reference to the cloud object for the channel, or null + */ + function getCloud() { + if ($this->hasElement(DOMIT_RSS_ELEMENT_CLOUD)) { + return $this->getElement(DOMIT_RSS_ELEMENT_CLOUD); + } + + return null; + } //getCloud + + /** + * Returns the ttl of the channel + * @return string The ttl of the channel, or an empty string + */ + function getTTL() { + return $this->getElementText(DOMIT_RSS_ELEMENT_TTL); + } //getTTL + + /** + * Returns the image of the channel + * @return object A reference to an image object for the channel, or null + */ + function getImage() { + if ($this->hasElement(DOMIT_RSS_ELEMENT_IMAGE)) { + return $this->getElement(DOMIT_RSS_ELEMENT_IMAGE); + } + + return null; + } //getImage + + /** + * Returns the rating of the channel + * @return string The rating of the channel, or an empty string + */ + function getRating() { + return $this->getElementText(DOMIT_RSS_ELEMENT_RATING); + } //getRating + + /** + * Returns the text input box of the channel + * @return object A reference to a text input box object for the channel, or null + */ + function getTextInput() { + if ($this->hasElement(DOMIT_RSS_ELEMENT_TEXTINPUT)) { + return $this->getElement(DOMIT_RSS_ELEMENT_TEXTINPUT); + } + + return null; + } //getTextInput + + /** + * Returns the skip days of the channel + * @return object A reference to the skip days object for the channel, or null + */ + function getSkipDays() { + if ($this->hasElement(DOMIT_RSS_ELEMENT_SKIPDAYS)) { + return $this->getElement(DOMIT_RSS_ELEMENT_SKIPDAYS); + } + + return null; + } //getSkipDays + + /** + * Returns the skip hours of the channel + * @return object A reference to the skip hours object for the channel, or null + */ + function getSkipHours() { + if ($this->hasElement(DOMIT_RSS_ELEMENT_SKIPHOURS)) { + return $this->getElement(DOMIT_RSS_ELEMENT_SKIPHOURS); + } + + return null; + } //getSkipHours + + /** + * Returns the number of items in the channel + * @return int The number of items in the channel + */ + function getItemCount() { + return count($this->domit_rss_items); + } //getItemCount + + /** + * Returns a reference to the item at the specified index + * @param int The index of the requested item + * @return Object A reference to the item at the specified index + */ + function &getItem($index) { + return $this->domit_rss_items[$index]; + } //getItem + + /** + * Returns the number of categories in the channel + * @return int The number of categories in the channel + */ + function getCategoryCount() { + return count($this->domit_rss_categories); + } //getCategoryCount + + /** + * Returns a reference to the category at the specified index + * @param int The index of the requested category + * @return Object A reference to the category at the specified index + */ + function &getCategory($index) { + return $this->domit_rss_categories[$index]; + } //getCategory +} //xml_domit_rss_channel + +/** +* Represents an RSS item +* +* @package domit-rss +* @subpackage domit-rss-main +* @author John Heinstein +*/ +class xml_domit_rss_item extends xml_domit_rss_elementindexer { + /** @var array A list of references to category items */ + var $domit_rss_categories = array(); + + /** + * Constructor + * @param Object A DOM node containing item data + */ + function xml_domit_rss_item(&$item) { + $this->node =& $item; + $this->rssDefinedElements = array('title','link','description','author','comments', + 'enclosure','guid','pubDate','source','domit_rss_categories'); + $this->_init(); + } //xml_domit_rss_item + + /** + * Performs initialization of the item element + */ + function _init(){ + $total = $this->node->childCount; + $categoryCounter = 0; + + for($i = 0; $i < $total; $i++) { + $currNode =& $this->node->childNodes[$i]; + $tagName = strtolower($currNode->nodeName); + + switch ($tagName) { + case DOMIT_RSS_ELEMENT_CATEGORY: + $this->categories[$categoryCounter] = new xml_domit_rss_category($currNode); + $categoryCounter++; + break; + case DOMIT_RSS_ELEMENT_ENCLOSURE: + $this->DOMIT_RSS_indexer[$tagName] = new xml_domit_rss_enclosure($currNode); + break; + case DOMIT_RSS_ELEMENT_SOURCE: + $this->DOMIT_RSS_indexer[$tagName] = new xml_domit_rss_source($currNode); + break; + case DOMIT_RSS_ELEMENT_GUID: + $this->DOMIT_RSS_indexer[$tagName] = new xml_domit_rss_guid($currNode); + break; + case DOMIT_RSS_ELEMENT_TITLE: + case DOMIT_RSS_ELEMENT_LINK: + case DOMIT_RSS_ELEMENT_DESCRIPTION: + case DOMIT_RSS_ELEMENT_AUTHOR: + case DOMIT_RSS_ELEMENT_COMMENTS: + case DOMIT_RSS_ELEMENT_PUBDATE: + $this->DOMIT_RSS_indexer[$tagName] = new xml_domit_rss_simpleelement($currNode); + break; + default: + $this->addIndexedElement($currNode); + //$this->DOMIT_RSS_indexer[$tagName] =& $currNode; + } + } + + if ($categoryCounter != 0) { + $this->DOMIT_RSS_indexer[DOMIT_RSS_ARRAY_CATEGORIES] =& $this->domit_rss_categories; + } + } //init + + /** + * Returns the title of the item + * @return string The title of the item, or an empty string + */ + function getTitle() { + return $this->getElementText(DOMIT_RSS_ELEMENT_TITLE); + } //getTitle + + /** + * Returns the url of the item + * @return string The url of the item, or an empty string + */ + function getLink() { + return $this->getElementText(DOMIT_RSS_ELEMENT_LINK); + } //getLink + + /** + * Returns a description of the item + * @return string A description of the item, or an empty string + */ + function getDescription() { + return $this->getElementText(DOMIT_RSS_ELEMENT_DESCRIPTION); + } //getDescription + + /** + * Returns the author of the item + * @return string The author of the item, or an empty string + */ + function getAuthor() { + return $this->getElementText(DOMIT_RSS_ELEMENT_AUTHOR); + } //getAuthor + + /** + * Returns the comments of the item + * @return string The comments of the item, or an empty string + */ + function getComments() { + return $this->getElementText(DOMIT_RSS_ELEMENT_COMMENTS); + } //getComments + + /** + * Returns the enclosure of the item + * @return object A reference to the enclosure object for the item, or null + */ + function getEnclosure() { + if ($this->hasElement(DOMIT_RSS_ELEMENT_ENCLOSURE)) { + return $this->getElement(DOMIT_RSS_ELEMENT_ENCLOSURE); + } + + return null; + } //getEnclosure + + /** + * Returns the guid of the item + * @return object A reference to the guid object for the item, or null + */ + function getGUID() { + if ($this->hasElement(DOMIT_RSS_ELEMENT_GUID)) { + return $this->getElement(DOMIT_RSS_ELEMENT_GUID); + } + + return null; + } //getGUID + + /** + * Returns the publication date of the item + * @return string The publication date of the item, or an empty string + */ + function getPubDate() { + return $this->getElementText(DOMIT_RSS_ELEMENT_PUBDATE); + } //getPubDate + + /** + * Returns the source of the item + * @return object A reference to the source object for the item, or null + */ + function getSource() { + if ($this->hasElement(DOMIT_RSS_ELEMENT_SOURCE)) { + return $this->getElement(DOMIT_RSS_ELEMENT_SOURCE); + } + + return null; + } //getSource + + /** + * Returns the number of categories in the item + * @return int The number of categories in the item + */ + function getCategoryCount() { + return count($this->domit_rss_categories); + } //getCategoryCount + + /** + * Returns a reference to the category at the specified index + * @param int The index of the requested category + * @return Object A reference to the category at the specified index + */ + function &getCategory($index) { + return $this->domit_rss_categories[$index]; + } //getCategory +} //xml_domit_rss_item + + +/** +* Represents an RSS category +* +* @package domit-rss +* @subpackage domit-rss-main +* @author John Heinstein +*/ +class xml_domit_rss_category extends xml_domit_rss_elementindexer { + /** + * Constructor + * @param Object A DOM node containing category data + */ + function xml_domit_rss_category(&$category) { + $this->node =& $category; + $this->_init(); + } //xml_domit_rss_category + + /** + * Returns the category + * @return string The category text + */ + function getCategory() { + return $this->node->firstChild->toString(); + } //getCategory + + /** + * Returns the category domain + * @return string The category domain + */ + function getDomain() { + return $this->getAttribute(DOMIT_RSS_ATTR_DOMAIN); + } //getDomain +} //xml_domit_rss_category + +/** +* Represents an RSS image +* +* @package domit-rss +* @subpackage domit-rss-main +* @author John Heinstein +*/ +class xml_domit_rss_image extends xml_domit_rss_elementindexer { + /** + * Constructor + * @param Object A DOM node containing image data + */ + function xml_domit_rss_image(&$image) { + $this->node =& $image; + $this->rssDefinedElements = array('title','link','description','url', + 'width', 'height'); + $this->_init(); + } //xml_domit_rss_image + + /** + * Performs initialization of image element + */ + function _init(){ + $total = $this->node->childCount; + + for($i = 0; $i < $total; $i++) { + $currNode =& $this->node->childNodes[$i]; + $tagName = strtolower($currNode->nodeName); + + switch ($tagName) { + case DOMIT_RSS_ELEMENT_TITLE: + case DOMIT_RSS_ELEMENT_LINK: + case DOMIT_RSS_ELEMENT_DESCRIPTION: + case DOMIT_RSS_ELEMENT_URL: + case DOMIT_RSS_ELEMENT_WIDTH: + case DOMIT_RSS_ELEMENT_HEIGHT: + $this->DOMIT_RSS_indexer[$tagName] = new xml_domit_rss_simpleelement($currNode); + break; + default: + $this->addIndexedElement($currNode); + //$this->DOMIT_RSS_indexer[$tagName] =& $currNode; + } + } + } //_init + + /** + * Returns the image title + * @return string The image title + */ + function getTitle() { + return $this->getElementText(DOMIT_RSS_ELEMENT_TITLE); + } //getTitle + + /** + * Returns the image link + * @return string The image link + */ + function getLink() { + return $this->getElementText(DOMIT_RSS_ELEMENT_LINK); + } //getLink + + /** + * Returns the image url + * @return string The image url + */ + function getUrl() { + return $this->getElementText(DOMIT_RSS_ELEMENT_URL); + } //getUrl + + /** + * Returns the image width in pixels + * @return string The image width (maximum 144, default 88) + */ + function getWidth() { + $myWidth = $this->getElementText(DOMIT_RSS_ELEMENT_WIDTH); + + if ($myWidth == '') { + $myWidth = '88'; + } + else if (intval($myWidth) > 144) { + $myWidth = '144'; + } + + return $myWidth; + } //getWidth + + /** + * Returns the image height in pixels + * @return string The image height (maximum 400, default 31) + */ + function getHeight() { + $myHeight = $this->getElementText(DOMIT_RSS_ELEMENT_HEIGHT); + + if ($myHeight == '') { + $myHeight = '31'; + } + else if (intval($myHeight) > 400) { + $myHeight = '400'; + } + + return $myHeight; + } //getHeight + + /** + * Returns the image description + * @return string The image description + */ + function getDescription() { + return $this->getElementText(DOMIT_RSS_ELEMENT_DESCRIPTION); + } //getDescription +} //xml_domit_rss_image + +/** +* Represents an RSS text input form +* +* @package domit-rss +* @subpackage domit-rss-main +* @author John Heinstein +*/ +class xml_domit_rss_textinput extends xml_domit_rss_elementindexer { + /** + * Constructor + * @param Object A DOM node containing text input data + */ + function xml_domit_rss_textinput(&$textinput) { + $this->node =& $textinput; + $this->rssDefinedElements = array('title','link','description','name'); + $this->_init(); + } //xml_domit_rss_textinput + + /** + * Performs initialization of textInput element + */ + function _init(){ + $total = $this->node->childCount; + + for($i = 0; $i < $total; $i++) { + $currNode =& $this->node->childNodes[$i]; + $tagName = strtolower($currNode->nodeName); + + switch ($tagName) { + case DOMIT_RSS_ELEMENT_TITLE: + case DOMIT_RSS_ELEMENT_LINK: + case DOMIT_RSS_ELEMENT_DESCRIPTION: + case DOMIT_RSS_ELEMENT_NAME: + $this->DOMIT_RSS_indexer[$tagName] = new xml_domit_rss_simpleelement($currNode); + break; + default: + $this->addIndexedElement($currNode); + //$this->DOMIT_RSS_indexer[$tagName] =& $currNode; + } + } + } //_init + + /** + * Returns the title of the text input + * @return string The title of the text input, or an empty string + */ + function getTitle() { + return $this->getElementText(DOMIT_RSS_ELEMENT_TITLE); + } //getTitle + + /** + * Returns a description of the text input + * @return string A description of the text input, or an empty string + */ + function getDescription() { + return $this->getElementText(DOMIT_RSS_ELEMENT_DESCRIPTION); + } //getDescription + + /** + * Returns the name of the text input + * @return string The name of the text input, or an empty string + */ + function getName() { + return $this->getElementText(DOMIT_RSS_ELEMENT_NAME); + } //getName + + /** + * Returns the url of the text input + * @return string The url of the text input, or an empty string + */ + function getLink() { + return $this->getElementText(DOMIT_RSS_ELEMENT_LINK); + } //getLink +} //xml_domit_rss_textinput + +/** +* Represents an RSS cloud +* +* @package domit-rss +* @subpackage domit-rss-main +* @author John Heinstein +*/ +class xml_domit_rss_cloud extends xml_domit_rss_elementindexer { + /** + * Constructor + * @param Object A DOM node containing cloud data + */ + function xml_domit_rss_cloud(&$cloud) { + $this->node =& $cloud; + $this->_init(); + } //xml_domit_rss_cloud + + /** + * Returns the domain of the cloud + * @return string The domain of the cloud + */ + function getDomain() { + return $this->getAttribute(DOMIT_RSS_ATTR_DOMAIN); + } //getDomain + + /** + * Returns the port of the cloud + * @return string The port of the cloud + */ + function getPort() { + return $this->getAttribute(DOMIT_RSS_ATTR_PORT); + } //getPort + + /** + * Returns the path of the cloud + * @return string The path of the cloud + */ + function getPath() { + return $this->getAttribute(DOMIT_RSS_ATTR_PATH); + } //getPath + + /** + * Returns the register procedure value of the cloud + * @return string The register procedure value of the cloud + */ + function getRegisterProcedure() { + return $this->getAttribute(DOMIT_RSS_ATTR_REGISTERPROCEDURE); + } //getRegisterProcedure + + /** + * Returns the protocol used by the cloud + * @return string The protocol used by the cloud + */ + function getProtocol() { + return $this->getAttribute(DOMIT_RSS_ATTR_PROTOCOL); + } //getProtocol +} //xml_domit_rss_cloud + +/** +* Represents an RSS enclosure +* +* @package domit-rss +* @subpackage domit-rss-main +* @author John Heinstein +*/ +class xml_domit_rss_enclosure extends xml_domit_rss_elementindexer { + /** + * Constructor + * @param Object A DOM node containing enclosure data + */ + function xml_domit_rss_enclosure(&$enclosure) { + $this->node =& $enclosure; + $this->_init(); + } //xml_domit_rss_enclosure + + /** + * Returns the url of the enclosure + * @return string The url of the enclosure + */ + function getUrl() { + return $this->getAttribute(DOMIT_RSS_ATTR_URL); + } //getUrl + + /** + * Returns the length of the enclosure + * @return string The length of the enclosure + */ + function getLength() { + return $this->getAttribute(DOMIT_RSS_ATTR_LENGTH); + } //getLength + + /** + * Returns the type of the enclosure + * @return string The type of the enclosure + */ + function getType() { + return $this->getAttribute(DOMIT_RSS_ATTR_TYPE); + } //getType +} //xml_domit_rss_enclosure + +/** +* Represents an RSS guid +* +* @package domit-rss +* @subpackage domit-rss-main +* @author John Heinstein +*/ +class xml_domit_rss_guid extends xml_domit_rss_elementindexer { + /** + * Constructor + * @param Object A DOM node containing guid data + */ + function xml_domit_rss_guid(&$guid) { + $this->node =& $guid; + $this->_init(); + } //xml_domit_rss_guid + + /** + * Returns the guid text + * @return string The guid text + */ + function getGuid() { + return $this->node->getText(); + } //getGuid + + /** + * Determines whether the guid is a permalink + * @return boolean True if the guid is a permalink (default true) + */ + function isPermaLink() { + if (!$this->node->hasAttribute(DOMIT_RSS_ATTR_ISPERMALINK)) { + return true; + } + else { + return (strtolower($this->node->getAttribute(DOMIT_RSS_ATTR_ISPERMALINK)) == "true"); + } + } //isPermaLink +} //xml_domit_rss_guid + +/** +* Represents an RSS source +* +* @package domit-rss +* @subpackage domit-rss-main +* @author John Heinstein +*/ +class xml_domit_rss_source extends xml_domit_rss_elementindexer { + /** + * Constructor + * @param Object A DOM node containing source data + */ + function xml_domit_rss_source(&$source) { + $this->node =& $source; + $this->_init(); + } //xml_domit_rss_source + + /** + * Returns the source text + * @return string The source text + */ + function getSource() { + return $this->node->getText(); + } //getSource + + /** + * Returns the url of the source + * @return string The url of the source + */ + function getUrl() { + return $this->getAttribute(DOMIT_RSS_ATTR_URL); + } //getUrl +} //xml_domit_rss_source + +/** +* Represents an RSS skipDays element +* +* @package domit-rss +* @subpackage domit-rss-main +* @author John Heinstein +*/ +class xml_domit_rss_skipdays extends xml_domit_rss_elementindexer { + /** + * Constructor + * @param Object A DOM node containing source data + */ + function xml_domit_rss_skipdays(&$skipdays) { + $this->node =& $skipdays; + $this->_init(); + } //xml_domit_rss_skipdays + + /** + * Returns the number of skip day items + * @return int The number of skip day items + */ + function getSkipDayCount() { + return $this->node->childCount; + } //getSkipDayCount + + /** + * Returns the day of the specified index (Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday) + * @param int The index of the requested day + * @return string The day of the specified index (Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday) + */ + function getSkipDay($index) { + return $this->node->childNodes[$index]->getText(); + } //getSkipDay +} //xml_domit_rss_skipdays + +/** +* Represents an RSS skipHours element +* +* @package domit-rss +* @subpackage domit-rss-main +* @author John Heinstein +*/ +class xml_domit_rss_skiphours extends xml_domit_rss_elementindexer { + /** + * Constructor + * @param Object A DOM node containing source data + */ + function xml_domit_rss_skiphours(&$skiphours) { + $this->node =& $skiphours; + $this->_init(); + } //xml_domit_rss_skiphours + + /** + * Returns the number of skip hour items + * @return int The number of skip hour items + */ + function getSkipHourCount() { + return $this->node->childCount; + } //getSkipHourCount + + /** + * Returns the hour of the specified index (0-23) + * @param int The index of the requested hour + * @return string The hour of the specified index (0-23) + */ + function getSkipHour($index) { + return $this->node->childNodes[$index]->getText(); + } //getSkipHour +} //xml_domit_rss_skiphours + +?> \ No newline at end of file diff --git a/oss/joomla/trunk/libraries/domit/xml_domit_rss_lite.php b/oss/joomla/trunk/libraries/domit/xml_domit_rss_lite.php new file mode 100644 index 00000000..0dacfa3a --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/xml_domit_rss_lite.php @@ -0,0 +1,244 @@ + +* @link http://www.engageinteractive.com/domitrss/ DOMIT! RSS Home Page +* DOMIT! RSS is Free Software +**/ + +if (!defined('DOMIT_RSS_INCLUDE_PATH')) { + define('DOMIT_RSS_INCLUDE_PATH', (dirname(__FILE__) . DIRECTORY_SEPARATOR)); +} + +/** current version of DOMIT! RSS Lite */ +define ('DOMIT_RSS_LITE_VERSION', '0.51'); + +require_once(DOMIT_RSS_INCLUDE_PATH . 'xml_domit_rss_shared.php'); + +/** +* The base DOMIT! RSS Lite document class +* +* @package domit-rss +* @subpackage domit-rss-lite +* @author John Heinstein +*/ +class xml_domit_rss_document_lite extends xml_domit_rss_base_document { + /** + * Constructor + * @param string Path to the rss file + * @param string Directory for cache files + * @param int Expiration time for a cache file + */ + function xml_domit_rss_document_lite($url = '', $cacheDir = './', $cacheTime = 3600) { + $this->parser = 'DOMIT_RSS_LITE'; + $this->xml_domit_rss_base_document($url, $cacheDir, $cacheTime); + } //xml_domit_rss_document_lite + + /** + * Performs initialization of the RSS document + */ + function _init() { + $total = $this->node->documentElement->childCount; + $itemCounter = 0; + $channelCounter = 0; + + for ($i = 0; $i < $total; $i++) { + $currNode =& $this->node->documentElement->childNodes[$i]; + $tagName = strtolower($currNode->nodeName); + + switch ($tagName) { + case DOMIT_RSS_ELEMENT_ITEM: + $this->domit_rss_items[$itemCounter] = new xml_domit_rss_item_lite($currNode); + $itemCounter++; + break; + case DOMIT_RSS_ELEMENT_CHANNEL: + $this->domit_rss_channels[$channelCounter] = new xml_domit_rss_channel_lite($currNode); + $channelCounter++; + break; + case DOMIT_RSS_ELEMENT_TITLE: + case DOMIT_RSS_ELEMENT_LINK: + case DOMIT_RSS_ELEMENT_DESCRIPTION: + $this->DOMIT_RSS_indexer[$tagName] = new xml_domit_rss_simpleelement($currNode); + break; + } + } + + if ($itemCounter != 0) { + $this->DOMIT_RSS_indexer[DOMIT_RSS_ARRAY_ITEMS] =& $this->domit_rss_items; + } + + if ($channelCounter != 0) { + $this->DOMIT_RSS_indexer[DOMIT_RSS_ARRAY_CHANNELS] =& $this->domit_rss_channels; + } + + $this->handleChannelElementsEmbedded(); + } //_init + + /** + * Returns the current version of DOMIT! RSS + * @return Object The current version of DOMIT! RSS + */ + function getVersion() { + return DOMIT_RSS_LITE_VERSION; + } //getVersion +} //xml_domit_rss_document_lite + +/** +* Represents an RSS channel +* +* @package domit-rss +* @subpackage domit-rss-lite +* @author John Heinstein +*/ +class xml_domit_rss_channel_lite extends xml_domit_rss_elementindexer { + /** @var array A list of references to channel items */ + var $domit_rss_items = array(); + + /** + * Constructor + * @param Object A DOM node containing channel data + */ + function xml_domit_rss_channel_lite(&$channel) { + $this->node =& $channel; + $this->_init(); + } //xml_domit_rss_channel_lite + + /** + * Performs initialization of the RSS channel element + */ + function _init() { + $total = $this->node->childCount; + $itemCounter = 0; + + for ($i = 0; $i < $total; $i++) { + $currNode =& $this->node->childNodes[$i]; + $tagName = strtolower($currNode->nodeName); + + switch ($tagName) { + case DOMIT_RSS_ELEMENT_ITEM: + $this->domit_rss_items[$itemCounter] = new xml_domit_rss_item_lite($currNode); + $itemCounter++; + break; + case DOMIT_RSS_ELEMENT_TITLE: + case DOMIT_RSS_ELEMENT_LINK: + case DOMIT_RSS_ELEMENT_DESCRIPTION: + $this->DOMIT_RSS_indexer[$tagName] = new xml_domit_rss_simpleelement($currNode); + break; + } + } + + if ($itemCounter != 0) { + $this->DOMIT_RSS_indexer[DOMIT_RSS_ARRAY_ITEMS] =& $this->domit_rss_items; + } + } //_init + + /** + * Returns the title of the channel + * @return string The title of the channel, or an empty string + */ + function getTitle() { + return $this->getElementText(DOMIT_RSS_ELEMENT_TITLE); + } //getTitle + + /** + * Returns the url of the channel + * @return string The url of the channel, or an empty string + */ + function getLink() { + return $this->getElementText(DOMIT_RSS_ELEMENT_LINK); + } //getLink + + /** + * Returns a description of the channel + * @return string A description of the channel, or an empty string + */ + function getDescription() { + return $this->getElementText(DOMIT_RSS_ELEMENT_DESCRIPTION); + } //getDescription + + /** + * Returns the number of items in the channel + * @return int The number of items in the channel + */ + function getItemCount() { + return count($this->domit_rss_items); + } //getItemCount + + /** + * Returns a reference to the item at the specified index + * @param int The index of the requested item + * @return Object A reference to the item at the specified index + */ + function &getItem($index) { + return $this->domit_rss_items[$index]; + } //getItem +} //xml_domit_rss_channel_lite + +/** +* Represents an RSS item +* +* @package domit-rss +* @subpackage domit-rss-lite +* @author John Heinstein +*/ +class xml_domit_rss_item_lite extends xml_domit_rss_elementindexer { + /** + * Constructor + * @param Object A DOM node containing item data + */ + function xml_domit_rss_item_lite(&$item) { + $this->node =& $item; + $this->_init(); + } //xml_domit_rss_item_lite + + /** + * Performs initialization of the item element + */ + function _init(){ + $total = $this->node->childCount; + + for($i = 0; $i < $total; $i++) { + $currNode =& $this->node->childNodes[$i]; + $tagName = strtolower($currNode->nodeName); + + switch ($tagName) { + case DOMIT_RSS_ELEMENT_TITLE: + case DOMIT_RSS_ELEMENT_LINK: + case DOMIT_RSS_ELEMENT_DESCRIPTION: + $this->DOMIT_RSS_indexer[$tagName] = new xml_domit_rss_simpleelement($currNode); + break; + } + } + } //init + + /** + * Returns the title of the item + * @return string The title of the item, or an empty string + */ + function getTitle() { + return $this->getElementText(DOMIT_RSS_ELEMENT_TITLE); + } //getTitle + + /** + * Returns the url of the item + * @return string The url of the item, or an empty string + */ + function getLink() { + return $this->getElementText(DOMIT_RSS_ELEMENT_LINK); + } //getLink + + /** + * Returns a description of the item + * @return string A description of the item, or an empty string + */ + function getDescription() { + return $this->getElementText(DOMIT_RSS_ELEMENT_DESCRIPTION); + } //getDescription +} //xml_domit_rss_item_lite + +?> \ No newline at end of file diff --git a/oss/joomla/trunk/libraries/domit/xml_domit_rss_shared.php b/oss/joomla/trunk/libraries/domit/xml_domit_rss_shared.php new file mode 100644 index 00000000..1962d79c --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/xml_domit_rss_shared.php @@ -0,0 +1,1025 @@ + +* @link http://www.engageinteractive.com/domitrss/ DOMIT! RSS Home Page +* DOMIT! RSS is Free Software +**/ + +/** channel constant */ +define('DOMIT_RSS_ELEMENT_CHANNEL', 'channel'); +/** item constant */ +define('DOMIT_RSS_ELEMENT_ITEM', 'item'); +/** title constant */ +define('DOMIT_RSS_ELEMENT_TITLE', 'title'); +/** link constant */ +define('DOMIT_RSS_ELEMENT_LINK', 'link'); +/** description constant */ +define('DOMIT_RSS_ELEMENT_DESCRIPTION', 'description'); + +/** version constant */ +define('DOMIT_RSS_ATTR_VERSION', 'version'); + +/** name of array containing list of existing RSS items */ +define('DOMIT_RSS_ARRAY_ITEMS', 'item'); //formerly named 'domit_rss_items' +/** name of array containing list of existing RSS channels */ +define('DOMIT_RSS_ARRAY_CHANNELS', 'channel'); //formerly named 'domit_rss_channels' +/** name of array containing list of existing RSS categories */ +define('DOMIT_RSS_ARRAY_CATEGORIES', 'category'); //formerly named 'domit_rss_categories' + +/** DOMIT RSS error, attempt to call an abstract method */ +define('DOMIT_RSS_ABSTRACT_METHOD_INVOCATION_ERR', 101); +/** DOMIT RSS error, specified element not present */ +define('DOMIT_RSS_ELEMENT_NOT_FOUND_ERR', 102); +/** DOMIT RSS error, specified attribute not present */ +define('DOMIT_RSS_ATTR_NOT_FOUND_ERR', 103); +/** DOMIT RSS error, parsing failed */ +define('DOMIT_RSS_PARSING_ERR', 104); + +//DOMIT! RSS Error Modes +/** continue on error */ +define('DOMIT_RSS_ONERROR_CONTINUE', 1); +/** die on error */ +define('DOMIT_RSS_ONERROR_DIE', 2); +/** die on error */ +define('DOMIT_RSS_ONERROR_RETURN', 3); + +/** +* The base class of all DOMIT! RSS objects +* +* @package domit-rss +* @author John Heinstein +*/ +class xml_domit_rss_base { + /** @var Object The underlying DOMIT! node of the element */ + var $node = null; + /** @var array A list of valid RSS defined child elements */ + var $rssDefinedElements = array(); + + /** + * Retrieves the underlying DOMIT node + * @return Object The underlying DOMIT node + */ + function getNode() { + return $this->node; + } //getNode + + /** + * Retrieves the text of the named attribute, checking first if the attribute exists + * @param string The attribute name + * @return string The attribute value, or an empty string + */ + function getAttribute($attr) { + if ($this->node->hasAttribute($attr)) { + return $this->node->getAttribute($attr); + } + + return ""; + } //getAttribute + + /** + * Checks whether the named attribute exists + * @param string The attribute name + * @return boolean True if the attribute exists + */ + function hasAttribute($attr) { + return (($this->node->nodeType == DOMIT_ELEMENT_NODE) && $this->node->hasAttribute($attr)); + } //hasAttribute + + /** + * Tests whether the named element is predefined by the RSS spec + * @param string The element name + * @return boolean True if the element is predefined by the RSS spec + */ + function isRSSDefined($elementName) { + $isDefined = false; + + foreach ($this->rssDefinedElements as $key => $value) { + if ($elementName == $value) { + $isDefined = true; + break; + } + } + + return $isDefined; + } //isRSSDefined + + /** + * Tests whether the named element has a single child text node + * @param string The element name + * @return boolean True if the named element has a single child text node + */ + function isSimpleRSSElement($elementName) { + $elementName = strtolower($elementName); + + if (isset($this->DOMIT_RSS_indexer[$elementName])) { + return (get_class($this->getElement($elementName)) == 'xml_domit_rss_simpleelement'); + } + else { + return false; + } + } //isSimpleRSSElement + + /** + * Generates a string representation of the node and its children + * @param boolean True if HTML readable output is desired + * @param boolean True if illegal xml characters in text nodes and attributes should be converted to entities + * @return string The string representation + */ + function get($htmlSafe = false, $subEntities = false) { + return $this->node->toString($htmlSafe, $subEntities); + } //toString + + /** + * Generates a normalized (formatted for readability) representation of the node and its children + * @param boolean True if HTML readable output is desired + * @param boolean True if illegal xml characters in text nodes and attributes should be converted to entities + * @return string The formatted string representation + */ + function toNormalizedString($htmlSafe = false, $subEntities = false) { + return $this->node->toNormalizedString($htmlSafe, $subEntities); + } //toNormalizedString +} //xml_domit_rss_base + + +/** +* Represents a collection of custom RSS elements, e.g. a set of dc:creator entries +* +* @package domit-rss +* @subpackage domit-rss-main +* @author John Heinstein +*/ +class xml_domit_rss_collection extends xml_domit_rss_elementindexer { + /** @var array An array holding the collection of custom elements */ + var $elements = array(); + /** @var int The number of custom elements in the collection */ + var $elementCount = 0; + + /** + * Adds a custom RSS element (DOM node) to the collection + * @param Object A DOM node representing a custom RSS element + */ + function addElement(&$node) { + $this->elements[] =& $node; + $this->elementCount++; + } //addElement + + /** + * Retrieves the element at the specified index + * @param int The index of the requested custom RSS element + * @return Object The DOMIT node representing the requested element + */ + function &getElementAt($index) { + return $this->elements[$index]; + } //getElementAt + + /** + * Retrieves the element at the specified index (alias for getElementAt) + * @param int The index of the requested custom RSS element + * @return Object The DOMIT node representing the requested element + */ + function &getElement($index) { + return $this->getElementAt($index); + } //getElement + + /** + * Returns the number of elements in the collection + * @return int The number of members in the collection + */ + function getElementCount() { + return $this->elementCount; + } //getElementCount + + /** + * Gets a text representation of the collection (applies the toString method to each member and concatenates) + * @return string The element text + */ + function getElementText() { + $total = $this->getElementCount(); + $result = ''; + + for ($i = 0; $i < $total; $i++) { + $result .= $currElement->toString(); + } + + return $result; + } //getElementText +} //xml_domit_rss_collection + + +/** +* Provides indexing functionality to RSS classes +* +* @package domit-rss +* @author John Heinstein +*/ +class xml_domit_rss_elementindexer extends xml_domit_rss_base { + /** @var Array Name based index to RSS elements */ + var $DOMIT_RSS_indexer = array(); + /** @var Array Numerical index to RSS elements; for optimization purposes, only set if requested by getElementAt */ + var $DOMIT_RSS_numericalIndexer; + + /** + * Performs generic initialization of elements + */ + function _init(){ + $total = $this->node->childCount; + + for($i = 0; $i < $total; $i++) { + $currNode =& $this->node->childNodes[$i]; + //$this->DOMIT_RSS_indexer[$currNode->nodeName] =& $currNode; + $this->addIndexedElement($currNode); + } + } //_init + + /** + * Adds a custom element (one not defined by the RSS specs, e..g., dc:creator) to the indexer + * @param Object A DOMIT! node representing the custom element + */ + function addIndexedElement(&$node) { + $tagName = strtolower($node->nodeName); + + if (isset($this->DOMIT_RSS_indexer[$tagName])) { + if (strtolower(get_class($this->DOMIT_RSS_indexer[$tagName])) == 'domit_element') { + $collection = new xml_domit_rss_collection(); + $collection->addElement($this->DOMIT_RSS_indexer[$tagName]); + $collection->addElement($node); + $this->DOMIT_RSS_indexer[$tagName] =& $collection; + } + else { + //Don't think I need this case??? + //$this->DOMIT_RSS_indexer[$tagName]->addElement($node); + } + } + else { + $this->DOMIT_RSS_indexer[$tagName] =& $node; + } + } //addIndexedElement + + /** + * Indicates whether the requested element is actually a collection of elements of the same type + * @param string The name of the requested element + * @return boolean True if a collection of elements exists + */ + function isCollection($elementName) { + $elementName = strtolower($elementName); + + if (isset($this->DOMIT_RSS_indexer[$elementName])) { + return (get_class($this->DOMIT_RSS_indexer[$elementName]) == 'xml_domit_rss_collection'); + } + else { + return false; + } + } //isCollection + + /** + * Indicates whether the requested element is a DOMIT! node + * @param string The name of the requested element + * @return boolean True if the requested element is a DOMIT! node + */ + function isNode($elementName) { + $elementName = strtolower($elementName); + + if (isset($this->DOMIT_RSS_indexer[$elementName])) { + return (strtolower(get_class($this->DOMIT_RSS_indexer[$elementName])) == 'domit_element'); + } + else { + return false; + } + } //isNode + + /** + * Indicates whether the requested element is a DOMIT! node (alias for isNode) + * @param string The name of the requested element + * @return boolean True if the requested element is a DOMIT! node + */ + function isCustomRSSElement($elementName) { + return isNode($elementName); + } //isCustomRSSElement + + /** + * Gets a named list of existing elements as a child of the current element + * @return array A named list of existing elements + */ + function getElementList() { + return array_keys($this->DOMIT_RSS_indexer); + } //getElementList + + /** + * Indicates whether a particular element exists + * @param string The name of the requested element + * @return boolean True if an element with the specified name exists + */ + function hasElement($elementName) { + return isset($this->DOMIT_RSS_indexer[strtolower($elementName)]); + } //hasElement + + /** + * Gets a reference to an element with the specified name + * @param string The name of the requested element + * @return mixed A reference to an element with the specified name, or the text of the element if it is a text node + */ + function &getElement($elementName) { + $elementName = strtolower($elementName); + + if (isset($this->DOMIT_RSS_indexer[$elementName])) { + return $this->DOMIT_RSS_indexer[$elementName]; + } + else { + xml_domit_rss_exception::raiseException(DOMIT_RSS_ELEMENT_NOT_FOUND_ERR, + 'Element ' . $elementName . ' not present.'); + } + } //getElement + + /** + * Gets a reference to an element at the specified index + * @param int The index of the requested element + * @return mixed A reference to an element at the specified index, or the text of the element if it is a text node + */ + function &getElementAt($index) { + $this->indexNumerically(); + + if (isset($this->DOMIT_RSS_numericalIndexer[$index])) { + return $this->DOMIT_RSS_numericalIndexer[$index]; + } + else { + xml_domit_rss_exception::raiseException(DOMIT_RSS_ELEMENT_NOT_FOUND_ERR, + 'Element ' . $index . ' not present.'); + } + } //getElementAt + + /** + * Populates an integer-based index for elements if one isn't already present. + */ + function indexNumerically() { + if (!isset($this->DOMIT_RSS_numericalIndexer)) { + $counter = 0; + + foreach ($this->DOMIT_RSS_indexer as $key => $value) { + $this->DOMIT_RSS_numericalIndexer[$counter] =& $this->DOMIT_RSS_indexer[$key]; + $counter++; + } + } + } //indexNumerically + + /** + * Gets the text of the specified element + * @param string The name of the requested element + * @return string The element text, or an empty string + */ + function getElementText($elementName) { + $elementName = strtolower($elementName); + return $this->_getElementText($elementName, $this->DOMIT_RSS_indexer); + } //getElementText + + /** + * Gets the text at the specified index + * @param int The index of the requested element + * @return string The element text, or an empty string + */ + function getElementTextAt($index) { + $this->indexNumerically(); + + return $this->_getElementText($index, $this->DOMIT_RSS_numericalIndexer); + } //getElementTextAt + + /** + * Gets the text at the specified index + * @param mixed The index or name of the requested element + * @param array The indexing array from which to extract data + * @return string The element text, or an empty string + */ + function _getElementText($index, &$myArray) { + if (isset($myArray[$index])) { + $element =& $myArray[$index]; + $result = ''; + + if (is_array($element)) { + //do nothing; data for domit_rss_channels, domit_rss_items, + //and domit_rss_categories should be extracted with their own methods + } + else { + switch (strtolower(get_class($element))) { + case 'xml_domit_rss_simpleelement': + $result = $element->getElementText(); + break; + + case 'xml_domit_rss_collection': + $result = $element->getElementText(); + break; + + case 'domit_element': + $total = $element->childCount; + + for ($i = 0; $i < $total; $i++) { + $currNode =& $element->childNodes[$i]; + + if ($currNode->nodeType == DOMIT_CDATA_SECTION_NODE) { + $result .= $currNode->nodeValue; + } + else { + $result .= $currNode->toString(); + } + } + break; + } + } + + return $result; + } + + return ''; + } //_getElementText +} //xml_domit_rss_elementindexer + + +/** +* A base class for DOMIT! RSS and DOMIT! RSS Lite documents +* +* @package domit-rss +* @author John Heinstein +*/ +class xml_domit_rss_base_document extends xml_domit_rss_elementindexer { + /** @var array An array of item elements (only present in some RSS formats) */ + var $domit_rss_items = array(); + /** @var array An array of existing channel elements */ + var $domit_rss_channels = array(); + /** @var array An array of existing category elements */ + var $domit_rss_categories = array(); + /** @var boolean True if caching is enabled */ + var $cacheEnabled = true; + /** @var Object A reference to the file caching object */ + var $cache; + /** @var boolean True if PEAR:Cache_Lite is to be used instead of php_text_cache */ + var $useCacheLite = false; + /** @var boolean True if php_http_client_generic is to be used instead of PHP get_file_contents */ + var $doUseHTTPClient = false; + /** @var string The name of the current parser - either 'DOMIT_RSS' or 'DOMIT_RSS_LITE' */ + var $parser; + /** @var object A reference to a http connection or proxy, if one is required */ + var $httpConnection = null; + /** @var int The timeout value for an http connection */ + var $rssTimeout = 0; + + /** + * Constructor + * @param string Path to the rss file + * @param string Directory in which cache files are to be stored + * @param int Expiration time (in seconds) for the cache file + * @return mixed Null if an url was not provided, true if an url was provided and parsing was successful, false otherwise + */ + function xml_domit_rss_base_document ($url = '', $cacheDir = './', $cacheTime = 3600) { + $success = null; + $this->createDocument(); + + if ($url != '') { //if rss data is from filesystem + if (substr($url, 0, 4) != "http") { + $rssText = $this->getTextFromFile($url); + $this->parseRSS($rssText); + } + else { + $this->createDefaultCache($cacheDir, $cacheTime); + $success = $this->loadRSS($url, $cacheDir, $cacheTime); + } + } + + return $success; + } //xml_domit_rss_base_document + + /** + * Specifies the default timeout value for connecting to a host + * @param int The number of seconds to timeout when attempting to connect to a server + */ + function setRSSTimeout($rssTimeout) { + $this->rssTimeout = $rssTimeout; + + if (!$this->useCacheLite && !($this->cache == null)) { + $this->cache->setTimeout($rssTimeout); + } + } //setRSSTimeout + + /** + * Specifies the parameters of the http conection used to obtain the xml data + * @param string The ip address or domain name of the connection + * @param string The path of the connection + * @param int The port that the connection is listening on + * @param int The timeout value for the connection + * @param string The user name, if authentication is required + * @param string The password, if authentication is required + */ + function setConnection($host, $path = '/', $port = 80, $timeout = 0, $user = null, $password = null) { + require_once(DOMIT_RSS_INCLUDE_PATH . 'php_http_client_generic.php'); + + $this->httpConnection = new php_http_client_generic($host, $path, $port, $timeout, $user, $password); + } //setConnection + + /** + * Specifies basic authentication for an http connection + * @param string The user name + * @param string The password + */ + function setAuthorization($user, $password) { + $this->httpConnection->setAuthorization($user, $password); + } //setAuthorization + + /** + * Specifies that a proxy is to be used to obtain the xml data + * @param string The ip address or domain name of the proxy + * @param string The path to the proxy + * @param int The port that the proxy is listening on + * @param int The timeout value for the connection + * @param string The user name, if authentication is required + * @param string The password, if authentication is required + */ + function setProxyConnection($host, $path = '/', $port = 80, $timeout = 0, $user = null, $password = null) { + require_once(DOMIT_RSS_INCLUDE_PATH . 'php_http_proxy.php'); + + $this->httpConnection = new php_http_proxy($host, $path, $port, $timeout, $user, $password); + } //setProxyConnection + + /** + * Specifies a user name and password for the proxy + * @param string The user name + * @param string The password + */ + function setProxyAuthorization($user, $password) { + $this->httpConnection->setProxyAuthorization($user, $password); + } //setProxyAuthorization + + /** + * Specifies whether an HTTP client should be used to establish a connection + * @param boolean True if an HTTP client is to be used to establish the connection + */ + function useHTTPClient($truthVal) { + $this->doUseHTTPClient = $truthVal; + } //useHTTPClient + + /** + * Returns the name of the parser + *@return string Either 'DOMIT_RSS' or 'DOMIT_RSS_LITE' + */ + function parsedBy() { + return $this->parser; + } //parsedBy + + /** + * Creates an empty DOMIT! document to contain the RSS nodes + */ + function createDocument() { + require_once(DOMIT_RSS_INCLUDE_PATH . 'xml_domit_include.php'); + $this->node = new DOMIT_Document(); + $this->node->resolveErrors(true); + } //createDocument + + /** + * Substitutes PEAR::Cache_Lite for the default php_text_cache + * @param boolean True if Cache Lite is to be used + * @param string Absolute or relative path to the Cache Lite library + * @param string Directory for cache files + * @param int Expiration time for a cache file + */ + function useCacheLite($doUseCacheLite, $pathToLibrary = './Lite.php', $cacheDir = './', $cacheTime = 3600) { + $this->useCacheLite = $doUseCacheLite; + + if ($doUseCacheLite) { + if (!file_exists($pathToLibrary)) { + $this->useCacheLite(false); + } + else { + require_once($pathToLibrary); + + $cacheOptions = array('cacheDir' => $cacheDir, 'lifeTime' => $cacheTime); + $this->cache = new Cache_Lite($cacheOptions); + } + } + else { + $this->createDefaultCache($cacheDir, $cacheTime); + } + } //useCacheLite + + /** + * Instantiates a default cache (php_text_cache) + * @param string Directory for cache files + * @param int Expiration time for a cache file + */ + function createDefaultCache($cacheDir = './', $cacheTime = 3600) { + require_once(DOMIT_RSS_INCLUDE_PATH . 'php_text_cache.php'); + $this->cache = new php_text_cache($cacheDir, $cacheTime, $this->rssTimeout); + } //initDefaultCache + + /** + * Disables caching mechanism + */ + function disableCache() { + $this->cacheEnabled = false; + } //initDefaultCache + + /** + * Loads and parses the RSS at the specified url + * @param string The url of the RSS feed + * @return boolean True if parsing is successful + */ + function loadRSS($url) { + if (substr($url, 0, 4) != "http") { + $rssText = $this->getTextFromFile($url); + return $this->parseRSS($rssText); + } + else { + if ($this->cacheEnabled && !isset($this->cache)) { + $this->createDefaultCache(); + $this->cache->httpConnection =& $this->httpConnection; + } + + $success = $this->loadRSSData($url); + + if ($success) { + $this->_init(); + } + + return $success; + } + } //loadRSS + + /** + * Parses the RSS text provided + * @param string The RSS text + * @return boolean True if parsing is successful + */ + function parseRSS($rssText) { + if ($this->cacheEnabled && !isset($this->cache)) $this->createDefaultCache(); + $success = $this->parseRSSData($rssText); + + if ($success) { + $this->_init(); + } + + return $success; + } //parseRSS + + /** + * Retrieves the RSS data from the url/cache file and parses + * @param string The url for the RSS data + * @return boolean True if parsing is successful + */ + function loadRSSData($url) { + $rssText = $this->getDataFromCache($url); + return $this->parseRSSData($rssText); + } //loadRSSData + + /** + * Retrieves the RSS data from the url/cache file + * @param string The url for the RSS data + * @return string The RSS data + */ + function getDataFromCache($url) { + if ($this->cacheEnabled) { + if ($this->useCacheLite) { + if ($rssText = $this->cache->get($url)) { + return $rssText; + } + else { + $rssText = $this->getTextFromFile($url); + if ($rssText != '') $this->cache->save($rssText, $url); + return $rssText; + } + } + else { + $this->cache->useHTTPClient($this->doUseHTTPClient); + return $this->cache->getData($url); + } + } + else { + return $this->getTextFromFile($url); + } + } //getDataFromCache + + /** + * Parses the RSS data provided + * @param string The the RSS data + * @return boolean True if parsing is successful + */ + function parseRSSData($rssText) { + if ($rssText != '') { + return $this->fromString($rssText); + } + else { + return false; + } + } //parseRSSData + + /** + * Reads in RSS text from a file and parses it + * @param string The file path + * @return boolean True if parsing is successful + */ + function &fromFile($filename) { + $success = $this->node->loadXML($filename, false); + return $success; + } //fromFile + + /** + * Reads in RSS text from a string and parses it + * @param string The RSS text + * @return boolean True if parsing is successful + */ + function &fromString($rssText) { + $success = $this->node->parseXML($rssText, false); + return $success; + } //fromString + + /** + * Establishes a connection, given an url + * @param string The url of the data + */ + function establishConnection($url) { + require_once(DOMIT_RSS_INCLUDE_PATH . 'php_http_client_generic.php'); + + $host = php_http_connection::formatHost($url); + $host = substr($host, 0, strpos($host, '/')); + + $this->setConnection($host, '/', 80, $this->rssTimeout); + } //establishConnection + + /** + * Get text from an url or file + * @param string The url or file path + * @return string The text contained in the url or file, or an empty string + */ + function getTextFromFile($filename) { + $fileContents = ''; + + if ($this->doUseHTTPClient) { + $this->establishConnection($filename); + $response =& $this->httpConnection->get($filename); + + if ($response != null) { + $fileContents = $response->getResponse(); + } + } + else { + if (function_exists('file_get_contents')) { + $fileContents = @file_get_contents($filename); + } + else { + require_once(DOMIT_RSS_INCLUDE_PATH . 'php_file_utilities.php'); + $fileContents =& php_file_utilities::getDataFromFile($filename, 'r'); + } + + if ($fileContents == '') { + $this->establishConnection($filename); + $response =& $this->httpConnection->get($filename); + + if ($response != null) { + $fileContents = $response->getResponse(); + } + } + } + + return $fileContents; + } //getTextFromFile + + /** + * Gets a reference to the underlying DOM document + * @return Object A reference to the underlying DOM document + */ + function &getDocument() { + return $this->node; + } //getDocument + + /** + * Gets a reference to the root DOM element + * @return Object A reference to the root DOM element + */ + function &getNode() { + return $this->node->documentElement; + } //getNode + + /** + * Forces channel elements that are external to a channel to be referenced as subelements of that channel + */ + function handleChannelElementsEmbedded() { + if (count($this->domit_rss_items) > 0) { + foreach ($this->domit_rss_channels as $key => $value) { + $this->domit_rss_channels[$key]->domit_rss_items =& $this->domit_rss_items; + + if (count($this->DOMIT_RSS_indexer) > 0) { + foreach ($this->DOMIT_RSS_indexer as $ikey => $ivalue) { + if ($ikey != DOMIT_RSS_ARRAY_CHANNELS) { + $this->domit_rss_channels[$key]->DOMIT_RSS_indexer[$ikey] =& $this->DOMIT_RSS_indexer[$ikey]; + unset($this->DOMIT_RSS_indexer[$ikey]); + } + } + } + } + } + } //handleChannelElementsEmbedded + + /** + * Returns the version of RSS used to format the data + * @return string The version of RSS used to format the data + */ + function getRSSVersion() { + $version = $this->node->documentElement->getAttribute(DOMIT_RSS_ATTR_VERSION); + + if ($version == '') { + $xmlns = $this->node->documentElement->getAttribute('xmlns'); + $total = strlen($xmlns); + + if (substr($xmlns, $total) == '/') { + $total--; + } + + for ($i = ($total - 1); $i > -1; $i--) { + $currentChar = substr($xmlns, $i); + + if ($currentChar == '/') { + break; + } + else { + $version = $currentChar . $version; + } + } + + } + + return $version; + } //getRSSVersion + + /** + * Returns the number of channels in the document + * @return int The number of channels in the document + */ + function getChannelCount() { + return count($this->domit_rss_channels); + } //getChannelCount() + + /** + * Returns a reference to the channel located at the specified index + * @return Object A reference to the channel located at the specified index + */ + function &getChannel($index) { + return $this->domit_rss_channels[$index]; + } //getChannel +} //xml_domit_rss_base_document + +/** +* Represents a simple RSS element, without attributes and only a single child text node +* +* @package domit-rss +* @subpackage domit-rss-main +* @author John Heinstein +*/ +class xml_domit_rss_simpleelement extends xml_domit_rss_elementindexer { + /** + * Constructor + * @param Object A DOM node containing element data + */ + function xml_domit_rss_simpleelement(&$element) { + $this->node =& $element; + } //xml_domit_rss_simpleelement + + /** + * Gets the text of the element + * @return string The element text + */ + function getElementText() { + $element =& $this->node; + $result = ''; + $total = $element->childCount; + + for ($i = 0; $i < $total; $i++) { + $currNode =& $element->childNodes[$i]; + + if ($currNode->nodeType == DOMIT_CDATA_SECTION_NODE) { + $result .= $currNode->nodeValue; + } + else { + $result .= $currNode->toString(); + } + } + + return $result; + } //getElementText +} //xml_domit_rss_simpleelement + +/** +* @global object Reference to custom error handler for DOMIT RSS Exception class +*/ +$GLOBALS['DOMIT_RSS_Exception_errorHandler'] = null; +/** +* @global int Error mode; specifies whether to die on error or simply return +*/ +//$GLOBALS['DOMIT_RSS_Exception_mode'] = DOMIT_RSS_ONERROR_RETURN; +// fixes bug identified here: sarahk.pcpropertymanager.com/blog/using-domit-rss/225/ +$GLOBALS['DOMIT_RSS_Exception_mode'] = 1; +/** +* @global string Log file for errors +*/ +$GLOBALS['DOMIT_RSS_Exception_log'] = null; + + +/** +* A DOMIT! RSS exception handling class +* +* @package domit-rss +* @author John Heinstein +*/ +class xml_domit_rss_exception { + /** + * Raises the specified exception + * @param int The error number + * @param string A string explanation of the error + */ + function raiseException($errorNum, $errorString) { + if ($GLOBALS['DOMIT_RSS_Exception_errorHandler'] != null) { + call_user_func($GLOBALS['DOMIT_RSS_Exception_errorHandler'], $errorNum, $errorString); + } + else { + $errorMessageText = $errorNum . ' ' . $errorString; + $errorMessage = 'Error: ' . $errorMessageText; + + if ((!isset($GLOBALS['DOMIT_RSS_ERROR_FORMATTING_HTML'])) || + ($GLOBALS['DOMIT_RSS_ERROR_FORMATTING_HTML'] == true)) { + $errorMessage = "

" . $errorMessage . "

"; + } + + //log error to file + if ((isset($GLOBALS['DOMIT_RSS_Exception_log'])) && + ($GLOBALS['DOMIT_RSS_Exception_log'] != null)) { + require_once(DOMIT_RSS_INCLUDE_PATH . 'php_file_utilities.php'); + + $logItem = "\n" . date('Y-m-d H:i:s') . 'DOMIT! RSS Error ' . $errorMessageText; + php_file_utilities::putDataToFile($GLOBALS['DOMIT_RSS_Exception_log'], $logItem, 'a'); + } + + switch ($GLOBALS['DOMIT_RSS_Exception_mode']) { + case DOMIT_RSS_ONERROR_CONTINUE: + return; + break; + + case DOMIT_RSS_ONERROR_DIE: + die($errorMessage); + break; + } + } + } //raiseException + + /** + * custom handler for DOM RSS errors + * @param object A reference to the custom error handler + */ + function setErrorHandler($method) { + $GLOBALS['DOMIT_RSS_Exception_errorHandler'] =& $method; + + require_once(DOMIT_RSS_INCLUDE_PATH . 'php_http_exceptions.php'); + $GLOBALS['HTTP_Exception_errorHandler'] =& $method; + + require_once(DOMIT_RSS_INCLUDE_PATH . 'xml_domit_shared.php'); + $GLOBALS['HTTP_Exception_errorHandler'] =& $method; + } //setErrorHandler + + /** + * Set error mode + * @param int The DOMIT RSS error mode + */ + function setErrorMode($mode) { + $GLOBALS['DOMIT_RSS_Exception_mode'] = $mode; + + require_once(DOMIT_RSS_INCLUDE_PATH . 'php_http_exceptions.php'); + require_once(DOMIT_RSS_INCLUDE_PATH . 'xml_domit_shared.php'); + + if ($mode == DOMIT_RSS_ONERROR_CONTINUE) { + $GLOBALS['HTTP_Exception_mode'] = HTTP_ONERROR_CONTINUE; + $GLOBALS['DOMIT_DOMException_mode'] = DOMIT_ONERROR_CONTINUE; + } + else { + $GLOBALS['HTTP_Exception_mode'] = HTTP_ONERROR_DIE; + $GLOBALS['DOMIT_DOMException_mode'] = DOMIT_ONERROR_DIE; + } + } //setErrorMode + + /** + * Set error mode + * @param boolean True if errors should be logged + * @param string Absolute or relative path to log file + */ + function setErrorLog($doLogErrors, $logfile) { + require_once(DOMIT_RSS_INCLUDE_PATH . 'php_http_exceptions.php'); + + if ($doLogErrors) { + $GLOBALS['DOMIT_RSS_Exception_log'] = $logfile; + $GLOBALS['HTTP_Exception_log'] = $logfile; + $GLOBALS['DOMIT_Exception_log'] = $logfile; + } + else { + $GLOBALS['DOMIT_RSS_Exception_log'] = null; + $GLOBALS['HTTP_Exception_log'] = null; + $GLOBALS['DOMIT_Exception_log'] = null; + } + } //setErrorLog +} //xml_domit_rss_exception + +?> \ No newline at end of file diff --git a/oss/joomla/trunk/libraries/domit/xml_domit_shared.php b/oss/joomla/trunk/libraries/domit/xml_domit_shared.php new file mode 100644 index 00000000..7d50510c --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/xml_domit_shared.php @@ -0,0 +1,267 @@ + +* @link http://www.engageinteractive.com/domit/ DOMIT! Home Page +* DOMIT! is Free Software +**/ + +if (!defined('DOMIT_INCLUDE_PATH')) { + /* Path to DOMIT! files */ + define('DOMIT_INCLUDE_PATH', (dirname(__FILE__) . "/")); +} + +//Nodes +/** DOM Element nodeType */ +define('DOMIT_ELEMENT_NODE', 1); +/** DOM Attr nodeType */ +define('DOMIT_ATTRIBUTE_NODE', 2); +/** DOM Text nodeType */ +define('DOMIT_TEXT_NODE', 3); +/** DOM CDATA Section nodeType */ +define('DOMIT_CDATA_SECTION_NODE', 4); +/** DOM Entity Reference nodeType */ +define('DOMIT_ENTITY_REFERENCE_NODE', 5); +/** DOM Entity nodeType */ +define('DOMIT_ENTITY_NODE', 6); +/** DOM Processing Instruction nodeType */ +define('DOMIT_PROCESSING_INSTRUCTION_NODE', 7); +/** DOM Comment nodeType */ +define('DOMIT_COMMENT_NODE', 8); +/** DOM Document nodeType */ +define('DOMIT_DOCUMENT_NODE', 9); +/** DOM DocType nodeType */ +define('DOMIT_DOCUMENT_TYPE_NODE', 10); +/** DOM Document Fragment nodeType */ +define('DOMIT_DOCUMENT_FRAGMENT_NODE', 11); +/** DOM Notation nodeType */ +define('DOMIT_NOTATION_NODE', 12); + +//DOM Level 1 Exceptions +/** DOM error: array index out of bounds */ +define('DOMIT_INDEX_SIZE_ERR', 1); +/** DOM error: text doesn't fit into a DOMString */ +define('DOMIT_DOMSTRING_SIZE_ERR', 2); +/** DOM error: node can't be inserted at this location */ +define('DOMIT_HIERARCHY_REQUEST_ERR', 3); +/** DOM error: node not a child of target document */ +define('DOMIT_WRONG_DOCUMENT_ERR', 4); +/** DOM error: invalid character specified */ +define('DOMIT_INVALID_CHARACTER_ERR', 5); +/** DOM error: data can't be added to current node */ +define('DOMIT_NO_DATA_ALLOWED_ERR', 6); +/** DOM error: node is read-only */ +define('DOMIT_NO_MODIFICATION_ALLOWED_ERR', 7); +/** DOM error: node can't be found in specified context */ +define('DOMIT_NOT_FOUND_ERR', 8); +/** DOM error: operation not supported by current implementation */ +define('DOMIT_NOT_SUPPORTED_ERR', 9); +/** DOM error: attribute currently in use elsewhere */ +define('DOMIT_INUSE_ATTRIBUTE_ERR', 10); + +//DOM Level 2 Exceptions +/** DOM error: attempt made to use an object that is no longer usable */ +define('DOMIT_INVALID_STATE_ERR', 11); +/** DOM error: invalid or illegal string specified */ +define('DOMIT_SYNTAX_ERR', 12); +/** DOM error: can't modify underlying type of node */ +define('DOMIT_INVALID_MODIFICATION_ERR', 13); +/** DOM error: attempt to change node in a way incompatible with namespaces */ +define('DOMIT_NAMESPACE_ERR', 14); +/** DOM error: operation unsupported by underlying object */ +define('DOMIT_INVALID_ACCESS_ERR', 15); + +//DOMIT! Exceptions +/** DOM error: attempt to instantiate abstract class */ +define('DOMIT_ABSTRACT_CLASS_INSTANTIATION_ERR', 100); +/** DOM error: attempt to call abstract method */ +define('DOMIT_ABSTRACT_METHOD_INVOCATION_ERR', 101); +/** DOM error: can't perform this action on or with Document Fragment */ +define('DOMIT_DOCUMENT_FRAGMENT_ERR', 102); + +//DOMIT! Error Modes +/** continue on error */ +define('DOMIT_ONERROR_CONTINUE', 1); +/** die on error */ +define('DOMIT_ONERROR_DIE', 2); + + +/** +*@global Object Instance of the UIDGenerator class +*/ +$GLOBALS['uidFactory'] = new UIDGenerator(); + +require_once(DOMIT_INCLUDE_PATH . 'xml_domit_nodemaps.php'); + +/** +* Generates unique ids for each node +* +* @package domit-xmlparser +* @author John Heinstein +*/ +class UIDGenerator { + /** @var int A seed value for generating uids */ + var $seed; + /** @var int A tally of the number of uids generated */ + var $counter = 0; + + /** + * UIDGenerator constructor + */ + function UIDGenerator() { + $this->seed = 'node' . time(); + } //UIDGenerator + + /** + * Generates a unique id + * @return uid + */ + function generateUID() { + return ($this->seed . $this->counter++); + } //generateUID +} //UIDGenerator + +/** +* @global object Reference to custom error handler for DOMException class +*/ +$GLOBALS['DOMIT_DOMException_errorHandler'] = null; +/** +* @global int Error mode; specifies whether to die on error or simply return +*/ +$GLOBALS['DOMIT_DOMException_mode'] = DOMIT_ONERROR_CONTINUE; +/** +* @global string Log file for errors +*/ +$GLOBALS['DOMIT_DOMException_log'] = null; + +/** +* A DOMIT! exception handling class +* +* @package domit-xmlparser +* @author John Heinstein +*/ +class DOMIT_DOMException { + /** + * Raises the specified exception + * @param int The error number + * @param string A string explanation of the error + */ + function raiseException($errorNum, $errorString) { + if ($GLOBALS['DOMIT_DOMException_errorHandler'] != null) { + call_user_func($GLOBALS['DOMIT_DOMException_errorHandler'], $errorNum, $errorString); + } + else { + $errorMessageText = $errorNum . ' ' . $errorString; + $errorMessage = 'Error: ' . $errorMessageText; + + if ((!isset($GLOBALS['DOMIT_ERROR_FORMATTING_HTML'])) || + ($GLOBALS['DOMIT_ERROR_FORMATTING_HTML'] == true)) { + $errorMessage = "

" . $errorMessage . "

"; + } + + //log error to file + if ((isset($GLOBALS['DOMIT_DOMException_log'])) && + ($GLOBALS['DOMIT_DOMException_log'] != null)) { + require_once(DOMIT_INCLUDE_PATH . 'php_file_utilities.php'); + $logItem = "\n" . date('Y-m-d H:i:s') . 'DOMIT! Error ' . $errorMessageText; + php_file_utilities::putDataToFile($GLOBALS['DOMIT_DOMException_log'], + $logItem, 'a'); + } + + switch ($GLOBALS['DOMIT_DOMException_mode']) { + case DOMIT_ONERROR_CONTINUE: + return; + break; + + case DOMIT_ONERROR_DIE: + die($errorMessage); + break; + } + } + } //raiseException + + /** + * custom handler for DOM errors + * @param object A reference to the custom error handler + */ + function setErrorHandler($method) { + $GLOBALS['DOMIT_DOMException_errorHandler'] =& $method; + } //setErrorHandler + + /** + * Set error mode + * @param int The DOM error mode + */ + function setErrorMode($mode) { + $GLOBALS['DOMIT_DOMException_mode'] = $mode; + } //setErrorMode + + /** + * Set error mode + * @param boolean True if errors should be logged + * @param string Absolute or relative path to log file + */ + function setErrorLog($doLogErrors, $logfile) { + if ($doLogErrors) { + $GLOBALS['DOMIT_DOMException_log'] = $logfile; + } + else { + $GLOBALS['DOMIT_DOMException_log'] = null; + } + } //setErrorLog +} //DOMIT_DOMException + +/** +* A class representing the DOM Implementation node +* +* @package domit-xmlparser +* @author John Heinstein +*/ +class DOMIT_DOMImplementation { + function hasFeature($feature, $version = null) { + if (strtoupper($feature) == 'XML') { + if (($version == '1.0') || ($version == '2.0') || ($version == null)) { + return true; + } + } + + return false; + } //hasFeature + + /** + * Creates a new DOMIT_Document node and appends a documentElement with the specified info + * @param string The namespaceURI of the documentElement + * @param string The $qualifiedName of the documentElement + * @param Object A document type node + * @return Object The new document fragment node + */ + function &createDocument($namespaceURI, $qualifiedName, &$docType) { + $xmldoc = new DOMIT_Document(); + $documentElement =& $xmldoc->createElementNS($namespaceURI, $qualifiedName); + + $xmldoc->setDocumentElement($documentElement); + + if ($docType != null) { + $xmldoc->doctype =& $docType; + } + + return $xmldoc; + } //createDocument + + /** + * Creates a new DOMIT_DocumentType node (not yet implemented!) + * @param string The $qualifiedName + * @param string The $publicID + * @param string The $systemID + * @return Object The new document type node + */ + function &createDocumentType($qualifiedName, $publicID, $systemID) { + //not yet implemented + DOMIT_DOMException::raiseException(DOMIT_NOT_SUPPORTED_ERROR, + ('Method createDocumentType is not yet implemented.')); + } //createDocumentType +} //DOMIT_DOMImplementation + +?> \ No newline at end of file diff --git a/oss/joomla/trunk/libraries/domit/xml_domit_utilities.php b/oss/joomla/trunk/libraries/domit/xml_domit_utilities.php new file mode 100644 index 00000000..98d72d1c --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/xml_domit_utilities.php @@ -0,0 +1,384 @@ + +* @link http://www.engageinteractive.com/domit/ DOMIT! Home Page +* DOMIT! is Free Software +**/ + +/** +*@global Array Translation table for predefined XML entities +*/ +$GLOBALS['DOMIT_PREDEFINED_ENTITIES'] = array('&' => '&', '<' => '<', '>' => '>', + '"' => '"', "'" => '''); +/** +* A set of utilities for the DOMIT! parser +* +* These methods are intended to be called statically +* +* @package domit-xmlparser +* @author John Heinstein +*/ +class DOMIT_Utilities { + /** + * Raises an error if an attempt to instantiate the class is made + */ + function DOMIT_Utilities() { + die("DOMIT_Utilities Error: this is a static class that should never be instantiated.\n" . + "Please use the following syntax to access methods of this class:\n" . + 'DOMIT_Utilities::methodName(parameters)'); + } //DOMIT_Utilities + + /** + * Generates a normalized (formatted for readability) representation of the node and its children + * @param Object The root node of the narmalization + * @param boolean True if illegal xml characters in text nodes and attributes should be converted to entities + * @return string The formatted string representation + */ + function toNormalizedString (&$node, $subEntities=false, $definedEntities) { + $node_level = 0; + $response = ''; + + //$node is a DOMIT_Document + if ($node->nodeType == DOMIT_DOCUMENT_NODE) { + $total = $node->childCount; + + for ($i = 0; $i < $total; $i++) { + $response .= DOMIT_Utilities::getNormalizedString($node->childNodes[$i], + $node_level, $subEntities, $definedEntities); + } + + return $response; + } + else { + return ($response . DOMIT_Utilities::getNormalizedString($node, + $node_level, $subEntities, $definedEntities)); + } + } //toNormalizedString + + /** + * Converts illegal XML characters to their entity representations + * @param string The text to be formatted + * @param array User defined translation table for entities + * @return string The formatted text + */ + function convertEntities($text, $definedEntities) { + global $DOMIT_PREDEFINED_ENTITIES; + $result = strtr($text, $DOMIT_PREDEFINED_ENTITIES); + $result = strtr($result, $definedEntities); + return $result; + } //convertEntities + + /** + * Gets a normalized (formatted for readability) representation of the current node + * @param Object The node to be normalized + * @param int The level in the DOM hierarchy where the node is located + * @param boolean True if illegal xml characters in text nodes and attributes should be converted to entities + * @param array User defined translation table for entities + * @return string The normalized string representation + */ + function getNormalizedString(&$node, $node_level, $subEntities=false, $definedEntities) { + $response = ''; + + switch ($node->nodeType) { + case DOMIT_ELEMENT_NODE: + $response .= DOMIT_Utilities::getNormalizedElementString($node, $response, + $node_level, $subEntities, $definedEntities); + break; + + case DOMIT_TEXT_NODE: + if ($node->nextSibling == null) { + $node_level--; + } + + $response .= ($subEntities ? + DOMIT_Utilities::convertEntities($node->nodeValue, $definedEntities) : + $node->nodeValue); + break; + + case DOMIT_CDATA_SECTION_NODE: + if ($node->nextSibling == null) { + $node_level--; + } + + $response .= 'nodeValue . ']]>'; + break; + + case DOMIT_ATTRIBUTE_NODE: + $response .= $node->toString(false, $subEntities); + break; + + case DOMIT_DOCUMENT_FRAGMENT_NODE: + $total = $node->childCount; + + for ($i = 0; $i < $total; $i++) { + $response .= DOMIT_Utilities::getNormalizedString($node->childNodes[$i], $node_level, + $subEntities, $definedEntities); + } + + break; + + case DOMIT_COMMENT_NODE: + $response .= ''; + + if ($node->nextSibling == null) { + $node_level--; + } + + $response .= DOMIT_Utilities::getIndentation($node_level) ; + + break; + + case DOMIT_PROCESSING_INSTRUCTION_NODE: + $response .= '<' . '?' . $node->nodeName . ' ' . $node->nodeValue . '?' . '>'; + + if ($node->nextSibling == null) { + $node_level--; + } + + $response .= DOMIT_Utilities::getIndentation($node_level) ; + + break; + + case DOMIT_DOCUMENT_TYPE_NODE: + $response .= $node->toString() . "\n"; + break; + } + + return $response; + } //getNormalizedString + + /** + * Gets a normalized (formatted for readability) representation of the current element + * @param Object The node to be normalized + * @param string The current normalized text + * @param int The level in the DOM hierarchy where the node is located + * @param boolean True if illegal xml characters in text nodes and attributes should be converted to entities + * @param array User defined translation table for entities + * @return string The normalized string representation + */ + function getNormalizedElementString(&$node, $response, $node_level, + $subEntities, $definedEntities) { + $response .= '<' . $node->nodeName; + + //get attributes text + if (is_object($node->attributes)) { //DOMIT + $response .= $node->attributes->toString(false, $subEntities); + + /* + if ($node->ownerDocument->isNamespaceAware) { + foreach ($node->namespaceURIMap as $key => $value) { + $response .= ' xmlns:' . $value . '="' . $key . '"'; + } + } + */ + } + else { //DOMIT_Lite + foreach ($node->attributes as $key => $value) { + $response .= ' ' . $key . '="'; + $response .= ($subEntities ? DOMIT_Utilities::convertEntities($value, + $definedEntities) : $value); + $response .= '"'; + } + } + + $node_level++; + + //if node is childless + if ($node->childCount == 0) { + if ($node->ownerDocument->doExpandEmptyElementTags) { + if (in_array($node->nodeName, $node->ownerDocument->expandEmptyElementExceptions)) { + $response .= ' />'; + } + else { + $response .= '>nodeName . '>'; + } + } + else { + if (in_array($node->nodeName, $node->ownerDocument->expandEmptyElementExceptions)) { + $response .= '>nodeName . '>'; + } + else { + $response .= ' />'; + } + } + } + else { + $response .= '>'; + + //get children + $myNodes =& $node->childNodes; + $total = $node->childCount; + + //no indentation if first child is a text node + if (!DOMIT_Utilities::isTextNode($node->firstChild)) { + $response .= DOMIT_Utilities::getIndentation($node_level); + } + + for ($i = 0; $i < $total; $i++) { + $child =& $myNodes[$i]; + $response .= DOMIT_Utilities::getNormalizedString($child, $node_level, + $subEntities, $definedEntities); + } + + $response .= 'nodeName . '>'; + } + + $node_level--; + + if ($node->nextSibling == null) { + $node_level--; + $response .= DOMIT_Utilities::getIndentation($node_level); + } + else { + //no indentation if next sibling is a text node + if (!DOMIT_Utilities::isTextNode($node->nextSibling)) { + $response .= DOMIT_Utilities::getIndentation($node_level); + } + } + + return $response; + } //getNormalizedElementString + + /** + * Determines whether the specified node is a Text node + * @param Object The node to be tested + * @return boolean True if the node is a Text node + */ + function isTextNode(&$node) { + $type = $node->nodeType; + return (($type == DOMIT_TEXT_NODE) || ($type == DOMIT_CDATA_SECTION_NODE)); + } //isTextNode + + /** + * Returns the indentation required for the specified node level + * @param int The current node level + * @return string The indentation required for the specified node level + */ + function getIndentation($node_level) { + $INDENT_LEN = ' '; + $indentation = "\n"; + + for ($i = 0; $i < $node_level; $i++) { + $indentation .= $INDENT_LEN; + } + + return $indentation; + } //getIndentation + + /** + * Removes the extension from the specified file name + * @param string The file name + * @return string The file name, stripped of its extension + */ + function removeExtension($fileName) { + $total = strlen($fileName); + $index = -1; + + for ($i = ($total - 1); $i >= 0; $i--) { + if ($fileName{$i} == '.') { + $index = $i; + } + } + + if ($index == -1) { + return $fileName; + } + + return (substr($fileName, 0, $index)); + } //removeExtension + + /** + * Determines whether the XML string is valid (NOT FULLY IMPLEMENTED!) + * @param string The XML text + * @return boolean True if the XML text is valid + */ + function validateXML($xmlText) { + //this does only rudimentary validation + //at this point in time + $isValid = true; + + if (is_string($xmlText)) { + $text = trim($xmlText); + + switch ($text) { + case '': + $isValid = false; + break; + } + } + else { + $isValid = false; + } + + return $isValid; + } //validateXML + + /** + * Set the browser header to interpret data as UTF-8 formatted + * @param string The content type of the data + */ + function printUTF8Header($contentType = 'text/html') { + echo header('Content-type: ' . $contentType . '; charset=utf-8'); + } //printUTF8Header + + /** + * Formats a string for presentation as HTML + * @param string The string to be formatted + * @param boolean True if the string is to be sent directly to output + * @return string The HTML formatted string + */ + function forHTML($text, $doPrint = false) { + if ($doPrint) { + print ('
' . htmlspecialchars($text) . '
'); + } + else { + return ('
' . htmlspecialchars($text) . '
'); + } + } //forHTML + + /** + * Generates a node tree from an array and appends it to the specified document or node + * @param object The document or node to which the child nodes should be appended + * @param array An associative multidimensional array of elements and values + */ + function fromArray (&$node, &$myArray) { + if ($node->nodeType == DOMIT_DOCUMENT_NODE) { + $docNode =& $node; + } + else { + $docNode =& $node->ownerDocument; + } + + foreach ($myArray as $key => $value) { + if (is_array($value)) { + //check for numeric indices + $total = count($value); + + if (($total > 0) && isset($value[0])){ + for ($i = 0; $i < $total; $i++) { + $node->appendChild($docNode->createElement($key)); + DOMIT_Utilities::fromArray($node->lastChild, $value[$i]); + } + } + else { + //generate child elements + $node->appendChild($docNode->createElement($key)); + DOMIT_Utilities::fromArray($node->lastChild, $value); + } + } + else { + $node->appendChild($docNode->createElement($key)); + $node->lastChild->appendChild($docNode->createTextNode($value)); + } + } + } //fromArray + + function parseAttributes() { + + } //parseAttributes +} //DOMIT_Utilities +?> \ No newline at end of file diff --git a/oss/joomla/trunk/libraries/domit/xml_domit_xpath.php b/oss/joomla/trunk/libraries/domit/xml_domit_xpath.php new file mode 100644 index 00000000..de1e6fae --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/xml_domit_xpath.php @@ -0,0 +1,1048 @@ + +* @link http://www.engageinteractive.com/domit/ DOMIT! Home Page +* DOMIT! is Free Software +**/ + +if (!defined('DOMIT_INCLUDE_PATH')) { + define('DOMIT_INCLUDE_PATH', (dirname(__FILE__) . "/")); +} + +/** Separator for absolute path */ +define('DOMIT_XPATH_SEPARATOR_ABSOLUTE', '/'); +/** Separator for relative path */ +define('DOMIT_XPATH_SEPARATOR_RELATIVE', '//'); +/** OR separator for multiple patterns */ +define('DOMIT_XPATH_SEPARATOR_OR', '|'); + +/** Constant for an absolute path search (starting at the document root) */ +define('DOMIT_XPATH_SEARCH_ABSOLUTE', 0); +/** Constant for a relative path search (starting at the level of the calling node) */ +define('DOMIT_XPATH_SEARCH_RELATIVE', 1); +/** Constant for a variable path search (finds all matches, regardless of place in the hierarchy) */ +define('DOMIT_XPATH_SEARCH_VARIABLE', 2); + + +/** +* DOMIT! XPath is an XPath parser. +*/ +class DOMIT_XPath { + /** @var Object The node from which the search is called */ + var $callingNode; + /** @var Object The node that is the current parent of the search */ + var $searchType; + /** @var array An array containing a series of path segments for which to search */ + var $arPathSegments = array(); + /** @var Object A DOMIT_NodeList of matching nodes */ + var $nodeList; + /** @var string A temporary string container */ + var $charContainer; + /** @var string The current character of the current pattern segment being parsed */ + var $currChar; + /** @var string The current pattern segment being parsed */ + var $currentSegment; + /** @var array A temporary node container for caching node references at the pattern level*/ + var $globalNodeContainer; + /** @var array A temporary node container for caching node references at the pattern segment level */ + var $localNodeContainer; + /** @var array Normalization table for XPath syntax */ + var $normalizationTable = array('child::' => '', 'self::' => '.', + 'attribute::' => '@', 'descendant::' => '*//', + "\t" => ' ', "\x0B" => ' '); + /** @var array A second-pass normalization table for XPath syntax */ + var $normalizationTable2 = array(' =' => '=', '= ' => '=', ' <' => '<', + ' >' => '>', '< ' => '<', '> ' => '>', + ' !' => '!', '( ' => '(', + ' )' => ')', ' ]' => ']', '] ' => ']', + ' [' => '[', '[ ' => '[', ' /' => '/', + '/ ' => '/', '"' => "'"); + /** @var array A third-pass normalization table for XPath syntax */ + var $normalizationTable3 = array('position()=' => '', + '/descendant-or-self::node()/' => "//", 'self::node()' => '.', + 'parent::node()' => '..'); + + /** + * Constructor - creates an empty DOMIT_NodeList to store matching nodes + */ + function DOMIT_XPath() { + require_once(DOMIT_INCLUDE_PATH . 'xml_domit_nodemaps.php'); + $this->nodeList = new DOMIT_NodeList(); + } //DOMIT_XPath + + /** + * Parses the supplied "path"-based pattern + * @param Object The node from which the search is called + * @param string The pattern + * @return Object The NodeList containing matching nodes + */ + function &parsePattern(&$node, $pattern, $nodeIndex = 0) { + $this->callingNode =& $node; + $pattern = $this->normalize(trim($pattern)); + + $this->splitPattern($pattern); + + $total = count($this->arPathSegments); + + //whole pattern level + for ($i = 0; $i < $total; $i++) { + $outerArray =& $this->arPathSegments[$i]; + $this->initSearch($outerArray); + + $outerTotal = count($outerArray); + $isInitialMatchAttempt = true; + + //variable path segment level + for ($j = 0; $j < $outerTotal; $j++) { + $innerArray =& $outerArray[$j]; + $innerTotal = count($innerArray); + + if (!$isInitialMatchAttempt) { + $this->searchType = DOMIT_XPATH_SEARCH_VARIABLE; + } + + //pattern segment level + for ($k = 0; $k < $innerTotal; $k++) { + $currentPattern = $innerArray[$k]; + + if (($k == 0) && ($currentPattern == null)) { + if ($innerTotal == 1) { + $isInitialMatchAttempt = false; + } + //else just skip current step and don't alter searchType + } + else { + if (!$isInitialMatchAttempt && ($k > 0)) { + $this->searchType = DOMIT_XPATH_SEARCH_RELATIVE; + } + + $this->currentSegment = $currentPattern; + $this->processPatternSegment(); + $isInitialMatchAttempt = false; + } + } + } + } + + if ($nodeIndex > 0) { + if ($nodeIndex <= count($this->globalNodeContainer)) { + return $this->globalNodeContainer[($nodeIndex - 1)]; + } + else { + $null = null; + return $null; + } + } + + if (count($this->globalNodeContainer) != 0) { + foreach ($this->globalNodeContainer as $key =>$value) { + $currNode =& $this->globalNodeContainer[$key]; + $this->nodeList->appendNode($currNode); + } + } + + return $this->nodeList; + } //parsePattern + + /** + * Generates a new globalNodeContainer of matches + */ + function processPatternSegment() { + $total = strlen($this->currentSegment); + $this->charContainer = ''; + $this->localNodeContainer = array(); + + for ($i = 0; $i < $total; $i++) { +// $this->currChar = $this->currentSegment{$i}; + $this->currChar = substr($this->currentSegment, $i, 1); + + switch ($this->currChar) { + case '@': + $this->selectAttribute(substr($this->currentSegment, ($this->currChar + 1))); + $this->updateNodeContainers(); + return; + //break; + + case '*': + if ($i == ($total - 1)) { + $this->selectNamedChild('*'); + } + else { + $this->charContainer .= $this->currChar; + } + break; + + case '.': + $this->charContainer .= $this->currChar; + + if ($i == ($total - 1)) { + if ($this->charContainer == '..') { + $this->selectParent(); + } + else { + return; + } + } + break; + + case ')': + $this->charContainer .= $this->currChar; + $this->selectNodesByFunction(); + break; + + case '[': + $this->parsePredicate($this->charContainer, + substr($this->currentSegment, ($i + 1))); + return; + //break; + + default: + $this->charContainer .= $this->currChar; + + } + } + + if ($this->charContainer != '') { + $this->selectNamedChild($this->charContainer); + } + + $this->updateNodeContainers(); + } //processPatternSegment + + /** + * Replaces the global node container with the local node container + */ + function updateNodeContainers() { + $this->globalNodeContainer =& $this->localNodeContainer; + unset($this->localNodeContainer); + } //updateNodeContainers + + + /** + * Parses a predicate expression [...] + * @param string The pattern segment containing the node expression + * @param string The pattern segment containing the predicate expression + */ + function parsePredicate($nodeName, $patternSegment) { + $arPredicates =& explode('][', $patternSegment); + + $total = count($arPredicates); + + $lastIndex = $total - 1; + $arPredicates[$lastIndex] = substr($arPredicates[$lastIndex], + 0, (strlen($arPredicates[$lastIndex]) - 1)); + + for ($i = 0; $i < $total; $i++) { + $isRecursive = ($this->searchType == DOMIT_XPATH_SEARCH_VARIABLE) ? true : false; + $currPredicate = $arPredicates[$i]; + + if (is_numeric($currPredicate)) { + if ($i == 0) { + $this->filterByIndex($nodeName, intval($currPredicate), $isRecursive); + } + else { + $this->refilterByIndex(intval($currPredicate)); + } + } + else { + if ($i == 0) { + $this->selectNamedChild($nodeName); + $this->updateNodeContainers(); + } + + $phpExpression = $this->predicateToPHP($currPredicate); + $this->filterByPHPExpression($phpExpression); + } + + $this->updateNodeContainers(); + } + + $this->charContainer = ''; + } //parsePredicate + + /** + * Converts the predicate into PHP evaluable code + * @param string The predicate + * @return string The converted PHP expression + */ + function predicateToPHP($predicate) { + $phpExpression = $predicate; + $currChar = ''; + $charContainer = ''; + $totalChars = strlen($predicate); + + for ($i = 0; $i < $totalChars; $i++) { + $currChar = substr($predicate, $i, 1); + + switch ($currChar) { + case '(': + case ')': + case ' ': + if ($charContainer != '') { + $convertedPredicate = $this->expressionToPHP($charContainer); + $phpExpression = str_replace($charContainer, $convertedPredicate, $phpExpression); + $charContainer = ''; + } + break; + + default: + $charContainer .= $currChar; + + } + } + + if ($charContainer != '') { + $convertedPredicate = $this->expressionToPHP($charContainer); + $phpExpression = str_replace($charContainer, $convertedPredicate, $phpExpression); + } + + return $phpExpression; + } //predicateToPHP + + + /** + * Converts the predicate expression into a PHP expression + * @param string The predicate expression + * @return string The converted PHP expression + */ + function expressionToPHP($expression) { + if ($expression == 'and') { + $expression = '&&'; + } + else if ($expression == 'or') { + $expression = '||'; + } + else if ($expression == 'not') { + $expression = '!'; + } + else { + $expression = trim($expression); + + if (strpos($expression, '@') !== false) { + if (strpos($expression, '>=') !== false) { + $expression = str_replace('@', ('floatval($' . "contextNode->getAttribute('"), $expression); + $expression = str_replace('>=', "')) >= floatval(", $expression); + if (!is_numeric($expression)) $expression = str_replace('floatval', '', $expression); + $expression .= ')'; + } + else if (strpos($expression, '<=') !== false) { + $expression = str_replace('@', ('floatval($' . "contextNode->getAttribute('"), $expression); + $expression = str_replace('<=', "')) <= floatval(", $expression); + if (!is_numeric($expression)) $expression = str_replace('floatval', '', $expression); + $expression .= ')'; + } + else if (strpos($expression, '!=') !== false) { + $expression = str_replace('@', ('$' . "contextNode->getAttribute('"), $expression); + $expression = str_replace('!=', "') != ", $expression); + } + else if (strpos($expression, '=') !== false) { + $expression = str_replace('@', ('$' . "contextNode->getAttribute('"), $expression); + $expression = str_replace('=', "') == ", $expression); + } + else if (strpos($expression, '>') !== false) { + $expression = str_replace('>', "')) > floatval(", $expression); //reverse so > doesn't get replaced + $expression = str_replace('@', ('floatval($' . "contextNode->getAttribute('"), $expression); + if (!is_numeric($expression)) $expression = str_replace('floatval', '', $expression); + $expression .= ')'; + } + else if (strpos($expression, '<') !== false) { + $expression = str_replace('@', ('floatval($' . "contextNode->getAttribute('"), $expression); + $expression = str_replace('<', "')) < floatval(", $expression); + if (!is_numeric($expression)) $expression = str_replace('floatval', '', $expression); + $expression .= ')'; + } + else { + $expression = str_replace('@', ('$' . "contextNode->hasAttribute('"), $expression); + $expression.= "')"; + } + } + else { + if (strpos($expression, '>=') !== false) { + $signPos = strpos($expression, '>='); + $elementName = trim(substr($expression, 0, $signPos)); + $elementValue = trim(substr($expression, ($signPos + 2))); + + $expression = '$' . "this->hasNamedChildElementGreaterThanOrEqualToValue(" . + '$' . "contextNode, '" . $elementName . "', " . + $elementValue . ')'; + } + else if (strpos($expression, '<=') !== false) { + $signPos = strpos($expression, '>='); + $elementName = trim(substr($expression, 0, $signPos)); + $elementValue = trim(substr($expression, ($signPos + 2))); + + $expression = '$' . "this->hasNamedChildElementLessThanOrEqualToValue(" . + '$' . "contextNode, '" . $elementName . "', " . + $elementValue . ')'; + } + else if (strpos($expression, '!=') !== false) { + $signPos = strpos($expression, '>='); + $elementName = trim(substr($expression, 0, $signPos)); + $elementValue = trim(substr($expression, ($signPos + 2))); + + $expression = '$' . "this->hasNamedChildElementNotEqualToValue(" . + '$' . "contextNode, '" . $elementName . "', " . + $elementValue . ')'; + } + else if (strpos($expression, '=') !== false) { + $signPos = strpos($expression, '='); + $elementName = trim(substr($expression, 0, $signPos)); + $elementValue = trim(substr($expression, ($signPos + 1))); + + $expression = '$' . "this->hasNamedChildElementEqualToValue(" . + '$' . "contextNode, '" . $elementName . "', " . + $elementValue . ')'; + + } + else if (strpos($expression, '>') !== false) { + $signPos = strpos($expression, '='); + $elementName = trim(substr($expression, 0, $signPos)); + $elementValue = trim(substr($expression, ($signPos + 1))); + + $expression = '$' . "this->hasNamedChildElementGreaterThanValue(" . + '$' . "contextNode, '" . $elementName . "', " . + $elementValue . ')'; + } + else if (strpos($expression, '<') !== false) { + $signPos = strpos($expression, '='); + $elementName = trim(substr($expression, 0, $signPos)); + $elementValue = trim(substr($expression, ($signPos + 1))); + + $expression = '$' . "this->hasNamedChildElementLessThanValue(" . + '$' . "contextNode, '" . $elementName . "', " . + $elementValue . ')'; + } + else { + $expression = '$' . "this->hasNamedChildElement(" . + '$' . "contextNode, '" . $expression . "')"; + } + } + } + + return $expression; + } //expressionToPHP + + /** + * Selects nodes that match the predicate expression + * @param string The predicate expression, formatted as a PHP expression + */ + function filterByPHPExpression($expression) { + if (count($this->globalNodeContainer) != 0) { + foreach ($this->globalNodeContainer as $key =>$value) { + $contextNode =& $this->globalNodeContainer[$key]; + + if ($contextNode->nodeType == DOMIT_ELEMENT_NODE) { + $evaluatedExpression = 'if (' . $expression . ") $" . + 'this->localNodeContainer[] =& $' . 'contextNode;'; + eval($evaluatedExpression); + } + } + } + } //filterByPHPExpression + + /** + * Selects nodes with child elements that match the specified name + * @param object The parent node of the child elements to match + * @param string The tag name to match on + * @return boolean True if a matching child element exists + */ + function hasNamedChildElement(&$parentNode, $nodeName) { + $total = $parentNode->childCount; + + for ($i = 0; $i < $total; $i++) { + $currNode =& $parentNode->childNodes[$i]; + + if (($currNode->nodeType == DOMIT_ELEMENT_NODE) && ($currNode->nodeName == $nodeName)) { + return true; + } + } + + return false; + } //hasNamedChildElement + + /** + * Selects nodes with child elements that match the specified name and text value + * @param object The parent node of the child elements to match + * @param string The tag name to match on + * @param string The text string to match on + * @return boolean True if a matching child element exists + */ + function hasNamedChildElementEqualToValue(&$parentNode, $nodeName, $nodeValue) { + $total = $parentNode->childCount; + + for ($i = 0; $i < $total; $i++) { + $currNode =& $parentNode->childNodes[$i]; + + if (($currNode->nodeType == DOMIT_ELEMENT_NODE) && + ($currNode->nodeName == $nodeName) && ($currNode->getText() == $nodeValue)) { + return true; + } + } + + return false; + } //hasNamedChildElementEqualToValue + + /** + * Selects nodes with child elements that are greater than or equal to the specified name and value + * @param object The parent node of the child elements to match + * @param string The tag name to match on + * @param string The text string to match on + * @return boolean True if a matching child element exists + */ + function hasNamedChildElementGreaterThanOrEqualToValue(&$parentNode, $nodeName, $nodeValue) { + $isNumeric = false; + + if (is_numeric($nodeValue)) { + $isNumeric = true; + $nodeValue = floatval($nodeValue); + } + + $total = $parentNode->childCount; + + for ($i = 0; $i < $total; $i++) { + $currNode =& $parentNode->childNodes[$i]; + + if (($currNode->nodeType == DOMIT_ELEMENT_NODE) && ($currNode->nodeName == $nodeName)) { + if ($isNumeric) {$compareVal = floatval($currNode->getText());} + else {$compareVal = $currNode->getText();} + if ($compareVal >= $nodeValue) return true; + } + } + + return false; + } //hasNamedChildElementGreaterThanOrEqualToValue + + + /** + * Selects nodes with child elements that are less than or equal to the specified name and value + * @param object The parent node of the child elements to match + * @param string The tag name to match on + * @param string The text string to match on + * @return boolean True if a matching child element exists + */ + function hasNamedChildElementLessThanOrEqualToValue(&$parentNode, $nodeName, $nodeValue) { + $isNumeric = false; + + if (is_numeric($nodeValue)) { + $isNumeric = true; + $nodeValue = floatval($nodeValue); + } + + $total = $parentNode->childCount; + + for ($i = 0; $i < $total; $i++) { + $currNode =& $parentNode->childNodes[$i]; + + if (($currNode->nodeType == DOMIT_ELEMENT_NODE) && ($currNode->nodeName == $nodeName)) { + if ($isNumeric) {$compareVal = floatval($currNode->getText());} + else {$compareVal = $currNode->getText();} + if ($compareVal <= $nodeValue) return true; + } + } + + return false; + } //hasNamedChildElementLessThanOrEqualToValue + + /** + * Selects nodes with child elements that are not equal to the specified name and value + * @param object The parent node of the child elements to match + * @param string The tag name to match on + * @param string The text string to match on + * @return boolean True if a matching child element exists + */ + function hasNamedChildElementNotEqualToValue(&$parentNode, $nodeName, $nodeValue) { + $isNumeric = false; + + if (is_numeric($nodeValue)) { + $isNumeric = true; + $nodeValue = floatval($nodeValue); + } + + $total = $parentNode->childCount; + + for ($i = 0; $i < $total; $i++) { + $currNode =& $parentNode->childNodes[$i]; + + if (($currNode->nodeType == DOMIT_ELEMENT_NODE) && ($currNode->nodeName == $nodeName)) { + if ($isNumeric) {$compareVal = floatval($currNode->getText());} + else {$compareVal = $currNode->getText();} + if ($compareVal != $nodeValue) return true; + } + } + + return false; + } //hasNamedChildElementNotEqualToValue + + /** + * Selects nodes with child elements that are greater than the specified name and value + * @param object The parent node of the child elements to match + * @param string The tag name to match on + * @param string The text string to match on + * @return boolean True if a matching child element exists + */ + function hasNamedChildElementGreaterThanValue(&$parentNode, $nodeName, $nodeValue) { + $isNumeric = false; + + if (is_numeric($nodeValue)) { + $isNumeric = true; + $nodeValue = floatval($nodeValue); + } + + $total = $parentNode->childCount; + + for ($i = 0; $i < $total; $i++) { + $currNode =& $parentNode->childNodes[$i]; + + if (($currNode->nodeType == DOMIT_ELEMENT_NODE) && ($currNode->nodeName == $nodeName)) { + if ($isNumeric) {$compareVal = floatval($currNode->getText());} + else {$compareVal = $currNode->getText();} + if ($compareVal > $nodeValue) return true; + } + } + + return false; + } //hasNamedChildElementGreaterThanValue + + + /** + * Selects nodes with child elements that are less than the specified name and value + * @param object The parent node of the child elements to match + * @param string The tag name to match on + * @param string The text string to match on + * @return boolean True if a matching child element exists + */ + function hasNamedChildElementLessThanValue(&$parentNode, $nodeName, $nodeValue) { + $isNumeric = false; + + if (is_numeric($nodeValue)) { + $isNumeric = true; + $nodeValue = floatval($nodeValue); + } + + $total = $parentNode->childCount; + + for ($i = 0; $i < $total; $i++) { + $currNode =& $parentNode->childNodes[$i]; + + if (($currNode->nodeType == DOMIT_ELEMENT_NODE) && ($currNode->nodeName == $nodeName)) { + if ($isNumeric) {$compareVal = floatval($currNode->getText());} + else {$compareVal = $currNode->getText();} + if ($compareVal < $nodeValue) return true; + } + } + + return false; + } //hasNamedChildElementLessThanValue + + /** + * Selects named elements of the specified index + * @param string The pattern segment containing the node expression + * @param int The index (base 1) of the matching node + * @param boolean True if the selection is to be performed recursively + */ + function refilterByIndex($index) { + if ($index > 1) { + if (count($this->globalNodeContainer) != 0) { + $counter = 0; + $lastParentID = null; + + foreach ($this->globalNodeContainer as $key =>$value) { + $currNode =& $this->globalNodeContainer[$key]; + + if (($lastParentID != null) && ($currNode->parentNode->uid != $lastParentID)) { + $counter = 0; + } + + $counter++; + + if (($counter == $index) && ($currNode->parentNode->uid == $lastParentID)) { + $this->localNodeContainer[] =& $currNode; + } + + $lastParentID = $currNode->parentNode->uid; + } + } + } + else { + $this->localNodeContainer =& $this->globalNodeContainer; + } + } //refilterByIndex + + + /** + * Selects named elements of the specified index + * @param string The pattern segment containing the node expression + * @param int The index (base 1) of the matching node + * @param boolean True if the selection is to be performed recursively + */ + function filterByIndex($nodeName, $index, $deep) { + if (count($this->globalNodeContainer) != 0) { + foreach ($this->globalNodeContainer as $key =>$value) { + $currNode =& $this->globalNodeContainer[$key]; + $this->_filterByIndex($currNode, $nodeName, $index, $deep); + } + } + } //filterByIndex + + /** + * Selects named elements of the specified index + * @param object The context node + * @param string The pattern segment containing the node expression + * @param int The index (base 1) of the matching node + * @param boolean True if the selection is to be performed recursively + */ + function _filterByIndex(&$contextNode, $nodeName, $index, $deep) { + if (($contextNode->nodeType == DOMIT_ELEMENT_NODE) || + ($contextNode->nodeType == DOMIT_DOCUMENT_NODE)) { + $total = $contextNode->childCount; + $nodeCounter = 0; + + for ($i = 0; $i < $total; $i++) { + $currChildNode =& $contextNode->childNodes[$i]; + + if ($currChildNode->nodeName == $nodeName) { + $nodeCounter++; + + if ($nodeCounter == $index) { + $this->localNodeContainer[] =& $currChildNode; + } + } + + if ($deep) { + $this->_filterByIndex($currChildNode, $nodeName, $index, $deep); + } + } + } + } //_filterByIndex + + /** + * Selects named elements with the specified named child + * @param string The pattern segment containing the node expression + * @param string The tag name of the matching child + * @param boolean True if the selection is to be performed recursively + */ + function filterByChildName($nodeName, $childName, $deep) { + if (count($this->globalNodeContainer) != 0) { + foreach ($this->globalNodeContainer as $key =>$value) { + $currNode =& $this->globalNodeContainer[$key]; + $this->_filterByChildName($currNode, $nodeName, $childName, $deep); + } + } + } //filterByChildName + + /** + * Selects named elements with the specified named child + * @param object The context node + * @param string The pattern segment containing the node expression + * @param string The tag name of the matching child + * @param boolean True if the selection is to be performed recursively + */ + function _filterByChildName(&$contextNode, $nodeName, $childName, $deep) { + if (($contextNode->nodeType == DOMIT_ELEMENT_NODE) || + ($contextNode->nodeType == DOMIT_DOCUMENT_NODE)) { + $total = $contextNode->childCount; + + for ($i = 0; $i < $total; $i++) { + $currChildNode =& $contextNode->childNodes[$i]; + + if (($currChildNode->nodeName == $nodeName) && + ($currChildNode->nodeType == DOMIT_ELEMENT_NODE)) { + $total2 = $currChildNode->childCount; + + for ($j = 0; $j < $total2; $j++) { + $currChildChildNode =& $currChildNode->childNodes[$j]; + + if ($currChildChildNode->nodeName == $childName) { + $this->localNodeContainer[] =& $currChildNode; + } + } + } + + if ($deep) { + $this->_filterByChildName($currChildNode, $nodeName, $childName, $deep); + } + } + } + } //_filterByChildName + + /** + * Selects named attributes of the current context nodes + * @param string The attribute name, or * to match all attributes + */ + function selectAttribute($attrName) { + if (count($this->globalNodeContainer) != 0) { + foreach ($this->globalNodeContainer as $key =>$value) { + $currNode =& $this->globalNodeContainer[$key]; + + $isRecursive = ($this->searchType == DOMIT_XPATH_SEARCH_VARIABLE) ? true : false; + $this->_selectAttribute($currNode, $attrName, $isRecursive); + } + } + + $this->charContainer = ''; + } //selectAttribute + + /** + * Selects all attributes of the context nodes + * @param object The context node + * @param string The attribute name, or * to match all attributes + * @param boolean True if the selection is to be performed recursively + */ + function _selectAttribute(&$contextNode, $attrName, $deep) { + if (($contextNode->nodeType == DOMIT_ELEMENT_NODE) || + ($contextNode->nodeType == DOMIT_DOCUMENT_NODE)) { + $total = $contextNode->childCount; + + for ($i = 0; $i < $total; $i++) { + $currNode =& $contextNode->childNodes[$i]; + + if ($currNode->nodeType == DOMIT_ELEMENT_NODE) { + if ($attrName == '*') { + $total2 = $currNode->attributes->getLength(); + + for ($j = 0; $j < $total2; $j++) { + $this->localNodeContainer[] =& $currNode->attributes->item($j); + } + } + else { + if ($currNode->hasAttribute($attrName)) { + $this->localNodeContainer[] =& $currNode->getAttributeNode($attrName); + } + } + } + + if ($deep) { + $this->_selectAttribute($currNode, $attrName, $deep); + } + } + } + } //_selectAttribute + + + /** + * Selects all child nodes of the current context nodes + * @param string The element name + */ + function selectNamedChild($tagName) { + if (count($this->globalNodeContainer) != 0) { + foreach ($this->globalNodeContainer as $key =>$value) { + $currNode =& $this->globalNodeContainer[$key]; + + $isRecursive = ($this->searchType == DOMIT_XPATH_SEARCH_VARIABLE) ? true : false; + $this->_selectNamedChild($currNode, $tagName, $isRecursive); + } + } + + $this->charContainer = ''; + } //selectNamedChild + + /** + * Selects all child nodes of the context node + * @param object The context node + * @param string The element name + * @param boolean True if the selection is to be performed recursively + */ + function _selectNamedChild(&$contextNode, $tagName, $deep = false) { + if (($contextNode->nodeType == DOMIT_ELEMENT_NODE) || + ($contextNode->nodeType == DOMIT_DOCUMENT_NODE)) { + $total = $contextNode->childCount; + + for ($i = 0; $i < $total; $i++) { + $currChildNode =& $contextNode->childNodes[$i]; + + if (($currChildNode->nodeType == DOMIT_ELEMENT_NODE) || + ($currChildNode->nodeType == DOMIT_DOCUMENT_NODE)) { + if (($tagName == '*') || ($tagName == $currChildNode->nodeName)) { + $this->localNodeContainer[] =& $currChildNode; + } + + if ($deep) { + $this->_selectNamedChild($currChildNode, $tagName, $deep); + } + } + } + } + } //_selectNamedChild + + /** + * Selects parent node of the current context nodes + */ + function selectParent() { + if (count($this->globalNodeContainer) != 0) { + foreach ($this->globalNodeContainer as $key =>$value) { + $currNode =& $this->globalNodeContainer[$key]; + + $isRecursive = ($this->searchType == DOMIT_XPATH_SEARCH_VARIABLE) ? true : false; + $this->_selectParent($currNode, $isRecursive); + } + } + + $this->charContainer = ''; + } //selectParent + + /** + * Selects parent node of the current context nodes + * @param object The context node + * @param boolean True if the selection is to be performed recursively + */ + function _selectParent(&$contextNode, $deep = false) { + if ($contextNode->nodeType == DOMIT_ELEMENT_NODE) { + if ($contextNode->parentNode != null) { + $this->localNodeContainer[] =& $contextNode->parentNode; + } + } + + if ($deep) { + if (($contextNode->nodeType == DOMIT_ELEMENT_NODE) || + ($contextNode->nodeType == DOMIT_DOCUMENT_NODE)) { + $total = $contextNode->childCount; + + for ($i = 0; $i < $total; $i++) { + $currNode =& $contextNode->childNodes[$i]; + + if ($currNode->nodeType == DOMIT_ELEMENT_NODE) { + $this->_selectParent($contextNode, $deep); + } + } + } + } + } //_selectParent + + /** + * Selects any nodes of the current context nodes which match the given function + */ + function selectNodesByFunction() { + $doProcess = false; + $targetNodeType = -1; + + switch (strtolower(trim($this->charContainer))) { + case 'last()': + if (count($this->globalNodeContainer) != 0) { + foreach ($this->globalNodeContainer as $key =>$value) { + $currNode =& $this->globalNodeContainer[$key]; + + if ($currNode->nodeType == DOMIT_ELEMENT_NODE) { + if ($currNode->lastChild != null) { + $this->localNodeContainer[] =& $currNode->lastChild; + } + } + } + } + break; + + case 'text()': + $doProcess = true; + $targetNodeType = DOMIT_TEXT_NODE; + break; + + case 'comment()': + $doProcess = true; + $targetNodeType = DOMIT_COMMENT_NODE; + break; + + case 'processing-instruction()': + $doProcess = true; + $targetNodeType = DOMIT_PROCESSING_INSTRUCTION_NODE; + break; + } + + if ($doProcess) { + if (count($this->globalNodeContainer) != 0) { + foreach ($this->globalNodeContainer as $key =>$value) { + $currNode =& $this->globalNodeContainer[$key]; + + if ($currNode->nodeType == DOMIT_ELEMENT_NODE) { + $total = $currNode->childCount; + + for ($j = 0; $j < $total; $j++) { + if ($currNode->childNodes[$j]->nodeType == $targetNodeType) { + $this->localNodeContainer[] =& $currNode->childNodes[$j]; + } + } + } + } + } + } + + $this->charContainer = ''; + } //selectNodesByFunction + + /** + * Splits the supplied pattern into searchable segments + * @param string The pattern + */ + function splitPattern($pattern) { + //split multiple patterns if they exist (e.g. pattern1 | pattern2 | pattern3) + $this->arPathSegments =& explode(DOMIT_XPATH_SEPARATOR_OR, $pattern); + + //split each pattern by relative path dividers (i.e., '//') + $total = count($this->arPathSegments); + + for ($i = 0; $i < $total; $i++) { + $this->arPathSegments[$i] =& explode(DOMIT_XPATH_SEPARATOR_RELATIVE, trim($this->arPathSegments[$i])); + + $currArray =& $this->arPathSegments[$i]; + $total2 = count($currArray); + + for ($j = 0; $j < $total2; $j++) { + $currArray[$j] =& explode(DOMIT_XPATH_SEPARATOR_ABSOLUTE, $currArray[$j]); + } + } + } //splitPattern + + /** + * Converts long XPath syntax into abbreviated XPath syntax + * @param string The pattern + * @return string The normalized pattern + */ + function normalize($pattern) { + $pattern = strtr($pattern, $this->normalizationTable); + + while (strpos($pattern, ' ') !== false) { + $pattern = str_replace(' ', ' ', $pattern); + } + + $pattern = strtr($pattern, $this->normalizationTable2); + $pattern = strtr($pattern, $this->normalizationTable3); + + return $pattern; + } //normalize + + /** + * Initializes the contextNode and searchType + * @param array The current array of path segments + * @return int The index of the first array item to begin the search at + */ + function initSearch(&$currArPathSegments) { + $this->globalNodeContainer = array(); + + if (is_null($currArPathSegments[0])) { + if (count($currArPathSegments) == 1) { + //variable path + $this->searchType = DOMIT_XPATH_SEARCH_VARIABLE; + $this->globalNodeContainer[] =& $this->callingNode->ownerDocument; + } + else { + //absolute path + $this->searchType = DOMIT_XPATH_SEARCH_ABSOLUTE; + $this->globalNodeContainer[] =& $this->callingNode->ownerDocument; + } + } + else { + //relative path + $this->searchType = DOMIT_XPATH_SEARCH_RELATIVE; + + if ($this->callingNode->uid != $this->callingNode->ownerDocument->uid) { + $this->globalNodeContainer[] =& $this->callingNode; + } + else { + $this->globalNodeContainer[] =& $this->callingNode->ownerDocument; + } + } + } //initSearch +} //DOMIT_XPath +?> \ No newline at end of file diff --git a/oss/joomla/trunk/libraries/domit/xml_saxy_lite_parser.php b/oss/joomla/trunk/libraries/domit/xml_saxy_lite_parser.php new file mode 100644 index 00000000..e0f522cf --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/xml_saxy_lite_parser.php @@ -0,0 +1,243 @@ + +* @link http://www.engageinteractive.com/saxy/ SAXY Home Page +* SAXY is Free Software +**/ + +if (!defined('SAXY_INCLUDE_PATH')) { + define('SAXY_INCLUDE_PATH', (dirname(__FILE__) . "/")); +} + +/** current version of SAXY Lite */ +define ('SAXY_LITE_VERSION', '1.0'); + +/** initial saxy lite parse state, before anything is encountered */ +define('SAXY_STATE_NONE', 0); +/** saxy lite parse state, processing main document */ +define('SAXY_STATE_PARSING', 1); + +require_once(SAXY_INCLUDE_PATH . 'xml_saxy_shared.php'); + +/** +* The SAX Parser class +* +* @package saxy-xmlparser +* @subpackage saxy-xmlparser-lite +* @author John Heinstein +*/ +class SAXY_Lite_Parser extends SAXY_Parser_Base { + /** + * Constructor for SAX parser + */ + function SAXY_Lite_Parser() { + $this->SAXY_Parser_Base(); + $this->state = SAXY_STATE_NONE; + } //SAXY_Lite_Parser + + /** + * Returns the current version of SAXY Lite + * @return Object The current version of SAXY Lite + */ + function getVersion() { + return SAXY_LITE_VERSION; + } //getVersion + + /** + * Processes the xml prolog, doctype, and any other nodes that exist outside of the main xml document + * @param string The xml text to be processed + * @return string The preprocessed xml text + */ + function preprocessXML($xmlText) { + //strip prolog + $xmlText = trim($xmlText); + $total = strlen($xmlText); + + for ($i = 0; $i < $total; $i++) { +// if ($xmlText{$i} == '<') { + if (substr($xmlText, $i, 1) == '<') { + switch ($xmlText{($i + 1)}) { + case '?': + case '!': + break; + default: + $this->state = SAXY_STATE_PARSING; + return (substr($xmlText, $i)); + } + } + } + } //preprocessXML + + /** + * The controlling method for the parsing process + * @param string The xml text to be processed + * @return boolean True if parsing is successful + */ + function parse ($xmlText) { + $xmlText = $this->preprocessXML($xmlText); + $total = strlen($xmlText); + + for ($i = 0; $i < $total; $i++) { +// $currentChar = $xmlText{$i}; + $currentChar = substr($xmlText, $i, 1); + + switch ($this->state) { + case SAXY_STATE_PARSING: + + switch ($currentChar) { + case '<': + if (substr($this->charContainer, 0, SAXY_CDATA_LEN) == SAXY_SEARCH_CDATA) { + $this->charContainer .= $currentChar; + } + else { + $this->parseBetweenTags($this->charContainer); + $this->charContainer = ''; + } + break; + + case '>': + if ((substr($this->charContainer, 0, SAXY_CDATA_LEN) == SAXY_SEARCH_CDATA) && + !(($this->getCharFromEnd($this->charContainer, 0) == ']') && + ($this->getCharFromEnd($this->charContainer, 1) == ']'))) { + $this->charContainer .= $currentChar; + } + else { + $this->parseTag($this->charContainer); + $this->charContainer = ''; + } + break; + + default: + $this->charContainer .= $currentChar; + } + + break; + } + } + + return true; + } //parse + + /** + * Parses an element tag + * @param string The interior text of the element tag + */ + function parseTag($tagText) { + $tagText = trim($tagText); + $firstChar = $tagText{0}; + $myAttributes = array(); + + switch ($firstChar) { + case '/': + $tagName = substr($tagText, 1); + $this->fireEndElementEvent($tagName); + break; + + case '!': + $upperCaseTagText = strtoupper($tagText); + + if (strpos($upperCaseTagText, SAXY_SEARCH_CDATA) !== false) { //CDATA Section + $total = strlen($tagText); + $openBraceCount = 0; + $textNodeText = ''; + + for ($i = 0; $i < $total; $i++) { +// $currentChar = $tagText{$i}; + $currentChar = substr($tagText, $i, 1); + + if (($currentChar == ']') && ($tagText{($i + 1)} == ']')) { + break; + } + else if ($openBraceCount > 1) { + $textNodeText .= $currentChar; + } + else if ($currentChar == '[') { //this won't be reached after the first open brace is found + $openBraceCount ++; + } + } + + if ($this->cDataSectionHandler == null) { + $this->fireCharacterDataEvent($textNodeText); + } + else { + $this->fireCDataSectionEvent($textNodeText); + } + } + else if (strpos($upperCaseTagText, SAXY_SEARCH_NOTATION) !== false) { //NOTATION node, discard + return; + } + else if (substr($tagText, 0, 2) == '!-') { //comment node, discard + return; + } + + break; + + case '?': + //Processing Instruction node, discard + return; + + default: + if ((strpos($tagText, '"') !== false) || (strpos($tagText, "'") !== false)) { + $total = strlen($tagText); + $tagName = ''; + + for ($i = 0; $i < $total; $i++) { +// $currentChar = $tagText{$i}; + $currentChar = substr($tagText, $i, 1); + + if (($currentChar == ' ') || ($currentChar == "\t") || + ($currentChar == "\n") || ($currentChar == "\r") || + ($currentChar == "\x0B")) { + $myAttributes = $this->parseAttributes(substr($tagText, $i)); + break; + } + else { + $tagName .= $currentChar; + } + } + + if (strrpos($tagText, '/') == (strlen($tagText) - 1)) { //check $tagText, but send $tagName + $this->fireStartElementEvent($tagName, $myAttributes); + $this->fireEndElementEvent($tagName); + } + else { + $this->fireStartElementEvent($tagName, $myAttributes); + } + } + else { + if (strpos($tagText, '/') !== false) { + $tagText = trim(substr($tagText, 0, (strrchr($tagText, '/') - 1))); + $this->fireStartElementEvent($tagText, $myAttributes); + $this->fireEndElementEvent($tagText); + } + else { + $this->fireStartElementEvent($tagText, $myAttributes); + } + } + } + } //parseTag + + /** + * Returns the current error code (non-functional for SAXY Lite) + * @return int The current error code + */ + function xml_get_error_code() { + return -1; + } //xml_get_error_code + + /** + * Returns a textual description of the error code (non-functional for SAXY Lite) + * @param int The error code + * @return string The error message + */ + function xml_error_string($code) { + return ""; + } //xml_error_string +} //SAXY_Lite_Parser +?> diff --git a/oss/joomla/trunk/libraries/domit/xml_saxy_parser.php b/oss/joomla/trunk/libraries/domit/xml_saxy_parser.php new file mode 100644 index 00000000..a468a909 --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/xml_saxy_parser.php @@ -0,0 +1,847 @@ + +* @link http://www.engageinteractive.com/saxy/ SAXY Home Page +* SAXY is Free Software +**/ + +if (!defined('SAXY_INCLUDE_PATH')) { + define('SAXY_INCLUDE_PATH', (dirname(__FILE__) . "/")); +} + +/** current version of SAXY */ +define ('SAXY_VERSION', '1.0'); + +/** default XML namespace */ +define ('SAXY_XML_NAMESPACE', 'http://www.w3.org/xml/1998/namespace'); + +/** saxy parse state, before prolog is encountered */ +define('SAXY_STATE_PROLOG_NONE', 0); +/** saxy parse state, in processing instruction */ +define('SAXY_STATE_PROLOG_PROCESSINGINSTRUCTION', 1); +/** saxy parse state, an exclamation mark has been encountered */ +define('SAXY_STATE_PROLOG_EXCLAMATION', 2); +/** saxy parse state, in DTD */ +define('SAXY_STATE_PROLOG_DTD', 3); +/** saxy parse state, an inline DTD */ +define('SAXY_STATE_PROLOG_INLINEDTD', 4); +/** saxy parse state, a comment */ +define('SAXY_STATE_PROLOG_COMMENT', 5); +/** saxy parse state, processing main document */ +define('SAXY_STATE_PARSING', 6); +/** saxy parse state, processing comment in main document */ +define('SAXY_STATE_PARSING_COMMENT', 7); + +//SAXY error codes; same as EXPAT error codes +/** no error */ +define('SAXY_XML_ERROR_NONE', 0); +/** out of memory error */ +define('SAXY_XML_ERROR_NO_MEMORY', 1); +/** syntax error */ +define('SAXY_XML_ERROR_SYNTAX', 2); +/** no elements in document */ +define('SAXY_XML_ERROR_NO_ELEMENTS', 3); +/** invalid token encountered error */ +define('SAXY_XML_ERROR_INVALID_TOKEN', 4); +/** unclosed token error */ +define('SAXY_XML_ERROR_UNCLOSED_TOKEN', 5); +/** partial character error */ +define('SAXY_XML_ERROR_PARTIAL_CHAR', 6); +/** mismatched tag error */ +define('SAXY_XML_ERROR_TAG_MISMATCH', 7); +/** duplicate attribute error */ +define('SAXY_XML_ERROR_DUPLICATE_ATTRIBUTE', 8); +/** junk after document element error */ +define('SAXY_XML_ERROR_JUNK_AFTER_DOC_ELEMENT', 9); +/** parameter enitity reference error */ +define('SAXY_XML_ERROR_PARAM_ENTITY_REF', 10); +/** undefined entity error */ +define('SAXY_XML_ERROR_UNDEFINED_ENTITY', 11); +/** recursive entity error */ +define('SAXY_XML_ERROR_RECURSIVE_ENTITY_REF', 12); +/** asynchronous entity error */ +define('SAXY_XML_ERROR_ASYNC_ENTITY', 13); +/** bad character reference error */ +define('SAXY_XML_ERROR_BAD_CHAR_REF', 14); +/** binary entity reference error */ +define('SAXY_XML_ERROR_BINARY_ENTITY_REF', 15); +/** attribute external entity error */ +define('SAXY_XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF', 16); +/** misplaced processing instruction error */ +define('SAXY_XML_ERROR_MISPLACED_XML_PI', 17); +/** unknown encoding error */ +define('SAXY_XML_ERROR_UNKNOWN_ENCODING', 18); +/** incorrect encoding error */ +define('SAXY_XML_ERROR_INCORRECT_ENCODING', 19); +/** unclosed CDATA Section error */ +define('SAXY_XML_ERROR_UNCLOSED_CDATA_SECTION', 20); +/** external entity handling error */ +define('SAXY_XML_ERROR_EXTERNAL_ENTITY_HANDLING', 21); + +require_once(SAXY_INCLUDE_PATH . 'xml_saxy_shared.php'); + +/** +* The SAX Parser class +* +* @package saxy-xmlparser +* @subpackage saxy-xmlparser-main +* @author John Heinstein +*/ +class SAXY_Parser extends SAXY_Parser_Base { + /** @var int The current error number */ + var $errorCode = SAXY_XML_ERROR_NONE; + /** @var Object A reference to the DocType event handler */ + var $DTDHandler = null; + /** @var Object A reference to the Comment event handler */ + var $commentHandler = null; + /** @var Object A reference to the Processing Instruction event handler */ + var $processingInstructionHandler = null; + /** @var Object A reference to the Start Namespace Declaration event handler */ + var $startNamespaceDeclarationHandler = null; + /** @var Object A reference to the End Namespace Declaration event handler */ + var $endNamespaceDeclarationHandler = null; + /** @var boolean True if SAXY takes namespaces into consideration when parsing element tags */ + var $isNamespaceAware = false; + /** @var array An indexed array containing associative arrays of namespace prefixes mapped to their namespace URIs */ + var $namespaceMap = array(); + /** @var array A stack used to determine when an end namespace event should be fired */ + var $namespaceStack = array(); + /** @var array A track used to track the uri of the current default namespace */ + var $defaultNamespaceStack = array(); + /** @var array A stack containing tag names of unclosed elements */ + var $elementNameStack = array(); + + /** + * Constructor for SAX parser + */ + function SAXY_Parser() { + $this->SAXY_Parser_Base(); + $this->state = SAXY_STATE_PROLOG_NONE; + } //SAXY_Parser + + /** + * Sets a reference to the handler for the DocType event + * @param mixed A reference to the DocType handler + */ + function xml_set_doctype_handler($handler) { + $this->DTDHandler =& $handler; + } //xml_set_doctype_handler + + /** + * Sets a reference to the handler for the Comment event + * @param mixed A reference to the Comment handler + */ + function xml_set_comment_handler($handler) { + $this->commentHandler =& $handler; + } //xml_set_comment_handler + + /** + * Sets a reference to the handler for the Processing Instruction event + * @param mixed A reference to the Processing Instruction handler + */ + function xml_set_processing_instruction_handler($handler) { + $this->processingInstructionHandler =& $handler; + } //xml_set_processing_instruction_handler + + /** + * Sets a reference to the handler for the Start Namespace Declaration event + * @param mixed A reference to the Start Namespace Declaration handler + */ + function xml_set_start_namespace_decl_handler($handler) { + $this->startNamespaceDeclarationHandler =& $handler; + } //xml_set_start_namespace_decl_handler + + /** + * Sets a reference to the handler for the End Namespace Declaration event + * @param mixed A reference to the Start Namespace Declaration handler + */ + function xml_set_end_namespace_decl_handler($handler) { + $this->endNamespaceDeclarationHandler =& $handler; + } //xml_set_end_namespace_decl_handler + + /** + * Specifies whether SAXY is namespace sensitive + * @param boolean True if SAXY is namespace aware + */ + function setNamespaceAwareness($isNamespaceAware) { + $this->isNamespaceAware =& $isNamespaceAware; + } //setNamespaceAwareness + + /** + * Returns the current version of SAXY + * @return Object The current version of SAXY + */ + function getVersion() { + return SAXY_VERSION; + } //getVersion + + /** + * Processes the xml prolog, doctype, and any other nodes that exist outside of the main xml document + * @param string The xml text to be processed + * @return string The preprocessed xml text + */ + function preprocessXML($xmlText) { + //strip prolog + $xmlText = trim($xmlText); + $startChar = -1; + $total = strlen($xmlText); + + for ($i = 0; $i < $total; $i++) { +// $currentChar = $xmlText{$i}; + $currentChar = substr($xmlText, $i, 1); + + switch ($this->state) { + case SAXY_STATE_PROLOG_NONE: + if ($currentChar == '<') { + $nextChar = $xmlText{($i + 1)}; + + if ($nextChar == '?') { + $this->state = SAXY_STATE_PROLOG_PROCESSINGINSTRUCTION; + $this->charContainer = ''; + } + else if ($nextChar == '!') { + $this->state = SAXY_STATE_PROLOG_EXCLAMATION; + $this->charContainer .= $currentChar; + break; + } + else { + $this->charContainer = ''; + $startChar = $i; + $this->state = SAXY_STATE_PARSING; + return (substr($xmlText, $startChar)); + } + } + + break; + + case SAXY_STATE_PROLOG_EXCLAMATION: + if ($currentChar == 'D') { + $this->state = SAXY_STATE_PROLOG_DTD; + $this->charContainer .= $currentChar; + } + else if ($currentChar == '-') { + $this->state = SAXY_STATE_PROLOG_COMMENT; + $this->charContainer = ''; + } + else { + //will trap ! and add it + $this->charContainer .= $currentChar; + } + + break; + + case SAXY_STATE_PROLOG_PROCESSINGINSTRUCTION: + if ($currentChar == '>') { + $this->state = SAXY_STATE_PROLOG_NONE; + $this->parseProcessingInstruction($this->charContainer); + $this->charContainer = ''; + } + else { + $this->charContainer .= $currentChar; + } + + break; + + case SAXY_STATE_PROLOG_COMMENT: + if ($currentChar == '>') { + $this->state = SAXY_STATE_PROLOG_NONE; + $this->parseComment($this->charContainer); + $this->charContainer = ''; + } + else if ($currentChar == '-') { + if ((($xmlText{($i + 1)} == '-') && ($xmlText{($i + 2)} == '>')) || + ($xmlText{($i + 1)} == '>') || + (($xmlText{($i - 1)} == '-') && ($xmlText{($i - 2)}== '!')) ){ + //do nothing + } + else { + $this->charContainer .= $currentChar; + } + } + else { + $this->charContainer .= $currentChar; + } + + break; + + case SAXY_STATE_PROLOG_DTD: + if ($currentChar == '[') { + $this->charContainer .= $currentChar; + $this->state = SAXY_STATE_PROLOG_INLINEDTD; + } + else if ($currentChar == '>') { + $this->state = SAXY_STATE_PROLOG_NONE; + + if ($this->DTDHandler != null) { + $this->fireDTDEvent($this->charContainer . $currentChar); + } + + $this->charContainer = ''; + } + else { + $this->charContainer .= $currentChar; + } + + break; + + case SAXY_STATE_PROLOG_INLINEDTD: + $previousChar = $xmlText{($i - 1)}; + + if (($currentChar == '>') && ($previousChar == ']')){ + $this->state = SAXY_STATE_PROLOG_NONE; + + if ($this->DTDHandler != null) { + $this->fireDTDEvent($this->charContainer . $currentChar); + } + + $this->charContainer = ''; + } + else { + $this->charContainer .= $currentChar; + } + + break; + + } + } + } //preprocessXML + + /** + * The controlling method for the parsing process + * @param string The xml text to be processed + * @return boolean True if parsing is successful + */ + function parse ($xmlText) { + $xmlText = $this->preprocessXML($xmlText); + $total = strlen($xmlText); + + for ($i = 0; $i < $total; $i++) { +// $currentChar = $xmlText{$i}; + $currentChar = substr($xmlText, $i, 1); + + switch ($this->state) { + case SAXY_STATE_PARSING: + switch ($currentChar) { + case '<': + if (substr($this->charContainer, 0, SAXY_CDATA_LEN) == SAXY_SEARCH_CDATA) { + $this->charContainer .= $currentChar; + } + else { + $this->parseBetweenTags($this->charContainer); + $this->charContainer = ''; + } + break; + + case '-': + if (($xmlText{($i - 1)} == '-') && ($xmlText{($i - 2)} == '!') + && ($xmlText{($i - 3)} == '<')) { + $this->state = SAXY_STATE_PARSING_COMMENT; + $this->charContainer = ''; + } + else { + $this->charContainer .= $currentChar; + } + break; + + case '>': + if ((substr($this->charContainer, 0, SAXY_CDATA_LEN) == SAXY_SEARCH_CDATA) && + !(($this->getCharFromEnd($this->charContainer, 0) == ']') && + ($this->getCharFromEnd($this->charContainer, 1) == ']'))) { + $this->charContainer .= $currentChar; + } + else { + $this->parseTag($this->charContainer); + $this->charContainer = ''; + } + break; + + default: + $this->charContainer .= $currentChar; + } + + break; + + case SAXY_STATE_PARSING_COMMENT: + switch ($currentChar) { + case '>': + if (($xmlText{($i - 1)} == '-') && ($xmlText{($i - 2)} == '-')) { + $this->fireCommentEvent(substr($this->charContainer, 0, + (strlen($this->charContainer) - 2))); + $this->charContainer = ''; + $this->state = SAXY_STATE_PARSING; + } + else { + $this->charContainer .= $currentChar; + } + break; + + default: + $this->charContainer .= $currentChar; + } + + break; + } + } + + return ($this->errorCode == 0); + } //parse + + /** + * Parses an element tag + * @param string The interior text of the element tag + */ + function parseTag($tagText) { + $tagText = trim($tagText); + $firstChar = $tagText{0}; + $myAttributes = array(); + + switch ($firstChar) { + case '/': + $tagName = substr($tagText, 1); + $this->_fireEndElementEvent($tagName); + break; + + case '!': + $upperCaseTagText = strtoupper($tagText); + + if (strpos($upperCaseTagText, SAXY_SEARCH_CDATA) !== false) { //CDATA Section + $total = strlen($tagText); + $openBraceCount = 0; + $textNodeText = ''; + + for ($i = 0; $i < $total; $i++) { +// $currentChar = $tagText{$i}; + $currentChar = substr($tagText, $i, 1); + + if (($currentChar == ']') && ($tagText{($i + 1)} == ']')) { + break; + } + else if ($openBraceCount > 1) { + $textNodeText .= $currentChar; + } + else if ($currentChar == '[') { //this won't be reached after the first open brace is found + $openBraceCount ++; + } + } + + if ($this->cDataSectionHandler == null) { + $this->fireCharacterDataEvent($textNodeText); + } + else { + $this->fireCDataSectionEvent($textNodeText); + } + } + else if (strpos($upperCaseTagText, SAXY_SEARCH_NOTATION) !== false) { //NOTATION node, discard + return; + } + /* + else if (substr($tagText, 0, 2) == '!-') { //comment node + if ($this->commentHandler != null) { + $this->fireCommentEvent(substr($tagText, 3, (strlen($tagText) - 5))); + } + } + */ + break; + + case '?': + //Processing Instruction node + $this->parseProcessingInstruction($tagText); + break; + + default: + if ((strpos($tagText, '"') !== false) || (strpos($tagText, "'") !== false)) { + $total = strlen($tagText); + $tagName = ''; + + for ($i = 0; $i < $total; $i++) { +// $currentChar = $tagText{$i}; + $currentChar = substr($tagText, $i, 1); + + if (($currentChar == ' ') || ($currentChar == "\t") || + ($currentChar == "\n") || ($currentChar == "\r") || + ($currentChar == "\x0B")) { + $myAttributes = $this->parseAttributes(substr($tagText, $i)); + break; + } + else { + $tagName .= $currentChar; + } + } + + if (strrpos($tagText, '/') == (strlen($tagText) - 1)) { //check $tagText, but send $tagName + $this->_fireStartElementEvent($tagName, $myAttributes); + $this->_fireEndElementEvent($tagName); + } + else { + $this->_fireStartElementEvent($tagName, $myAttributes); + } + } + else { + if (strpos($tagText, '/') !== false) { + $tagText = trim(substr($tagText, 0, (strrchr($tagText, '/') - 1))); + $this->_fireStartElementEvent($tagText, $myAttributes); + $this->_fireEndElementEvent($tagText); + } + else { + $this->_fireStartElementEvent($tagText, $myAttributes); + } + } + } + } //parseTag + + /** + * Fires a start element event and pushes the element name onto the elementName stack + * @param string The start element tag name + * @param Array The start element attributes + */ + function _fireStartElementEvent($tagName, &$myAttributes) { + $this->elementNameStack[] = $tagName; + + if ($this->isNamespaceAware) { + $this->detectStartNamespaceDeclaration($myAttributes); + $tagName = $this->expandNamespacePrefix($tagName); + + $this->expandAttributePrefixes($myAttributes); + } + + $this->fireStartElementEvent($tagName, $myAttributes); + } //_fireStartElementEvent + + /** + * Expands attribute prefixes to full namespace uri + * @param Array The start element attributes + */ + function expandAttributePrefixes(&$myAttributes) { + $arTransform = array(); + + foreach ($myAttributes as $key => $value) { + if (strpos($key, 'xmlns') === false) { + if (strpos($key, ':') !== false) { + $expandedTag = $this->expandNamespacePrefix($key); + $arTransform[$key] = $expandedTag; + } + } + } + + foreach ($arTransform as $key => $value) { + $myAttributes[$value] = $myAttributes[$key]; + unset($myAttributes[$key]); + } + } //expandAttributePrefixes + + /** + * Expands the namespace prefix (if one exists) to the full namespace uri + * @param string The tagName with the namespace prefix + * @return string The tagName, with the prefix expanded to the namespace uri + */ + function expandNamespacePrefix($tagName) { + $stackLen = count($this->defaultNamespaceStack); + $defaultNamespace = $this->defaultNamespaceStack[($stackLen - 1)]; + + $colonIndex = strpos($tagName, ':'); + + if ($colonIndex !== false) { + $prefix = substr($tagName, 0, $colonIndex); + + if ($prefix != 'xml') { + $tagName = $this->getNamespaceURI($prefix) . substr($tagName, $colonIndex); + } + else { + $tagName = SAXY_XML_NAMESPACE . substr($tagName, $colonIndex); + } + } + else if ($defaultNamespace != '') { + $tagName = $defaultNamespace . ':' . $tagName; + } + + return $tagName; + } //expandNamespacePrefix + + /** + * Searches the namespaceMap for the specified prefix, and returns the full namespace URI + * @param string The namespace prefix + * @return string The namespace uri + */ + function getNamespaceURI($prefix) { + $total = count($this->namespaceMap); + $uri = $prefix; //in case uri can't be found, just send back prefix + //should really generate an error, but worry about this later + //reset($this->namespaceMap); + + for ($i = ($total - 1); $i >= 0; $i--) { + $currMap =& $this->namespaceMap[$i]; + + if (isset($currMap[$prefix])) { + $uri = $currMap[$prefix]; + break; + } + } + + return $uri; + } //getNamespaceURI + + /** + * Searches the attributes array for an xmlns declaration and fires an event if found + * @param Array The start element attributes + */ + function detectStartNamespaceDeclaration(&$myAttributes) { + $namespaceExists = false; + $namespaceMapUpper = 0; + $userDefinedDefaultNamespace = false; + $total = count($myAttributes); + + foreach ($myAttributes as $key => $value) { + if (strpos($key, 'xmlns') !== false) { + //add an array to store all namespaces for the current element + if (!$namespaceExists) { + $this->namespaceMap[] = array(); + $namespaceMapUpper = count($this->namespaceMap) - 1; + } + + //check for default namespace override, i.e. xmlns='...' + if (strpos($key, ':') !== false) { + $prefix = $namespaceMapKey = substr($key, 6); + $this->namespaceMap[$namespaceMapUpper][$namespaceMapKey] = $value; + } + else { + $prefix = ''; + $userDefinedDefaultNamespace = true; + + //if default namespace '', store in map using key ':' + $this->namespaceMap[$namespaceMapUpper][':'] = $value; + $this->defaultNamespaceStack[] = $value; + } + + $this->fireStartNamespaceDeclarationEvent($prefix, $value); + $namespaceExists = true; + + unset($myAttributes[$key]); + } + } + + //store the default namespace (inherited from the parent elements so grab last one) + if (!$userDefinedDefaultNamespace) { + $stackLen = count($this->defaultNamespaceStack); + if ($stackLen == 0) { + $this->defaultNamespaceStack[] = ''; + } + else { + $this->defaultNamespaceStack[] = + $this->defaultNamespaceStack[($stackLen - 1)]; + } + } + + $this->namespaceStack[] = $namespaceExists; + } //detectStartNamespaceDeclaration + + /** + * Fires an end element event and pops the element name from the elementName stack + * @param string The end element tag name + */ + function _fireEndElementEvent($tagName) { + $lastTagName = array_pop($this->elementNameStack); + + //check for mismatched tag error + if ($lastTagName != $tagName) { + $this->errorCode = SAXY_XML_ERROR_TAG_MISMATCH; + } + + if ($this->isNamespaceAware) { + $tagName = $this->expandNamespacePrefix($tagName); + $this->fireEndElementEvent($tagName); + $this->detectEndNamespaceDeclaration(); + $defaultNamespace = array_pop($this->defaultNamespaceStack); + } + else { + $this->fireEndElementEvent($tagName); + } + } //_fireEndElementEvent + + /** + * Determines whether an end namespace declaration event should be fired + */ + function detectEndNamespaceDeclaration() { + $isNamespaceEnded = array_pop($this->namespaceStack); + + if ($isNamespaceEnded) { + $map = array_pop($this->namespaceMap); + + foreach ($map as $key => $value) { + if ($key == ':') { + $key = ''; + } + $this->fireEndNamespaceDeclarationEvent($key); + } + } + } //detectEndNamespaceDeclaration + + /** + * Parses a processing instruction + * @param string The interior text of the processing instruction + */ + function parseProcessingInstruction($data) { + $endTarget = 0; + $total = strlen($data); + + for ($x = 2; $x < $total; $x++) { +// if (trim($data{$x}) == '') { + if (trim(substr($data, $x, 1)) == '') { + $endTarget = $x; + break; + } + } + + $target = substr($data, 1, ($endTarget - 1)); + $data = substr($data, ($endTarget + 1), ($total - $endTarget - 2)); + + if ($this->processingInstructionHandler != null) { + $this->fireProcessingInstructionEvent($target, $data); + } + } //parseProcessingInstruction + + /** + * Parses a comment + * @param string The interior text of the comment + */ + function parseComment($data) { + if ($this->commentHandler != null) { + $this->fireCommentEvent($data); + } + } //parseComment + + /** + * Fires a doctype event + * @param string The doctype data + */ + function fireDTDEvent($data) { + call_user_func($this->DTDHandler, $this, $data); + } //fireDTDEvent + + /** + * Fires a comment event + * @param string The text of the comment + */ + function fireCommentEvent($data) { + call_user_func($this->commentHandler, $this, $data); + } //fireCommentEvent + + /** + * Fires a processing instruction event + * @param string The processing instruction data + */ + function fireProcessingInstructionEvent($target, $data) { + call_user_func($this->processingInstructionHandler, $this, $target, $data); + } //fireProcessingInstructionEvent + + /** + * Fires a start namespace declaration event + * @param string The namespace prefix + * @param string The namespace uri + */ + function fireStartNamespaceDeclarationEvent($prefix, $uri) { + call_user_func($this->startNamespaceDeclarationHandler, $this, $prefix, $uri); + } //fireStartNamespaceDeclarationEvent + + /** + * Fires an end namespace declaration event + * @param string The namespace prefix + */ + function fireEndNamespaceDeclarationEvent($prefix) { + call_user_func($this->endNamespaceDeclarationHandler, $this, $prefix); + } //fireEndNamespaceDeclarationEvent + + /** + * Returns the current error code + * @return int The current error code + */ + function xml_get_error_code() { + return $this->errorCode; + } //xml_get_error_code + + /** + * Returns a textual description of the error code + * @param int The error code + * @return string The error message + */ + function xml_error_string($code) { + switch ($code) { + case SAXY_XML_ERROR_NONE: + return "No error"; + break; + case SAXY_XML_ERROR_NO_MEMORY: + return "Out of memory"; + break; + case SAXY_XML_ERROR_SYNTAX: + return "Syntax error"; + break; + case SAXY_XML_ERROR_NO_ELEMENTS: + return "No elements in document"; + break; + case SAXY_XML_ERROR_INVALID_TOKEN: + return "Invalid token"; + break; + case SAXY_XML_ERROR_UNCLOSED_TOKEN: + return "Unclosed token"; + break; + case SAXY_XML_ERROR_PARTIAL_CHAR: + return "Partial character"; + break; + case SAXY_XML_ERROR_TAG_MISMATCH: + return "Tag mismatch"; + break; + case SAXY_XML_ERROR_DUPLICATE_ATTRIBUTE: + return "Duplicate attribute"; + break; + case SAXY_XML_ERROR_JUNK_AFTER_DOC_ELEMENT: + return "Junk encountered after document element"; + break; + case SAXY_XML_ERROR_PARAM_ENTITY_REF: + return "Parameter entity reference error"; + break; + case SAXY_XML_ERROR_UNDEFINED_ENTITY: + return "Undefined entity"; + break; + case SAXY_XML_ERROR_RECURSIVE_ENTITY_REF: + return "Recursive entity reference"; + break; + case SAXY_XML_ERROR_ASYNC_ENTITY: + return "Asynchronous internal entity found in external entity"; + break; + case SAXY_XML_ERROR_BAD_CHAR_REF: + return "Bad character reference"; + break; + case SAXY_XML_ERROR_BINARY_ENTITY_REF: + return "Binary entity reference"; + break; + case SAXY_XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF: + return "Attribute external entity reference"; + break; + case SAXY_XML_ERROR_MISPLACED_XML_PI: + return "Misplaced processing instruction"; + break; + case SAXY_XML_ERROR_UNKNOWN_ENCODING: + return "Unknown encoding"; + break; + case SAXY_XML_ERROR_INCORRECT_ENCODING: + return "Incorrect encoding"; + break; + case SAXY_XML_ERROR_UNCLOSED_CDATA_SECTION: + return "Unclosed CDATA Section"; + break; + case SAXY_XML_ERROR_EXTERNAL_ENTITY_HANDLING: + return "Problem in external entity handling"; + break; + default: + return "No definition for error code " . $code; + break; + } + } //xml_error_string + +} //SAXY_Parser +?> diff --git a/oss/joomla/trunk/libraries/domit/xml_saxy_shared.php b/oss/joomla/trunk/libraries/domit/xml_saxy_shared.php new file mode 100644 index 00000000..6d760077 --- /dev/null +++ b/oss/joomla/trunk/libraries/domit/xml_saxy_shared.php @@ -0,0 +1,294 @@ + +* @link http://www.engageinteractive.com/saxy/ SAXY Home Page +* SAXY is Free Software +**/ + +/** the initial characters of a cdata section */ +define('SAXY_SEARCH_CDATA', '![CDATA['); +/** the length of the initial characters of a cdata section */ +define('SAXY_CDATA_LEN', 8); +/** the initial characters of a notation */ +define('SAXY_SEARCH_NOTATION', '!NOTATION'); +/** the initial characters of a doctype */ +define('SAXY_SEARCH_DOCTYPE', '!DOCTYPE'); +/** saxy parse state, just before parsing an attribute */ +define('SAXY_STATE_ATTR_NONE', 0); +/** saxy parse state, parsing an attribute key */ +define('SAXY_STATE_ATTR_KEY', 1); +/** saxy parse state, parsing an attribute value */ +define('SAXY_STATE_ATTR_VALUE', 2); + +/** +* The base SAX Parser class +* +* @package saxy-xmlparser +* @author John Heinstein +*/ +class SAXY_Parser_Base { + /** @var int The current state of the parser */ + var $state; + /** @var int A temporary container for parsed characters */ + var $charContainer; + /** @var Object A reference to the start event handler */ + var $startElementHandler; + /** @var Object A reference to the end event handler */ + var $endElementHandler; + /** @var Object A reference to the data event handler */ + var $characterDataHandler; + /** @var Object A reference to the CDATA Section event handler */ + var $cDataSectionHandler = null; + /** @var boolean True if predefined entities are to be converted into characters */ + var $convertEntities = true; + /** @var Array Translation table for predefined entities */ + var $predefinedEntities = array('&' => '&', '<' => '<', '>' => '>', + '"' => '"', ''' => "'"); + /** @var Array User defined translation table for entities */ + var $definedEntities = array(); + /** @var boolean True if whitespace is to be preserved during parsing. NOT YET IMPLEMENTED! */ + var $preserveWhitespace = false; + + + /** + * Constructor for SAX parser + */ + function SAXY_Parser_Base() { + $this->charContainer = ''; + } //SAXY_Parser_Base + + /** + * Sets a reference to the handler for the start element event + * @param mixed A reference to the start element handler + */ + function xml_set_element_handler($startHandler, $endHandler) { + $this->startElementHandler = $startHandler; + $this->endElementHandler = $endHandler; + } //xml_set_element_handler + + /** + * Sets a reference to the handler for the data event + * @param mixed A reference to the data handler + */ + function xml_set_character_data_handler($handler) { + $this->characterDataHandler =& $handler; + } //xml_set_character_data_handler + + /** + * Sets a reference to the handler for the CDATA Section event + * @param mixed A reference to the CDATA Section handler + */ + function xml_set_cdata_section_handler($handler) { + $this->cDataSectionHandler =& $handler; + } //xml_set_cdata_section_handler + + /** + * Sets whether predefined entites should be replaced with their equivalent characters during parsing + * @param boolean True if entity replacement is to occur + */ + function convertEntities($truthVal) { + $this->convertEntities = $truthVal; + } //convertEntities + + /** + * Appends an array of entity mappings to the existing translation table + * + * Intended mainly to facilitate the conversion of non-ASCII entities into equivalent characters + * + * @param array A list of entity mappings in the format: array('&' => '&'); + */ + function appendEntityTranslationTable($table) { + $this->definedEntities = $table; + } //appendEntityTranslationTable + + + /** + * Gets the nth character from the end of the string + * @param string The text to be queried + * @param int The index from the end of the string + * @return string The found character + */ + function getCharFromEnd($text, $index) { + $len = strlen($text); + $char = $text{($len - 1 - $index)}; + + return $char; + } //getCharFromEnd + + /** + * Parses the attributes string into an array of key / value pairs + * @param string The attribute text + * @return Array An array of key / value pairs + */ + function parseAttributes($attrText) { + $attrText = trim($attrText); + $attrArray = array(); + $maybeEntity = false; + + $total = strlen($attrText); + $keyDump = ''; + $valueDump = ''; + $currentState = SAXY_STATE_ATTR_NONE; + $quoteType = ''; + + for ($i = 0; $i < $total; $i++) { +// $currentChar = $attrText{$i}; + $currentChar = substr($attrText, $i, 1); + + if ($currentState == SAXY_STATE_ATTR_NONE) { + if (trim($currentChar != '')) { + $currentState = SAXY_STATE_ATTR_KEY; + } + } + + switch ($currentChar) { + case "\t": + if ($currentState == SAXY_STATE_ATTR_VALUE) { + $valueDump .= $currentChar; + } + else { + $currentChar = ''; + } + break; + + case "\x0B": //vertical tab + case "\n": + case "\r": + $currentChar = ''; + break; + + case '=': + if ($currentState == SAXY_STATE_ATTR_VALUE) { + $valueDump .= $currentChar; + } + else { + $currentState = SAXY_STATE_ATTR_VALUE; + $quoteType = ''; + $maybeEntity = false; + } + break; + + case '"': + if ($currentState == SAXY_STATE_ATTR_VALUE) { + if ($quoteType == '') { + $quoteType = '"'; + } + else { + if ($quoteType == $currentChar) { + // Joomla! hack + if (isset( $this ) && $this->convertEntities && $maybeEntity) { + $valueDump = strtr($valueDump, $this->predefinedEntities); + $valueDump = strtr($valueDump, $this->definedEntities); + } + + $keyDump = trim($keyDump); + $attrArray[$keyDump] = $valueDump; + $keyDump = $valueDump = $quoteType = ''; + $currentState = SAXY_STATE_ATTR_NONE; + } + else { + $valueDump .= $currentChar; + } + } + } + break; + + case "'": + if ($currentState == SAXY_STATE_ATTR_VALUE) { + if ($quoteType == '') { + $quoteType = "'"; + } + else { + if ($quoteType == $currentChar) { + // Joomla! hack + if (isset( $this ) && $this->convertEntities && $maybeEntity) { + $valueDump = strtr($valueDump, $this->predefinedEntities); + $valueDump = strtr($valueDump, $this->definedEntities); + } + + $keyDump = trim($keyDump); + $attrArray[$keyDump] = $valueDump; + $keyDump = $valueDump = $quoteType = ''; + $currentState = SAXY_STATE_ATTR_NONE; + } + else { + $valueDump .= $currentChar; + } + } + } + break; + + case '&': + //might be an entity + $maybeEntity = true; + $valueDump .= $currentChar; + break; + + default: + if ($currentState == SAXY_STATE_ATTR_KEY) { + $keyDump .= $currentChar; + } + else { + $valueDump .= $currentChar; + } + } + } + + return $attrArray; + } //parseAttributes + + /** + * Parses character data + * @param string The character data + */ + function parseBetweenTags($betweenTagText) { + if (trim($betweenTagText) != ''){ + $this->fireCharacterDataEvent($betweenTagText); + } + } //parseBetweenTags + + /** + * Fires a start element event + * @param string The start element tag name + * @param Array The start element attributes + */ + function fireStartElementEvent($tagName, $attributes) { + call_user_func($this->startElementHandler, $this, $tagName, $attributes); + } //fireStartElementEvent + + /** + * Fires an end element event + * @param string The end element tag name + */ + function fireEndElementEvent($tagName) { + call_user_func($this->endElementHandler, $this, $tagName); + } //fireEndElementEvent + + /** + * Fires a character data event + * @param string The character data + */ + function fireCharacterDataEvent($data) { + if ($this->convertEntities && ((strpos($data, "&") != -1))) { + $data = strtr($data, $this->predefinedEntities); + $data = strtr($data, $this->definedEntities); + } + + call_user_func($this->characterDataHandler, $this, $data); + } //fireCharacterDataEvent + + /** + * Fires a CDATA Section event + * @param string The CDATA Section data + */ + function fireCDataSectionEvent($data) { + call_user_func($this->cDataSectionHandler, $this, $data); + } //fireCDataSectionEvent +} //SAXY_Parser_Base + +?> diff --git a/oss/joomla/trunk/libraries/geshi/geshi.php b/oss/joomla/trunk/libraries/geshi/geshi.php new file mode 100644 index 00000000..f5bdcb9a --- /dev/null +++ b/oss/joomla/trunk/libraries/geshi/geshi.php @@ -0,0 +1,2832 @@ + + * @copyright (C) 2004 - 2007 Nigel McNie + * @license http://gnu.org/copyleft/gpl.html GNU GPL + * + */ + +// +// GeSHi Constants +// You should use these constant names in your programs instead of +// their values - you never know when a value may change in a future +// version +// + +/** The version of this GeSHi file */ +define('GESHI_VERSION', '1.0.7.19'); + +// Define the root directory for the GeSHi code tree +if (!defined('GESHI_ROOT')) { + /** The root directory for GeSHi */ + define('GESHI_ROOT', dirname(__FILE__) . DIRECTORY_SEPARATOR); +} +/** The language file directory for GeSHi + @access private */ +define('GESHI_LANG_ROOT', GESHI_ROOT . 'geshi' . DIRECTORY_SEPARATOR); + + +// Line numbers - use with enable_line_numbers() +/** Use no line numbers when building the result */ +define('GESHI_NO_LINE_NUMBERS', 0); +/** Use normal line numbers when building the result */ +define('GESHI_NORMAL_LINE_NUMBERS', 1); +/** Use fancy line numbers when building the result */ +define('GESHI_FANCY_LINE_NUMBERS', 2); + +// Container HTML type +/** Use nothing to surround the source */ +define('GESHI_HEADER_NONE', 0); +/** Use a "div" to surround the source */ +define('GESHI_HEADER_DIV', 1); +/** Use a "pre" to surround the source */ +define('GESHI_HEADER_PRE', 2); + +// Capatalisation constants +/** Lowercase keywords found */ +define('GESHI_CAPS_NO_CHANGE', 0); +/** Uppercase keywords found */ +define('GESHI_CAPS_UPPER', 1); +/** Leave keywords found as the case that they are */ +define('GESHI_CAPS_LOWER', 2); + +// Link style constants +/** Links in the source in the :link state */ +define('GESHI_LINK', 0); +/** Links in the source in the :hover state */ +define('GESHI_HOVER', 1); +/** Links in the source in the :active state */ +define('GESHI_ACTIVE', 2); +/** Links in the source in the :visited state */ +define('GESHI_VISITED', 3); + +// Important string starter/finisher +// Note that if you change these, they should be as-is: i.e., don't +// write them as if they had been run through htmlentities() +/** The starter for important parts of the source */ +define('GESHI_START_IMPORTANT', ''); +/** The ender for important parts of the source */ +define('GESHI_END_IMPORTANT', ''); + +/**#@+ + * @access private + */ +// When strict mode applies for a language +/** Strict mode never applies (this is the most common) */ +define('GESHI_NEVER', 0); +/** Strict mode *might* apply, and can be enabled or + disabled by {@link GeSHi::enable_strict_mode()} */ +define('GESHI_MAYBE', 1); +/** Strict mode always applies */ +define('GESHI_ALWAYS', 2); + +// Advanced regexp handling constants, used in language files +/** The key of the regex array defining what to search for */ +define('GESHI_SEARCH', 0); +/** The key of the regex array defining what bracket group in a + matched search to use as a replacement */ +define('GESHI_REPLACE', 1); +/** The key of the regex array defining any modifiers to the regular expression */ +define('GESHI_MODIFIERS', 2); +/** The key of the regex array defining what bracket group in a + matched search to put before the replacement */ +define('GESHI_BEFORE', 3); +/** The key of the regex array defining what bracket group in a + matched search to put after the replacement */ +define('GESHI_AFTER', 4); +/** The key of the regex array defining a custom keyword to use + for this regexp's html tag class */ +define('GESHI_CLASS', 5); + +/** Used in language files to mark comments */ +define('GESHI_COMMENTS', 0); + +// Error detection - use these to analyse faults +/** No sourcecode to highlight was specified + * @deprecated + */ +define('GESHI_ERROR_NO_INPUT', 1); +/** The language specified does not exist */ +define('GESHI_ERROR_NO_SUCH_LANG', 2); +/** GeSHi could not open a file for reading (generally a language file) */ +define('GESHI_ERROR_FILE_NOT_READABLE', 3); +/** The header type passed to {@link GeSHi::set_header_type()} was invalid */ +define('GESHI_ERROR_INVALID_HEADER_TYPE', 4); +/** The line number type passed to {@link GeSHi::enable_line_numbers()} was invalid */ +define('GESHI_ERROR_INVALID_LINE_NUMBER_TYPE', 5); +/**#@-*/ + + +/** + * The GeSHi Class. + * + * Please refer to the documentation for GeSHi 1.0.X that is available + * at http://qbnz.com/highlighter/documentation.php for more information + * about how to use this class. + * + * @package geshi + * @author Nigel McNie + * @copyright (C) 2004 - 2007 Nigel McNie + */ +class GeSHi { + /**#@+ + * @access private + */ + /** + * The source code to highlight + * @var string + */ + var $source = ''; + + /** + * The language to use when highlighting + * @var string + */ + var $language = ''; + + /** + * The data for the language used + * @var array + */ + var $language_data = array(); + + /** + * The path to the language files + * @var string + */ + var $language_path = GESHI_LANG_ROOT; + + /** + * The error message associated with an error + * @var string + * @todo check err reporting works + */ + var $error = false; + + /** + * Possible error messages + * @var array + */ + var $error_messages = array( + GESHI_ERROR_NO_SUCH_LANG => 'GeSHi could not find the language {LANGUAGE} (using path {PATH})', + GESHI_ERROR_FILE_NOT_READABLE => 'The file specified for load_from_file was not readable', + GESHI_ERROR_INVALID_HEADER_TYPE => 'The header type specified is invalid', + GESHI_ERROR_INVALID_LINE_NUMBER_TYPE => 'The line number type specified is invalid' + ); + + /** + * Whether highlighting is strict or not + * @var boolean + */ + var $strict_mode = false; + + /** + * Whether to use CSS classes in output + * @var boolean + */ + var $use_classes = false; + + /** + * The type of header to use. Can be one of the following + * values: + * + * - GESHI_HEADER_PRE: Source is outputted in a "pre" HTML element. + * - GESHI_HEADER_DIV: Source is outputted in a "div" HTML element. + * - GESHI_HEADER_NONE: No header is outputted. + * + * @var int + */ + var $header_type = GESHI_HEADER_PRE; + + /** + * Array of permissions for which lexics should be highlighted + * @var array + */ + var $lexic_permissions = array( + 'KEYWORDS' => array(), + 'COMMENTS' => array('MULTI' => true), + 'REGEXPS' => array(), + 'ESCAPE_CHAR' => true, + 'BRACKETS' => true, + 'SYMBOLS' => true, + 'STRINGS' => true, + 'NUMBERS' => true, + 'METHODS' => true, + 'SCRIPT' => true + ); + + /** + * The time it took to parse the code + * @var double + */ + var $time = 0; + + /** + * The content of the header block + * @var string + */ + var $header_content = ''; + + /** + * The content of the footer block + * @var string + */ + var $footer_content = ''; + + /** + * The style of the header block + * @var string + */ + var $header_content_style = ''; + + /** + * The style of the footer block + * @var string + */ + var $footer_content_style = ''; + + /** + * The styles for hyperlinks in the code + * @var array + */ + var $link_styles = array(); + + /** + * Whether important blocks should be recognised or not + * @var boolean + * @deprecated + * @todo REMOVE THIS FUNCTIONALITY! + */ + var $enable_important_blocks = false; + + /** + * Styles for important parts of the code + * @var string + * @deprecated + * @todo As above - rethink the whole idea of important blocks as it is buggy and + * will be hard to implement in 1.2 + */ + var $important_styles = 'font-weight: bold; color: red;'; // Styles for important parts of the code + + /** + * Whether CSS IDs should be added to the code + * @var boolean + */ + var $add_ids = false; + + /** + * Lines that should be highlighted extra + * @var array + */ + var $highlight_extra_lines = array(); + + /** + * Styles of extra-highlighted lines + * @var string + */ + var $highlight_extra_lines_style = 'color: #cc0; background-color: #ffc;'; + + /** + * Number at which line numbers should start at + * @var int + */ + var $line_numbers_start = 1; + + /** + * The overall style for this code block + * @var string + */ + var $overall_style = ''; + + /** + * The style for the actual code + * @var string + */ + var $code_style = 'font-family: \'Courier New\', Courier, monospace; font-weight: normal;'; + + /** + * The overall class for this code block + * @var string + */ + var $overall_class = ''; + + /** + * The overall ID for this code block + * @var string + */ + var $overall_id = ''; + + /** + * Line number styles + * @var string + */ + var $line_style1 = 'font-family: \'Courier New\', Courier, monospace; color: black; font-weight: normal; font-style: normal;'; + + /** + * Line number styles for fancy lines + * @var string + */ + var $line_style2 = 'font-weight: bold;'; + + /** + * Flag for how line nubmers are displayed + * @var boolean + */ + var $line_numbers = GESHI_NO_LINE_NUMBERS; + + /** + * The "nth" value for fancy line highlighting + * @var int + */ + var $line_nth_row = 0; + + /** + * The size of tab stops + * @var int + */ + var $tab_width = 8; + + /** + * Default target for keyword links + * @var string + */ + var $link_target = ''; + + /** + * The encoding to use for entity encoding + * NOTE: no longer used + * @var string + */ + var $encoding = 'utf-8'; + + /** + * Should keywords be linked? + * @var boolean + */ + var $keyword_links = true; + + /**#@-*/ + + /** + * Creates a new GeSHi object, with source and language + * + * @param string The source code to highlight + * @param string The language to highlight the source with + * @param string The path to the language file directory. This + * is deprecated! I've backported the auto path + * detection from the 1.1.X dev branch, so now it + * should be automatically set correctly. If you have + * renamed the language directory however, you will + * still need to set the path using this parameter or + * {@link GeSHi::set_language_path()} + * @since 1.0.0 + */ + function GeSHi($source, $language, $path = '') { + $this->set_source($source); + $this->set_language_path($path); + $this->set_language($language); + } + + /** + * Returns an error message associated with the last GeSHi operation, + * or false if no error has occured + * + * @return string|false An error message if there has been an error, else false + * @since 1.0.0 + */ + function error() { + if ($this->error) { + $msg = $this->error_messages[$this->error]; + $debug_tpl_vars = array( + '{LANGUAGE}' => $this->language, + '{PATH}' => $this->language_path + ); + foreach ($debug_tpl_vars as $tpl => $var) { + $msg = str_replace($tpl, $var, $msg); + } + return "
GeSHi Error: $msg (code $this->error)
"; + } + return false; + } + + /** + * Gets a human-readable language name (thanks to Simon Patterson + * for the idea :)) + * + * @return string The name for the current language + * @since 1.0.2 + */ + function get_language_name() { + if (GESHI_ERROR_NO_SUCH_LANG == $this->error) { + return $this->language_data['LANG_NAME'] . ' (Unknown Language)'; + } + return $this->language_data['LANG_NAME']; + } + + /** + * Sets the source code for this object + * + * @param string The source code to highlight + * @since 1.0.0 + */ + function set_source($source) { + $this->source = $source; + $this->highlight_extra_lines = array(); + } + + /** + * Sets the language for this object + * + * @param string The name of the language to use + * @since 1.0.0 + */ + function set_language($language) { + $this->error = false; + $this->strict_mode = GESHI_NEVER; + + $language = preg_replace('#[^a-zA-Z0-9\-_]#', '', $language); + $this->language = strtolower($language); + + $file_name = $this->language_path . $this->language . '.php'; + if (!is_readable($file_name)) { + $this->error = GESHI_ERROR_NO_SUCH_LANG; + return; + } + // Load the language for parsing + $this->load_language($file_name); + } + + /** + * Sets the path to the directory containing the language files. Note + * that this path is relative to the directory of the script that included + * geshi.php, NOT geshi.php itself. + * + * @param string The path to the language directory + * @since 1.0.0 + * @deprecated The path to the language files should now be automatically + * detected, so this method should no longer be needed. The + * 1.1.X branch handles manual setting of the path differently + * so this method will disappear in 1.2.0. + */ + function set_language_path($path) { + if ($path) { + $this->language_path = ('/' == substr($path, strlen($path) - 1, 1)) ? $path : $path . '/'; + $this->set_language($this->language); // otherwise set_language_path has no effect + } + } + + /** + * Sets the type of header to be used. + * + * If GESHI_HEADER_DIV is used, the code is surrounded in a "div".This + * means more source code but more control over tab width and line-wrapping. + * GESHI_HEADER_PRE means that a "pre" is used - less source, but less + * control. Default is GESHI_HEADER_PRE. + * + * From 1.0.7.2, you can use GESHI_HEADER_NONE to specify that no header code + * should be outputted. + * + * @param int The type of header to be used + * @since 1.0.0 + */ + function set_header_type($type) { + if (GESHI_HEADER_DIV != $type && GESHI_HEADER_PRE != $type && GESHI_HEADER_NONE != $type) { + $this->error = GESHI_ERROR_INVALID_HEADER_TYPE; + return; + } + $this->header_type = $type; + // Set a default overall style if the header is a
+ if (GESHI_HEADER_DIV == $type && !$this->overall_style) { + $this->overall_style = 'font-family: monospace;'; + } + } + + /** + * Sets the styles for the code that will be outputted + * when this object is parsed. The style should be a + * string of valid stylesheet declarations + * + * @param string The overall style for the outputted code block + * @param boolean Whether to merge the styles with the current styles or not + * @since 1.0.0 + */ + function set_overall_style($style, $preserve_defaults = false) { + if (!$preserve_defaults) { + $this->overall_style = $style; + } + else { + $this->overall_style .= $style; + } + } + + /** + * Sets the overall classname for this block of code. This + * class can then be used in a stylesheet to style this object's + * output + * + * @param string The class name to use for this block of code + * @since 1.0.0 + */ + function set_overall_class($class) { + $this->overall_class = $class; + } + + /** + * Sets the overall id for this block of code. This id can then + * be used in a stylesheet to style this object's output + * + * @param string The ID to use for this block of code + * @since 1.0.0 + */ + function set_overall_id($id) { + $this->overall_id = $id; + } + + /** + * Sets whether CSS classes should be used to highlight the source. Default + * is off, calling this method with no arguments will turn it on + * + * @param boolean Whether to turn classes on or not + * @since 1.0.0 + */ + function enable_classes($flag = true) { + $this->use_classes = ($flag) ? true : false; + } + + /** + * Sets the style for the actual code. This should be a string + * containing valid stylesheet declarations. If $preserve_defaults is + * true, then styles are merged with the default styles, with the + * user defined styles having priority + * + * Note: Use this method to override any style changes you made to + * the line numbers if you are using line numbers, else the line of + * code will have the same style as the line number! Consult the + * GeSHi documentation for more information about this. + * + * @param string The style to use for actual code + * @param boolean Whether to merge the current styles with the new styles + */ + function set_code_style($style, $preserve_defaults = false) { + if (!$preserve_defaults) { + $this->code_style = $style; + } + else { + $this->code_style .= $style; + } + } + + /** + * Sets the styles for the line numbers. + * + * @param string The style for the line numbers that are "normal" + * @param string|boolean If a string, this is the style of the line + * numbers that are "fancy", otherwise if boolean then this + * defines whether the normal styles should be merged with the + * new normal styles or not + * @param boolean If set, is the flag for whether to merge the "fancy" + * styles with the current styles or not + * @since 1.0.2 + */ + function set_line_style($style1, $style2 = '', $preserve_defaults = false) { + if (is_bool($style2)) { + $preserve_defaults = $style2; + $style2 = ''; + } + if (!$preserve_defaults) { + $this->line_style1 = $style1; + $this->line_style2 = $style2; + } + else { + $this->line_style1 .= $style1; + $this->line_style2 .= $style2; + } + } + + /** + * Sets whether line numbers should be displayed. + * + * Valid values for the first parameter are: + * + * - GESHI_NO_LINE_NUMBERS: Line numbers will not be displayed + * - GESHI_NORMAL_LINE_NUMBERS: Line numbers will be displayed + * - GESHI_FANCY_LINE_NUMBERS: Fancy line numbers will be displayed + * + * For fancy line numbers, the second parameter is used to signal which lines + * are to be fancy. For example, if the value of this parameter is 5 then every + * 5th line will be fancy. + * + * @param int How line numbers should be displayed + * @param int Defines which lines are fancy + * @since 1.0.0 + */ + function enable_line_numbers($flag, $nth_row = 5) { + if (GESHI_NO_LINE_NUMBERS != $flag && GESHI_NORMAL_LINE_NUMBERS != $flag + && GESHI_FANCY_LINE_NUMBERS != $flag) { + $this->error = GESHI_ERROR_INVALID_LINE_NUMBER_TYPE; + } + $this->line_numbers = $flag; + $this->line_nth_row = $nth_row; + } + + /** + * Sets the style for a keyword group. If $preserve_defaults is + * true, then styles are merged with the default styles, with the + * user defined styles having priority + * + * @param int The key of the keyword group to change the styles of + * @param string The style to make the keywords + * @param boolean Whether to merge the new styles with the old or just + * to overwrite them + * @since 1.0.0 + */ + function set_keyword_group_style($key, $style, $preserve_defaults = false) { + if (!$preserve_defaults) { + $this->language_data['STYLES']['KEYWORDS'][$key] = $style; + } + else { + $this->language_data['STYLES']['KEYWORDS'][$key] .= $style; + } + } + + /** + * Turns highlighting on/off for a keyword group + * + * @param int The key of the keyword group to turn on or off + * @param boolean Whether to turn highlighting for that group on or off + * @since 1.0.0 + */ + function set_keyword_group_highlighting($key, $flag = true) { + $this->lexic_permissions['KEYWORDS'][$key] = ($flag) ? true : false; + } + + /** + * Sets the styles for comment groups. If $preserve_defaults is + * true, then styles are merged with the default styles, with the + * user defined styles having priority + * + * @param int The key of the comment group to change the styles of + * @param string The style to make the comments + * @param boolean Whether to merge the new styles with the old or just + * to overwrite them + * @since 1.0.0 + */ + function set_comments_style($key, $style, $preserve_defaults = false) { + if (!$preserve_defaults) { + $this->language_data['STYLES']['COMMENTS'][$key] = $style; + } + else { + $this->language_data['STYLES']['COMMENTS'][$key] .= $style; + } + } + + /** + * Turns highlighting on/off for comment groups + * + * @param int The key of the comment group to turn on or off + * @param boolean Whether to turn highlighting for that group on or off + * @since 1.0.0 + */ + function set_comments_highlighting($key, $flag = true) { + $this->lexic_permissions['COMMENTS'][$key] = ($flag) ? true : false; + } + + /** + * Sets the styles for escaped characters. If $preserve_defaults is + * true, then styles are merged with the default styles, with the + * user defined styles having priority + * + * @param string The style to make the escape characters + * @param boolean Whether to merge the new styles with the old or just + * to overwrite them + * @since 1.0.0 + */ + function set_escape_characters_style($style, $preserve_defaults = false) { + if (!$preserve_defaults) { + $this->language_data['STYLES']['ESCAPE_CHAR'][0] = $style; + } + else { + $this->language_data['STYLES']['ESCAPE_CHAR'][0] .= $style; + } + } + + /** + * Turns highlighting on/off for escaped characters + * + * @param boolean Whether to turn highlighting for escape characters on or off + * @since 1.0.0 + */ + function set_escape_characters_highlighting($flag = true) { + $this->lexic_permissions['ESCAPE_CHAR'] = ($flag) ? true : false; + } + + /** + * Sets the styles for brackets. If $preserve_defaults is + * true, then styles are merged with the default styles, with the + * user defined styles having priority + * + * This method is DEPRECATED: use set_symbols_style instead. + * This method will be removed in 1.2.X + * + * @param string The style to make the brackets + * @param boolean Whether to merge the new styles with the old or just + * to overwrite them + * @since 1.0.0 + * @deprecated In favour of set_symbols_style + */ + function set_brackets_style($style, $preserve_defaults = false) { + if (!$preserve_defaults) { + $this->language_data['STYLES']['BRACKETS'][0] = $style; + } + else { + $this->language_data['STYLES']['BRACKETS'][0] .= $style; + } + } + + /** + * Turns highlighting on/off for brackets + * + * This method is DEPRECATED: use set_symbols_highlighting instead. + * This method will be remove in 1.2.X + * + * @param boolean Whether to turn highlighting for brackets on or off + * @since 1.0.0 + * @deprecated In favour of set_symbols_highlighting + */ + function set_brackets_highlighting($flag) { + $this->lexic_permissions['BRACKETS'] = ($flag) ? true : false; + } + + /** + * Sets the styles for symbols. If $preserve_defaults is + * true, then styles are merged with the default styles, with the + * user defined styles having priority + * + * @param string The style to make the symbols + * @param boolean Whether to merge the new styles with the old or just + * to overwrite them + * @since 1.0.1 + */ + function set_symbols_style($style, $preserve_defaults = false) { + if (!$preserve_defaults) { + $this->language_data['STYLES']['SYMBOLS'][0] = $style; + } + else { + $this->language_data['STYLES']['SYMBOLS'][0] .= $style; + } + // For backward compatibility + $this->set_brackets_style ($style, $preserve_defaults); + } + + /** + * Turns highlighting on/off for symbols + * + * @param boolean Whether to turn highlighting for symbols on or off + * @since 1.0.0 + */ + function set_symbols_highlighting($flag) { + $this->lexic_permissions['SYMBOLS'] = ($flag) ? true : false; + // For backward compatibility + $this->set_brackets_highlighting ($flag); + } + + /** + * Sets the styles for strings. If $preserve_defaults is + * true, then styles are merged with the default styles, with the + * user defined styles having priority + * + * @param string The style to make the escape characters + * @param boolean Whether to merge the new styles with the old or just + * to overwrite them + * @since 1.0.0 + */ + function set_strings_style($style, $preserve_defaults = false) { + if (!$preserve_defaults) { + $this->language_data['STYLES']['STRINGS'][0] = $style; + } + else { + $this->language_data['STYLES']['STRINGS'][0] .= $style; + } + } + + /** + * Turns highlighting on/off for strings + * + * @param boolean Whether to turn highlighting for strings on or off + * @since 1.0.0 + */ + function set_strings_highlighting($flag) { + $this->lexic_permissions['STRINGS'] = ($flag) ? true : false; + } + + /** + * Sets the styles for numbers. If $preserve_defaults is + * true, then styles are merged with the default styles, with the + * user defined styles having priority + * + * @param string The style to make the numbers + * @param boolean Whether to merge the new styles with the old or just + * to overwrite them + * @since 1.0.0 + */ + function set_numbers_style($style, $preserve_defaults = false) { + if (!$preserve_defaults) { + $this->language_data['STYLES']['NUMBERS'][0] = $style; + } + else { + $this->language_data['STYLES']['NUMBERS'][0] .= $style; + } + } + + /** + * Turns highlighting on/off for numbers + * + * @param boolean Whether to turn highlighting for numbers on or off + * @since 1.0.0 + */ + function set_numbers_highlighting($flag) { + $this->lexic_permissions['NUMBERS'] = ($flag) ? true : false; + } + + /** + * Sets the styles for methods. $key is a number that references the + * appropriate "object splitter" - see the language file for the language + * you are highlighting to get this number. If $preserve_defaults is + * true, then styles are merged with the default styles, with the + * user defined styles having priority + * + * @param int The key of the object splitter to change the styles of + * @param string The style to make the methods + * @param boolean Whether to merge the new styles with the old or just + * to overwrite them + * @since 1.0.0 + */ + function set_methods_style($key, $style, $preserve_defaults = false) { + if (!$preserve_defaults) { + $this->language_data['STYLES']['METHODS'][$key] = $style; + } + else { + $this->language_data['STYLES']['METHODS'][$key] .= $style; + } + } + + /** + * Turns highlighting on/off for methods + * + * @param boolean Whether to turn highlighting for methods on or off + * @since 1.0.0 + */ + function set_methods_highlighting($flag) { + $this->lexic_permissions['METHODS'] = ($flag) ? true : false; + } + + /** + * Sets the styles for regexps. If $preserve_defaults is + * true, then styles are merged with the default styles, with the + * user defined styles having priority + * + * @param string The style to make the regular expression matches + * @param boolean Whether to merge the new styles with the old or just + * to overwrite them + * @since 1.0.0 + */ + function set_regexps_style($key, $style, $preserve_defaults = false) { + if (!$preserve_defaults) { + $this->language_data['STYLES']['REGEXPS'][$key] = $style; + } + else { + $this->language_data['STYLES']['REGEXPS'][$key] .= $style; + } + } + + /** + * Turns highlighting on/off for regexps + * + * @param int The key of the regular expression group to turn on or off + * @param boolean Whether to turn highlighting for the regular expression group on or off + * @since 1.0.0 + */ + function set_regexps_highlighting($key, $flag) { + $this->lexic_permissions['REGEXPS'][$key] = ($flag) ? true : false; + } + + /** + * Sets whether a set of keywords are checked for in a case sensitive manner + * + * @param int The key of the keyword group to change the case sensitivity of + * @param boolean Whether to check in a case sensitive manner or not + * @since 1.0.0 + */ + function set_case_sensitivity($key, $case) { + $this->language_data['CASE_SENSITIVE'][$key] = ($case) ? true : false; + } + + /** + * Sets the case that keywords should use when found. Use the constants: + * + * - GESHI_CAPS_NO_CHANGE: leave keywords as-is + * - GESHI_CAPS_UPPER: convert all keywords to uppercase where found + * - GESHI_CAPS_LOWER: convert all keywords to lowercase where found + * + * @param int A constant specifying what to do with matched keywords + * @since 1.0.1 + * @todo Error check the passed value + */ + function set_case_keywords($case) { + $this->language_data['CASE_KEYWORDS'] = $case; + } + + /** + * Sets how many spaces a tab is substituted for + * + * Widths below zero are ignored + * + * @param int The tab width + * @since 1.0.0 + */ + function set_tab_width($width) { + $this->tab_width = intval($width); + } + + /** + * Enables/disables strict highlighting. Default is off, calling this + * method without parameters will turn it on. See documentation + * for more details on strict mode and where to use it. + * + * @param boolean Whether to enable strict mode or not + * @since 1.0.0 + */ + function enable_strict_mode($mode = true) { + if (GESHI_MAYBE == $this->language_data['STRICT_MODE_APPLIES']) { + $this->strict_mode = ($mode) ? true : false; + } + } + + /** + * Disables all highlighting + * + * @since 1.0.0 + * @todo Rewrite with an array traversal + */ + function disable_highlighting() { + foreach ($this->lexic_permissions as $key => $value) { + if (is_array($value)) { + foreach ($value as $k => $v) { + $this->lexic_permissions[$key][$k] = false; + } + } + else { + $this->lexic_permissions[$key] = false; + } + } + // Context blocks + $this->enable_important_blocks = false; + } + + /** + * Enables all highlighting + * + * @since 1.0.0 + * @todo Rewrite with array traversal + */ + function enable_highlighting() { + foreach ($this->lexic_permissions as $key => $value) { + if (is_array($value)) { + foreach ($value as $k => $v) { + $this->lexic_permissions[$key][$k] = true; + } + } + else { + $this->lexic_permissions[$key] = true; + } + } + // Context blocks + $this->enable_important_blocks = true; + } + + /** + * Given a file extension, this method returns either a valid geshi language + * name, or the empty string if it couldn't be found + * + * @param string The extension to get a language name for + * @param array A lookup array to use instead of the default + * @since 1.0.5 + * @todo Re-think about how this method works (maybe make it private and/or make it + * a extension->lang lookup?) + * @todo static? + */ + function get_language_name_from_extension( $extension, $lookup = array() ) { + if ( !$lookup ) { + $lookup = array( + 'actionscript' => array('as'), + 'ada' => array('a', 'ada', 'adb', 'ads'), + 'apache' => array('conf'), + 'asm' => array('ash', 'asm'), + 'asp' => array('asp'), + 'bash' => array('sh'), + 'c' => array('c', 'h'), + 'c_mac' => array('c', 'h'), + 'caddcl' => array(), + 'cadlisp' => array(), + 'cdfg' => array('cdfg'), + 'cpp' => array('cpp', 'h', 'hpp'), + 'csharp' => array(), + 'css' => array('css'), + 'delphi' => array('dpk', 'dpr'), + 'html4strict' => array('html', 'htm'), + 'java' => array('java'), + 'javascript' => array('js'), + 'lisp' => array('lisp'), + 'lua' => array('lua'), + 'mpasm' => array(), + 'nsis' => array(), + 'objc' => array(), + 'oobas' => array(), + 'oracle8' => array(), + 'pascal' => array('pas'), + 'perl' => array('pl', 'pm'), + 'php' => array('php', 'php5', 'phtml', 'phps'), + 'python' => array('py'), + 'qbasic' => array('bi'), + 'sas' => array('sas'), + 'smarty' => array(), + 'vb' => array('bas'), + 'vbnet' => array(), + 'visualfoxpro' => array(), + 'xml' => array('xml') + ); + } + + foreach ($lookup as $lang => $extensions) { + foreach ($extensions as $ext) { + if ($ext == $extension) { + return $lang; + } + } + } + return ''; + } + + /** + * Given a file name, this method loads its contents in, and attempts + * to set the language automatically. An optional lookup table can be + * passed for looking up the language name. If not specified a default + * table is used + * + * The language table is in the form + *
array(
+     *   'lang_name' => array('extension', 'extension', ...),
+     *   'lang_name' ...
+     * );
+ * + * @todo Complete rethink of this and above method + * @since 1.0.5 + */ + function load_from_file($file_name, $lookup = array()) { + if (is_readable($file_name)) { + $this->set_source(implode('', file($file_name))); + $this->set_language($this->get_language_name_from_extension(substr(strrchr($file_name, '.'), 1), $lookup)); + } + else { + $this->error = GESHI_ERROR_FILE_NOT_READABLE; + } + } + + /** + * Adds a keyword to a keyword group for highlighting + * + * @param int The key of the keyword group to add the keyword to + * @param string The word to add to the keyword group + * @since 1.0.0 + */ + function add_keyword($key, $word) { + $this->language_data['KEYWORDS'][$key][] = $word; + } + + /** + * Removes a keyword from a keyword group + * + * @param int The key of the keyword group to remove the keyword from + * @param string The word to remove from the keyword group + * @since 1.0.0 + */ + function remove_keyword($key, $word) { + $this->language_data['KEYWORDS'][$key] = + array_diff($this->language_data['KEYWORDS'][$key], array($word)); + } + + /** + * Creates a new keyword group + * + * @param int The key of the keyword group to create + * @param string The styles for the keyword group + * @param boolean Whether the keyword group is case sensitive ornot + * @param array The words to use for the keyword group + * @since 1.0.0 + */ + function add_keyword_group($key, $styles, $case_sensitive = true, $words = array()) { + $words = (array) $words; + $this->language_data['KEYWORDS'][$key] = $words; + $this->lexic_permissions['KEYWORDS'][$key] = true; + $this->language_data['CASE_SENSITIVE'][$key] = $case_sensitive; + $this->language_data['STYLES']['KEYWORDS'][$key] = $styles; + } + + /** + * Removes a keyword group + * + * @param int The key of the keyword group to remove + * @since 1.0.0 + */ + function remove_keyword_group ($key) { + unset($this->language_data['KEYWORDS'][$key]); + unset($this->lexic_permissions['KEYWORDS'][$key]); + unset($this->language_data['CASE_SENSITIVE'][$key]); + unset($this->language_data['STYLES']['KEYWORDS'][$key]); + } + + /** + * Sets the content of the header block + * + * @param string The content of the header block + * @since 1.0.2 + */ + function set_header_content($content) { + $this->header_content = $content; + } + + /** + * Sets the content of the footer block + * + * @param string The content of the footer block + * @since 1.0.2 + */ + function set_footer_content($content) { + $this->footer_content = $content; + } + + /** + * Sets the style for the header content + * + * @param string The style for the header content + * @since 1.0.2 + */ + function set_header_content_style($style) { + $this->header_content_style = $style; + } + + /** + * Sets the style for the footer content + * + * @param string The style for the footer content + * @since 1.0.2 + */ + function set_footer_content_style($style) { + $this->footer_content_style = $style; + } + + /** + * Sets the base URL to be used for keywords + * + * @param int The key of the keyword group to set the URL for + * @param string The URL to set for the group. If {FNAME} is in + * the url somewhere, it is replaced by the keyword + * that the URL is being made for + * @since 1.0.2 + */ + function set_url_for_keyword_group($group, $url) { + $this->language_data['URLS'][$group] = $url; + } + + /** + * Sets styles for links in code + * + * @param int A constant that specifies what state the style is being + * set for - e.g. :hover or :visited + * @param string The styles to use for that state + * @since 1.0.2 + */ + function set_link_styles($type, $styles) { + $this->link_styles[$type] = $styles; + } + + /** + * Sets the target for links in code + * + * @param string The target for links in the code, e.g. _blank + * @since 1.0.3 + */ + function set_link_target($target) { + if (!$target) { + $this->link_target = ''; + } + else { + $this->link_target = ' target="' . $target . '" '; + } + } + + /** + * Sets styles for important parts of the code + * + * @param string The styles to use on important parts of the code + * @since 1.0.2 + */ + function set_important_styles($styles) { + $this->important_styles = $styles; + } + + /** + * Sets whether context-important blocks are highlighted + * + * @todo REMOVE THIS SHIZ FROM GESHI! + * @deprecated + */ + function enable_important_blocks($flag) { + $this->enable_important_blocks = ( $flag ) ? true : false; + } + + /** + * Whether CSS IDs should be added to each line + * + * @param boolean If true, IDs will be added to each line. + * @since 1.0.2 + */ + function enable_ids($flag = true) { + $this->add_ids = ($flag) ? true : false; + } + + /** + * Specifies which lines to highlight extra + * + * @param mixed An array of line numbers to highlight, or just a line + * number on its own. + * @since 1.0.2 + * @todo Some data replication here that could be cut down on + */ + function highlight_lines_extra($lines) { + if (is_array($lines)) { + foreach ($lines as $line) { + $this->highlight_extra_lines[intval($line)] = intval($line); + } + } + else { + $this->highlight_extra_lines[intval($lines)] = intval($lines); + } + } + + /** + * Sets the style for extra-highlighted lines + * + * @param string The style for extra-highlighted lines + * @since 1.0.2 + */ + function set_highlight_lines_extra_style($styles) { + $this->highlight_extra_lines_style = $styles; + } + + /** + * Sets what number line numbers should start at. Should + * be a positive integer, and will be converted to one. + * + * Warning: Using this method will add the "start" + * attribute to the <ol> that is used for line numbering. + * This is not valid XHTML strict, so if that's what you + * care about then don't use this method. Firefox is getting + * support for the CSS method of doing this in 1.1 and Opera + * has support for the CSS method, but (of course) IE doesn't + * so it's not worth doing it the CSS way yet. + * + * @param int The number to start line numbers at + * @since 1.0.2 + */ + function start_line_numbers_at($number) { + $this->line_numbers_start = abs(intval($number)); + } + + /** + * Sets the encoding used for htmlspecialchars(), for international + * support. + * + * NOTE: This is not needed for now because htmlspecialchars() is not + * being used (it has a security hole in PHP4 that has not been patched). + * Maybe in a future version it may make a return for speed reasons, but + * I doubt it. + * + * @param string The encoding to use for the source + * @since 1.0.3 + */ + function set_encoding($encoding) { + if ($encoding) { + $this->encoding = $encoding; + } + } + + /** + * Turns linking of keywords on or off. + * + * @param boolean If true, links will be added to keywords + */ + function enable_keyword_links($enable = true) { + $this->keyword_links = ($enable) ? true : false; + } + + /** + * Returns the code in $this->source, highlighted and surrounded by the + * nessecary HTML. + * + * This should only be called ONCE, cos it's SLOW! If you want to highlight + * the same source multiple times, you're better off doing a whole lot of + * str_replaces to replace the <span>s + * + * @since 1.0.0 + */ + function parse_code () { + // Start the timer + $start_time = microtime(); + + // Firstly, if there is an error, we won't highlight + if ($this->error) { + $result = GeSHi::hsc($this->source); + // Timing is irrelevant + $this->set_time($start_time, $start_time); + return $this->finalise($result); + } + + // Replace all newlines to a common form. + $code = str_replace("\r\n", "\n", $this->source); + $code = str_replace("\r", "\n", $code); + // Add spaces for regular expression matching and line numbers + $code = "\n" . $code . "\n"; + + // Initialise various stuff + $length = strlen($code); + $STRING_OPEN = ''; + $CLOSE_STRING = false; + $ESCAPE_CHAR_OPEN = false; + $COMMENT_MATCHED = false; + // Turn highlighting on if strict mode doesn't apply to this language + $HIGHLIGHTING_ON = ( !$this->strict_mode ) ? true : ''; + // Whether to highlight inside a block of code + $HIGHLIGHT_INSIDE_STRICT = false; + $HARDQUOTE_OPEN = false; + $STRICTATTRS = ''; + $stuff_to_parse = ''; + $result = ''; + + // "Important" selections are handled like multiline comments + // @todo GET RID OF THIS SHIZ + if ($this->enable_important_blocks) { + $this->language_data['COMMENT_MULTI'][GESHI_START_IMPORTANT] = GESHI_END_IMPORTANT; + } + + if ($this->strict_mode) { + // Break the source into bits. Each bit will be a portion of the code + // within script delimiters - for example, HTML between < and > + $parts = array(0 => array(0 => '')); + $k = 0; + for ($i = 0; $i < $length; $i++) { + $char = substr($code, $i, 1); + if (!$HIGHLIGHTING_ON) { + foreach ($this->language_data['SCRIPT_DELIMITERS'] as $key => $delimiters) { + foreach ($delimiters as $open => $close) { + // Get the next little bit for this opening string + $check = substr($code, $i, strlen($open)); + // If it matches... + if ($check == $open) { + // We start a new block with the highlightable + // code in it + $HIGHLIGHTING_ON = $open; + $i += strlen($open) - 1; + $char = $open; + $parts[++$k][0] = $char; + + // No point going around again... + break(2); + } + } + } + } + else { + foreach ($this->language_data['SCRIPT_DELIMITERS'] as $key => $delimiters) { + foreach ($delimiters as $open => $close) { + if ($open == $HIGHLIGHTING_ON) { + // Found the closing tag + break(2); + } + } + } + // We check code from our current position BACKWARDS. This is so + // the ending string for highlighting can be included in the block + $check = substr($code, $i - strlen($close) + 1, strlen($close)); + if ($check == $close) { + $HIGHLIGHTING_ON = ''; + // Add the string to the rest of the string for this part + $parts[$k][1] = ( isset($parts[$k][1]) ) ? $parts[$k][1] . $char : $char; + $parts[++$k][0] = ''; + $char = ''; + } + } + $parts[$k][1] = ( isset($parts[$k][1]) ) ? $parts[$k][1] . $char : $char; + } + $HIGHLIGHTING_ON = ''; + } + else { + // Not strict mode - simply dump the source into + // the array at index 1 (the first highlightable block) + $parts = array( + 1 => array( + 0 => '', + 1 => $code + ) + ); + } + + // Now we go through each part. We know that even-indexed parts are + // code that shouldn't be highlighted, and odd-indexed parts should + // be highlighted + foreach ($parts as $key => $data) { + $part = $data[1]; + // If this block should be highlighted... + if ($key % 2) { + if ($this->strict_mode) { + // Find the class key for this block of code + foreach ($this->language_data['SCRIPT_DELIMITERS'] as $script_key => $script_data) { + foreach ($script_data as $open => $close) { + if ($data[0] == $open) { + break(2); + } + } + } + + if ($this->language_data['STYLES']['SCRIPT'][$script_key] != '' && + $this->lexic_permissions['SCRIPT']) { + // Add a span element around the source to + // highlight the overall source block + if (!$this->use_classes && + $this->language_data['STYLES']['SCRIPT'][$script_key] != '') { + $attributes = ' style="' . $this->language_data['STYLES']['SCRIPT'][$script_key] . '"'; + } + else { + $attributes = ' class="sc' . $script_key . '"'; + } + $result .= ""; + $STRICTATTRS = $attributes; + } + } + + if (!$this->strict_mode || $this->language_data['HIGHLIGHT_STRICT_BLOCK'][$script_key]) { + // Now, highlight the code in this block. This code + // is really the engine of GeSHi (along with the method + // parse_non_string_part). + $length = strlen($part); + for ($i = 0; $i < $length; $i++) { + // Get the next char + $char = substr($part, $i, 1); + $hq = isset($this->language_data['HARDQUOTE']) ? $this->language_data['HARDQUOTE'][0] : false; + // Is this char the newline and line numbers being used? + if (($this->line_numbers != GESHI_NO_LINE_NUMBERS + || count($this->highlight_extra_lines) > 0) + && $char == "\n") { + // If so, is there a string open? If there is, we should end it before + // the newline and begin it again (so when
  • s are put in the source + // remains XHTML compliant) + // note to self: This opens up possibility of config files specifying + // that languages can/cannot have multiline strings??? + if ($STRING_OPEN) { + if (!$this->use_classes) { + $attributes = ' style="' . $this->language_data['STYLES']['STRINGS'][0] . '"'; + } + else { + $attributes = ' class="st0"'; + } + $char = '' . $char . ""; + } + } + else if ($char == $STRING_OPEN) { + // A match of a string delimiter + if (($this->lexic_permissions['ESCAPE_CHAR'] && $ESCAPE_CHAR_OPEN) || + ($this->lexic_permissions['STRINGS'] && !$ESCAPE_CHAR_OPEN)) { + $char = GeSHi::hsc($char) . ''; + } + $escape_me = false; + if ($HARDQUOTE_OPEN) { + if ($ESCAPE_CHAR_OPEN) { + $escape_me = true; + } + else { + foreach ($this->language_data['HARDESCAPE'] as $hardesc) { + if (substr($part, $i, strlen($hardesc)) == $hardesc) { + $escape_me = true; + break; + } + } + } + } + + if (!$ESCAPE_CHAR_OPEN) { + $STRING_OPEN = ''; + $CLOSE_STRING = true; + } + if (!$escape_me) { + $HARDQUOTE_OPEN = false; + } + $ESCAPE_CHAR_OPEN = false; + } + else if (in_array($char, $this->language_data['QUOTEMARKS']) && + ($STRING_OPEN == '') && $this->lexic_permissions['STRINGS']) { + // The start of a new string + $STRING_OPEN = $char; + if (!$this->use_classes) { + $attributes = ' style="' . $this->language_data['STYLES']['STRINGS'][0] . '"'; + } + else { + $attributes = ' class="st0"'; + } + $char = "" . GeSHi::hsc($char); + + $result .= $this->parse_non_string_part( $stuff_to_parse ); + $stuff_to_parse = ''; + } + else if ($hq && substr($part, $i, strlen($hq)) == $hq && + ($STRING_OPEN == '') && $this->lexic_permissions['STRINGS']) { + // The start of a hard quoted string + $STRING_OPEN = $this->language_data['HARDQUOTE'][1]; + if (!$this->use_classes) { + $attributes = ' style="' . $this->language_data['STYLES']['STRINGS'][0] . '"'; + } + else { + $attributes = ' class="st0"'; + } + $char = "" . $hq; + $i += strlen($hq) - 1; + $HARDQUOTE_OPEN = true; + $result .= $this->parse_non_string_part($stuff_to_parse); + $stuff_to_parse = ''; + } + else if ($char == $this->language_data['ESCAPE_CHAR'] && $STRING_OPEN != '') { + // An escape character + if (!$ESCAPE_CHAR_OPEN) { + $ESCAPE_CHAR_OPEN = !$HARDQUOTE_OPEN; // true unless $HARDQUOTE_OPEN + if ($HARDQUOTE_OPEN) { + foreach ($this->language_data['HARDESCAPE'] as $hard) { + if (substr($part, $i, strlen($hard)) == $hard) { + $ESCAPE_CHAR_OPEN = true; + break; + } + } + } + if ($ESCAPE_CHAR_OPEN && $this->lexic_permissions['ESCAPE_CHAR']) { + if (!$this->use_classes) { + $attributes = ' style="' . $this->language_data['STYLES']['ESCAPE_CHAR'][0] . '"'; + } + else { + $attributes = ' class="es0"'; + } + $char = "" . $char; + if (substr($code, $i + 1, 1) == "\n") { + // escaping a newline, what's the point in putting the span around + // the newline? It only causes hassles when inserting line numbers + $char .= ''; + $ESCAPE_CHAR_OPEN = false; + } + } + } + else { + $ESCAPE_CHAR_OPEN = false; + if ($this->lexic_permissions['ESCAPE_CHAR']) { + $char .= ''; + } + } + } + else if ($ESCAPE_CHAR_OPEN) { + if ($this->lexic_permissions['ESCAPE_CHAR']) { + $char .= ''; + } + $ESCAPE_CHAR_OPEN = false; + $test_str = $char; + } + else if ($STRING_OPEN == '') { + // Is this a multiline comment? + foreach ($this->language_data['COMMENT_MULTI'] as $open => $close) { + $com_len = strlen($open); + $test_str = substr( $part, $i, $com_len ); + $test_str_match = $test_str; + if ($open == $test_str) { + $COMMENT_MATCHED = true; + //@todo If remove important do remove here + if ($this->lexic_permissions['COMMENTS']['MULTI'] || + $test_str == GESHI_START_IMPORTANT) { + if ($test_str != GESHI_START_IMPORTANT) { + if (!$this->use_classes) { + $attributes = ' style="' . $this->language_data['STYLES']['COMMENTS']['MULTI'] . '"'; + } + else { + $attributes = ' class="coMULTI"'; + } + $test_str = "" . GeSHi::hsc($test_str); + } + else { + if (!$this->use_classes) { + $attributes = ' style="' . $this->important_styles . '"'; + } + else { + $attributes = ' class="imp"'; + } + // We don't include the start of the comment if it's an + // "important" part + $test_str = ""; + } + } + else { + $test_str = GeSHi::hsc($test_str); + } + + $close_pos = strpos( $part, $close, $i + strlen($close) ); + + $oops = false; + if ($close_pos === false) { + $close_pos = strlen($part); + $oops = true; + } + else { + $close_pos -= ($com_len - strlen($close)); + } + + // Short-cut through all the multiline code + $rest_of_comment = GeSHi::hsc(substr($part, $i + $com_len, $close_pos - $i)); + if (($this->lexic_permissions['COMMENTS']['MULTI'] || + $test_str_match == GESHI_START_IMPORTANT) && + ($this->line_numbers != GESHI_NO_LINE_NUMBERS || + count($this->highlight_extra_lines) > 0)) { + // strreplace to put close span and open span around multiline newlines + $test_str .= str_replace("\n", "\n", $rest_of_comment); + } + else { + $test_str .= $rest_of_comment; + } + + if ($this->lexic_permissions['COMMENTS']['MULTI'] || + $test_str_match == GESHI_START_IMPORTANT) { + $test_str .= ''; + if ($oops) { + $test_str .= "\n"; + } + } + $i = $close_pos + $com_len - 1; + // parse the rest + $result .= $this->parse_non_string_part($stuff_to_parse); + $stuff_to_parse = ''; + break; + } + } + // If we haven't matched a multiline comment, try single-line comments + if (!$COMMENT_MATCHED) { + foreach ($this->language_data['COMMENT_SINGLE'] as $comment_key => $comment_mark) { + $com_len = strlen($comment_mark); + $test_str = substr($part, $i, $com_len); + if ($this->language_data['CASE_SENSITIVE'][GESHI_COMMENTS]) { + $match = ($comment_mark == $test_str); + } + else { + $match = (strtolower($comment_mark) == strtolower($test_str)); + } + if ($match) { + $COMMENT_MATCHED = true; + if ($this->lexic_permissions['COMMENTS'][$comment_key]) { + if (!$this->use_classes) { + $attributes = ' style="' . $this->language_data['STYLES']['COMMENTS'][$comment_key] . '"'; + } + else { + $attributes = ' class="co' . $comment_key . '"'; + } + $test_str = "" . GeSHi::hsc($this->change_case($test_str)); + } + else { + $test_str = GeSHi::hsc($test_str); + } + $close_pos = strpos($part, "\n", $i); + $oops = false; + if ($close_pos === false) { + $close_pos = strlen($part); + $oops = true; + } + $test_str .= GeSHi::hsc(substr($part, $i + $com_len, $close_pos - $i - $com_len)); + if ($this->lexic_permissions['COMMENTS'][$comment_key]) { + $test_str .= ""; + } + // Take into account that the comment might be the last in the source + if (!$oops) { + $test_str .= "\n"; + } + $i = $close_pos; + // parse the rest + $result .= $this->parse_non_string_part($stuff_to_parse); + $stuff_to_parse = ''; + break; + } + } + } + } + else if ($STRING_OPEN != '') { + // Otherwise, convert it to HTML form + if (strtolower($this->encoding) == 'utf-8') { + //only escape <128 (we don't want to break multibyte chars) + if (ord($char) < 128) { + $char = GeSHi::hsc($char); + } + } + else { + //encode everthing + $char = GeSHi::hsc($char); + } + } + // Where are we adding this char? + if (!$COMMENT_MATCHED) { + if (($STRING_OPEN == '') && !$CLOSE_STRING) { + $stuff_to_parse .= $char; + } + else { + $result .= $char; + $CLOSE_STRING = false; + } + } + else { + $result .= $test_str; + $COMMENT_MATCHED = false; + } + } + // Parse the last bit + $result .= $this->parse_non_string_part($stuff_to_parse); + $stuff_to_parse = ''; + } + else { + if ($STRICTATTRS != '') { + $part = str_replace("\n", "\n", GeSHi::hsc($part)); + $STRICTATTRS = ''; + } + $result .= $part; + } + // Close the that surrounds the block + if ($this->strict_mode && $this->language_data['STYLES']['SCRIPT'][$script_key] != '' && + $this->lexic_permissions['SCRIPT']) { + $result .= ''; + } + } + else { + // Else not a block to highlight + $result .= GeSHi::hsc($part); + } + } + + // Parse the last stuff (redundant?) + $result .= $this->parse_non_string_part($stuff_to_parse); + + // Lop off the very first and last spaces + $result = substr($result, 1, -1); + + // Are we still in a string? + if ($STRING_OPEN) { + $result .= ''; + } + + // We're finished: stop timing + $this->set_time($start_time, microtime()); + + return $this->finalise($result); + } + + /** + * Swaps out spaces and tabs for HTML indentation. Not needed if + * the code is in a pre block... + * + * @param string The source to indent + * @return string The source with HTML indenting applied + * @since 1.0.0 + * @access private + */ + function indent($result) { + /// Replace tabs with the correct number of spaces + if (false !== strpos($result, "\t")) { + $lines = explode("\n", $result); + foreach ($lines as $key => $line) { + if (false === strpos($line, "\t")) { + $lines[$key] = $line; + continue; + } + + $pos = 0; + $tab_width = $this->tab_width; + $length = strlen($line); + $result_line = ''; + + $IN_TAG = false; + for ($i = 0; $i < $length; $i++) { + $char = substr($line, $i, 1); + // Simple engine to work out whether we're in a tag. + // If we are we modify $pos. This is so we ignore HTML + // in the line and only workout the tab replacement + // via the actual content of the string + // This test could be improved to include strings in the + // html so that < or > would be allowed in user's styles + // (e.g. quotes: '<' '>'; or similar) + if ($IN_TAG && '>' == $char) { + $IN_TAG = false; + $result_line .= '>'; + ++$pos; + } + else if (!$IN_TAG && '<' == $char) { + $IN_TAG = true; + $result_line .= '<'; + ++$pos; + } + else if (!$IN_TAG && '&' == $char) { + $substr = substr($line, $i + 3, 4); + //$substr_5 = substr($line, 5, 1); + $posi = strpos($substr, ';'); + if (false !== $posi) { + $pos += $posi + 3; + } + $result_line .= '&'; + } + else if (!$IN_TAG && "\t" == $char) { + $str = ''; + // OPTIMISE - move $strs out. Make an array: + // $tabs = array( + // 1 => ' ', + // 2 => '  ', + // 3 => '   ' etc etc + // to use instead of building a string every time + $strs = array(0 => ' ', 1 => ' '); + for ($k = 0; $k < ($tab_width - (($i - $pos) % $tab_width)); $k++) $str .= $strs[$k % 2]; + $result_line .= $str; + $pos++; + + if (false === strpos($line, "\t", $i + 1)) { + $result_line .= substr($line, $i + 1); + break; + } + } + else if ($IN_TAG) { + ++$pos; + $result_line .= $char; + } + else { + $result_line .= $char; + //++$pos; + } + } + $lines[$key] = $result_line; + } + $result = implode("\n", $lines); + } + // Other whitespace + $result = str_replace(' ', '  ', $result); + $result = str_replace(' ', '  ', $result); + $result = str_replace("\n ", "\n ", $result); + + if ($this->line_numbers == GESHI_NO_LINE_NUMBERS) { + $result = nl2br($result); + } + return $result; + } + + /** + * Changes the case of a keyword for those languages where a change is asked for + * + * @param string The keyword to change the case of + * @return string The keyword with its case changed + * @since 1.0.0 + * @access private + */ + function change_case($instr) { + if ($this->language_data['CASE_KEYWORDS'] == GESHI_CAPS_UPPER) { + return strtoupper($instr); + } + else if ($this->language_data['CASE_KEYWORDS'] == GESHI_CAPS_LOWER) { + return strtolower($instr); + } + return $instr; + } + + /** + * Adds a url to a keyword where needed. + * + * @param string The keyword to add the URL HTML to + * @param int What group the keyword is from + * @param boolean Whether to get the HTML for the start or end + * @return The HTML for either the start or end of the HTML <a> tag + * @since 1.0.2 + * @access private + * @todo Get rid of ender + */ + function add_url_to_keyword($keyword, $group, $start_or_end) { + if (!$this->keyword_links) { + // Keyword links have been disabled + return; + } + + if (isset($this->language_data['URLS'][$group]) && + $this->language_data['URLS'][$group] != '' && + substr($keyword, 0, 5) != '</') { + // There is a base group for this keyword + if ($start_or_end == 'BEGIN') { + // HTML workaround... not good form (tm) but should work for 1.0.X + if ($keyword != '') { + // Old system: strtolower + //$keyword = ( $this->language_data['CASE_SENSITIVE'][$group] ) ? $keyword : strtolower($keyword); + // New system: get keyword from language file to get correct case + foreach ($this->language_data['KEYWORDS'][$group] as $word) { + if (strtolower($word) == strtolower($keyword)) { + break; + } + } + $word = ( substr($word, 0, 4) == '<' ) ? substr($word, 4) : $word; + $word = ( substr($word, -4) == '>' ) ? substr($word, 0, strlen($word) - 4) : $word; + if (!$word) return ''; + + return '<|UR1|"' . + str_replace( + array('{FNAME}', '.'), + array(GeSHi::hsc($word), ''), + $this->language_data['URLS'][$group] + ) . '">'; + } + return ''; + // HTML fix. Again, dirty hackage... + } + else if (!($this->language == 'html4strict' && ('>' == $keyword || '<' == $keyword))) { + return ''; + } + } + } + + /** + * Takes a string that has no strings or comments in it, and highlights + * stuff like keywords, numbers and methods. + * + * @param string The string to parse for keyword, numbers etc. + * @since 1.0.0 + * @access private + * @todo BUGGY! Why? Why not build string and return? + */ + function parse_non_string_part(&$stuff_to_parse) { + $stuff_to_parse = ' ' . GeSHi::hsc($stuff_to_parse); + $stuff_to_parse_pregquote = preg_quote($stuff_to_parse, '/'); + $func = '$this->change_case'; + $func2 = '$this->add_url_to_keyword'; + + // + // Regular expressions + // + foreach ($this->language_data['REGEXPS'] as $key => $regexp) { + if ($this->lexic_permissions['REGEXPS'][$key]) { + if (is_array($regexp)) { + $stuff_to_parse = preg_replace( + "/" . + str_replace('/', '\/', $regexp[GESHI_SEARCH]) . + "/{$regexp[GESHI_MODIFIERS]}", + "{$regexp[GESHI_BEFORE]}<|!REG3XP$key!>{$regexp[GESHI_REPLACE]}|>{$regexp[GESHI_AFTER]}", + $stuff_to_parse + ); + } + else { + $stuff_to_parse = preg_replace( "/(" . str_replace('/', '\/', $regexp) . ")/", "<|!REG3XP$key!>\\1|>", $stuff_to_parse); + } + } + } + + // + // Highlight numbers. This regexp sucks... anyone with a regexp that WORKS + // here wins a cookie if they send it to me. At the moment there's two doing + // almost exactly the same thing, except the second one prevents a number + // being highlighted twice (eg 5) + // Put /NUM!/ in for the styles, which gets replaced at the end. + // + // NEW ONE: Brice Bernard + // + if ($this->lexic_permissions['NUMBERS'] && preg_match('#[0-9]#', $stuff_to_parse )) { + $stuff_to_parse = preg_replace('/([-+]?\\b(?:[0-9]*\\.)?[0-9]+\\b)/', '<|/NUM!/>\\1|>', $stuff_to_parse); + } + + // Highlight keywords + // if there is a couple of alpha symbols there *might* be a keyword + if (preg_match('#[a-zA-Z]{2,}#', $stuff_to_parse)) { + foreach ($this->language_data['KEYWORDS'] as $k => $keywordset) { + if ($this->lexic_permissions['KEYWORDS'][$k]) { + foreach ($keywordset as $keyword) { + $keyword = preg_quote($keyword, '/'); + // + // This replacement checks the word is on it's own (except if brackets etc + // are next to it), then highlights it. We don't put the color=" for the span + // in just yet - otherwise languages with the keywords "color" or "or" have + // a fit. + // + if (false !== stristr($stuff_to_parse_pregquote, $keyword )) { + $stuff_to_parse .= ' '; + // Might make a more unique string for putting the number in soon + // Basically, we don't put the styles in yet because then the styles themselves will + // get highlighted if the language has a CSS keyword in it (like CSS, for example ;)) + $styles = "/$k/"; + if ($this->language_data['CASE_SENSITIVE'][$k]) { + $stuff_to_parse = preg_replace( + "/([^a-zA-Z0-9\$_\|\#;>|^])($keyword)(?=[^a-zA-Z0-9_<\|%\-&])/e", + "'\\1' . $func2('\\2', '$k', 'BEGIN') . '<|$styles>' . $func('\\2') . '|>' . $func2('\\2', '$k', 'END')", + $stuff_to_parse + ); + } + else { + // Change the case of the word. + // hackage again... must... release... 1.2... + if ('smarty' == $this->language) { $hackage = '\/'; } else { $hackage = ''; } + $stuff_to_parse = preg_replace( + "/([^a-zA-Z0-9\$_\|\#;>$hackage|^])($keyword)(?=[^a-zA-Z0-9_<\|%\-&])/ie", + "'\\1' . $func2('\\2', '$k', 'BEGIN') . '<|$styles>' . $func('\\2') . '|>' . $func2('\\2', '$k', 'END')", + $stuff_to_parse + ); + } + $stuff_to_parse = substr($stuff_to_parse, 0, strlen($stuff_to_parse) - 1); + } + } + } + } + } + + // + // Now that's all done, replace /[number]/ with the correct styles + // + foreach ($this->language_data['KEYWORDS'] as $k => $kws) { + if (!$this->use_classes) { + $attributes = ' style="' . $this->language_data['STYLES']['KEYWORDS'][$k] . '"'; + } + else { + $attributes = ' class="kw' . $k . '"'; + } + $stuff_to_parse = str_replace("/$k/", $attributes, $stuff_to_parse); + } + + // Put number styles in + if (!$this->use_classes && $this->lexic_permissions['NUMBERS']) { + $attributes = ' style="' . $this->language_data['STYLES']['NUMBERS'][0] . '"'; + } + else { + $attributes = ' class="nu0"'; + } + $stuff_to_parse = str_replace('/NUM!/', $attributes, $stuff_to_parse); + + // + // Highlight methods and fields in objects + // + if ($this->lexic_permissions['METHODS'] && $this->language_data['OOLANG']) { + foreach ($this->language_data['OBJECT_SPLITTERS'] as $key => $splitter) { + if (false !== stristr($stuff_to_parse, $splitter)) { + if (!$this->use_classes) { + $attributes = ' style="' . $this->language_data['STYLES']['METHODS'][$key] . '"'; + } + else { + $attributes = ' class="me' . $key . '"'; + } + $stuff_to_parse = preg_replace("/(" . preg_quote($this->language_data['OBJECT_SPLITTERS'][$key], 1) . "[\s]*)([a-zA-Z\*\(][a-zA-Z0-9_\*]*)/", "\\1<|$attributes>\\2|>", $stuff_to_parse); + } + } + } + + // + // Highlight brackets. Yes, I've tried adding a semi-colon to this list. + // You try it, and see what happens ;) + // TODO: Fix lexic permissions not converting entities if shouldn't + // be highlighting regardless + // + if ($this->lexic_permissions['BRACKETS']) { + $code_entities_match = array('[', ']', '(', ')', '{', '}'); + if (!$this->use_classes) { + $code_entities_replace = array( + '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">[|>', + '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">]|>', + '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">(|>', + '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">)|>', + '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">{|>', + '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">}|>', + ); + } + else { + $code_entities_replace = array( + '<| class="br0">[|>', + '<| class="br0">]|>', + '<| class="br0">(|>', + '<| class="br0">)|>', + '<| class="br0">{|>', + '<| class="br0">}|>', + ); + } + $stuff_to_parse = str_replace( $code_entities_match, $code_entities_replace, $stuff_to_parse ); + } + + // + // Add class/style for regexps + // + foreach ($this->language_data['REGEXPS'] as $key => $regexp) { + if ($this->lexic_permissions['REGEXPS'][$key]) { + if (!$this->use_classes) { + $attributes = ' style="' . $this->language_data['STYLES']['REGEXPS'][$key] . '"'; + } + else { + if(is_array($this->language_data['REGEXPS'][$key]) && + array_key_exists(GESHI_CLASS, $this->language_data['REGEXPS'][$key])) { + $attributes = ' class="' + . $this->language_data['REGEXPS'][$key][GESHI_CLASS] . '"'; + } + else { + $attributes = ' class="re' . $key . '"'; + } + } + $stuff_to_parse = str_replace("!REG3XP$key!", "$attributes", $stuff_to_parse); + } + } + + // Replace with . for urls + $stuff_to_parse = str_replace('', '.', $stuff_to_parse); + // Replace <|UR1| with link_styles[GESHI_LINK])) { + if ($this->use_classes) { + $stuff_to_parse = str_replace('<|UR1|', 'link_target . ' href=', $stuff_to_parse); + } + else { + $stuff_to_parse = str_replace('<|UR1|', 'link_target . ' style="' . $this->link_styles[GESHI_LINK] . '" href=', $stuff_to_parse); + } + } + else { + $stuff_to_parse = str_replace('<|UR1|', 'link_target . ' href=', $stuff_to_parse); + } + + // + // NOW we add the span thingy ;) + // + + $stuff_to_parse = str_replace('<|', '', '', $stuff_to_parse ); + + return substr($stuff_to_parse, 1); + } + + /** + * Sets the time taken to parse the code + * + * @param microtime The time when parsing started + * @param microtime The time when parsing ended + * @since 1.0.2 + * @access private + */ + function set_time($start_time, $end_time) { + $start = explode(' ', $start_time); + $end = explode(' ', $end_time); + $this->time = $end[0] + $end[1] - $start[0] - $start[1]; + } + + /** + * Gets the time taken to parse the code + * + * @return double The time taken to parse the code + * @since 1.0.2 + */ + function get_time() { + return $this->time; + } + + /** + * Gets language information and stores it for later use + * + * @access private + * @todo Needs to load keys for lexic permissions for keywords, regexps etc + */ + function load_language($file_name) { + $this->enable_highlighting(); + $language_data = array(); + require $file_name; + // Perhaps some checking might be added here later to check that + // $language data is a valid thing but maybe not + $this->language_data = $language_data; + // Set strict mode if should be set + if ($this->language_data['STRICT_MODE_APPLIES'] == GESHI_ALWAYS) { + $this->strict_mode = true; + } + // Set permissions for all lexics to true + // so they'll be highlighted by default + foreach ($this->language_data['KEYWORDS'] as $key => $words) { + $this->lexic_permissions['KEYWORDS'][$key] = true; + } + foreach ($this->language_data['COMMENT_SINGLE'] as $key => $comment) { + $this->lexic_permissions['COMMENTS'][$key] = true; + } + foreach ($this->language_data['REGEXPS'] as $key => $regexp) { + $this->lexic_permissions['REGEXPS'][$key] = true; + } + // Set default class for CSS + $this->overall_class = $this->language; + } + + /** + * Takes the parsed code and various options, and creates the HTML + * surrounding it to make it look nice. + * + * @param string The code already parsed + * @return string The code nicely finalised + * @since 1.0.0 + * @access private + */ + function finalise($parsed_code) { + // Remove end parts of important declarations + // This is BUGGY!! My fault for bad code: fix coming in 1.2 + // @todo Remove this crap + if ($this->enable_important_blocks && + (strstr($parsed_code, GeSHi::hsc(GESHI_START_IMPORTANT)) === false)) { + $parsed_code = str_replace(GeSHi::hsc(GESHI_END_IMPORTANT), '', $parsed_code); + } + + // Add HTML whitespace stuff if we're using the
    header + if ($this->header_type != GESHI_HEADER_PRE) { + $parsed_code = $this->indent($parsed_code); + } + + // purge some unnecessary stuff + $parsed_code = preg_replace('#]+>(\s*)#', '\\1', $parsed_code); + $parsed_code = preg_replace('#]+>(\s*)
    #', '\\1', $parsed_code); + + // If we are using IDs for line numbers, there needs to be an overall + // ID set to prevent collisions. + if ($this->add_ids && !$this->overall_id) { + $this->overall_id = 'geshi-' . substr(md5(microtime()), 0, 4); + } + + // If we're using line numbers, we insert
  • s and appropriate + // markup to style them (otherwise we don't need to do anything) + if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) { + // If we're using the
     header, we shouldn't add newlines because
    +            // the 
     will line-break them (and the 
  • s already do this for us) + $ls = ($this->header_type != GESHI_HEADER_PRE) ? "\n" : ''; + // Get code into lines + $code = explode("\n", $parsed_code); + // Set vars to defaults for following loop + $parsed_code = ''; + $i = 0; + $attrs = array(); + + // Foreach line... + foreach ($code as $line) { + // Make lines have at least one space in them if they're empty + // BenBE: Checking emptiness using trim instead of relying on blanks + if ('' == trim($line)) { + $line = ' '; + } + // If this is a "special line"... + if ($this->line_numbers == GESHI_FANCY_LINE_NUMBERS && + $i % $this->line_nth_row == ($this->line_nth_row - 1)) { + // Set the attributes to style the line + if ($this->use_classes) { + //$attr = ' class="li2"'; + $attrs['class'][] = 'li2'; + $def_attr = ' class="de2"'; + } + else { + //$attr = ' style="' . $this->line_style2 . '"'; + $attrs['style'][] = $this->line_style2; + // This style "covers up" the special styles set for special lines + // so that styles applied to special lines don't apply to the actual + // code on that line + $def_attr = ' style="' . $this->code_style . '"'; + } + // Span or div? + $start = ""; + $end = '
  • '; + } + else { + if ($this->use_classes) { + //$attr = ' class="li1"'; + $attrs['class'][] = 'li1'; + $def_attr = ' class="de1"'; + } + else { + //$attr = ' style="' . $this->line_style1 . '"'; + $attrs['style'][] = $this->line_style1; + $def_attr = ' style="' . $this->code_style . '"'; + } + $start = ""; + $end = ''; + } + + ++$i; + // Are we supposed to use ids? If so, add them + if ($this->add_ids) { + $attrs['id'][] = "$this->overall_id-$i"; + } + if ($this->use_classes && in_array($i, $this->highlight_extra_lines)) { + $attrs['class'][] = 'ln-xtra'; + } + if (!$this->use_classes && in_array($i, $this->highlight_extra_lines)) { + $attrs['style'][] = $this->highlight_extra_lines_style; + } + + // Add in the line surrounded by appropriate list HTML + $attr_string = ' '; + foreach ($attrs as $key => $attr) { + $attr_string .= $key . '="' . implode(' ', $attr) . '" '; + } + $attr_string = substr($attr_string, 0, -1); + $parsed_code .= "$start$line$end$ls"; + $attrs = array(); + } + } + else { + // No line numbers, but still need to handle highlighting lines extra. + // Have to use divs so the full width of the code is highlighted + $code = explode("\n", $parsed_code); + $parsed_code = ''; + $i = 0; + foreach ($code as $line) { + // Make lines have at least one space in them if they're empty + // BenBE: Checking emptiness using trim instead of relying on blanks + if ('' == trim($line)) { + $line = ' '; + } + if (in_array(++$i, $this->highlight_extra_lines)) { + if ($this->use_classes) { + $parsed_code .= '
    '; + } + else { + $parsed_code .= "
    highlight_extra_lines_style}\">"; + } + // Remove \n because it stuffs up
     header
    +                    $parsed_code .= $line . "
    "; + } + else { + $parsed_code .= $line . "\n"; + } + } + } + + if ($this->header_type == GESHI_HEADER_PRE) { + // enforce line numbers when using pre + $parsed_code = str_replace('
  • ', '
  •  
  • ', $parsed_code); + } + + return $this->header() . chop($parsed_code) . $this->footer(); + } + + /** + * Creates the header for the code block (with correct attributes) + * + * @return string The header for the code block + * @since 1.0.0 + * @access private + */ + function header() { + // Get attributes needed + $attributes = $this->get_attributes(); + + $ol_attributes = ''; + + if ($this->line_numbers_start != 1) { + $ol_attributes .= ' start="' . $this->line_numbers_start . '"'; + } + + // Get the header HTML + $header = $this->format_header_content(); + + if (GESHI_HEADER_NONE == $this->header_type) { + if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) { + return "$header"; + } + return $header; + } + + // Work out what to return and do it + if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) { + if ($this->header_type == GESHI_HEADER_PRE) { + return "$header"; + } + else if ($this->header_type == GESHI_HEADER_DIV) { + return "$header"; + } + } + else { + if ($this->header_type == GESHI_HEADER_PRE) { + return "$header"; + } + else if ($this->header_type == GESHI_HEADER_DIV) { + return "$header"; + } + } + } + + /** + * Returns the header content, formatted for output + * + * @return string The header content, formatted for output + * @since 1.0.2 + * @access private + */ + function format_header_content() { + $header = $this->header_content; + if ($header) { + if ($this->header_type == GESHI_HEADER_PRE) { + $header = str_replace("\n", '', $header); + } + $header = $this->replace_keywords($header); + + if ($this->use_classes) { + $attr = ' class="head"'; + } + else { + $attr = " style=\"{$this->header_content_style}\""; + } + return "$header
    "; + } + } + + /** + * Returns the footer for the code block. + * + * @return string The footer for the code block + * @since 1.0.0 + * @access private + */ + function footer() { + $footer_content = $this->format_footer_content(); + + if (GESHI_HEADER_NONE == $this->header_type) { + return ($this->line_numbers != GESHI_NO_LINE_NUMBERS) ? '' . $footer_content + : $footer_content; + } + + if ($this->header_type == GESHI_HEADER_DIV) { + if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) { + return "$footer_content"; + } + return "$footer_content"; + } + else { + if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) { + return "$footer_content"; + } + return "$footer_content"; + } + } + + /** + * Returns the footer content, formatted for output + * + * @return string The footer content, formatted for output + * @since 1.0.2 + * @access private + */ + function format_footer_content() { + $footer = $this->footer_content; + if ($footer) { + if ($this->header_type == GESHI_HEADER_PRE) { + $footer = str_replace("\n", '', $footer);; + } + $footer = $this->replace_keywords($footer); + + if ($this->use_classes) { + $attr = ' class="foot"'; + } + else { + $attr = " style=\"{$this->footer_content_style}\""; + } + return "$footer"; + } + } + + /** + * Replaces certain keywords in the header and footer with + * certain configuration values + * + * @param string The header or footer content to do replacement on + * @return string The header or footer with replaced keywords + * @since 1.0.2 + * @access private + */ + function replace_keywords($instr) { + $keywords = $replacements = array(); + + $keywords[] = '