--- /dev/null
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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
+\f
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
--- /dev/null
+PHPTAL for non PEAR users
+
+This package contains PHPTAL and its dependencies.
+
+It also contains the PEAR.php file, used for Error handling in PHPTAL
+and its dependencies.
+
+Installation:
+
+copy the content of libs in one of your php include path.
+
+
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+// $id$
+
+define('ALGO_MAP_VERSION', '0.1.1');
+
+/**
+ * Applies a callback(or code) to the elements of given arrays.
+ *
+ * This function acts much like the perl 'map' keyword, it pass through all
+ * array parameters and apply a callback function to each elements of
+ * these arrays.
+ *
+ * See examples below to an overview of Algo_map() usage.
+ *
+ * *IMPORTANT*
+ *
+ * Please note that the callback function needs to return something, when you
+ * don't want your function to return an element or an hashtable, just return
+ * an empty array().
+ *
+ *
+ * example 1 : Simple use, this example compose an hashtable with an array.
+ *
+ * <?php
+ * function myfunc($a)
+ * {
+ * return array($a=>1);
+ * }
+ *
+ * $a_pam = array("apple", "pie", "cherry");
+ *
+ * $result = Algo_map('myfunc', $a_pam);
+ *
+ * // $result ==> array("apple"=>1, "pie"=>1, "cherry"=>1);
+ * ?>
+ *
+ * You can pass more than one array as parameters. The number of elements of
+ * each array should be at least equal to the number of elements of the first
+ * array.
+ *
+ * If count($a1) > count($a2), elements of $a2 that doesn't exists
+ * will be replaced by a false value.
+ *
+ *
+ * example 2 : Using more than one array with Algo_map()
+ *
+ * <?php
+ * function myfunc($a, $b)
+ * {
+ * return array($a * $b);
+ * }
+ *
+ * $a_nbr = array(1,2,3,4);
+ * $b_nbr = array(1,2,4,8,16);
+ *
+ * $result = Algo_map('myfunc', $a_nbr, $b_nbr);
+ *
+ * // $result ==> array(1, 4, 12, 32);
+ * ?>
+ *
+ *
+ * example 3 : Problem of array size
+ *
+ * <?php
+ * function myfunc($a, $b)
+ * {
+ * return array($a * $b);
+ * }
+ *
+ * $a_nbr = array(1,2,3,4,16);
+ * $b_nbr = array(1,2,4,8);
+ *
+ * $result = Algo_map('myfunc', $a_nbr, $b_nbr);
+ *
+ * // $result ==> array(1, 4, 12, 32, 0);
+ * // the fifth element is equal to 0 because the fifth element of $b_nbr
+ * // doesn't exists and is replaced by false(ie:0) in the callback
+ * // function.
+ * ?>
+ *
+ *
+ * example 4 : Defining the function code inside the Algo_map() call
+ *
+ * <?php
+ * $a_t = array("a","b","c","d");
+ * $b_t = array("z","b","e","d");
+ *
+ * $result = Algo_map(
+ * // first element match the function arguments two vars $a and $b
+ * // which will be filled with $a_t[x] and $b_t[x] for x=0 to len(a)
+ * '$a,$b',
+ * // the second parameter is the callback function code
+ * // map always return an array (or an hashtable)
+ * 'return($a==$b ? $a : array());',
+ * $a_t,
+ * $b_t
+ * );
+ *
+ * // $result ==> array("b","d");
+ * ?>
+ *
+ *
+ * example 5 : Object method callback
+ *
+ * <?php
+ * class Foo {
+ * function myCallback($a){
+ * return "-$a-";
+ * }
+ * };
+ *
+ * $foo = new Foo();
+ * $array = array('a','b','c');
+ * $result = Algo_map($foo, 'myCallback', $array);
+ *
+ * // $result = array('-a-', '-b-', '-c-');
+ * ?>
+ *
+ *
+ * @param mixed ... See examples
+ * @return array
+ * @author Laurent Bedubourg <codebringer@free.fr>
+ * @date 2001-09-20
+ */
+function Algo_map()
+{
+ // we retrieve the list of map arguments
+ $argv = func_get_args();
+ // the first argument is the function name
+ $func = array_shift($argv);
+
+ if (is_object($func)) {
+ // if first argument is an object, this map
+ // has a callback to some method
+ $obj = $func;
+ $func = array_shift($argv);
+ } else if (!function_exists($func)) {
+ // if the function isn't defined,
+ // we'll try to create the function
+ // the first argument was the string
+ // of function arguments(ie:'$a,$b,$c')
+
+ // The code is the second argument
+ $code = array_shift($argv);
+
+ // We create the new function
+ // it will be a lambda style function
+ $func = create_function($func, $code);
+ }
+
+ // prepare the result array
+ $result = array();
+
+ // we'll apply the new function to
+ // each element of arrays passed as
+ // parameters
+ while (count($argv[0]) > 0) {
+ // prepare the array of arguments
+ $fargs = array();
+
+ for($i = 0; $i < count($argv); $i++) {
+ // for each array to map
+ // we add the next element of the
+ // array to the argument array
+ $fargs[] = array_shift($argv[$i]);
+ }
+
+ if (isset($obj)) {
+ $result = array_merge($result,
+ call_user_func_array(array($obj,$func),
+ $fargs));
+ } else {
+ // we merge the result of the function
+ // with allready found results
+ $result = array_merge($result,
+ // call the function with the array
+ // of arguments
+ call_user_func_array($func, $fargs));
+ }
+ }
+
+ // let's return the result array
+ return $result;
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+require_once "PEAR.php";
+
+define('GETTEXT_NATIVE', 1);
+define('GETTEXT_PHP', 2);
+
+/**
+ * Generic gettext static class.
+ *
+ * This class allows gettext usage with php even if the gettext support is
+ * not compiled in php.
+ *
+ * The developper can choose between the GETTEXT_NATIVE support and the
+ * GETTEXT_PHP support on initialisation. If native is not supported, the
+ * system will fall back to PHP support.
+ *
+ * On both systems, this package add a variable interpolation system so you can
+ * translate entire dynamic sentences in stead of peace of sentences.
+ *
+ * Small example without pear error lookup :
+ *
+ * <?php
+ * require_once "GetText.php";
+ *
+ * GetText::init();
+ * GetText::setLanguage('fr_Fr'); // may throw GetText_Error
+ * GetText::addDomain('myAppDomain'); // may throw GetText_Error
+ * GetText::setVar('login', $login);
+ * GetText::setVar('name', $name);
+ *
+ * // may throw GetText_Error
+ * echo GetText::gettext('Welcome ${name}, you\'re connected with login ${login}');
+ *
+ * // should echo something like :
+ * //
+ * // "Bienvenue Jean-Claude, vous êtes connecté en tant qu'utilisateur jcaccount"
+ * //
+ * // or if fr_FR translation does not exists
+ * //
+ * // "Welcome Jean-Claude, you're connected with login jcaccount"
+ *
+ * ?>
+ *
+ * A gettext mini-howto should be provided with this package, if you're new
+ * to gettext usage, please read it to learn how to build a gettext
+ * translation directory (locale).
+ *
+ * @todo Tools to manage gettext files in php.
+ *
+ * - non traducted domains / keys
+ * - modification of keys
+ * - domain creation, preparation, delete, ...
+ * - tool to extract required messages from TOF templates
+ *
+ * @version 0.5
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class GetText
+{
+ /**
+ * This method returns current gettext support class.
+ *
+ * @return GetText_Support
+ * @static 1
+ * @access private
+ */
+ function &_support($set=false)
+ {
+ static $supportObject;
+ if ($set !== false) {
+ $supportObject = $set;
+ } elseif (!isset($supportObject)) {
+ trigger_error("GetText not initialized !". endl.
+ "Please call GetText::init() before calling ".
+ "any GetText function !".endl
+ , E_USER_ERROR);
+ }
+ return $supportObject;
+ }
+
+ /**
+ * Initialize gettext package.
+ *
+ * This method instantiate the gettext support depending on managerType
+ * value.
+ *
+ * GETTEXT_NATIVE try to use gettext php support and fail back to PHP
+ * support if not installed.
+ *
+ * GETTEXT_PHP explicitely request the usage of PHP support.
+ *
+ * @param int $managerType
+ * Gettext support type.
+ *
+ * @access public
+ * @static 1
+ */
+ function init($managerType = GETTEXT_NATIVE)
+ {
+ if ($managerType == GETTEXT_NATIVE) {
+ if (function_exists('gettext')) {
+ return GetText::_support(new GetText_NativeSupport());
+ }
+ }
+ // fail back to php support
+ return GetText::_support(new GetText_PHPSupport());
+ }
+
+ /**
+ * Set the language to use for traduction.
+ *
+ * @param string $langCode
+ * The language code usually defined as ll_CC, ll is the two letter
+ * language code and CC is the two letter country code.
+ *
+ * @throws GetText_Error if language is not supported by your system.
+ */
+ function setLanguage($langCode)
+ {
+ $support =& GetText::_support();
+ return $support->setLanguage($langCode);
+ }
+
+ /**
+ * Add a translation domain.
+ *
+ * The domain name is usually the name of the .po file you wish to use.
+ * For example, if you created a file 'locale/ll_CC/LC_MESSAGES/myapp.po',
+ * you'll use 'myapp' as the domain name.
+ *
+ * @param string $domain
+ * The domain name.
+ *
+ * @param string $path optional
+ * The path to the locale directory (ie: /path/to/locale/) which
+ * contains ll_CC directories.
+ */
+ function addDomain($domain, $path=false)
+ {
+ $support =& GetText::_support();
+ return $support->addDomain($domain, $path);
+ }
+
+ /**
+ * Retrieve the translation for specified key.
+ *
+ * @param string $key
+ * String to translate using gettext support.
+ */
+ function gettext($key)
+ {
+ $support =& GetText::_support();
+ return $support->gettext($key);
+ }
+
+ /**
+ * Add a variable to gettext interpolation system.
+ *
+ * @param string $key
+ * The variable name.
+ *
+ * @param string $value
+ * The variable value.
+ */
+ function setVar($key, $value)
+ {
+ $support =& GetText::_support();
+ return $support->setVar($key, $value);
+ }
+
+ /**
+ * Add an hashtable of variables.
+ *
+ * @param hashtable $hash
+ * PHP associative array of variables.
+ */
+ function setVars($hash)
+ {
+ $support =& GetText::_support();
+ return $support->setVars($hash);
+ }
+
+ /**
+ * Reset interpolation variables.
+ */
+ function reset()
+ {
+ $support =& GetText::_support();
+ return $support->reset();
+ }
+}
+
+
+/**
+ * Interface to gettext native support.
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ * @access private
+ */
+class GetText_NativeSupport
+{
+ var $_interpolationVars = array();
+
+ /**
+ * Set gettext language code.
+ * @throws GetText_Error
+ */
+ function setLanguage($langCode)
+ {
+ putenv("LANG=$langCode");
+ putenv("LC_ALL=$langCode");
+ putenv("LANGUAGE=$langCode");
+ $set = setlocale(LC_ALL, "$langCode");
+ if ($set === false) {
+ $str = sprintf('Language code "%s" not supported by your system',
+ $langCode);
+ $err = new GetText_Error($str);
+ return PEAR::raiseError($err);
+ }
+ }
+
+ /**
+ * Add a translation domain.
+ */
+ function addDomain($domain, $path=false)
+ {
+ if ($path === false) {
+ bindtextdomain($domain, "./locale/");
+ } else {
+ bindtextdomain($domain, $path);
+ }
+ textdomain($domain);
+ }
+
+ /**
+ * Retrieve translation for specified key.
+ *
+ * @access private
+ */
+ function _getTranslation($key)
+ {
+ return gettext($key);
+ }
+
+
+ /**
+ * Reset interpolation variables.
+ */
+ function reset()
+ {
+ $this->_interpolationVars = array();
+ }
+
+ /**
+ * Set an interpolation variable.
+ */
+ function setVar($key, $value)
+ {
+ $this->_interpolationVars[$key] = $value;
+ }
+
+ /**
+ * Set an associative array of interpolation variables.
+ */
+ function setVars($hash)
+ {
+ $this->_interpolationVars = array_merge($this->_interpolationVars,
+ $hash);
+ }
+
+ /**
+ * Retrieve translation for specified key.
+ *
+ * @param string $key -- gettext msgid
+ * @throws GetText_Error
+ */
+ function gettext($key)
+ {
+ $value = $this->_getTranslation($key);
+ if ($value === false) {
+ $str = sprintf('Unable to locate gettext key "%s"', $key);
+ $err = new GetText_Error($str);
+ return PEAR::raiseError($err);
+ }
+
+ while (preg_match('/\$\{(.*?)\}/sm', $value, $m)) {
+ list($src, $var) = $m;
+
+ // retrieve variable to interpolate in context, throw an exception
+ // if not found.
+ $varValue = $this->_getVar($var);
+ if ($varValue === false) {
+ $str = sprintf('Interpolation error, var "%s" not set', $var);
+ $err = new GetText_Error($str);
+ return PEAR::raiseError($err);
+ }
+ $value = str_replace($src, $varValue, $value);
+ }
+ return $value;
+ }
+
+ /**
+ * Retrieve an interpolation variable value.
+ *
+ * @return mixed
+ * @access private
+ */
+ function _getVar($name)
+ {
+ if (!array_key_exists($name, $this->_interpolationVars)) {
+ return false;
+ }
+ return $this->_interpolationVars[$name];
+ }
+}
+
+
+/**
+ * Implementation of GetText support for PHP.
+ *
+ * This implementation is abble to cache .po files into php files returning the
+ * domain translation hashtable.
+ *
+ * @access private
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class GetText_PHPSupport extends GetText_NativeSupport
+{
+ var $_path = 'locale/';
+ var $_langCode = false;
+ var $_domains = array();
+ var $_end = -1;
+ var $_jobs = array();
+
+ /**
+ * Set the translation domain.
+ *
+ * @param string $langCode -- language code
+ * @throws GetText_Error
+ */
+ function setLanguage($langCode)
+ {
+ // if language already set, try to reload domains
+ if ($this->_langCode !== false and $this->_langCode != $langCode) {
+ foreach ($this->_domains as $domain) {
+ $this->_jobs[] = array($domain->name, $domain->path);
+ }
+ $this->_domains = array();
+ $this->_end = -1;
+ }
+
+ $this->_langCode = $langCode;
+
+ // this allow us to set the language code after
+ // domain list.
+ while (count($this->_jobs) > 0) {
+ list($domain, $path) = array_shift($this->_jobs);
+ $err = $this->addDomain($domain, $path);
+ // error raised, break jobs
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+ }
+ }
+
+ /**
+ * Add a translation domain.
+ *
+ * @param string $domain -- Domain name
+ * @param string $path optional -- Repository path
+ * @throws GetText_Error
+ */
+ function addDomain($domain, $path = "./locale/")
+ {
+ if (array_key_exists($domain, $this->_domains)) {
+ return;
+ }
+
+ if (!$this->_langCode) {
+ $this->_jobs[] = array($domain, $path);
+ return;
+ }
+
+ $err = $this->_loadDomain($domain, $path);
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+
+ $this->_end++;
+ }
+
+ /**
+ * Load a translation domain file.
+ *
+ * This method cache the translation hash into a php file unless
+ * GETTEXT_NO_CACHE is defined.
+ *
+ * @param string $domain -- Domain name
+ * @param string $path optional -- Repository
+ * @throws GetText_Error
+ * @access private
+ */
+ function _loadDomain($domain, $path = "./locale")
+ {
+ $srcDomain = $path . "/$this->_langCode/LC_MESSAGES/$domain.po";
+ $phpDomain = $path . "/$this->_langCode/LC_MESSAGES/$domain.php";
+
+ if (!file_exists($srcDomain)) {
+ $str = sprintf('Domain file "%s" not found.', $srcDomain);
+ $err = new GetText_Error($str);
+ return PEAR::raiseError($err);
+ }
+
+ $d = new GetText_Domain();
+ $d->name = $domain;
+ $d->path = $path;
+
+ if (!file_exists($phpDomain)
+ || (filemtime($phpDomain) < filemtime($srcDomain))) {
+
+ // parse and compile translation table
+ $parser = new GetText_PHPSupport_Parser();
+ $hash = $parser->parse($srcDomain);
+ if (!defined('GETTEXT_NO_CACHE')) {
+ $comp = new GetText_PHPSupport_Compiler();
+ $err = $comp->compile($hash, $srcDomain);
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+ }
+ $d->_keys = $hash;
+ } else {
+ $d->_keys = include $phpDomain;
+ }
+ $this->_domains[] =& $d;
+ }
+
+ /**
+ * Implementation of gettext message retrieval.
+ */
+ function _getTranslation($key)
+ {
+ for ($i = $this->_end; $i >= 0; $i--) {
+ if ($this->_domains[$i]->hasKey($key)) {
+ return $this->_domains[$i]->get($key);
+ }
+ }
+ return $key;
+ }
+}
+
+/**
+ * Class representing a domain file for a specified language.
+ *
+ * @access private
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class GetText_Domain
+{
+ var $name;
+ var $path;
+
+ var $_keys = array();
+
+ function hasKey($key)
+ {
+ return array_key_exists($key, $this->_keys);
+ }
+
+ function get($key)
+ {
+ return $this->_keys[$key];
+ }
+}
+
+/**
+ * This class is used to parse gettext '.po' files into php associative arrays.
+ *
+ * @access private
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class GetText_PHPSupport_Parser
+{
+ var $_hash = array();
+ var $_currentKey;
+ var $_currentValue;
+
+ /**
+ * Parse specified .po file.
+ *
+ * @return hashtable
+ * @throws GetText_Error
+ */
+ function parse($file)
+ {
+ $this->_hash = array();
+ $this->_currentKey = false;
+ $this->_currentValue = "";
+
+ if (!file_exists($file)) {
+ $str = sprintf('Unable to locate file "%s"', $file);
+ $err = new GetText_Error($str);
+ return PEAR::raiseError($err);
+ }
+ $i=0;
+ $lines = file($file);
+ foreach ($lines as $line) {
+ $this->_parseLine($line, ++$i);
+ }
+ $this->_storeKey();
+
+ return $this->_hash;
+ }
+
+ /**
+ * Parse one po line.
+ *
+ * @access private
+ */
+ function _parseLine($line, $nbr)
+ {
+ if (preg_match('/^\s*?#/', $line)) { return; }
+ if (preg_match('/^\s*?msgid \"(.*?)(?!<\\\)\"/', $line, $m)) {
+ $this->_storeKey();
+ $this->_currentKey = $m[1];
+ return;
+ }
+ if (preg_match('/^\s*?msgstr \"(.*?)(?!<\\\)\"/', $line, $m)) {
+ $this->_currentValue .= $m[1];
+ return;
+ }
+ if (preg_match('/^\s*?\"(.*?)(?!<\\\)\"/', $line, $m)) {
+ $this->_currentValue .= $m[1];
+ return;
+ }
+ }
+
+ /**
+ * Store last key/value pair into building hashtable.
+ *
+ * @access private
+ */
+ function _storeKey()
+ {
+ if ($this->_currentKey === false) return;
+ $this->_currentValue = str_replace('\\n', "\n", $this->_currentValue);
+ $this->_hash[$this->_currentKey] = $this->_currentValue;
+ $this->_currentKey = false;
+ $this->_currentValue = "";
+ }
+}
+
+
+/**
+ * This class write a php file from a gettext hashtable.
+ *
+ * The produced file return the translation hashtable on include.
+ *
+ * @throws GetText_Error
+ * @access private
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class GetText_PHPSupport_Compiler
+{
+ /**
+ * Write hash in an includable php file.
+ */
+ function compile(&$hash, $sourcePath)
+ {
+ $destPath = preg_replace('/\.po$/', '.php', $sourcePath);
+ $fp = @fopen($destPath, "w");
+ if (!$fp) {
+ $str = sprintf('Unable to open "%s" in write mode.', $destPath);
+ $err = new GetText_Error($str);
+ return PEAR::raiseError($err);
+ }
+ fwrite($fp, '<?php' . "\n");
+ fwrite($fp, 'return array(' . "\n");
+ foreach ($hash as $key => $value) {
+ $key = str_replace("'", "\\'", $key);
+ $value = str_replace("'", "\\'", $value);
+ fwrite($fp, ' \'' . $key . '\' => \'' . $value . "',\n");
+ }
+ fwrite($fp, ');' . "\n");
+ fwrite($fp, '?>');
+ fclose($fp);
+ }
+}
+
+/**
+ * GetText related error.
+ */
+class GetText_Error extends PEAR_Error {}
+
+?>
--- /dev/null
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 of the PHP license, |
+// | that is bundled with this package in the file LICENSE, and is |
+// | available at through the world-wide-web at |
+// | http://www.php.net/license/2_02.txt. |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Sterling Hughes <sterling@php.net> |
+// | Stig Bakken <ssb@fast.no> |
+// | Tomas V.V.Cox <cox@idecnet.com> |
+// +----------------------------------------------------------------------+
+//
+// $Id$
+//
+
+define('PEAR_ERROR_RETURN', 1);
+define('PEAR_ERROR_PRINT', 2);
+define('PEAR_ERROR_TRIGGER', 4);
+define('PEAR_ERROR_DIE', 8);
+define('PEAR_ERROR_CALLBACK', 16);
+define('PEAR_ZE2', (function_exists('version_compare') &&
+ version_compare(zend_version(), "2-dev", "ge")));
+
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ define('OS_WINDOWS', true);
+ define('OS_UNIX', false);
+ define('PEAR_OS', 'Windows');
+} else {
+ define('OS_WINDOWS', false);
+ define('OS_UNIX', true);
+ define('PEAR_OS', 'Unix'); // blatant assumption
+}
+
+$GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN;
+$GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE;
+$GLOBALS['_PEAR_destructor_object_list'] = array();
+$GLOBALS['_PEAR_shutdown_funcs'] = array();
+$GLOBALS['_PEAR_error_handler_stack'] = array();
+
+ini_set('track_errors', true);
+
+/**
+ * Base class for other PEAR classes. Provides rudimentary
+ * emulation of destructors.
+ *
+ * If you want a destructor in your class, inherit PEAR and make a
+ * destructor method called _yourclassname (same name as the
+ * constructor, but with a "_" prefix). Also, in your constructor you
+ * have to call the PEAR constructor: $this->PEAR();.
+ * The destructor method will be called without parameters. Note that
+ * at in some SAPI implementations (such as Apache), any output during
+ * the request shutdown (in which destructors are called) seems to be
+ * discarded. If you need to get any debug information from your
+ * destructor, use error_log(), syslog() or something similar.
+ *
+ * IMPORTANT! To use the emulated destructors you need to create the
+ * objects by reference, ej: $obj =& new PEAR_child;
+ *
+ * @since PHP 4.0.2
+ * @author Stig Bakken <ssb@fast.no>
+ * @see http://pear.php.net/manual/
+ */
+class PEAR
+{
+ // {{{ properties
+
+ /**
+ * Whether to enable internal debug messages.
+ *
+ * @var bool
+ * @access private
+ */
+ var $_debug = false;
+
+ /**
+ * Default error mode for this object.
+ *
+ * @var int
+ * @access private
+ */
+ var $_default_error_mode = null;
+
+ /**
+ * Default error options used for this object when error mode
+ * is PEAR_ERROR_TRIGGER.
+ *
+ * @var int
+ * @access private
+ */
+ var $_default_error_options = null;
+
+ /**
+ * Default error handler (callback) for this object, if error mode is
+ * PEAR_ERROR_CALLBACK.
+ *
+ * @var string
+ * @access private
+ */
+ var $_default_error_handler = '';
+
+ /**
+ * Which class to use for error objects.
+ *
+ * @var string
+ * @access private
+ */
+ var $_error_class = 'PEAR_Error';
+
+ /**
+ * An array of expected errors.
+ *
+ * @var array
+ * @access private
+ */
+ var $_expected_errors = array();
+
+ // }}}
+
+ // {{{ constructor
+
+ /**
+ * Constructor. Registers this object in
+ * $_PEAR_destructor_object_list for destructor emulation if a
+ * destructor object exists.
+ *
+ * @param string $error_class (optional) which class to use for
+ * error objects, defaults to PEAR_Error.
+ * @access public
+ * @return void
+ */
+ function PEAR($error_class = null)
+ {
+ $classname = get_class($this);
+ if ($this->_debug) {
+ print "PEAR constructor called, class=$classname\n";
+ }
+ if ($error_class !== null) {
+ $this->_error_class = $error_class;
+ }
+ while ($classname) {
+ $destructor = "_$classname";
+ if (method_exists($this, $destructor)) {
+ global $_PEAR_destructor_object_list;
+ $_PEAR_destructor_object_list[] = &$this;
+ break;
+ } else {
+ $classname = get_parent_class($classname);
+ }
+ }
+ }
+
+ // }}}
+ // {{{ destructor
+
+ /**
+ * Destructor (the emulated type of...). Does nothing right now,
+ * but is included for forward compatibility, so subclass
+ * destructors should always call it.
+ *
+ * See the note in the class desciption about output from
+ * destructors.
+ *
+ * @access public
+ * @return void
+ */
+ function _PEAR() {
+ if ($this->_debug) {
+ printf("PEAR destructor called, class=%s\n", get_class($this));
+ }
+ }
+
+ // }}}
+ // {{{ getStaticProperty()
+
+ /**
+ * If you have a class that's mostly/entirely static, and you need static
+ * properties, you can use this method to simulate them. Eg. in your method(s)
+ * do this: $myVar = &PEAR::getStaticProperty('myVar');
+ * You MUST use a reference, or they will not persist!
+ *
+ * @access public
+ * @param string $class The calling classname, to prevent clashes
+ * @param string $var The variable to retrieve.
+ * @return mixed A reference to the variable. If not set it will be
+ * auto initialised to NULL.
+ */
+ function &getStaticProperty($class, $var)
+ {
+ static $properties;
+ return $properties[$class][$var];
+ }
+
+ // }}}
+ // {{{ registerShutdownFunc()
+
+ /**
+ * Use this function to register a shutdown method for static
+ * classes.
+ *
+ * @access public
+ * @param mixed $func The function name (or array of class/method) to call
+ * @param mixed $args The arguments to pass to the function
+ * @return void
+ */
+ function registerShutdownFunc($func, $args = array())
+ {
+ $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
+ }
+
+ // }}}
+ // {{{ isError()
+
+ /**
+ * Tell whether a value is a PEAR error.
+ *
+ * @param mixed $data the value to test
+ * @access public
+ * @return bool true if parameter is an error
+ */
+ function isError($data) {
+ return (bool)(is_object($data) &&
+ (get_class($data) == 'pear_error' ||
+ is_subclass_of($data, 'pear_error')));
+ }
+
+ // }}}
+ // {{{ setErrorHandling()
+
+ /**
+ * Sets how errors generated by this DB object should be handled.
+ * Can be invoked both in objects and statically. If called
+ * statically, setErrorHandling sets the default behaviour for all
+ * PEAR objects. If called in an object, setErrorHandling sets
+ * the default behaviour for that object.
+ *
+ * @param int $mode
+ * One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
+ * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE or
+ * PEAR_ERROR_CALLBACK.
+ *
+ * @param mixed $options
+ * When $mode is PEAR_ERROR_TRIGGER, this is the error level (one
+ * of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
+ *
+ * When $mode is PEAR_ERROR_CALLBACK, this parameter is expected
+ * to be the callback function or method. A callback
+ * function is a string with the name of the function, a
+ * callback method is an array of two elements: the element
+ * at index 0 is the object, and the element at index 1 is
+ * the name of the method to call in the object.
+ *
+ * When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is
+ * a printf format string used when printing the error
+ * message.
+ *
+ * @access public
+ * @return void
+ * @see PEAR_ERROR_RETURN
+ * @see PEAR_ERROR_PRINT
+ * @see PEAR_ERROR_TRIGGER
+ * @see PEAR_ERROR_DIE
+ * @see PEAR_ERROR_CALLBACK
+ *
+ * @since PHP 4.0.5
+ */
+
+ function setErrorHandling($mode = null, $options = null)
+ {
+ if (isset($this)) {
+ $setmode = &$this->_default_error_mode;
+ $setoptions = &$this->_default_error_options;
+ } else {
+ $setmode = &$GLOBALS['_PEAR_default_error_mode'];
+ $setoptions = &$GLOBALS['_PEAR_default_error_options'];
+ }
+
+ switch ($mode) {
+ case PEAR_ERROR_RETURN:
+ case PEAR_ERROR_PRINT:
+ case PEAR_ERROR_TRIGGER:
+ case PEAR_ERROR_DIE:
+ case null:
+ $setmode = $mode;
+ $setoptions = $options;
+ break;
+
+ case PEAR_ERROR_CALLBACK:
+ $setmode = $mode;
+ if ((is_string($options) && function_exists($options)) ||
+ (is_array($options) && method_exists(@$options[0], @$options[1])))
+ {
+ $setoptions = $options;
+ } else {
+ trigger_error("invalid error callback", E_USER_WARNING);
+ }
+ break;
+
+ default:
+ trigger_error("invalid error mode", E_USER_WARNING);
+ break;
+ }
+ }
+
+ // }}}
+ // {{{ expectError()
+
+ /**
+ * This method is used to tell which errors you expect to get.
+ * Expected errors are always returned with error mode
+ * PEAR_ERROR_RETURN. Expected error codes are stored in a stack,
+ * and this method pushes a new element onto it. The list of
+ * expected errors are in effect until they are popped off the
+ * stack with the popExpect() method.
+ *
+ * Note that this method can not be called statically
+ *
+ * @param mixed $code a single error code or an array of error codes to expect
+ *
+ * @return int the new depth of the "expected errors" stack
+ * @access public
+ */
+ function expectError($code = '*')
+ {
+ if (is_array($code)) {
+ array_push($this->_expected_errors, $code);
+ } else {
+ array_push($this->_expected_errors, array($code));
+ }
+ return sizeof($this->_expected_errors);
+ }
+
+ // }}}
+ // {{{ popExpect()
+
+ /**
+ * This method pops one element off the expected error codes
+ * stack.
+ *
+ * @return array the list of error codes that were popped
+ */
+ function popExpect()
+ {
+ return array_pop($this->_expected_errors);
+ }
+
+ // }}}
+ // {{{ _checkDelExpect()
+
+ /**
+ * This method checks unsets an error code if available
+ *
+ * @param mixed error code
+ * @return bool true if the error code was unset, false otherwise
+ * @access private
+ * @since PHP 4.3.0
+ */
+ function _checkDelExpect($error_code)
+ {
+ $deleted = false;
+
+ foreach ($this->_expected_errors AS $key => $error_array) {
+ if (in_array($error_code, $error_array)) {
+ unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
+ $deleted = true;
+ }
+
+ // clean up empty arrays
+ if (0 == count($this->_expected_errors[$key])) {
+ unset($this->_expected_errors[$key]);
+ }
+ }
+ return $deleted;
+ }
+
+ // }}}
+ // {{{ delExpect()
+
+ /**
+ * This method deletes all occurences of the specified element from
+ * the expected error codes stack.
+ *
+ * @param mixed $error_code error code that should be deleted
+ * @return mixed list of error codes that were deleted or error
+ * @access public
+ * @since PHP 4.3.0
+ */
+ function delExpect($error_code)
+ {
+ $deleted = false;
+
+ if ((is_array($error_code) && (0 != count($error_code)))) {
+ // $error_code is a non-empty array here;
+ // we walk through it trying to unset all
+ // values
+ foreach($error_code AS $key => $error) {
+ if ($this->_checkDelExpect($error)) {
+ $deleted = true;
+ } else {
+ $deleted = false;
+ }
+ }
+ return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
+ } elseif (!empty($error_code)) {
+ // $error_code comes alone, trying to unset it
+ if ($this->_checkDelExpect($error_code)) {
+ return true;
+ } else {
+ return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
+ }
+ } else {
+ // $error_code is empty
+ return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
+ }
+ }
+
+ // }}}
+ // {{{ raiseError()
+
+ /**
+ * This method is a wrapper that returns an instance of the
+ * configured error class with this object's default error
+ * handling applied. If the $mode and $options parameters are not
+ * specified, the object's defaults are used.
+ *
+ * @param mixed $message a text error message or a PEAR error object
+ *
+ * @param int $code a numeric error code (it is up to your class
+ * to define these if you want to use codes)
+ *
+ * @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
+ * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE or
+ * PEAR_ERROR_CALLBACK.
+ *
+ * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter
+ * specifies the PHP-internal error level (one of
+ * E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
+ * If $mode is PEAR_ERROR_CALLBACK, this
+ * parameter specifies the callback function or
+ * method. In other error modes this parameter
+ * is ignored.
+ *
+ * @param string $userinfo If you need to pass along for example debug
+ * information, this parameter is meant for that.
+ *
+ * @param string $error_class The returned error object will be
+ * instantiated from this class, if specified.
+ *
+ * @param bool $skipmsg If true, raiseError will only pass error codes,
+ * the error message parameter will be dropped.
+ *
+ * @access public
+ * @return object a PEAR error object
+ * @see PEAR::setErrorHandling
+ * @since PHP 4.0.5
+ */
+ function &raiseError($message = null,
+ $code = null,
+ $mode = null,
+ $options = null,
+ $userinfo = null,
+ $error_class = null,
+ $skipmsg = false)
+ {
+ // The error is yet a PEAR error object
+ if (is_object($message)) {
+ $code = $message->getCode();
+ $userinfo = $message->getUserInfo();
+ $error_class = $message->getType();
+ $message = $message->getMessage();
+ }
+
+ if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) {
+ if ($exp[0] == "*" ||
+ (is_int(reset($exp)) && in_array($code, $exp)) ||
+ (is_string(reset($exp)) && in_array($message, $exp))) {
+ $mode = PEAR_ERROR_RETURN;
+ }
+ }
+ // No mode given, try global ones
+ if ($mode === null) {
+ // Class error handler
+ if (isset($this) && isset($this->_default_error_mode)) {
+ $mode = $this->_default_error_mode;
+ $options = $this->_default_error_options;
+ // Global error handler
+ } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {
+ $mode = $GLOBALS['_PEAR_default_error_mode'];
+ $options = $GLOBALS['_PEAR_default_error_options'];
+ }
+ }
+
+ if ($error_class !== null) {
+ $ec = $error_class;
+ } elseif (isset($this) && isset($this->_error_class)) {
+ $ec = $this->_error_class;
+ } else {
+ $ec = 'PEAR_Error';
+ }
+ if ($skipmsg) {
+ return new $ec($code, $mode, $options, $userinfo);
+ } else {
+ return new $ec($message, $code, $mode, $options, $userinfo);
+ }
+ }
+
+ // }}}
+ // {{{ throwError()
+
+ /**
+ * Simpler form of raiseError with fewer options. In most cases
+ * message, code and userinfo are enough.
+ *
+ * @param string $message
+ *
+ */
+ function &throwError($message = null,
+ $code = null,
+ $userinfo = null)
+ {
+ if (isset($this)) {
+ return $this->raiseError($message, $code, null, null, $userinfo);
+ } else {
+ return PEAR::raiseError($message, $code, null, null, $userinfo);
+ }
+ }
+
+ // }}}
+ // {{{ pushErrorHandling()
+
+ /**
+ * Push a new error handler on top of the error handler options stack. With this
+ * you can easily override the actual error handler for some code and restore
+ * it later with popErrorHandling.
+ *
+ * @param mixed $mode (same as setErrorHandling)
+ * @param mixed $options (same as setErrorHandling)
+ *
+ * @return bool Always true
+ *
+ * @see PEAR::setErrorHandling
+ */
+ function pushErrorHandling($mode, $options = null)
+ {
+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+ if (isset($this)) {
+ $def_mode = &$this->_default_error_mode;
+ $def_options = &$this->_default_error_options;
+ } else {
+ $def_mode = &$GLOBALS['_PEAR_default_error_mode'];
+ $def_options = &$GLOBALS['_PEAR_default_error_options'];
+ }
+ $stack[] = array($def_mode, $def_options);
+
+ if (isset($this)) {
+ $this->setErrorHandling($mode, $options);
+ } else {
+ PEAR::setErrorHandling($mode, $options);
+ }
+ $stack[] = array($mode, $options);
+ return true;
+ }
+
+ // }}}
+ // {{{ popErrorHandling()
+
+ /**
+ * Pop the last error handler used
+ *
+ * @return bool Always true
+ *
+ * @see PEAR::pushErrorHandling
+ */
+ function popErrorHandling()
+ {
+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+ array_pop($stack);
+ list($mode, $options) = $stack[sizeof($stack) - 1];
+ array_pop($stack);
+ if (isset($this)) {
+ $this->setErrorHandling($mode, $options);
+ } else {
+ PEAR::setErrorHandling($mode, $options);
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ loadExtension()
+
+ /**
+ * OS independant PHP extension load. Remember to take care
+ * on the correct extension name for case sensitive OSes.
+ *
+ * @param string $ext The extension name
+ * @return bool Success or not on the dl() call
+ */
+ function loadExtension($ext)
+ {
+ if (!extension_loaded($ext)) {
+ if (OS_WINDOWS) {
+ $suffix = '.dll';
+ } elseif (PHP_OS == 'HP-UX') {
+ $suffix = '.sl';
+ } elseif (PHP_OS == 'AIX') {
+ $suffix = '.a';
+ } elseif (PHP_OS == 'OSX') {
+ $suffix = '.bundle';
+ } else {
+ $suffix = '.so';
+ }
+ return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
+ }
+ return true;
+ }
+
+ // }}}
+}
+
+// {{{ _PEAR_call_destructors()
+
+function _PEAR_call_destructors()
+{
+ global $_PEAR_destructor_object_list;
+ if (is_array($_PEAR_destructor_object_list) &&
+ sizeof($_PEAR_destructor_object_list))
+ {
+ reset($_PEAR_destructor_object_list);
+ while (list($k, $objref) = each($_PEAR_destructor_object_list)) {
+ $classname = get_class($objref);
+ while ($classname) {
+ $destructor = "_$classname";
+ if (method_exists($objref, $destructor)) {
+ $objref->$destructor();
+ break;
+ } else {
+ $classname = get_parent_class($classname);
+ }
+ }
+ }
+ // Empty the object list to ensure that destructors are
+ // not called more than once.
+ $_PEAR_destructor_object_list = array();
+ }
+
+ // Now call the shutdown functions
+ if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) {
+ foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
+ call_user_func_array($value[0], $value[1]);
+ }
+ }
+}
+
+// }}}
+
+class PEAR_Error
+{
+ // {{{ properties
+
+ var $error_message_prefix = '';
+ var $mode = PEAR_ERROR_RETURN;
+ var $level = E_USER_NOTICE;
+ var $code = -1;
+ var $message = '';
+ var $userinfo = '';
+ var $backtrace = null;
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * PEAR_Error constructor
+ *
+ * @param string $message message
+ *
+ * @param int $code (optional) error code
+ *
+ * @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN,
+ * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER or
+ * PEAR_ERROR_CALLBACK
+ *
+ * @param mixed $options (optional) error level, _OR_ in the case of
+ * PEAR_ERROR_CALLBACK, the callback function or object/method
+ * tuple.
+ *
+ * @param string $userinfo (optional) additional user/debug info
+ *
+ * @access public
+ *
+ */
+ function PEAR_Error($message = 'unknown error', $code = null,
+ $mode = null, $options = null, $userinfo = null)
+ {
+ if ($mode === null) {
+ $mode = PEAR_ERROR_RETURN;
+ }
+ $this->message = $message;
+ $this->code = $code;
+ $this->mode = $mode;
+ $this->userinfo = $userinfo;
+ if (function_exists("debug_backtrace")) {
+ $this->backtrace = debug_backtrace();
+ }
+ if ($mode & PEAR_ERROR_CALLBACK) {
+ $this->level = E_USER_NOTICE;
+ $this->callback = $options;
+ } else {
+ if ($options === null) {
+ $options = E_USER_NOTICE;
+ }
+ $this->level = $options;
+ $this->callback = null;
+ }
+ if ($this->mode & PEAR_ERROR_PRINT) {
+ if (is_null($options) || is_int($options)) {
+ $format = "%s";
+ } else {
+ $format = $options;
+ }
+ printf($format, $this->getMessage());
+ }
+ if ($this->mode & PEAR_ERROR_TRIGGER) {
+ trigger_error($this->getMessage(), $this->level);
+ }
+ if ($this->mode & PEAR_ERROR_DIE) {
+ $msg = $this->getMessage();
+ if (is_null($options) || is_int($options)) {
+ $format = "%s";
+ if (substr($msg, -1) != "\n") {
+ $msg .= "\n";
+ }
+ } else {
+ $format = $options;
+ }
+ die(sprintf($format, $msg));
+ }
+ if ($this->mode & PEAR_ERROR_CALLBACK) {
+ if (is_string($this->callback) && strlen($this->callback)) {
+ call_user_func($this->callback, $this);
+ } elseif (is_array($this->callback) &&
+ sizeof($this->callback) == 2 &&
+ is_object($this->callback[0]) &&
+ is_string($this->callback[1]) &&
+ strlen($this->callback[1])) {
+ @call_user_func($this->callback, $this);
+ }
+ }
+ }
+
+ // }}}
+ // {{{ getMode()
+
+ /**
+ * Get the error mode from an error object.
+ *
+ * @return int error mode
+ * @access public
+ */
+ function getMode() {
+ return $this->mode;
+ }
+
+ // }}}
+ // {{{ getCallback()
+
+ /**
+ * Get the callback function/method from an error object.
+ *
+ * @return mixed callback function or object/method array
+ * @access public
+ */
+ function getCallback() {
+ return $this->callback;
+ }
+
+ // }}}
+ // {{{ getMessage()
+
+
+ /**
+ * Get the error message from an error object.
+ *
+ * @return string full error message
+ * @access public
+ */
+ function getMessage()
+ {
+ return ($this->error_message_prefix . $this->message);
+ }
+
+
+ // }}}
+ // {{{ getCode()
+
+ /**
+ * Get error code from an error object
+ *
+ * @return int error code
+ * @access public
+ */
+ function getCode()
+ {
+ return $this->code;
+ }
+
+ // }}}
+ // {{{ getType()
+
+ /**
+ * Get the name of this error/exception.
+ *
+ * @return string error/exception name (type)
+ * @access public
+ */
+ function getType()
+ {
+ return get_class($this);
+ }
+
+ // }}}
+ // {{{ getUserInfo()
+
+ /**
+ * Get additional user-supplied information.
+ *
+ * @return string user-supplied information
+ * @access public
+ */
+ function getUserInfo()
+ {
+ return $this->userinfo;
+ }
+
+ // }}}
+ // {{{ getDebugInfo()
+
+ /**
+ * Get additional debug information supplied by the application.
+ *
+ * @return string debug information
+ * @access public
+ */
+ function getDebugInfo()
+ {
+ return $this->getUserInfo();
+ }
+
+ // }}}
+ // {{{ getBacktrace()
+
+ /**
+ * Get the call backtrace from where the error was generated.
+ * Supported with PHP 4.3.0 or newer.
+ *
+ * @param int $frame (optional) what frame to fetch
+ * @return array Backtrace, or NULL if not available.
+ * @access public
+ */
+ function getBacktrace($frame = null)
+ {
+ if ($frame === null) {
+ return $this->backtrace;
+ }
+ return $this->backtrace[$frame];
+ }
+
+ // }}}
+ // {{{ addUserInfo()
+
+ function addUserInfo($info)
+ {
+ if (empty($this->userinfo)) {
+ $this->userinfo = $info;
+ } else {
+ $this->userinfo .= " ** $info";
+ }
+ }
+
+ // }}}
+ // {{{ toString()
+
+ /**
+ * Make a string representation of this object.
+ *
+ * @return string a string with an object summary
+ * @access public
+ */
+ function toString() {
+ $modes = array();
+ $levels = array(E_USER_NOTICE => 'notice',
+ E_USER_WARNING => 'warning',
+ E_USER_ERROR => 'error');
+ if ($this->mode & PEAR_ERROR_CALLBACK) {
+ if (is_array($this->callback)) {
+ $callback = get_class($this->callback[0]) . '::' .
+ $this->callback[1];
+ } else {
+ $callback = $this->callback;
+ }
+ return sprintf('[%s: message="%s" code=%d mode=callback '.
+ 'callback=%s prefix="%s" info="%s"]',
+ get_class($this), $this->message, $this->code,
+ $callback, $this->error_message_prefix,
+ $this->userinfo);
+ }
+ if ($this->mode & PEAR_ERROR_PRINT) {
+ $modes[] = 'print';
+ }
+ if ($this->mode & PEAR_ERROR_TRIGGER) {
+ $modes[] = 'trigger';
+ }
+ if ($this->mode & PEAR_ERROR_DIE) {
+ $modes[] = 'die';
+ }
+ if ($this->mode & PEAR_ERROR_RETURN) {
+ $modes[] = 'return';
+ }
+ return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.
+ 'prefix="%s" info="%s"]',
+ get_class($this), $this->message, $this->code,
+ implode("|", $modes), $levels[$this->level],
+ $this->error_message_prefix,
+ $this->userinfo);
+ }
+
+ // }}}
+}
+
+register_shutdown_function("_PEAR_call_destructors");
+
+/*
+ * Local Variables:
+ * mode: php
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+/*
+ * This file:
+ *
+ * - Include PHPTAL dependencies
+ * - Define PHPTAL attributes
+ * - Define PHPTAL aliases
+ * - Define PHPTAL rules
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+
+$__d = dirname(__FILE__);
+require_once "PEAR.php";
+
+if (OS_WINDOWS) {
+ define('PHPTAL_PATH_SEP', '\\');
+} else {
+ define('PHPTAL_PATH_SEP', '/');
+}
+
+function _phptal_os_path_join()
+{
+ $args = func_get_args();
+ return join(PHPTAL_PATH_SEP, $args);
+}
+
+require_once 'Types/Errors.php';
+require_once 'Types/OString.php';
+
+require_once _phptal_os_path_join($__d, 'PHPTAL', 'Cache.php');
+require_once _phptal_os_path_join($__d, 'PHPTAL', 'Context.php');
+require_once _phptal_os_path_join($__d, 'PHPTAL', 'Filter.php');
+require_once _phptal_os_path_join($__d, 'PHPTAL', 'LoopControler.php');
+require_once _phptal_os_path_join($__d, 'PHPTAL', 'OutputControl.php');
+require_once _phptal_os_path_join($__d, 'PHPTAL', 'Template.php');
+require_once _phptal_os_path_join($__d, 'PHPTAL', 'Macro.php');
+require_once _phptal_os_path_join($__d, 'PHPTAL', 'I18N.php');
+
+require_once _phptal_os_path_join($__d, 'PHPTAL', 'SourceResolver.php');
+require_once _phptal_os_path_join($__d, 'PHPTAL', 'SourceLocator.php');
+
+
+define('PHPTAL_VERSION', '0.7.0');
+define('PHPTAL_MARK', str_replace('.', '_', PHPTAL_VERSION) . '_');
+define('PHPTAL_DEFAULT_CACHE_DIR', '/tmp/');
+
+/**
+ * This define is used to select the templates output format.
+ *
+ * There's few differences between XHTML and XML but they these differences can
+ * break some browsers output.
+ *
+ * Default PHPTAL output mode is XHTML.
+ */
+define('PHPTAL_XHTML', 1);
+
+/**
+ * This define is used to select the templates output format.
+ *
+ * The XML mode does not worry about XHTML specificity and echo every entity
+ * in a <entity></entity> format.
+ */
+define('PHPTAL_XML', 2);
+
+/**
+ * @var _phptal_namespaces
+ * @type array
+ *
+ * This array contains the list of all known attribute namespaces, if an
+ * attribute belonging to one of this namespaces is not recognized by PHPTAL,
+ * an exception will be raised.
+ *
+ * These namespaces will be drop from resulting xml/xhtml unless the parser
+ * is told to keep them.
+ *
+ * @access private
+ * @static 1
+ */
+global $_phptal_namespaces;
+$_phptal_namespaces = array('TAL', 'METAL', 'I18N', 'PHPTAL');
+
+
+define('_PHPTAL_SURROUND', 1);
+define('_PHPTAL_REPLACE', 2);
+define('_PHPTAL_CONTENT', 3);
+
+/**
+ * @var _phptal_dictionary
+ * @type hashtable
+ *
+ * This dictionary contains ALL known PHPTAL attributes. Unknown attributes
+ * will be echoed in result as xhtml/xml ones.
+ *
+ * The value define how and when the attribute handler will be called during
+ * code generation.
+ *
+ * @access private
+ * @static 1
+ */
+global $_phptal_dictionary;
+$_phptal_dictionary = array(
+ 'TAL:DEFINE' => _PHPTAL_REPLACE, // set a context variable
+ 'TAL:CONDITION' => _PHPTAL_SURROUND, // print tag content only when condition true
+ 'TAL:REPEAT' => _PHPTAL_SURROUND, // repeat over an iterable
+ 'TAL:CONTENT' => _PHPTAL_CONTENT, // replace tag content
+ 'TAL:REPLACE' => _PHPTAL_REPLACE, // replace entire tag
+ 'TAL:ATTRIBUTES' => _PHPTAL_REPLACE, // dynamically set tag attributes
+ 'TAL:OMIT-TAG' => _PHPTAL_SURROUND, // omit to print tag but not its content
+ 'TAL:COMMENT' => _PHPTAL_SURROUND, // do nothing
+ 'TAL:ON-ERROR' => _PHPTAL_SURROUND, // replace content with this if error occurs
+
+ 'METAL:DEFINE-MACRO' => _PHPTAL_SURROUND, // define a template macro
+ 'METAL:USE-MACRO' => _PHPTAL_REPLACE, // use a template macro
+ 'METAL:DEFINE-SLOT' => _PHPTAL_SURROUND, // define a macro slot
+ 'METAL:FILL-SLOT' => _PHPTAL_SURROUND, // fill a macro slot
+
+ 'PHPTAL:INCLUDE' => _PHPTAL_REPLACE, // include an external template
+ 'PHPTAL:SRC-INCLUDE' => _PHPTAL_CONTENT, // include external file without parsing
+
+ 'I18N:TRANSLATE' => _PHPTAL_CONTENT, // translate some data using GetText package
+ 'I18N:NAME' => _PHPTAL_SURROUND, // prepare a translation name
+ 'I18N:ATTRIBUTES' => _PHPTAL_REPLACE, // translate tag attributes values
+);
+
+/**
+ * @var _phptal_aliases
+ * @type hashtable
+ *
+ * Create aliases for attributes. If an alias is found during parsing, the
+ * matching phptal attribute will be used.
+ *
+ * @access private
+ * @static 1
+ */
+global $_phptal_aliases;
+$_phptal_aliases = array(
+ 'TAL:INCLUDE' => 'PHPTAL:INCLUDE',
+ 'TAL:SRC-INCLUDE'=> 'PHPTAL:SRC-INCLUDE',
+);
+
+/**
+ * @var _phptal_rules_order
+ * @type hashtable
+ *
+ * This rule associative array represents both ordering and exclusion
+ * mecanism for template attributes.
+ *
+ * All known attributes must appear here and must be associated with
+ * an occurence priority.
+ *
+ * When more than one phptal attribute appear in the same tag, they
+ * will execute in following order.
+ *
+ * @access private
+ * @static 1
+ */
+global $_phptal_rules_order;
+$_phptal_rules_order = array(
+ 'TAL:OMIT-TAG' => 0, // surround -> $tag->disableHeadFootPrint()
+
+ 'TAL:ON-ERROR' => 1, // surround
+
+ 'METAL:DEFINE-MACRO' => 3, // surround
+ 'TAL:DEFINE' => 3, // replace
+ 'I18N:NAME' => 3, // replace
+ 'I18N:TRANSLATE' => 3, // content
+
+ 'TAL:CONDITION' => 4, // surround
+
+ 'TAL:REPEAT' => 5, // surround
+
+ 'I18N:ATTRIBUTES' => 6, // replace
+ 'TAL:ATTRIBUTES' => 6, // replace
+ 'TAL:REPLACE' => 6, // replace
+ 'METAL:USE-MACRO' => 6, // replace
+ 'PHPTAL:SRC-INCLUDE' => 6, // replace
+ 'PHPTAL:INCLUDE' => 6, // replace
+ 'METAL:DEFINE-SLOT' => 6, // replace
+ 'METAL:FILL-SLOT' => 6, // replace
+
+ 'TAL:CONTENT' => 7, // content
+
+ 'TAL:COMMENT' => 8, // surround
+);
+
+/**
+ * @var _phptal_xhtml_content_free_tags
+ * @type array
+ *
+ * This array contains XHTML tags that must be echoed in a <tag/> form
+ * instead of the <tag></tag> form.
+ *
+ * In fact, some browsers does not support the later form so PHPTAL
+ * ensure these tags are correctly echoed.
+ */
+global $_phptal_xhtml_empty_tags;
+$_phptal_xhtml_empty_tags = array(
+ 'AREA',
+ 'BASE',
+ 'BASEFONT',
+ 'BR',
+ 'COL',
+ 'FRAME',
+ 'HR',
+ 'IMG',
+ 'INPUT',
+ 'ISINDEX',
+ 'LINK',
+ 'META',
+ 'PARAM',
+);
+
+/**
+ * @var _phptal_xhtml_boolean_attributes
+ * @type array
+ *
+ * This array contains XHTML attributes that must be echoed in a minimized
+ * form. Some browsers (non HTML4 compliants are unable to interpret those
+ * attributes.
+ *
+ * The output will definitively not be an xml document !!
+ * PreFilters should be set to modify xhtml input containing these attributes.
+ */
+global $_phptal_xhtml_boolean_attributes;
+$_phptal_xhtml_boolean_attributes = array(
+ 'compact',
+ 'nowrap',
+ 'ismap',
+ 'declare',
+ 'noshade',
+ 'checked',
+ 'disabled',
+ 'readonly',
+ 'multiple',
+ 'selected',
+ 'noresize',
+ 'defer'
+);
+
+/**
+ * Shortcut to PHPTAL_Template for lazzy ones (me first).
+ */
+class PHPTAL extends PHPTAL_Template {}
+
+/**
+ * PEAR compliant class name.
+ */
+class HTML_Template_PHPTAL extends PHPTAL_Template {}
+
+/**
+ * PEAR compliant class name.
+ */
+class HTML_Template_PHPTAL_Filter extends PHPTAL_Filter {}
+
+/**
+ * PEAR compliant class name.
+ */
+class HTML_Template_PHPTAL_SourceLocator extends PHPTAL_SourceLocator {}
+
+/**
+ * PEAR compliant class name.
+ */
+class HTML_Template_PHPTAL_SourceResolver extends PHPTAL_SourceResolver {}
+
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+/**
+ * Base class for every kind of PHPTAL attribute.
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class PHPTAL_Attribute
+{
+ var $expression;
+
+ function PHPTAL_Attribute($exp)
+ {
+ $this->expression = $exp;
+ }
+
+ function activate(&$g, &$tag){}
+ function start(&$g, &$tag){}
+ function end(&$g, &$tag){}
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+class PHPTAL_Attribute_I18N_Attributes extends PHPTAL_Attribute
+{
+ var $_overwritten = array();
+
+ function activate(&$g, &$tag)
+ {
+ global $_phptal_xhtml_boolean_attributes;
+ $g->requireGettext();
+
+ $g->doPrintString('<', $tag->name());
+
+ $attributes = $tag->attributes();
+
+ // prepare expressions for attributes to translate
+ $exp = new PHPTAL_Expression($g, $tag, $this->expression);
+ $exp->setPolicy(_PHPTAL_ES_RECEIVER_IS_TEMP);
+ $err = $exp->prepare();
+ if (PEAR::isError($err)) { return $err; }
+
+ // more than one attribute to translate
+ if ($exp->countSubs() > 0) {
+ foreach ($exp->subs() as $subExpression) {
+ $err = $this->_attributeExpression($g, $tag, $subExpression);
+ if (PEAR::isError($err)) { return $err; }
+ }
+ } else {
+ $err = $this->_attributeExpression($g, $tag, $exp);
+ if (PEAR::isError($err)) { return $err; }
+ }
+
+ // echo non overwritten attributes
+ foreach ($attributes as $key=>$value) {
+ $test_key = strtolower($key);
+ if (!in_array($test_key, $this->_overwritten)) {
+ // beware of xhtml boolean attributes
+ if ($tag->_parser->_outputMode() == PHPTAL_XHTML
+ && in_array($test_key, $_phptal_xhtml_boolean_attributes)) {
+ $g->doPrintString(' '.$key);
+ } else {
+ $g->doPrintString(' '.$key.'="');
+ $g->doPrintString($value);
+ $g->doPrintString('"');
+ }
+ }
+ }
+
+ if ($tag->_isXHTMLEmptyElement()) {
+ $g->doPrintString(' />');
+ return;
+ }
+
+ if ($tag->hasContent()) {
+ $g->doPrintString('>');
+ } else {
+ $g->doPrintString('/>');
+ }
+
+ $err = $tag->generateContent($g);
+ if (PEAR::isError($err)) { return $err; }
+
+ if ($tag->hasContent()) {
+ $g->doPrintString('</', $tag->name(), '>');
+ }
+ }
+
+ function _attributeExpression(&$g, &$tag, &$subExpression)
+ {
+ $attributes = $tag->attributes();
+ $default = false;
+ $name = strtolower($subExpression->getReceiver());
+
+ // set default attribute value in $default variable
+ if (preg_match('/\bdefault\b/sm', $subExpression->_src)) {
+ if (!array_key_exists($name, $attributes)) {
+ $g->doAffectResult('$__default__', 'false');
+ } else {
+ $value = $attributes[$name];
+ $value = str_replace('\'', '\\\'', $value);
+ $g->doAffectResult('$__default__', '\''. $value . '\'');
+ }
+ $default = true;
+ }
+
+ $real = str_replace('__phptales_dd__', ':', $name);
+ $this->_overwritten[] = $real;
+ $err = $subExpression->generate();
+ if (PEAR::isError($err)) { return $err; }
+
+ $this->_printAttribute($g, $tag, $real, $name, $default);
+ }
+
+ function _printAttribute(&$g, &$tag, $realName, $varName, $default=false)
+ {
+ global $_phptal_xhtml_boolean_attributes;
+
+ // watch boolean attributes on XHTML mode
+ if ($tag->_parser->_outputMode() == PHPTAL_XHTML
+ && in_array($realName, $_phptal_xhtml_boolean_attributes)) {
+ $g->doIf('!PEAR::isError($'.$varName.') && $'.$varName);
+ $g->doPrintString(' '.$realName);
+ $g->endBlock();
+ } elseif ($default) {
+ $g->doIf('!PEAR::isError($'.$varName.') && false !== $'.$varName.' && null !== $'.$varName);
+ $g->doPrintString(' '. $realName . '="');
+ $code = sprintf('$__tpl__->_translate(trim($%s))', $varName);
+ $g->doPrintRes($code, true);
+ // $g->doPrintVar($varName);
+ $g->doPrintString('"');
+ $g->endBlock();
+ } else {
+ $g->doPrintString(' '. $realName . '="');
+ $code = sprintf('$__tpl__->_translate(trim($%s))', $varName);
+ $g->doPrintRes($code, true);
+ // $g->doPrintVar($varName);
+ $g->doPrintString('"');
+ }
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+class PHPTAL_Attribute_I18N_Name extends PHPTAL_Attribute
+{
+ function start(&$g, &$tag)
+ {
+ $g->requireGettext();
+ $g->doOBStart();
+ }
+
+ function end(&$g, &$tag)
+ {
+ $temp = $g->newTemporaryVar();
+ $g->doOBEnd($temp);
+ $code = sprintf('$__tpl__->_setTranslateVar(\'%s\', %s)',
+ $this->expression,
+ $temp);
+ $g->execute($code);
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+class PHPTAL_Attribute_I18N_Translate extends PHPTAL_Attribute
+{
+ function activate(&$g, &$tag)
+ {
+ $g->requireGettext();
+
+ if (strlen($this->expression) == 0) {
+ $key = "'". $this->_preparseGetTextKey($tag) ."'";
+ } else {
+ $exp = new PHPTAL_Expression($g, $tag, $this->expression);
+ $exp->setPolicy(_PHPTAL_ES_RECEIVER_IS_TEMP);
+ $key = $g->newTemporaryVar();
+ $exp->setReceiver($key);
+ $err = $exp->generate();
+ if (PEAR::isError($err)) { return $err; }
+ }
+
+ // children may contains i18n:name attributes,
+ // we ignore output but parse content before calling translation.
+ $g->doOBStart();
+ foreach ($tag->_children as $child) {
+ $child->generateCode($g);
+ }
+ $g->doOBClean();
+ // $code = sprintf('$__tpl__->_translate(\'%s\')', $key);
+ $code = sprintf('$__tpl__->_translate(%s)', $key);
+ $g->doPrintRes($code, true);
+ }
+
+ function _preparseGetTextKey(&$tag)
+ {
+ $key = "";
+ foreach ($tag->_children as $child) {
+ if ($child->isData()) {
+ $str = preg_replace('/\s+/sm', ' ', $child->_content);
+ $key .= trim($str) . ' ';
+ } else {
+ $is_i18n_name = false;
+ // look for i18n:name
+ foreach ($child->_surround_attributes as $att) {
+ if (get_class($att) == strtolower("PHPTAL_Attribute_I18N_Name")) {
+ $key .= '${' . $att->expression . '}';
+ $is_i18n_name = true;
+ }
+ }
+ // recursive call to preparse key for non i18n:name tags
+ if (!$is_i18n_name) {
+ $key .= PHPTAL_Attribute_I18N_Translate::_preparseGetTextKey($child) . ' ';
+ }
+ }
+ }
+
+ // remove every thing that has more than 1 space
+ $key = preg_replace('/\s+/sm', ' ', $key);
+ $key = trim($key);
+ return $key;
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+class PHPTAL_Attribute_METAL_Define_Macro extends PHPTAL_Attribute
+{
+ function start(&$g, &$tag)
+ {
+ $g->doMacroDeclare($this->expression);
+ }
+
+ function end(&$g, &$tag)
+ {
+ $g->doMacroEnd();
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+class PHPTAL_Attribute_METAL_Define_slot extends PHPTAL_Attribute
+{
+ function start(&$g, &$tag)
+ {
+ $slotName = $this->expression;
+ $cond = sprintf('$__ctx__->has(\'slots/%s\')', $slotName);
+ $g->doIf($cond);
+ $g->doPrintContext('slots/'.$slotName, true);
+ $g->doElse();
+ }
+
+ function end(&$g, &$tag)
+ {
+ $g->endBlock();
+ }
+};
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+class PHPTAL_Attribute_METAL_Fill_slot extends PHPTAL_Attribute
+{
+ function start(&$g, &$tag)
+ {
+ $g->doOBStart();
+ }
+
+ function end(&$g, &$tag)
+ {
+ $g->doOBEndInContext('slots/'.$this->expression);
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+class PHPTAL_Attribute_METAL_Use_Macro extends PHPTAL_Attribute
+{
+ function activate(&$g, &$tag)
+ {
+ $g->doOBStart();
+ $err = $tag->generateContent($g);
+ if (PEAR::isError($err)) { return $err; }
+ $g->doOBClean();
+
+ $path = $g->newTemporaryVar();
+
+ $g->doAffectResult($path, "'". PHPTAL_ES_path_in_string($this->expression,"'") . "'");
+
+ $temp = $g->newTemporaryVar();
+
+ // push error
+ $g->execute('$__old_error = $__ctx__->_errorRaised');
+ $g->execute('$__ctx__->_errorRaised = false');
+
+ $g->doAffectResult($temp, 'new PHPTAL_Macro($__tpl__, '. $path .')');
+ $g->doAffectResult($temp, $temp.'->execute($__tpl__)');
+
+ $g->doIf('PEAR::isError('.$temp.')');
+ $g->execute('$__ctx__->_errorRaised = '.$temp );
+ $g->endBlock();
+
+ $g->doPrintVar($temp, true);
+
+ // restore error
+ $g->doIf('!$__ctx__->_errorRaised');
+ $g->execute('$__ctx__->_errorRaised = $__old_error');
+ $g->endBlock();
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+class PHPTAL_Attribute_PHPTAL_Include extends PHPTAL_Attribute
+{
+ function activate(&$g, &$tag)
+ {
+ $exp = new PHPTAL_Expression($g, $tag, $this->expression);
+ $temp = $g->newTemporaryVar();
+ $exp->setPolicy(_PHPTAL_ES_RECEIVER_IS_TEMP);
+ $exp->setReceiver($temp);
+ $err = $exp->generate();
+ if (PEAR::isError($err)) { return $err; }
+
+ $tpl = $g->newTemporaryVar();
+ $g->doAffectResult($tpl, 'new PHPTAL_Template('.$temp.', $__tpl__->_repository, $__tpl__->_cacheDir)');
+ $g->execute($tpl.'->setParent($__tpl__)');
+ $g->execute($tpl.'->setContext($__ctx__)');
+ $g->doPrintRes($tpl.'->execute()', true);
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+class PHPTAL_Attribute_PHPTAL_Src_include extends PHPTAL_Attribute
+{
+ function activate(&$g, &$tag)
+ {
+ $temp = $g->newTemporaryVar();
+
+ $exp = new PHPTAL_Expression($g, $tag, $this->expression);
+ $exp->setPolicy(_PHPTAL_ES_RECEIVER_IS_TEMP);
+ $exp->setReceiver($temp);
+ $err = $exp->generate();
+ if (PEAR::isError($err)) { return $err; }
+
+ $g->doAffectResult($temp, '$__tpl__->realpath('.$temp.')');
+ $g->doIf('PEAR::isError('.$temp.')');
+ $g->execute('$__ctx__->_errorRaised = true');
+ $g->doPrintVar($temp);
+ $g->doElse();
+ $g->doPrintRes('join("", file('.$temp.'))', true);
+ $g->endBlock();
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+/**
+ * tal:attributes attribute handler.
+ *
+ * This template attribute defines xhtml entity attributes.
+ *
+ * Assuming link variable :
+ *
+ * $link->href = "http://www.google.fr";
+ * $link->title = "google search engine";
+ * $link->text = "google";
+ *
+ * The template code :
+ *
+ * <a href="http://www.example.com"
+ * title="sample title"
+ * class="cssLink"
+ *
+ * tal:attributes="href link/href; title link/title"
+ * tal:content="href link/text"
+ *
+ * >sample text</a>
+ *
+ * Will produce :
+ *
+ * <a class="cssLink"
+ * href="http://www.google.com"
+ * title="google search engine"
+ * >google</a>
+ *
+ * As shown above, non overwritten attributes are keep in result xhtml.
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class PHPTAL_Attribute_TAL_Attributes extends PHPTAL_Attribute
+{
+ var $_overwritten = array();
+
+ function activate(&$g, &$tag)
+ {
+ global $_phptal_xhtml_boolean_attributes;
+
+ $g->doPrintString('<', $tag->name());
+
+ $atts = $tag->attributes();
+
+ $exp = new PHPTAL_Expression($g, $tag, $this->expression);
+ $exp->setPolicy(_PHPTAL_ES_RECEIVER_IS_TEMP);
+ $err = $exp->prepare();
+ if (PEAR::isError($err)) { return $err; }
+
+ if ($exp->countSubs() > 0) {
+ foreach ($exp->subs() as $sub) {
+ $err = $this->_attributeExpression($g, $tag, $sub);
+ if (PEAR::isError($err)) { return $err; }
+ }
+ } else {
+ $err = $this->_attributeExpression($g, $tag, $exp);
+ if (PEAR::isError($err)) { return $err; }
+ }
+
+ // echo non overwritten xhtml attributes
+
+ foreach ($atts as $key=>$value) {
+ $test_key = strtolower($key);
+ if (!in_array($test_key, $this->_overwritten)) {
+ // boolean attributes
+ if ($tag->_parser->_outputMode() == PHPTAL_XHTML
+ && in_array($test_key, $_phptal_xhtml_boolean_attributes)) {
+ $g->doPrintString(' '.$key);
+ } else {
+ $g->doPrintString(' '.$key .'="');
+ $g->doPrintString($value);
+ $g->doPrintString('"');
+ }
+ }
+ }
+
+ if ($tag->_isXHTMLEmptyElement()) {
+ $g->doPrintString(' />');
+ return;
+ }
+
+ // continue tag show
+ if ($tag->hasContent()) {
+ $g->doPrintString('>');
+ } else {
+ $g->doPrintString('/>');
+ }
+
+ $err = $tag->generateContent($g);
+ if (PEAR::isError($err)) { return $err; }
+
+ if ($tag->hasContent()) {
+ $g->doPrintString('</', $tag->name(), '>');
+ }
+ }
+
+ function _printAttribute(&$g, &$tag, &$realName, &$varName, $default=false)
+ {
+ global $_phptal_xhtml_boolean_attributes;
+ // watch boolean attributes on XHTML mode
+ if ($tag->_parser->_outputMode() == PHPTAL_XHTML
+ && in_array($realName, $_phptal_xhtml_boolean_attributes)) {
+ $g->doIf('!PEAR::isError($'.$varName.') && $'.$varName);
+ $g->doPrintString(' '.$realName);
+ $g->endBlock();
+ } elseif ($default) {
+ $g->doIf('!PEAR::isError($'.$varName.') && false !== $'.$varName.' && null !== $'.$varName);
+ $g->doPrintString(' '. $realName . '="');
+ $g->doPrintVar($varName);
+ $g->doPrintString('"');
+ $g->endBlock();
+ } else {
+ $g->doPrintString(' '. $realName . '="');
+ $g->doPrintVar($varName);
+ $g->doPrintString('"');
+ }
+ }
+
+ function _attributeExpression(&$g, &$tag, &$sub)
+ {
+ $atts = $tag->attributes();
+ $default = false;
+ $name = strtolower($sub->getReceiver());
+
+ // default in tal:attributes use default tag attributes
+ // if (preg_match('/\|\s*?\bdefault\b/sm', $sub->_src)) {
+ if (preg_match('/\bdefault\b/sm', $sub->_src)) {
+ // ensure the attribute as a default value set in source
+ // template
+ $attrs = $tag->attributes();
+ if (!array_key_exists($name, $atts)){
+ $g->doAffectResult('$__default__', 'false');
+ } else {
+ // store the default value in __default__ variable
+ $g->doAffectResult('$__default__',
+ '\''. str_replace('\'', '\\\'', $atts[$name]) . '\'' );
+ }
+ $default = true;
+ }
+
+ $real = str_replace('__phptales_dd__', ':', $name);
+ $this->_overwritten[] = $real;
+ $err = $sub->generate();
+ if (PEAR::isError($err)) { return $err; }
+
+ $this->_printAttribute($g, $tag, $real, $name, $default);
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+class PHPTAL_Attribute_TAL_Comment extends PHPTAL_Attribute
+{
+ function start(&$g, &$tag)
+ {
+ $g->doComment($this->expression);
+ }
+
+ function end(&$g, &$tag)
+ {}
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+
+class PHPTAL_Attribute_TAL_Condition extends PHPTAL_Attribute
+{
+ function start(&$g, &$tag)
+ {
+ $exp = new PHPTAL_Expression($g, $tag, $this->expression);
+ $exp->setPolicy(_PHPTAL_ES_RECEIVER_IS_TEMP);
+ $test_var = $g->newTemporaryVar();
+ $exp->setReceiver($test_var);
+ $err = $exp->generate();
+ if (PEAR::isError($err)) { return $err; }
+ $g->doIf("!PEAR::isError($test_var) && $test_var");
+ }
+
+ function end(&$g, &$tag)
+ {
+ $g->endBlock();
+ }
+};
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+/**
+ * Handle tal:content attributes
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class PHPTAL_Attribute_TAL_Content extends PHPTAL_Attribute
+{
+ function activate(&$g, &$tag)
+ {
+ if (preg_match('/\|\s*?\bdefault\b/sm', $this->expression)) {
+ $g->doOBStart();
+ foreach ($tag->_children as $child) {
+ $err = $child->generateCode($g);
+ if (PEAR::isError($err)) { return $err; }
+ }
+ $g->doOBEnd('$__default__');
+ $default = true;
+ }
+
+ $g->setSource($tag->name(), $tag->line);
+ $exp = new PHPTAL_Expression($g, $tag, $this->expression);
+ $exp->setPolicy(_PHPTAL_ES_RECEIVER_IS_OUTPUT);
+ $err = $exp->generate();
+ if (PEAR::isError($err)) { return $err; }
+
+ if (isset($default)) { $g->execute('unset($__default__)'); }
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+class PHPTAL_Attribute_TAL_Define extends PHPTAL_Attribute
+{
+ function activate(&$g, &$tag)
+ {
+ if (preg_match('/\|\s*?\bdefault\b/sm', $this->expression)) {
+ $g->doOBStart();
+ $err = $tag->generateContent($g);
+ if (PEAR::isError($err)) { return $err; }
+ $g->doOBEnd('$__default__');
+ $default = true;
+ }
+
+ // use node content to set variable
+ if (preg_match('/^[a-z0-9_]+$/i', $this->expression)) {
+ $g->doOBStart();
+ $err = $tag->generateContent($g);
+ if (PEAR::isError($err)) { return $err; }
+ $g->doOBEndInContext($this->expression);
+ } else {
+ $exp = new PHPTAL_Expression($g, $tag, $this->expression);
+ $exp->setPolicy(_PHPTAL_ES_RECEIVER_IS_CONTEXT);
+ $err = $exp->generate();
+ if (PEAR::isError($err)) { return $err; }
+ }
+ if (isset($default)) { $g->execute('unset($__default__)'); }
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+class PHPTAL_Attribute_TAL_Omit_tag extends PHPTAL_Attribute
+{
+ function start(&$g, &$tag)
+ {
+ $tag->disableHeadFoot();
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+class PHPTAL_Attribute_TAL_On_Error extends PHPTAL_Attribute
+{
+ function start(&$g, &$tag)
+ {
+ $g->doComment("OnError handling");
+ $g->execute('$__ctx__->cleanError()');
+ $g->doOBStart();
+ }
+
+ function end(&$g, &$tag)
+ {
+ $exp = new PHPTAL_Expression($g, $tag, $this->expression);
+ $g->doIf("\$__ctx__->errorRaised()");
+ $g->doOBClean();
+ $exp->setPolicy(_PHPTAL_ES_RECEIVER_IS_OUTPUT);
+ $err = $exp->generate();
+ if (PEAR::isError($err)) { return $err; }
+ $g->doElse();
+ $g->doOBPrint();
+ $g->endBlock();
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+class PHPTAL_Attribute_TAL_Repeat extends PHPTAL_Attribute
+{
+ var $in = "__in__";
+ var $out = "__out__";
+
+ function start(&$g, &$tag)
+ {
+ $g->setSource($tag->name(), $tag->line);
+
+ $g->doComment('new loop');
+ $exp = new PHPTAL_Expression($g, $tag, $this->expression);
+ $exp->setPolicy(_PHPTAL_ES_RECEIVER_IS_TEMP);
+ $err = $exp->prepare();
+ if (PEAR::isError($err)) { return $err; }
+
+ $this->out = $exp->getReceiver();
+ $temp = $g->newTemporaryVar();
+ $exp->setReceiver($temp);
+ $err = $exp->generate(); // now $temp points to the loop data
+ if (PEAR::isError($err)) { return $err; }
+
+ $loop = $g->newTemporaryVar();
+ $this->loop = $loop;
+ $g->doAffectResult($loop,
+ '& new PHPTAL_LoopControler($__ctx__, "'. $this->out . '", '
+ . $temp .');');
+ $g->doIf('PEAR::isError('.$loop.'->_error)');
+ $g->doPrintVar($loop.'->_error');
+ $g->doElse();
+ // $g->execute($loop.'->prepare()');
+ $g->doWhile($loop.'->isValid()');
+
+ }
+
+ function end(&$g, &$tag)
+ {
+ $g->execute($this->loop.'->next()');
+ $g->endBlock();
+ $g->endBlock();
+ $g->doComment('end loop');
+ }
+}
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+class PHPTAL_Attribute_TAL_Replace extends PHPTAL_Attribute
+{
+ function activate(&$g, &$tag)
+ {
+ if (preg_match('/\|\s*?\bdefault\b/sm', $this->expression)) {
+ $g->doOBStart();
+ foreach ($tag->_children as $child) {
+ $err = $child->generateCode($g);
+ if (PEAR::isError($err)) { return $err; }
+ }
+ $g->doOBEnd('$__default__');
+ $default = true;
+ }
+ $g->setSource($tag->name(), $tag->line);
+ $exp = new PHPTAL_Expression($g, $tag, $this->expression);
+ $exp->setPolicy(_PHPTAL_ES_RECEIVER_IS_OUTPUT);
+ $err = $exp->generate();
+ if (PEAR::isError($err)) { return $err; }
+
+ if (isset($default)) { $g->execute('unset($__default__)'); }
+ }
+};
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+/**
+ * Interface to PHPTAL cache system.
+ *
+ * Implement this interface and use PHPTAL_Template::setCacheManager() method
+ * to intercept macro and template execution with your own cache system.
+ *
+ * The aim of this system is to fine grain the caching of PHPTAL results
+ * allowing the php coder to use whatever cache system he prefers with a
+ * good granularity as he can cache result of specific templates execution,
+ * and specific macros calls.
+ *
+ * @see Documentation.txt
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class PHPTAL_Cache
+{
+ /**
+ * Called each time a template has to be executed.
+ *
+ * This method must return the cache value or the template execution
+ * return.
+ *
+ * function template(&$tpl, $path, &$context)
+ * {
+ * // return cache if exists
+ * // else realy process template
+ * $res = $tpl->_process();
+ * // cache the result if needed
+ * // and return it
+ * return $res;
+ * }
+ *
+ * @param PHPTAL_Template -- the template that must be cached/executed
+ * @param string $path -- the template path
+ * @param PHPTAL_Context $ctx -- the execution context
+ *
+ * @return string
+ */
+ function template(&$tpl, $path, &$context)
+ {
+ return $tpl->_process();
+ }
+
+
+ /**
+ * Called each time a macro needs to be executed.
+ *
+ * This method allow cache on macro result. It must return the cache value
+ * or at least the macro execution result.
+ *
+ * function macro(&$macro, $file, $name, &$context)
+ * {
+ * // return cache if exists
+ * // else really process macro
+ * $res = $macro->_process();
+ * // cache the result if needed
+ * // and return it
+ * return $res
+ * }
+ *
+ *
+ * @param PHPTAL_Macro -- the macro to executed
+ * @param string $file -- the macro source file
+ * @param string $name -- the macro name
+ * @param PHPTAL_Context $context -- the current execution context
+ *
+ * @return string
+ */
+ function macro(&$macro, $file, $name, &$context)
+ {
+ return $macro->_process();
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+
+// What are Context and Resolvers
+// ==============================
+//
+// In phptal, variables and methods are referenced by path without distinction,
+// also, arrays and hashtable elements are accessed using a path like
+// myarray/10 to reach the 11's element of myarray or myhash/mykey to
+// reach the value of mykey in myhash.
+//
+// This allow things like 'myobject/mymethod/10/othermethod' which is usefull
+// in templates.
+//
+// The Context is an array containing all template variables.
+// When asked to reach a path, it look for the variable type of the first path
+// element and generate a resolver matching the variable type.
+// Then this resolver is asked to resolve the rest of the path, etc... up to
+// the final path element.
+//
+// Resolver knows how to handle a given path relatively to the object they
+// handle.
+//
+// For instance, an array resolver can handle access to its array elements
+// given a key (string) or the element id (int).
+//
+// The array resolver also bring an array method : count which return the
+// number of elements in the array.
+//
+// Note
+// ----
+//
+// If a name conflict occurs between an object's variable and one of its
+// methods, the variable is return in stead of the method.
+//
+
+/**
+ * Template context handler.
+ *
+ * This object produce a translation between variables passed to the template
+ * and regular template pathes.
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class PHPTAL_Context
+{
+ var $_array = array();
+ var $_errorRaised = false;
+
+ /**
+ * Context constructor.
+ *
+ * @param hashtable $hash (opt)
+ */
+ function PHPTAL_Context($hash=array())
+ {
+ $this->_array = $hash;
+ }
+
+ /**
+ * Set a value by reference.
+ *
+ * @param string $path -- Variable path
+ * @param mixed $value -- Reference to variable.
+ */
+ function setRef($path, &$value)
+ {
+ $this->remove($path);
+ $this->_array[$path] =& $value;
+ }
+
+ /**
+ * Set a context variable.
+ *
+ * @param string $path -- Variable path
+ * @param mixed $value -- Value
+ */
+ function set($path, $value)
+ {
+ $this->remove($path);
+ $this->_array[$path] = $value;
+ }
+
+ /**
+ * Remove a context variable.
+ *
+ * @param string $path -- Context path
+ */
+ function remove($path)
+ {
+ if (array_key_exists($path, $this->_array)) {
+ unset($this->_array[$path]);
+ }
+ }
+
+ /**
+ * Test if the context can resolve specified path.
+ *
+ * @param string $path
+ * Path to a context resource.
+ *
+ * @return boolean
+ */
+ function has($path)
+ {
+ if (array_key_exists($path, $this->_array)) {
+ return true;
+ }
+ $resp =& PHPTAL_ArrayResolver::get($this->_array, $path);
+ if (PEAR::isError($resp)) {
+ return false;
+ } else if ($resp === null) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Return the context associative object.
+ */
+ function &getHash()
+ {
+ return $this->_array;
+ }
+
+ /**
+ * Retrieve specified context resource.
+ *
+ * @param string $path
+ * Path to a context resource.
+ *
+ * @return mixed
+ * @throws NameError
+ */
+ function &get($path)
+ {
+ if (array_key_exists($path, $this->_array)) {
+ return $this->_array[$path];
+ }
+ $resp =& PHPTAL_ArrayResolver::get($this->_array, $path);
+ if (PEAR::isError($resp)) {
+ $this->_errorRaised = $resp;
+ } else if ($resp === null) {
+ $this->_errorRaised = true;
+ }
+ return $resp;
+ }
+
+ /**
+ * Retrieve specified context resouce as a string object
+ *
+ * @param string $path
+ * Path to a context resource.
+ *
+ * @return string
+ */
+ function &getToString($path)
+ {
+ $o = $this->get($path);
+ if (is_object($o) && method_exists($o, "toString")) {
+ return $o->toString();
+ }
+ if (is_object($o) || is_array($o)) {
+ ob_start();
+ print_r($o); // var_dump($o);
+ $res = ob_get_contents();
+ ob_end_clean();
+ return $res;
+ }
+ return $o;
+ }
+
+ /**
+ * Tells if an error was raised by this context accessing an unknown path.
+ *
+ * @return boolean
+ */
+ function errorRaised()
+ {
+ $r = $this->_errorRaised;
+ $this->_errorRaised = false;
+ return $r;
+ }
+
+ /**
+ * Clean error handler.
+ */
+ function cleanError()
+ {
+ $this->_errorRaised = false;
+ }
+}
+
+/**
+ * Object resolver.
+ *
+ * Resolver pathes relatives to object.
+ *
+ * @access private
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class PHPTAL_ObjectResolver
+{
+ function &get(&$obj, $subpath)
+ {
+ list($first, $next) = PHPTAL_path_explode($subpath);
+
+ if (method_exists($obj, $first)) {
+ // reference to a variable of the handled object
+ $value =& $obj->$first();
+ } elseif (array_key_exists($first, $obj)) {
+ // reference to an object variable
+ $value =& $obj->$first;
+ } elseif (is_a($obj, 'OHash') && $obj->containsKey($first)) {
+ return $obj->get($first);
+ } elseif (is_a($obj, 'OArray') && preg_match('/^[0-9]+$/', $first)) {
+ return $obj->get((int)$first);
+ } else {
+ $err = new NameError($subpath . " not found");
+ return $err;
+ }
+ // more to resolve in value
+ if ($next) {
+ return PHPTAL_resolve($next, $value, $obj);
+ }
+ return $value;
+ }
+}
+
+
+/**
+ * Array resolver.
+ *
+ * Resolve pathes relative to arrays and hashtables.
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class PHPTAL_ArrayResolver
+{
+ function &get(&$array, $subpath)
+ {
+ list($first, $next) = PHPTAL_path_explode($subpath);
+
+ if ($first == "length" && !$next && !array_key_exists("length", $array)) {
+ return count($array);
+ }
+
+ if ($next === false) {
+ if ($first == 'count') { return count($array); }
+ if ($first == 'keys') { return array_keys($array); }
+ if ($first == 'values'){ return array_values($array); }
+ if (!array_key_exists($first, $array)) {
+ $err = new NameError("(ArrayResolver) Context does not contains key '$first'");
+ return $err;
+ }
+ return $array[$first];
+ }
+
+ if (!array_key_exists($first, $array)) {
+ $err = new NameError("(ArrayResolver) Context does not contains key '$first'");
+ return $err;
+ }
+ $temp =& $array[$first];
+ if ($temp) {
+ return PHPTAL_resolve($next, $temp, $array);
+ }
+ }
+}
+
+
+/**
+ * String resolver.
+ *
+ * Add method to string type.
+ */
+class PHPTAL_StringResolver
+{
+ function get(&$str, $subpath)
+ {
+ list($first, $next) = PHPTAL_path_explode($subpath);
+ if ($next) {
+ $err = new TypeError("string methods have no sub path (string.$subpath)");
+ return $err;
+ }
+ if ($first == "len") {
+ return strlen($str);
+ }
+ $err = new NameError("string type has no method named '$first'");
+ return $err;
+ }
+};
+
+/**
+ * Retrieve a resolver given a value and a parent.
+ */
+function &PHPTAL_resolve($path, &$value, &$parent)
+{
+ if (is_array($value)) return PHPTAL_ArrayResolver::get($value, $path);
+ if (is_object($value)) return PHPTAL_ObjectResolver::get($value, $path);
+ if (is_string($value)) return PHPTAL_StringResolver::get($value, $path);
+ $err = new TypeError("unable to find adequate resolver for '".gettype($value)."' remaining path is '$path'");
+ return $err;
+}
+
+/**
+ * Shift the first elemetn of a phptal path.
+ *
+ * Returns an array containing the first element of the path and the rest of
+ * the path.
+ */
+function PHPTAL_path_explode($path)
+{
+ if (preg_match('|^(.*?)\/(.*?)$|', $path, $match)) {
+ array_shift($match);
+ return $match;
+ } else {
+ return array($path, false);
+ }
+
+ $pos = strpos($path,".");
+ if ($pos !== false) {
+ $first = substr($path, 0, $pos);
+ $next = substr($path, $pos+1);
+ return array($first, $next);
+ }
+ return array($path, false);
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+define('_PHPTAL_ES_RECEIVER_IS_CONTEXT', 1);
+define('_PHPTAL_ES_RECEIVER_IS_NONE', 2);
+define('_PHPTAL_ES_RECEIVER_IS_TEMP', 4);
+define('_PHPTAL_ES_RECEIVER_IS_OUTPUT', 8);
+
+
+// Important note:
+//
+// The PHPTALES system works quite well but it's not yet fully optimized, it
+// should be refactored at some points of my TODO list.
+//
+
+
+class PHPTAL_Expression
+{
+ var $_tag;
+ var $_gen;
+ var $_src;
+ var $_subs = array();
+ var $_policy;
+ var $_receiver = false;
+ var $_prepared = false;
+ var $_structure = false;
+
+ function PHPTAL_Expression(&$generator, &$tag, $str)
+ {
+ $this->_tag =& $tag;
+ $this->_gen =& $generator;
+ $this->_src = $str;
+ }
+
+ function setPolicy($policy)
+ {
+ $this->_policy = $policy;
+ }
+
+ /**
+ * Prepare the expression.
+ *
+ * This method explode the expression into sub expression and prepare each
+ * expression for parsing.
+ *
+ * @throws PHPTAL_ExpressionError
+ * If receiver policy fail.
+ */
+ function prepare()
+ {
+ if ($this->_prepared) { return; }
+ $test = $this->_src;
+
+ // some sub expression detected
+ while (preg_match('/^(.*?)(?<!;);(?!;)/sm', $test, $m)) {
+ list($src, $exp) = $m;
+
+ $x = new PHPTAL_Expression($this->_gen, $this->_tag, $exp);
+ $x->setPolicy($this->_policy);
+ $x->setReceiver($this->getReceiver());
+ $err = $x->prepare();
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+ $this->_subs[] = $x;
+
+ $test = substr($test, strlen($src));
+ }
+ // if subs, insert last one
+ if ($this->countSubs() > 0 && strlen(trim($test)) > 0) {
+ $exp = $test;
+ $x = new PHPTAL_Expression($this->_gen, $this->_tag, $exp);
+ $x->setPolicy($this->_policy);
+ $x->setReceiver($this->getReceiver());
+ $err = $x->prepare();
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+ $this->_subs[] = $x;
+ } else {
+ // otherwise, just remove expression delimiters from source
+ // and apply the receiver policy
+ $exp = $test;
+ $exp = str_replace(';;', ';', $exp);
+ $this->_src = $exp;
+ if (strlen($exp) == 0) return;
+
+ $err = $this->_extractReceiver();
+ if (PEAR::isError($err)){
+ return $err;
+ }
+
+ if (!$this->_receiver
+ && ($this->_policy & _PHPTAL_ES_RECEIVER_IS_CONTEXT
+ || $this->_policy & _PHPTAL_ES_RECEIVER_IS_TEMP)) {
+ $str = sprintf('Receiver required in expression \'%s\' from %s:%d',
+ $this->_src,
+ $this->_tag->_parser->_file,
+ $this->_tag->line);
+ $err = new PHPTAL_ExpressionError($str);
+ return PEAR::raiseError($err);
+ }
+
+ if ($this->_policy & _PHPTAL_ES_RECEIVER_IS_NONE && $this->_receiver) {
+ $str = sprintf('Unexpected receiver \'%s\' in expression \'%s\' from %s:%d',
+ $this->_receiver,
+ $this->_src,
+ $this->_tag->_parser->_file,
+ $this->_tag->line);
+ $err = new PHPTAL_ExpressionError($str);
+ return PEAR::raiseError($err);
+ }
+ }
+
+ $this->_prepared = true;
+ }
+
+ function _extractReceiver()
+ {
+ global $_phptal_es_namespaces;
+
+ $this->_src = preg_replace('/^\s+/sm', '', $this->_src);
+ if (preg_match('/^([a-z:A-Z_0-9]+)\s+([^\|].*?)$/sm', $this->_src, $m)) {
+ // the receiver looks like xxxx:aaaa
+ //
+ // we must ensure that it's not a known phptales namespaces
+ if (preg_match('/^([a-zA-Z_0-9]+):/', $m[1], $sub)) {
+ $ns = $sub[1];
+ // known namespace, just break
+ if (function_exists('phptal_es_'.$ns)) {
+ // in_array(strtolower($ns), $_phptal_es_namespaces)) {
+ return;
+ }
+ }
+
+ if ($this->_receiver) {
+ $str = sprintf('Receiver already set to \'%s\' in \'%s\'',
+ $this->_receiver,
+ $this->_src);
+ $err = new PHPTAL_ExpressionError($str);
+ return PEAR::raiseError($err);
+ }
+
+ $this->_receiver = $m[1];
+ //
+ // that the way to replace : in setters (usually this this should
+ // only be used under tal:attributes tag !!!!
+ //
+ $this->_receiver = str_replace(':', '__phptales_dd__', $this->_receiver);
+ $this->_src = $m[2];
+ if ($this->_receiver == "structure") {
+ $this->_structure = true;
+ $this->_receiver = false;
+ $this->_extractReceiver();
+ }
+ }
+ }
+
+ /**
+ * Retrieve the number of sub expressions.
+ */
+ function countSubs()
+ {
+ return count($this->_subs);
+ }
+
+ function &subs()
+ {
+ return $this->_subs;
+ }
+
+ /**
+ * Returns true if a receiver is set for this expression.
+ */
+ function hasReceiver()
+ {
+ return $this->_receiver != false;
+ }
+
+ /**
+ * Retrieve receiver's name.
+ */
+ function getReceiver()
+ {
+ return $this->_receiver;
+ }
+
+ /**
+ * Set expression receiver.
+ */
+ function setReceiver($name)
+ {
+ $this->_receiver = $name;
+ }
+
+ /**
+ * Generate php code for this expression.
+ */
+ function generate()
+ {
+ $err = $this->prepare();
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+
+ if ($this->countSubs() > 0) {
+ foreach ($this->_subs as $sub) {
+ $err = $sub->generate();
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+ }
+ } else {
+ $exp = $this->_src;
+ if (strlen($exp) == 0) return;
+
+ // expression may be composed of alternatives | list of expression
+ // they are evaluated with a specific policy : _PHPTAL_ES_SEQUENCE
+ //
+ // 'string:' break the sequence as no alternative exists after a
+ // string which is always true.
+ if (preg_match('/\s*?\|\s*?string:/sm', $exp, $m)) {
+ $search = $m[0];
+ $str = strpos($exp, $search);
+ // $str = strpos($exp, ' | string:');
+ // if ($str !== false) {
+ $seq = preg_split('/(\s*?\|\s*?)/sm', substr($exp, 0, $str));
+ $seq[]= substr($exp, $str + 2);
+ } else {
+ $seq = preg_split('/(\s*?\|\s*?)/sm', $exp);
+ }
+
+ // not a sequence
+ if (count($seq) == 1) {
+ $code = PHPTAL_Expression::_GetCode($this, $exp);
+ if (PEAR::isError($code)) { return $code; }
+
+ $temp = $this->_gen->newTemporaryVar();
+ $this->_gen->doAffectResult($temp, $code);
+ return $this->_useResult($temp);
+ } else {
+ return $this->_evaluateSequence($seq);
+ }
+ }
+ }
+
+
+ function _evaluateSequence($seq)
+ {
+ $temp = $this->_gen->newTemporaryVar();
+ $this->_gen->doIf('!$__ctx__->_errorRaised');
+ $this->_gen->doDo();
+ foreach ($seq as $s) {
+ // skip empty parts
+ if (strlen(trim($s)) > 0) {
+ $code = PHPTAL_Expression::_GetCode($this, $s);
+ if (PEAR::isError($code)) { return $code; }
+
+ $this->_gen->doUnset($temp);
+ $this->_gen->doAffectResult($temp, $code);
+ $this->_gen->doIf('!PEAR::isError(' . $temp . ') && ' .
+ // $temp .' != false && '.
+ $temp . ' !== null');
+ $this->_gen->execute('$__ctx__->_errorRaised = false');
+ $this->_gen->execute('break');
+ $this->_gen->endBlock();
+ }
+ }
+ $this->_gen->doEndDoWhile('0');
+ // test errorRaised
+ $this->_gen->doElse();
+
+ // $this->_gen->doAffectResult($temp, '""'); // $__ctx__->_errorRaised');
+ $this->_gen->doAffectResult($temp, '$__ctx__->_errorRaised');
+ $this->_gen->execute('$__ctx__->_errorRaised = false');
+ $this->_gen->endBlock();
+ // ----------------
+
+ $err = $this->_useResult($temp);
+
+ // $this->_gen->endBlock();
+ return $err;
+ }
+
+ function _useResult($temp)
+ {
+ if ($this->_policy & _PHPTAL_ES_RECEIVER_IS_TEMP) {
+ $this->_gen->doReference($this->_receiver, $temp);
+ } else if ($this->_policy & _PHPTAL_ES_RECEIVER_IS_OUTPUT) {
+ $this->_gen->doPrintVar($temp, $this->_structure);
+ } else if ($this->_policy & _PHPTAL_ES_RECEIVER_IS_CONTEXT) {
+ $this->_gen->doContextSet($this->_receiver, $temp);
+ } else {
+ $err = new PHPTAL_ExpressionError("Expression '$this->_src' Don't know what to do with result.");
+ return PEAR::raiseError($err);
+ }
+ }
+
+ /**
+ * Retrieve a function namespace for given string and the associated
+ * expression.
+ *
+ * Examples:
+ *
+ * The function namespace of 'php:XXXX' is 'php'
+ * The function namespace of 'XXXX' is 'path'
+ * The function namespace of 'foo:bar::baz' is 'foo'
+ *
+ * @param string $str
+ * Expression string without receiver
+ *
+ * @return array
+ * An array composed as follow : array('ns', 'exp'),
+ * Where 'ns' is the function namespace and 'exp', is the
+ * source string without the 'ns:' part.
+ */
+ function _FindFunctionNamespace($str)
+ {
+ $str = preg_replace('/^\s/sm', '', $str);
+ if (preg_match('/^([a-z0-9\-]+):(?!>:)(.*?)$/ism', $str, $m)) {
+ list($ns, $path) = array_slice($m, 1);
+ $ns = str_replace('-', '_', $ns);
+ return array($ns, $path);
+ }
+ return array('path', $str);
+ }
+
+ /**
+ * Get the code for a ns:args string.
+ */
+ function _GetCode(&$exp, $str)
+ {
+ list($ns, $args) = PHPTAL_Expression::_FindFunctionNamespace($str);
+ $func = "PHPTAL_ES_$ns";
+ if (!function_exists($func)) {
+ $err = new PHPTAL_ExpressionError("Unknown function $func in '$str'");
+ return PEAR::raiseError($err);
+ }
+ return $func($exp, $args);
+ }
+}
+
+/**
+ * Thrown on expression parsing error.
+ */
+class PHPTAL_ExpressionError extends PEAR_Error
+{
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+/**
+ * @var $_phptal_es_namespaces
+ * @type array
+ *
+ * This array contains phptales namespaces, it means php:, string:, exists:,
+ * not: and path: at this time.
+ */
+global $_phptal_es_namespaces;
+$_phptal_es_namespaces = array('not', 'php', 'string', 'exists', 'path', 'not-exists');
+
+/**
+ * @var $_phptal_es_keywords
+ * @type array
+ *
+ * List of reserved phptales keywords.
+ */
+global $_phptal_es_keywords;
+$_phptal_es_keywords = array('nothing', 'default', 'structure');
+
+global $_phptal_es_php_types;
+$_phptal_es_php_types = array('true', 'false', 'null');
+
+/**
+ * string: expression type
+ */
+function PHPTAL_ES_string(&$exp, $value)
+{
+ $value = PHPTAL_ES_path_in_string($value);
+ // $value = str_replace('$$', '\$\$', $value);
+ $value = '"' . $value . '"';
+ $value = preg_replace('/^\" ?\"\. /', '', $value);
+ $value = preg_replace('/\.\"\"$/', '', $value);
+ return $value;
+}
+
+/**
+ * not: expression type
+ */
+function PHPTAL_ES_not(&$exp, $value)
+{
+ return "! " . PHPTAL_ES_path($exp, $value);
+}
+
+/**
+ * not-exists: expression type
+ */
+function PHPTAL_ES_not_exists(&$exp, $value)
+{
+ return "! " . PHPTAL_ES_exists($exp, $value);
+}
+
+/**
+ * path: expression type
+ *
+ * @todo default -- existing text inside tag
+ * options -- ?? keyword arguments passed to template ??
+ * repeat -- repeat variables
+ * attrs -- initial values of tag attributes
+ * CONTEXTS -- list of standard names (above list) in case one of these
+ * names was hidden by a local variable.
+ */
+function PHPTAL_ES_path(&$exp, $value)
+{
+ $value = trim($value);
+ // special case : 'string' authorized
+ if ($value[0] == "'") { return PHPTAL_ES_path_in_string($value,"'"); }
+ // number case
+ if (preg_match('/^[0-9\.]*$/', $value)) { return $value; }
+ // php constants are accessed though @ keyword
+ if (preg_match('/^@[_a-z][0-9a-z_]*$/i', $value)) { return substr($value, 1); }
+ if ($value == "nothing") { return 'null'; }
+ if ($value == "default") { return '$__default__'; }
+ $value = PHPTAL_ES_path_in_string($value);
+ return '$__ctx__->get("'. $value .'")';
+}
+
+
+function PHPTAL_ES_path_toString(&$exp, $value)
+{
+ $value = trim($value);
+ if ($value == "nothing") { return 'null'; }
+ if ($value == "default") { return '$__default__'; }
+ if (preg_match('/^@[_a-z][0-9a-z_]*$/i', $value)) { return substr($value, 1); }
+ return '$__ctx__->getToString("'. $value .'")';
+}
+
+
+/**
+ * exists: expression type
+ */
+function PHPTAL_ES_exists(&$exp, $value)
+{
+ return '$__ctx__->has("'. trim($value). '")';
+}
+
+/**
+ * php: expression type
+ */
+function PHPTAL_ES_php(&$exp, $str)
+{
+ $parser = new PHPTAL_ES_PHP_Parser($exp, $str);
+ return $parser->evaluate();
+}
+
+
+//
+// utility functions (internal)
+//
+
+function PHPTAL_ES_path_in_string($arg, $c='"')
+{
+ list($match,$repl) = _PHPTAL_context_accessed($arg);
+ for ($i=0; $i<count($match); $i++) {
+ $null = "";
+ $repl[$i] = $c . ". " . PHPTAL_ES_path_toString($null, $repl[$i]) . " ." . $c;
+ $pos = strpos($arg, $match[$i]);
+ $arg = substr($arg, 0, $pos)
+ . $repl[$i]
+ . substr($arg, $pos + strlen($match[$i]));
+ }
+ if ($c == '"') {
+ $arg = str_replace('$$','\$', $arg);
+ } else {
+ $arg = str_replace('$$', '$', $arg);
+ }
+ return $arg;
+}
+
+function _PHPTAL_context_accessed($str)
+{
+ if (preg_match_all('/((?<!\$)\$\{?([@_a-zA-z0-9.\/\-]+)\}?)/', $str, $match)) {
+ return array_slice($match, 1);
+ }
+ return array(array(),array());
+}
+
+//
+// php: expression parser
+//
+
+class PHPTAL_ES_PHP_Parser
+{
+ var $_exp;
+ var $_gen;
+ var $_str;
+ var $_aliases = array();
+ var $_code = "";
+ var $_last_was_array = false;
+
+ function PHPTAL_ES_PHP_Parser(&$expression, $str)
+ {
+ $this->_exp =& $expression;
+ $this->_gen =& $expression->_gen;
+ $this->_str = $str;
+ }
+
+ function evaluate()
+ {
+ $value = $this->_str;
+ $strings = array();
+
+ // extract strings and replace context calls
+ if (preg_match_all('/(\'.*?(?<!\\\)\')/sm', $value, $m)) {
+ list(,$m) = $m;
+ foreach ($m as $str) {
+ $s_rplc = PHPTAL_ES_path_in_string($str, "'");
+ $s_rplc = preg_replace('/^\' ?\'\. /', '', $s_rplc);
+ $s_rplc = preg_replace('/\.\'\'$/', '', $s_rplc);
+ $value = str_replace($str, '#_STRING_'. count($strings) . '_#', $value);
+ $strings[] = $s_rplc;
+ }
+ }
+
+ list($match, $replc) = _PHPTAL_context_accessed($value);
+ $contexts = array();
+ foreach ($match as $m) {
+ $i = count($contexts);
+ $contexts[] = $replc[$i];
+ $value = str_replace($m, '#_CONTEXT_'. $i . '_#', $value);
+ }
+
+ // replace or, and, lt, gt, etc...
+ $value = $this->_php_test_modifiers($value);
+ $value = $this->_php_vars($value);
+
+ // restore strings
+ $i = 0;
+ foreach ($strings as $str) {
+ $value = str_replace('#_STRING_'. $i . '_#', $str, $value);
+ $i++;
+ }
+
+ $i = 0;
+ foreach ($contexts as $c) {
+ $value = str_replace('#_CONTEXT_' . $i . '_#', PHPTAL_ES_path($this->_exp, $c), $value);
+ $i++;
+ }
+
+ // experimental, compile php: content
+
+ require_once "Types/Code.php";
+
+ $code = new Code();
+ $code->setCode($value .";");
+ $err = $code->compile();
+ if (PEAR::isError($err)) {
+ return $this->_raiseCompileError($value, $err);
+ }
+
+ return $value;
+ }
+
+ function _php_test_modifiers($exp)
+ {
+ $exp = preg_replace('/\bnot\b/i', ' !', $exp);
+ $exp = preg_replace('/\bne\b/i', ' != ', $exp);
+ $exp = preg_replace('/\band\b/i', ' && ', $exp);
+ $exp = preg_replace('/\bor\b/i', ' || ', $exp);
+ $exp = preg_replace('/\blt\b/i', ' < ', $exp);
+ $exp = preg_replace('/\bgt\b/i', ' > ', $exp);
+ $exp = preg_replace('/\bge\b/i', ' >= ', $exp);
+ $exp = preg_replace('/\ble\b/i', ' <= ', $exp);
+ $exp = preg_replace('/\beq\b/i', ' == ', $exp);
+ return $exp;
+ }
+
+ function _php_vars($arg)
+ {
+ $arg = preg_replace('/\s*\/\s*/',' / ', $arg);
+ $arg = preg_replace("/\s*\(\s*/","(", $arg );
+ $arg = preg_replace('/\s*\)\s*/',') ', $arg );
+ $arg = preg_replace('/\s*\[\s*/','[', $arg );
+ $arg = preg_replace('/\s*\]\s*/',']', $arg );
+ $arg = preg_replace('/\s*,\s*/',' , ', $arg );
+
+ $result = "";
+ $path = false;
+ $last_path = false;
+ $last_was_array = false;
+
+ $i = 0;
+ while ($i < strlen($arg)) {
+ $c = $arg[$i];
+ if (preg_match('/[a-z_]/i', $c)) {
+ $path .= $c;
+ } else if (preg_match('/[0-9]/', $c) && $path) {
+ $path .= $c;
+ } else if (preg_match('/[\/\.]/', $c) && $path) {
+ $last_path = $path;
+ $path .= $c;
+ } else if (preg_match('/[\/\.]/', $c) && $this->_last_was_array) {
+ $result .= '->';
+ } else if ($c == '(') {
+ if ($last_path) {
+ $result .= $this->pathRequest($last_path);
+ $result .= '->';
+ $path = substr($path, strlen($last_path) +1);
+ $last_path = false;
+ }
+ $result .= $path . '(';
+ $this->_last_was_array = false;
+ $path = false;
+ } else if ($c == '#') {
+ if ($path) {
+ $result .= $this->pathRequest($path);
+ $path = false;
+ $last_path = false;
+ }
+ $next = strpos($arg, '#', $i+1);
+ $result .= substr($arg, $i, $next - $i +1);
+ $i = $next;
+ } else if ($c == ':') {
+ if ($arg[$i+1] != ':') { // error
+ }
+ $i++;
+ $result .= $path;
+ $result .= '::';
+ $path = false;
+ $last_path = false;
+
+ } else if ($c == '@') {
+ // access to defined variable, append to result
+ // up to non word character
+ $i++; // skip @ character
+ do {
+ $c = $arg[$i];
+ $result .= $c;
+ $i++;
+ } while ($i < strlen($arg) && preg_match('/[a-z_0-9]/i', $arg[$i]));
+ $i--; // reset last character
+
+ } else {
+ if ($path) {
+ $result .= $this->pathRequest($path);
+ $path = false;
+ }
+ $result .= $c;
+ if ($c == ']') {
+ $last_path = false;
+ $path = false;
+ $this->_last_was_array = true;
+ } else {
+ $this->_last_was_array = false;
+ }
+ }
+ $i++;
+ }
+
+ // don't forget the last bit
+ if (isset($path) && $path) {
+ $result .= $this->pathRequest($path);
+ }
+
+ return $result;
+ }
+
+ function pathRequest($path)
+ {
+ global $_phptal_es_php_types;
+
+ if (!preg_match('/[a-z]/i', $path)){
+ return $path;
+ }
+ if ($this->_last_was_array) {
+ return str_replace('.', '->', $path);
+ }
+ if (in_array($path, $_phptal_es_php_types)) {
+ return $path;
+ }
+
+ $concatenate = false;
+ $path = str_replace('.','/', $path);
+ if (substr($path,-1) == '/') {
+ $path = substr($path, 0, -1);
+ $concatenate = true;
+ }
+
+ if (array_key_exists($path, $this->_aliases)) {
+ $res = $this->_aliases[$path];
+ } else {
+ $res = $this->_gen->newTemporaryVar();
+ $this->_aliases[$path] = $res;
+ $this->_gen->doContextGet($res, $path);
+ }
+
+ if ($concatenate) {
+ $res .= ' .';
+ }
+ return $res;
+ }
+
+ function _raiseCompileError($code, $err)
+ {
+ $str = sprintf("Expression 'php:' compilation error in %s:%d".endl,
+ $this->_exp->_tag->_parser->_file,
+ $this->_exp->_tag->line);
+ return PEAR::raiseError($str);
+ }
+}
+
+
+?>
--- /dev/null
+<?php
+
+define('PHPTAL_PRE_FILTER', 1);
+define('PHPTAL_POST_FILTER', 2);
+
+/**
+ * Interface for source filter.
+ *
+ * This interface may be used to implement input / output filters.
+ *
+ * If the template intermediate php code is up to date, pre filters won't be
+ * used on it.
+ *
+ * Output filters are only called on main template result.
+ *
+ *
+ * <?
+ * class MyFilter extends PHPTAL_Filter
+ * {
+ * function filter(&$tpl, $data, $mode)
+ * {
+ * // just to present $mode usage for input/output filters
+ * if ($mode == PHPTAL_POST_FILTER) {
+ * return PEAR::raiseError("MyFilter mustn't be used as a pre-filter');
+ * }
+ *
+ * // remove html comments from template source
+ * return preg_replace('/(<\!--.*?-->)/sm', '', $data);
+ * }
+ * }
+ *
+ * $tpl = PHPTAL('mytemplate.html');
+ * $tpl->addInputFilter( new MyFilter() );
+ * echo $tpl->execute();
+ *
+ * ?>
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class PHPTAL_Filter
+{
+ /**
+ * Filter some template source string.
+ *
+ * @param PHPTAL_Template $tpl
+ * The template which invoked this filter.
+ *
+ * @param string $data
+ * Data to filter.
+ *
+ * @param int $mode
+ * PHPTAL_PRE_FILTER | PHPTAL_POST_FILTER depending if this filter
+ * is registered as a pre-filter or as a post-filter.
+ *
+ */
+ function filter(&$tpl, $data, $mode)
+ {
+ return $data;
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+class PHPTAL_Generator
+{
+ var $_temp_id = 0;
+ var $_str_buffer = "";
+ var $_tab = "";
+ var $_tab_save = "";
+ var $_code = "";
+ var $_closed = false;
+ var $_fname;
+ var $_funcName;
+
+ var $_macros = array();
+ var $_stacks = array();
+ var $_current_macro = false;
+
+ var $_gettext_required = false;
+
+ var $_headers = false;
+
+ function PHPTAL_Generator($fname, $func_name)
+ {
+ $this->_fname = $fname;
+ $this->_funcName = $func_name;
+ $this->appendln('<?php');
+ $this->doComment(str_pad("", 78, "-"));
+ $this->doComment("This code was generate by PHPTAL based on the template source file :");
+ $this->doComment("$fname");
+ $this->doComment(str_pad("", 78, "-"));
+ $this->appendln();
+ $this->appendln('function ', $func_name, '(&$__tpl__)');
+ $this->appendln('{');
+ $this->tabInc();
+ $this->appendln('$__ctx__ =& $__tpl__->getContext();');
+ $this->appendln('$__out__ = new PHPTAL_OutputControl($__ctx__, $__tpl__->getEncoding());');
+ $this->appendln('$__ctx__->set("repeat", array());');
+ }
+
+ function setHeaders($h)
+ {
+ $this->_headers = $h;
+ if ($this->_headers) {
+ $this->doHeadersPrint();
+ }
+ }
+
+ function requireGettext()
+ {
+ $this->_gettext_required = true;
+ }
+
+ function getCode()
+ {
+ if (!$this->_closed) {
+ $this->_flushOutput();
+ $this->appendln('return $__out__->toString();');
+ $this->endBlock();
+
+ foreach ($this->_macros as $name=>$code) {
+ $this->_code .= $code;
+ }
+ if ($this->_gettext_required) {
+ $this->_code = preg_replace('/^<\?php/sm',
+ '<?php require_once "GetText.php";',
+ $this->_code,
+ 1);
+ }
+ $this->append('?>');
+ $this->_closed = true;
+ }
+ return $this->_code;
+ }
+
+ function execute($code)
+ {
+ $this->_flushOutput();
+ $this->appendln(trim($code), ";");
+ }
+
+ function doComment($str)
+ {
+ $this->_flushOutput();
+ $this->appendln('// ',$str);
+ }
+
+ function setSource($tagName, $line)
+ {
+ $this->doComment('TAG ' . $tagName . ' AT LINE ' . $line);
+ $this->appendln('$_src_tag = "'. $tagName. '"; ', '$_src_line = '. $line. ';');
+ }
+
+ // loop
+ function doDo()
+ {
+ $this->_flushOutput();
+ $this->appendln("do {");
+ $this->tabInc();
+ }
+
+ function doEndDoWhile($condition)
+ {
+ $this->tabDec();
+ $this->appendln("} while($condition);");
+ }
+
+ function doWhile($condition)
+ {
+ $this->_flushOutput();
+ $this->appendln("while ($condition) {");
+ $this->tabInc();
+ }
+
+ // conditionals
+ function doIf($condition)
+ {
+ $this->_flushOutput();
+ $this->appendln("if ($condition) {");
+ $this->tabInc();
+ }
+
+ function doElseIf($condition)
+ {
+ $this->_str_buffer = "";
+ $this->endBlock();
+ $this->appendln("else if ($condition) {");
+ $this->tabInc();
+ }
+
+ function doElse()
+ {
+ $this->endBlock();
+ $this->appendln("else {");
+ $this->tabInc();
+ }
+
+ //
+ function doMacroDeclare($name)
+ {
+ $this->_flushOutput();
+ $this->_stacks[] = $this->_code;
+ $this->_code = "";
+ $this->_current_macro = $name;
+ $this->_tab_save = $this->_tab;
+ $this->_tab = "";
+ $this->appendln('function ', $this->_funcName,'_',$name, '(&$__tpl__)');
+ $this->appendln('{');
+ $this->tabInc();
+ $this->appendln('$__ctx__ =& $__tpl__->getContext();');
+ $this->appendln('$__out__ = new PHPTAL_OutputControl($__ctx__, $__tpl__->getEncoding());');
+ if ($this->_headers) {
+ $this->doHeadersPrint();
+ }
+ }
+
+ function doHeadersPrint()
+ {
+ $this->_flushOutput();
+ $this->doIf('! $__tpl__->_headers');
+ $str = str_replace("'", "\\'", $this->_headers);
+ $str = "'" . $str . "'";
+ $str = PHPTAL_ES_path_in_string($str, "'");
+ $this->appendln('$__tpl__->_headers = ' . $str .';');
+ $this->endBlock();
+ }
+
+ function doMacroEnd()
+ {
+ $this->_flushOutput();
+ $this->appendln('return $__out__->toString();');
+ $this->endBlock();
+ $this->_macros[$this->_current_macro] = $this->_code;
+ $this->_code = array_pop($this->_stacks);
+ $this->_current_macro = false;
+ $this->_tab = $this->_tab_save;
+ }
+
+ function doAffectResult($dest, $code)
+ {
+ if ($dest[0] != '$') { $dest = '$'.$dest; }
+ // test &
+ $this->appendln("$dest = $code;");
+ }
+
+ //
+ function doPrintString()
+ {
+ $args = func_get_args();
+ $this->_str_buffer .= join("", $args);
+ // $this->appendln('$__out__->write(', $str, ');');
+ }
+
+ function doPrintVar($var, $structure=false)
+ {
+ $this->_flushOutput();
+ if ($var[0] != '$') {
+ $var = '$' . $var;
+ }
+ if ($structure) {
+ $this->appendln('$__out__->writeStructure(', $var, ');');
+ } else {
+ $this->appendln('$__out__->write(', $var, ');');
+ }
+ }
+
+ function doPrintRes($code, $structure=false)
+ {
+ $this->_flushOutput();
+ if ($structure) {
+ $this->appendln('$__out__->writeStructure(', $code, ');');
+ } else {
+ $this->appendln('$__out__->write(', $code, ');');
+ }
+ }
+
+ function doPrintContext($path, $structure=false)
+ {
+ $code = sprintf('$__ctx__->get(\'%s\')', $path);
+ $this->doPrintRes($code, $structure);
+ }
+
+ // output buffering control
+ function doOBStart()
+ {
+ $this->_flushOutput();
+ $this->appendln('$__out__->pushBuffer();');
+ }
+
+ function doOBEnd($dest)
+ {
+ $this->_flushOutput();
+ // test &
+ $this->appendln($dest, ' =& $__out__->popBuffer();');
+ }
+
+ function doOBClean()
+ {
+ $this->_flushOutput();
+ $this->appendln('$__out__->popBuffer();');
+ }
+
+ function doOBPrint()
+ {
+ $this->_flushOutput();
+ $this->appendln('$__out__->writeStructure($__out__->popBuffer());');
+ }
+
+ function doOBEndInContext($dest)
+ {
+ $this->doContextSet($dest, '$__out__->popBuffer()');
+ }
+
+ //
+ function doReference($dest, $source)
+ {
+ $this->_flushOutput();
+ if ($dest[0] != '$') { $dest = '$'.$dest; }
+ if ($source[0] != '$') { $source = '$'.$source; }
+ // test &
+ $this->appendln("$dest =& $source;");
+ }
+
+ function doUnset($var)
+ {
+ $this->appendln("unset($var);");
+ }
+
+ // create a new temporary variable (non context) and return its name
+ function newTemporaryVar()
+ {
+ return '$temp_' . $this->_temp_id++;
+ }
+
+ function releaseTemporaryVar($name)
+ {
+ $this->doUnset($name);
+ }
+
+ // context methods
+
+ function doContextSet($out, $code)
+ {
+ $this->_flushOutput();
+ if ($out[0] != '$')
+ { $out = '"'.$out.'"'; }
+ // test & (Ref)
+ $this->appendln("\$__ctx__->setRef($out, $code);");
+ }
+
+ function doContextGet($out, $path)
+ {
+ $this->_flushOutput();
+ if ($out[0] != '$')
+ { $out = '$' . $out; }
+ // test &
+ $this->appendln("$out =& \$__ctx__->get(\"$path\");");
+ }
+
+ function endBlock()
+ {
+ $this->tabDec();
+ $this->appendln('}');
+ }
+
+ function tabInc()
+ {
+ $this->_flushOutput();
+ $this->_tab .= " ";
+ }
+
+ function tabDec()
+ {
+ $this->_flushOutput();
+ $this->_tab = substr($this->_tab, 4);
+ }
+
+ function appendln()
+ {
+ $args = func_get_args();
+ $str = join("", $args);
+ $this->_code .= $this->_tab . $str . "\n";
+ }
+
+ function append()
+ {
+ $args = func_get_args();
+ $str = join("", $args);
+ $this->_code .= $this->_tab . $str;
+ }
+
+ function _flushOutput()
+ {
+ if ($this->_str_buffer == "") return;
+ $this->_str_buffer = str_replace("'", "\\'", $this->_str_buffer);
+ $this->_str_buffer = "'" . $this->_str_buffer . "'";
+ $this->_str_buffer = PHPTAL_ES_path_in_string($this->_str_buffer, "'");
+ $this->appendln('$__out__->writeStructure(', $this->_str_buffer, ');');
+ $this->_str_buffer = "";
+ }
+}
+
+?>
--- /dev/null
+<?php
+
+class PHPTAL_I18N
+{
+ function set($varName, $value)
+ {
+ return GetText::setVar($varName, $value);
+ }
+
+ function translate($key)
+ {
+ return GetText::gettext($key);
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+require_once "Types/OArray.php";
+require_once "Types/OHash.php";
+require_once "Types/Iterator.php";
+
+/**
+ * Template loop execution controler.
+ *
+ * This object is instantiated by the template on each loop.
+ *
+ * LoopControlers accept different types of loop target.
+ *
+ * - array
+ * - objects having an getNewIterator() method returning an Iterator object.
+ * - Iterator objects which produce isValid(), next(), key() and index()
+ * methods.
+ * please note that key() return index() for non associative data.
+ *
+ * Other types are rejected by the loop controler on runtime producing
+ * a TypeError exception.
+ *
+ * The loop controler install its iterator under the path "repeat/item"
+ * where "item" is the name of the output var (defined in the template).
+ *
+ * Thus, the template can access differents methods of the iterator:
+ * repeat/someitem/key
+ * repeat/someitem/index
+ * repeat/someitem/next
+ * repeat/someitem/last
+ *
+ * If not all of these methods are implemented in iterators, ask
+ * iterator maintainers.
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ *
+ */
+class PHPTAL_LoopControler
+{
+ var $_context;
+ var $_data;
+ var $_data_name;
+ var $_iterator;
+ var $_error;
+
+ /**
+ * Controler constructor.
+ *
+ * @param PHPTAL_Context $context
+ * The template context.
+ * @param string $data_name
+ * The item data name.
+ * @param mixed $data
+ * Loop resource.
+ */
+ function PHPTAL_LoopControler(&$context, $data_name, $data)
+ {
+ $this->_context =& $context;
+ $this->_data =& $data;
+ $this->_data_name = $data_name;
+
+ // ensure that data is not an error
+ if (PEAR::isError($data)) {
+ $this->_error =& $data;
+ return $data;
+ }
+
+ // accepted objects
+ //
+ // - iteratable implementing getNewIterator() method
+ // - iterator implementing next(), isValid() and index() methods
+ // - db_result produced by PEAR::DB package
+ //
+ if (is_object($data)) {
+ if (method_exists($data, "getNewIterator")) {
+
+ $this->_iterator =& $data->getNewIterator();
+
+ } elseif (is_a("iterator", $data)
+ || (method_exists($data, 'next')
+ && method_exists($data, 'isValid')
+ && method_exists($data, 'index'))) {
+
+ $this->_iterator =& $data;
+
+ } elseif (get_class($data) == 'db_result') {
+
+ $this->_iterator = new PHPTAL_DBResultIterator($data);
+
+ } else {
+
+ $err = new TypeError("PHPTAL loop controler received a non Iterable object ("
+ . get_class($data) . ")");
+ $this->_error =& $err;
+ return PEAR::raiseError($err);
+ }
+ } elseif (is_array($data)) {
+ //
+ // array are accepted thanks to OArrayIterator
+ //
+ reset($data);
+ if (count($data) > 0 && array_key_exists(0, $data)) {
+ // echo "OArray::iterator", RxObject::ClassName($data), endl;
+ $this->_data = new OArray($data);
+ $this->_iterator =& $this->_data->getNewIterator();
+ } else {
+ // echo "OHash::iterator", RxObject::ClassName($data), endl;
+ $this->_data = new OHash($data);
+ $this->_iterator =& $this->_data->getNewIterator();
+ }
+ } else {
+ $err = new TypeError("phptal loop controler received a non Iterable value ("
+ . gettype($data) . ")");
+ $this->_error =& $err;
+ return PEAR::raiseError($err);
+ }
+
+ //
+ // install loop in repeat context array
+ //
+ $repeat =& $this->_context->get("repeat");
+ if (array_key_exists($this->_data_name, $repeat)) {
+ unset($repeat[$this->_data_name]);
+ }
+ $repeat[$this->_data_name] =& $this;
+
+ // $this->_context->setRef($this->_data_name, $temp);
+ $temp =& $this->_iterator->value();
+ $this->_context->set($this->_data_name, $temp);
+ return $temp;
+ }
+
+ /**
+ * Return current item index.
+ *
+ * @return int
+ */
+ function index()
+ {
+ return $this->_iterator->index();
+ }
+
+ /**
+ * Return current item key or index for non associative iterators.
+ *
+ * @return mixed
+ */
+ function key()
+ {
+ if (method_exists($this->_iterator, "key")) {
+ return $this->_iterator->key();
+ } else {
+ return $this->_iterator->index();
+ }
+ }
+
+ /**
+ * Index is in range(0, length-1), the number in in range(1, length).
+ *
+ * @return int
+ */
+ function number()
+ {
+ return $this->index() + 1;
+ }
+
+ /**
+ * Return true if index is even.
+ *
+ * @return boolean
+ */
+ function even()
+ {
+ return !$this->odd();
+ }
+
+ /**
+ * Return true if index is odd.
+ *
+ * @return boolean
+ */
+ function odd()
+ {
+ return ($this->index() % 2);
+ }
+
+ /**
+ * Return true if at the begining of the loop.
+ *
+ * @return boolean
+ */
+ function start()
+ {
+ return ($this->index() == 0);
+ }
+
+ /**
+ * Return true if at the end of the loop (no more item).
+ *
+ * @return boolean
+ */
+ function end()
+ {
+ return ($this->length() == $this->number());
+ }
+
+ function isValid()
+ {
+ return $this->_iterator->isValid();
+ }
+
+ /**
+ * Return the length of the data (total number of iterations).
+ *
+ * @return int
+ */
+ function length()
+ {
+ return $this->_data->size();
+ }
+
+ /**
+ * Retrieve next iterator value.
+ *
+ * @return mixed
+ */
+ function &next()
+ {
+ $temp =& $this->_iterator->next();
+ if (!$this->_iterator->isValid()) {
+ return false;
+ }
+
+ // $this->_context->setRef($this->_data_name, $temp);
+ $this->_context->set($this->_data_name, $temp);
+ return $temp;
+ }
+};
+
+
+/**
+ * Iterator for DB_Result PEAR object.
+ *
+ * This class is an implementation of the Iterator Interface
+ * for DB_Result objects produced by the usage of PEAR::DB package.
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class PHPTAL_DBResultIterator extends Iterator
+{
+ var $_src;
+ var $_index = -1;
+ var $_end = false;
+ var $_value;
+
+ /**
+ * Iterator constructor.
+ *
+ * @param DB_Result $result
+ * The query result.
+ */
+ function PHPTAL_DBResultIterator(&$result)
+ {
+ $this->_src =& $result;
+ $this->reset();
+ }
+
+ function reset()
+ {
+ if ($this->size() == 0) {
+ $this->_end = true;
+ return;
+ }
+
+ $this->_index = 0;
+ $this->_end = false;
+ unset($this->_value);
+ $this->_value = $this->_src->fetchRow();
+ }
+
+ /**
+ * Return the number of rows in this result.
+ *
+ * @return int
+ */
+ function size()
+ {
+ if (!isset($this->_size)) {
+ $this->_size = $this->_src->numRows();
+ }
+ return $this->_size;
+ }
+
+ /**
+ * Returns true if end of iterator has not been reached yet.
+ */
+ function isValid()
+ {
+ return !$this->_end;
+ }
+
+ /**
+ * Return the next row in this result.
+ *
+ * This method calls fetchRow() on the DB_Result, the return type depends
+ * of the DB_result->fetchmod. Please specify it before executing the
+ * template.
+ *
+ * @return mixed
+ */
+ function &next()
+ {
+ if ($this->_end || ++ $this->_index >= $this->size()) {
+ $this->_end = true;
+ return false;
+ }
+
+ unset($this->_value);
+ $this->_value = $this->_src->fetchRow();
+ return $this->_value;
+ }
+
+ /**
+ * Return current row.
+ *
+ * @return mixed
+ */
+ function &value()
+ {
+ return $this->_value;
+ }
+
+ /**
+ * Return current row index in resultset.
+ *
+ * @return int
+ */
+ function index()
+ {
+ return $this->_index;
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+/*
+ * Macro invoker.
+ */
+class PHPTAL_Macro extends PHPTAL_Template
+{
+ var $_path = false;
+ var $_name;
+
+ /**
+ * Macro constructor.
+ *
+ * @param PHPTAL_Template $caller
+ * The macro caller may be a template or a macro.
+ * @param string $path
+ * The macro path/name
+ */
+ function PHPTAL_Macro(&$caller, $path)
+ {
+ $this->_name = $path;
+
+ // extract macro path part, if none found, we'll assume macro is
+ // in the caller template.
+
+ if (preg_match('/(.*?)\/([a-zA-Z0-9_]*?)$/', $this->_name, $match)) {
+ list(, $this->_path, $this->_name) = $match;
+ } else {
+ $this->_sourceFile = $caller->_sourceFile;
+ }
+
+ // call parent constructor
+ $this->PHPTAL_Template($this->_path,
+ $caller->_repository,
+ $caller->_cacheDir);
+
+ $this->setParent($caller);
+ $this->setEncoding($caller->getEncoding());
+ }
+
+ /**
+ * Execute macro with caller context.
+ *
+ * @return string
+ */
+ function execute()
+ {
+ if ($this->_path !== false) {
+ $err = $this->_prepare();
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+ }
+ return $this->_cacheManager->macro($this,
+ $this->_sourceFile,
+ $this->_name,
+ $this->_parent->getContext());
+ }
+
+ /**
+ * Really process macro parsing/invocation.
+ *
+ * @return string
+ */
+ function _process()
+ {
+ if ($this->_path !== false) {
+ $err = $this->_load();
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+ } else {
+ $this->_funcName = $this->_parent->_funcName;
+ }
+
+ $func = $this->_funcName . '_' . $this->_name;
+
+ if (!function_exists($func)) {
+ $err = "Unknown macro '$this->_name'";
+ return PEAR::raiseError($err);
+ }
+
+ return $func($this->_parent);
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+class PHPTAL_OutputControl
+{
+ var $_buffer = "";
+ var $_buffers = array();
+ var $_quoteStyle = ENT_COMPAT;
+ var $_encoding = 'UTF-8';
+ var $_context;
+
+ function PHPTAL_OutputControl(&$context, $encoding='UTF-8')
+ {
+ $this->_context = $context;
+ $this->_encoding = $encoding;
+ }
+
+ function pushBuffer()
+ {
+ $this->_buffers[] = $this->_buffer;
+ $this->_buffer = "";
+ }
+
+ function popBuffer()
+ {
+ $res = $this->_buffer;
+ $this->_buffer = array_pop($this->_buffers);
+ return $res;
+ }
+
+ function write(&$str)
+ {
+ if (is_object($str)) {
+ if (PEAR::isError($str)) {
+ $str = "[". get_class($str) . ": " . $str->message . "]";
+ // $this->_context->cleanError();
+ } else {
+ $str = Types::toString($str);
+ }
+ }
+
+ if (defined("PHPTAL_DIRECT_OUTPUT") && count($this->_buffers) == 0) {
+ // echo htmlentities($str);
+ // support for cyrillic strings thanks to Igor E. Poteryaev
+ echo htmlentities($str, $this->_quoteStyle, $this->_encoding);
+ } else {
+ // $this->_buffer .= htmlentities($str);
+ // support for cyrillic strings thanks to Igor E. Poteryaev
+ $this->_buffer .= htmlentities($str, $this->_quoteStyle, $this->_encoding);
+ }
+ }
+
+ function writeStructure($str)
+ {
+ if (is_object($str)) {
+ if (PEAR::isError($str)) {
+ $str = "[" . $str->message . "]";
+ // $this->_context->cleanError();
+ } else {
+ $str = Types::toString($str);
+ }
+ }
+
+ if (defined("PHPTAL_DIRECT_OUTPUT") && count($this->_buffers) == 0) {
+ echo $str;
+ } else {
+ $this->_buffer .= $str;
+ }
+ }
+
+ function &toString()
+ {
+ return $this->_buffer;
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+$__d = dirname(__FILE__);
+require_once _phptal_os_path_join($__d, 'XML_Parser.php');
+require_once _phptal_os_path_join($__d, 'Attribute.php');
+require_once _phptal_os_path_join($__d, 'Expression.php');
+require_once _phptal_os_path_join($__d, 'ExpressionFunctions.php');
+require_once _phptal_os_path_join($__d, 'Generator.php');
+require_once _phptal_os_path_join($__d, 'Tag.php');
+
+$_t = _phptal_os_path_join($__d, 'Attribute', 'TAL');
+require_once _phptal_os_path_join($_t, 'Attributes.php');
+require_once _phptal_os_path_join($_t, 'Comment.php');
+require_once _phptal_os_path_join($_t, 'Content.php');
+require_once _phptal_os_path_join($_t, 'Condition.php');
+require_once _phptal_os_path_join($_t, 'Define.php');
+require_once _phptal_os_path_join($_t, 'Omit_tag.php');
+require_once _phptal_os_path_join($_t, 'Replace.php');
+require_once _phptal_os_path_join($_t, 'On_error.php');
+require_once _phptal_os_path_join($_t, 'Repeat.php');
+
+$_t = _phptal_os_path_join($__d, 'Attribute', 'PHPTAL');
+require_once _phptal_os_path_join($_t, 'Include.php');
+require_once _phptal_os_path_join($_t, 'Src_include.php');
+
+$_t = _phptal_os_path_join($__d, 'Attribute', 'METAL');
+require_once _phptal_os_path_join($_t, 'Define_macro.php');
+require_once _phptal_os_path_join($_t, 'Use_macro.php');
+require_once _phptal_os_path_join($_t, 'Define_slot.php');
+require_once _phptal_os_path_join($_t, 'Fill_slot.php');
+
+$_t = _phptal_os_path_join($__d, 'Attribute', 'I18N');
+require_once _phptal_os_path_join($_t, 'Translate.php');
+require_once _phptal_os_path_join($_t, 'Name.php');
+require_once _phptal_os_path_join($_t, 'Attributes.php');
+
+
+/**
+ * PHPTAL template parser.
+ *
+ * This object implements PHPTAL_XML_Parser interface and will accept only
+ * well formed xml templates.
+ *
+ *
+ * Parser object has two aims :
+ *
+ * - generate the template structure tree
+ * - generate the php source code this structure represents
+ *
+ * Once this job is accomplished, the parser object should be destroyed and
+ * MUST NOT be used to parse another template. It's a one time and drop
+ * object.
+ *
+ *
+ * Note about code generation :
+ *
+ * The final source code is ready to write into a php file.
+ *
+ * The code generation process requires a function name which should represent
+ * the template unique id (Template class makes an md5 over the source file
+ * path to create this id).
+ *
+ * @version 0.1
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class PHPTAL_Parser extends PHPTAL_XML_Parser
+{
+ // root gat
+ var $_root;
+ // document headers (string)
+ var $_headers = false;
+ var $_started = false;
+
+ var $_docType;
+ var $_inDocType;
+
+ // current tag
+ var $_current;
+
+ // source file name
+ var $_file;
+
+ // activate xhtml empty tags lookup
+ var $_outputFormat = PHPTAL_XHTML;
+
+ // keep xmlns:* attributes ?
+ var $_keepXMLNS = false;
+
+ /**
+ * Parse a template string.
+ *
+ * @param string $file
+ * The template source file.
+ * @param string $src
+ * The template source.
+ *
+ * @throws PHPTAL_ParseError
+ */
+ function parse($file, $src)
+ {
+ $this->_file = $file;
+ $this->_initRoot();
+ return $this->parseString($src, true);
+ }
+
+ /**
+ * Generate php code.
+ *
+ * After template parsing, this method must be called to generate php code
+ * from the template tree.
+ *
+ * @param string func_name
+ * The template function name
+ *
+ * @access private
+ * @return string
+ */
+ function generateCode($func_name)
+ {
+ $gen = new PHPTAL_Generator($this->_file, $func_name);
+ $gen->setHeaders($this->_headers);
+ $err = $this->_root->generateCode($gen);
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+ return $gen->getCode();
+ }
+
+ /**
+ * Return a string representation of the underlying xhtml tree.
+ *
+ * This method is for debug purpose.
+ *
+ * @access private
+ * @return string
+ */
+ function toString()
+ {
+ $buf = new StringBuffer();
+ $buf->appendln('Template tree [\'', $this->_file, '\']');
+ $buf->append($this->_root->toString());
+ return $buf->toString();
+ }
+
+
+ // ----------------------------------------------------------------------
+ // Private methods
+ // ----------------------------------------------------------------------
+
+ /**
+ * Initialize root node.
+ *
+ * @access private
+ */
+ function _initRoot()
+ {
+ $this->_headers = "";
+ $this->_started = false;
+ $this->_root = new PHPTAL_Tag($this, "#root", array());
+ $this->_current =& $this->_root;
+ }
+
+ /**
+ * Push a node as the current one.
+ *
+ * @param PHPTAL_Node tag
+ * @access private
+ */
+ function _push(&$tag)
+ {
+ $this->_current->addChild($tag);
+ unset($this->_current);
+ $this->_current =& $tag;
+ }
+
+ /**
+ * Push a node into the current one.
+ *
+ * @param PHPTAL_Node tag
+ * @access private
+ */
+ function _pushChild(&$tag)
+ {
+ $this->_current->addChild($tag);
+ }
+
+ /**
+ * Pop the last node (go up a level in tree).
+ *
+ * @access private
+ */
+ function _pop()
+ {
+ $temp =& $this->_current;
+ unset($this->_current);
+ if ($temp != $this->_root) {
+ $this->_current =& $temp->getParent();
+ } else {
+ $this->_current =& $this->_root;
+ }
+ }
+
+ /*
+ * getter/setter for the output mode.
+ *
+ * @param int $mode optional
+ * PHPTAL_XML or PHPTAL_XHTML
+ */
+ function _outputMode($mode=false)
+ {
+ if ($mode !== false) {
+ $this->_outputFormat = $mode;
+ }
+ return $this->_outputFormat;
+ }
+
+ // ----------------------------------------------------------------------
+ // XML callbacks methods
+ // ----------------------------------------------------------------------
+
+ /**
+ * xml callback
+ *
+ * @access private
+ */
+ function onElementStart($name, $attributes)
+ {
+ global $_phptal_namespaces;
+
+ $this->_started = true;
+
+ if (strpos($name, ':') !== false) {
+ list($domain, $extend) = split(':', strtoupper($name));
+ if (($extend == 'BLOCK') && (in_array($domain, $_phptal_namespaces))) {
+ $attributes = PHPTAL_Parser::extendZptBlockAttributes($domain, $attributes);
+ $attributes['tal:omit-tag'] = '';
+ }
+ }
+
+ // separate phptal attributes from xhtml ones
+ // if an attribute is not found, an error is raised.
+ $split = PHPTAL_Parser::TemplateAttributes($attributes);
+ if (PEAR::isError($split)) {
+ return $split;
+ }
+
+ // no error, the returned value is a tuple
+ list($phptal, $attributes) = $split;
+
+ // sort phptal attributes
+ $phptal = PHPTAL_Parser::OrderTemplateAttributes($phptal);
+ if (PEAR::isError($phptal)) {
+ return $phptal;
+ }
+
+ // create the tag and add its template attributes
+ $tag = new PHPTAL_Tag($this, $name, $attributes);
+ foreach ($phptal as $t) {
+ $tag->appendTemplateAttribute($t);
+ unset($t); // $t is appended by reference
+ }
+
+ $tag->line = $this->getLineNumber();
+ $this->_push($tag);
+ }
+
+ /**
+ * Extends ZPT attributes withing a *:block element so these attributes can
+ * be used by phptal parser.
+ *
+ * @access private
+ */
+ function extendZptBlockAttributes($domain, $attributes)
+ {
+ global $_phptal_dictionary;
+ $result = array();
+ foreach ($attributes as $key => $value) {
+ $expected = strtoupper("$domain:$key");
+ if (array_key_exists($expected, $_phptal_dictionary)) {
+ $result[$expected] = $value;
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * xml callback
+ *
+ * @access private
+ */
+ function onElementData($data)
+ {
+ // ${xxxx} variables are evaluated during code
+ // generation whithin the CodeGenerator under the
+ // printString() method.
+ $tag = new PHPTAL_Tag($this, "#cdata", array());
+ $tag->setContent($data);
+ $this->_pushChild($tag);
+ }
+
+ /**
+ * xml callback
+ *
+ * @access private
+ */
+ function onSpecific($data)
+ {
+ // fix xml parser '&' => '&' automatic conversion
+ $data = str_replace('&', '&', $data);
+
+ if ($this->_current->name() == "#root" && !$this->_started) {
+ $this->_headers .= $data;
+ return;
+ }
+ $tag = new PHPTAL_Tag($this, "#cdata", array());
+ $tag->setContent($data);
+ $this->_pushChild($tag);
+ }
+
+ /**
+ * xml callback
+ *
+ * @access private
+ */
+ function onElementClose($name)
+ {
+ if ($this->_current == null) {
+ return $this->_raiseNoTagExpected($name);
+ }
+ if ($this->_current->name() != $name) {
+ return $this->_raiseUnexpectedTagClosure($name);
+ }
+ $this->_pop();
+ }
+
+ // ----------------------------------------------------------------------
+ // Static methods
+ // ----------------------------------------------------------------------
+
+ /**
+ * Lookup template attributes in given hashtable.
+ *
+ * This method separate xml attributes from template attributes
+ * and return an array composed of the array of formers and the array of
+ * laters.
+ *
+ * @access private
+ * @static 1
+ *
+ * @param hashtable attrs
+ * Attributes hash
+ *
+ * @return array
+ */
+ function TemplateAttributes($attrs)
+ {
+ global $_phptal_dictionary, $_phptal_aliases, $_phptal_namespaces;
+ $phptal = array();
+ $att = array();
+
+ foreach ($attrs as $key=>$exp) {
+
+ $test_key = strtoupper($key);
+ $ns = preg_replace('/(:.*?)$/', '', $test_key);
+ $sns = preg_replace('/^(.*?:)/', '', $test_key);
+ // dictionary lookup
+ if (array_key_exists($test_key, $_phptal_dictionary)) {
+ $phptal[$test_key] = $exp;
+ }
+ // alias lookup
+ elseif (array_key_exists($test_key, $_phptal_aliases)) {
+ $phptal[ $_phptal_aliases[$test_key] ] = $exp;
+ }
+ // the namespace is known but the the attribute is not
+ elseif (in_array($ns, $_phptal_namespaces)) {
+ return $this->_raiseUnknownAttribute($test_key);
+ }
+ // regular xml/xhtml attribute (skip namespaces declaration)
+ elseif ($ns !== 'XMLNS' || $this->_keepXMLNS
+ || !in_array($sns, $_phptal_namespaces)) {
+ $att[$key] = $exp;
+ }
+ }
+ return array($phptal, $att);
+ }
+
+ /**
+ * Order phptal attributes array using $_phptal_rules_order array.
+ *
+ * @static 1
+ * @access private
+ *
+ * @param array phptal
+ * Array of phptal attributes (will be modified)
+ */
+ function OrderTemplateAttributes(&$phptal)
+ {
+ global $_phptal_rules_order, $_phptal_dictionary;
+
+ // order elements by their name using the rule table
+ $result = array();
+ foreach ($phptal as $akey=>$exp) {
+
+ // retrieve attribute handler class
+ $class = "PHPTAL_ATTRIBUTE_" . str_replace(":", "_", $akey);
+ $class = str_replace("-", "_", $class);
+ if (!class_exists($class)) {
+ return $this->_raiseAttributeNotFound($akey, $class);
+ }
+
+ $hdl = new $class($exp);
+ $hdl->name = $akey;
+ $hdl->_phptal_type = $_phptal_dictionary[$akey];
+
+ // resolve attributes conflict
+ $pos = $_phptal_rules_order[$akey];
+ if (array_key_exists($pos, $result)) {
+ return $this->_raiseAttConflict($akey, $result[$pos]->name);
+ }
+
+ // order elements by their order rule
+ $result[$_phptal_rules_order[$akey]] = $hdl;
+ unset($hdl);
+ }
+ return $result;
+ }
+
+ // ----------------------------------------------------------------------
+ // Errors raising methods
+ // ----------------------------------------------------------------------
+
+ function _raiseAttributeNotFound($att, $class)
+ {
+ $str = sprintf("Attribute '%s' exists in dictionary but class '%s' ".
+ "was not found",
+ $att, $class);
+ $err = new PHPTAL_ParseError($str);
+ return PEAR::raiseError($err);
+ }
+
+ function _raiseUnknownAttribute($att)
+ {
+ $str = sprintf("Unknown PHPTAL attribute '%s' in %s at line %d",
+ $att, $this->_file, $this->getLineNumber());
+ $err = new PHPTAL_UnknownAttribute($str);
+ return PEAR::raiseError($err);
+ }
+
+ function _raiseUnexpectedTagClosure($name)
+ {
+ $str = sprintf("Non matching tag '%s' error in xml file '%s' at line %d"
+ . endl . "waiting for end of tag '%s' declared at line %d.",
+ $name, $this->_file, $this->getLineNumber(),
+ $this->_current->name(), $this->_current->line);
+ $err = new PHPTAL_ParseError($str);
+ return PEAR::raiseError($err);
+ }
+
+ function _raiseNoTagExpected($name)
+ {
+ $str = sprintf("Bad xml error in xml file '%s' at line %d". endl
+ ."Found closing tag '%s' while no current tag is waited.",
+ $this->_file, $this->getLineNumber(), $name);
+ $err = new PHPTAL_ParseError($str);
+ return PEAR::raiseError($err);
+ }
+
+ function _raiseAttConflict($a2, $a1)
+ {
+ $str = sprintf('Template Attribute conflict in \'%s\' at line %d'. endl
+ . ' %s must not be used in the same tag as %s',
+ $this->_file, $this->getLineNumber(), $a1, $a2);
+ $err = new PHPTAL_AttributeConflict($str);
+ return PEAR::raiseError($err);
+ }
+};
+
+/**
+ * Error raised at parse time when some bad xml is found or when some tag
+ * is unknown or is badly used.
+ */
+class PHPTAL_ParseError extends PEAR_Error {}
+
+/**
+ * Error raised when an unknown PHPTAL tag is requested by the template.
+ */
+class PHPTAL_UnknownAttribute extends PHPTAL_ParseError {}
+
+/**
+ * Error raised when a template attribute conflicts with another one.
+ */
+class PHPTAL_AttributeConflict extends PHPTAL_ParseError {}
+
+
+?>
--- /dev/null
+<?php
+
+/**
+ * Interface for template source locators.
+ *
+ * A locator is a way to retrieve a PHPTAL template source otherwhere than in
+ * the file system.
+ *
+ * The default behavour of this class is to act like a file system locator.
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class PHPTAL_SourceLocator
+{
+ var $path;
+
+ function PHPTAL_SourceLocator($path)
+ {
+ $this->path = $path;
+ }
+
+ /**
+ * Returns an absolute path to this resource.
+ *
+ * The result of this method is used to generate some unique
+ * md5 value which represent the template php function name
+ * and the intermediate php file name.
+ *
+ * @return string
+ */
+ function realPath()
+ {
+ return $this->path;
+ }
+
+ /**
+ * Return source last modified date in a filemtime format.
+ *
+ * The result is compared to php intermediate mtime to decide
+ * weither or not to re-parse the template source.
+ *
+ * @return int
+ */
+ function lastModified()
+ {
+ return filemtime($this->path);
+ }
+
+ /**
+ * Return the template source.
+ *
+ * This method is invoqued if the template has to be parsed.
+ *
+ * @return string
+ */
+ function data()
+ {
+ return join('', file($this->path));
+ }
+}
+
+?>
--- /dev/null
+<?php
+
+/**
+ * This class is used to resolve template source path outside the file system.
+ *
+ * Given a path, a template repository and a template caller path, one resolver
+ * must decide whether or not it can retrieve the template source.
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class PHPTAL_SourceResolver
+{
+ /**
+ * Resolve a template source path.
+ *
+ * This method is invoked each time a template source has to be
+ * located.
+ *
+ * This method must returns a PHPTAL_SourceLocator object which
+ * 'point' to the template source and is able to retrieve it.
+ *
+ * If the resolver does not handle this kind of path, it must return
+ * 'false' so PHPTAL will ask other resolvers.
+ *
+ * @param string $path -- path to resolve
+ *
+ * @param string $repository -- templates repository if specified on
+ * on template creation.
+ *
+ * @param string $callerPath -- caller realpath when a template look
+ * for an external template or macro,
+ * this should be usefull for relative urls
+ *
+ * @return PHPTAL_SourceLocator | false
+ */
+ function resolve($path, $repository=false, $callerPath=false)
+ {
+ // first look at an absolute path
+ $pathes = array($path);
+ // search in the caller directory
+ if ($callerPath) {
+ $pathes[] = dirname($callerPath) . PHPTAL_PATH_SEP . $path;
+ }
+ // search in the template repository
+ if ($repository) {
+ $pathes[] = $repository . PHPTAL_PATH_SEP . $path;
+ }
+ // search in the defined repository
+ if (defined('PHPTAL_REPOSITORY')) {
+ $pathes[] = PHPTAL_REPOSITORY . PHPTAL_PATH_SEP . $path;
+ }
+ foreach ($pathes as $ftest) {
+ if (file_exists($ftest)) {
+ $realpath = realpath($ftest);
+ $locator = new PHPTAL_SourceLocator($realpath);
+ return $locator;
+ }
+ }
+ return false;
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+/**
+ * Represents an xhtml entity.
+ *
+ * To obtain a fine granularity of code generation the Tag code generation
+ * process is divided into logical sub sequences.
+ *
+ * surround head // call surround attributes
+ * replace // call replace and skip head -> foo sequence
+ * || head // echo tag start (see disableHeadFoot())
+ * content // call content replace and skip cdata seq
+ * || cdata && child // echo tag content and generate children code
+ * foot // echo tag end (see disableHeadFoot())
+ * surround foot // call surround attributes
+ *
+ *
+ * Some attributes like 'omit' requires to disable the head / foot sequences
+ * whithout touching content sequences, the disableHeadFoot() method must be
+ * used in that aim.
+ *
+ * @version 0.1
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class PHPTAL_Tag
+{
+ /**
+ * @var int $line The template line which produced this tag.
+ */
+ var $line = 0;
+
+ // tree properties
+
+ var $_name = "#cdata";
+ var $_attrs = array();
+ var $_children = array();
+ var $_content = "";
+ var $_parent = null;
+
+ // template system properties
+
+ var $_parser;
+ var $_replace_attributes = array();
+ var $_content_attributes = array();
+ var $_surround_attributes = array();
+ var $_head_foot_disabled = false;
+
+
+ /**
+ * Tag constructor.
+ *
+ * @param string $name
+ * The tag name.
+ *
+ * @param hashtable $attrs
+ * Tag xhtml attributes.
+ */
+ function PHPTAL_Tag(&$parser, $name, $attrs)
+ {
+ $this->_parser =& $parser;
+ $this->_name = $name;
+ $this->_attrs = $attrs;
+ }
+
+ function appendTemplateAttribute(&$hdlr)
+ {
+ switch ($hdlr->_phptal_type)
+ {
+ case _PHPTAL_REPLACE:
+ $this->_replace_attributes[] =& $hdlr;
+ break;
+ case _PHPTAL_CONTENT:
+ $this->_content_attributes[] =& $hdlr;
+ break;
+ case _PHPTAL_SURROUND:
+ $this->_surround_attributes[] =& $hdlr;
+ break;
+ default:
+ return PEAR::raiseError(
+ "PHPTAL internal error : bad attribute type : "
+ . get_class($hdlr));
+ }
+ }
+
+ function name()
+ {
+ return $this->_name;
+ }
+
+ function isData()
+ {
+ return $this->_name == "#cdata";
+ }
+
+ function attributes()
+ {
+ return $this->_attrs;
+ }
+
+ function setParent(&$node)
+ {
+ $this->_parent =& $node;
+ }
+
+ function &getParent()
+ {
+ return $this->_parent;
+ }
+
+ function addChild(&$node)
+ {
+ $node->setParent($this);
+ $this->_children[] = &$node;
+ }
+
+ function setContent($str)
+ {
+ $this->_content = $str;
+ }
+
+ function hasContent()
+ {
+ return (count($this->_content_attributes) == 0
+ || count($this->_children) == 0
+ || strlen($this->_content) == 0);
+ }
+
+ function toString($tab="")
+ {
+ $buf = new StringBuffer();
+ $buf->appendln($tab, '+ node ', $this->name());
+ for ($i=0; $i < count($this->_children); $i++) {
+ $child =& $this->_children[$i];
+ $buf->append($child->toString($tab . " "));
+ unset($child);
+ }
+ return $buf->toString();
+ }
+
+ function generateCode(&$g)
+ {
+ // if text node, just print the content and return
+ if ($this->_name == "#cdata") {
+ $g->doPrintString($this->_content);
+ return;
+ }
+
+ if ($this->_name == "#root") {
+ return $this->generateContent($g);
+ }
+
+ // if replace attributes exists, they will handle
+ // this tag rendering
+ if (count($this->_replace_attributes) > 0) {
+ $err = $this->surroundHead($g);
+ if (PEAR::isError($err)) { return $err; }
+
+ for ($i=0; $i<count($this->_replace_attributes); $i++) {
+ $h =& $this->_replace_attributes[$i];
+ $err = $h->activate($g, $this);
+ if (PEAR::isError($err)) { return $err; }
+ unset($h);
+ }
+ return $this->surroundFoot($g);
+ }
+
+ $err = $this->surroundHead($g);
+ if (PEAR::isError($err)) { return $err; }
+
+ $this->printHead($g);
+
+ $err = $this->generateContent($g);
+ if (PEAR::isError($err)) { return $err; }
+
+ $this->printFoot($g);
+
+ $err = $this->surroundFoot($g);
+ if (PEAR::isError($err)) { return $err; }
+ }
+
+ function printHead(&$g)
+ {
+ if ($this->headFootDisabled()) return;
+
+ $g->doPrintString('<', $this->_name);
+
+ $this->printAttributes($g);
+
+ if ($this->hasContent() && !$this->_isXHTMLEmptyElement()) {
+ $g->doPrintString('>');
+ } else {
+ $g->doPrintString('/>');
+ }
+ }
+
+ function printFoot(&$g)
+ {
+ if ($this->headFootDisabled()) return;
+
+ if ($this->hasContent() && !$this->_isXHTMLEmptyElement()) {
+ $g->doPrintString('</', $this->_name, '>');
+ }
+ }
+
+ function printAttributes(&$g)
+ {
+ global $_phptal_xhtml_boolean_attributes;
+ foreach ($this->_attrs as $key=>$value) {
+ if ($this->_parser->_outputMode() == PHPTAL_XHTML
+ && in_array(strtolower($key), $_phptal_xhtml_boolean_attributes)) {
+ $g->doPrintString(" $key");
+ } else {
+ $g->doPrintString(" $key=\"$value\"");
+ }
+ }
+ }
+
+ function surroundHead(&$g)
+ {
+ // if some surround attributes, we activate
+ // their header method
+ for ($i=0; $i<count($this->_surround_attributes); $i++) {
+ $h =& $this->_surround_attributes[$i];
+ $err = $h->start($g, $this);
+ if (PEAR::isError($err)) { return $err; }
+ unset($h);
+ }
+ }
+
+ function surroundFoot(&$g)
+ {
+ // close surrounders in reverse order of course
+ for ($i = (count($this->_surround_attributes)-1); $i >= 0; $i--) {
+ $err = $this->_surround_attributes[$i]->end($g, $this);
+ if (PEAR::isError($err)) { return $err; }
+ }
+ }
+
+ function generateContent(&$g)
+ {
+ if (count($this->_content_attributes) > 0) {
+ // time for content attributes,
+ foreach ($this->_content_attributes as $h) {
+ $err = $h->activate($g, $this);
+ if (PEAR::isError($err)) { return $err; }
+ }
+ } else {
+ // if none, we just ask children to generate their code
+ foreach ($this->_children as $child) {
+ $err = $child->generateCode($g);
+ if (PEAR::isError($err)) { return $err; }
+ }
+ }
+ }
+
+ function disableHeadFoot()
+ {
+ $this->_head_foot_disabled = true;
+ }
+
+ function headFootDisabled()
+ {
+ return $this->_head_foot_disabled;print_r($this->_root);
+ }
+
+ function _isXHTMLEmptyElement()
+ {
+ global $_phptal_xhtml_empty_tags;
+ if ($this->_parser->_outputMode() != PHPTAL_XHTML) {
+ return false;
+ }
+ return in_array(strtoupper($this->name()), $_phptal_xhtml_empty_tags);
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+
+class PHPTAL_Template
+{
+ var $_ctx;
+ var $_code;
+ var $_codeFile;
+ var $_funcName;
+ var $_sourceFile;
+ var $_error = false;
+ var $_repository = false;
+ var $_cacheDir = false;
+ var $_parent = false;
+ var $_parentPath = false;
+ var $_prepared = false;
+ var $_cacheManager;
+
+ var $_outputMode = PHPTAL_XHTML;
+
+ var $_inputFilters;
+ var $_outputFilters;
+ var $_resolvers;
+ var $_locator;
+
+ var $_headers = false;
+
+ var $_translator;
+
+ var $_encoding = 'UTF-8';
+
+ /**
+ * Template object constructor.
+ *
+ * @param string $file -- The source file name
+ * @param string $repository optional -- Your templates root.
+ * @param string $cache_dir optional -- Intermediate php code repository.
+ */
+ function PHPTAL_Template($file, $repository=false, $cache_dir=false)
+ {
+ $this->_sourceFile = $file;
+ $this->_repository = $repository;
+
+ // deduce intermediate php code cache directory
+ if (!$cache_dir) {
+ if (defined('PHPTAL_CACHE_DIR')) {
+ $cache_dir = PHPTAL_CACHE_DIR;
+ } else {
+ $cache_dir = PHPTAL_DEFAULT_CACHE_DIR;
+ }
+ }
+ $this->_cacheDir = $cache_dir;
+
+ // instantiate a new context for this template
+ // !!! this context may be overwritten by a parent context
+ $this->_ctx = new PHPTAL_Context();
+
+ // create resolver vector and the default filesystem resolver
+ $this->_resolvers = new OArray();
+ $this->_resolvers->push(new PHPTAL_SourceResolver());
+
+ // vector for source filters
+ $this->_inputFilters = new OArray();
+ $this->_outputFilters = new OArray();
+
+ // if no cache manager set, we instantiate default dummy one
+ if (!isset($this->_cacheManager)) {
+ $this->_cacheManager = new PHPTAL_Cache();
+ }
+ }
+
+ /**
+ * Set template ouput type.
+ *
+ * Default output is XHTML, so you'll have to call this method only for
+ * specific xml documents with PHPTAL_XML parameter.
+ *
+ * @param int $mode -- output mode (PHPTAL_XML) as default system use XHTML
+ */
+ function setOutputMode($mode)
+ {
+ $this->_outputMode = $mode;
+ }
+
+ /**
+ * Replace template context with specified hashtable.
+ *
+ * @param hash hashtable -- Associative array.
+ */
+ function setAll($hash)
+ {
+ $this->_ctx = new PHPTAL_Context($hash);
+ }
+
+ /**
+ * Set a template context value.
+ *
+ * @param string $key -- The context key
+ * @param mixed $value -- The context value
+ */
+ function set($name, $value)
+ {
+ $this->_ctx->set($name, $value);
+ }
+
+ /**
+ * Set a template context value by reference.
+ *
+ * @param string $name -- The template context key
+ * @param mixed $value -- The template context value
+ */
+ function setRef($name, &$value)
+ {
+ $this->_ctx->setRef($name, $value);
+ }
+
+ /**
+ * Retrieve template context object.
+ *
+ * @return PHPTAL_Context
+ */
+ function &getContext()
+ {
+ return $this->_ctx;
+ }
+
+ /**
+ * Set the template context object.
+ *
+ * @param PHPTAL_Context $ctx -- The context object
+ */
+ function setContext(&$ctx)
+ {
+ $this->_ctx =& $ctx;
+ }
+
+ /**
+ * Set the cache manager to use for Template an Macro calls.
+ *
+ * @param PHPTAL_Cache $mngr -- Cache object that will be used to cache
+ * template and macros results.
+ */
+ function setCacheManager(&$mngr)
+ {
+ $this->_cacheManager =& $mngr;
+ }
+
+ /**
+ * Retrieve the cache manager used in this template.
+ *
+ * @return PHPTAL_Cache
+ */
+ function &getCacheManager()
+ {
+ return $this->_cacheManager;
+ }
+
+ /**
+ * Set the I18N implementation to use in this template.
+ *
+ * @param PHPTAL_I18N $tr -- I18N implementation
+ */
+ function setTranslator(&$tr)
+ {
+ $this->_translator =& $tr;
+ }
+
+ /**
+ * The translator used by this template.
+ *
+ * @return PHPTAL_I18N
+ */
+ function &getTranslator()
+ {
+ return $this->_translator;
+ }
+
+ /**
+ * Test if the template file exists.
+ * @deprecated use isValid() instead
+ * @return boolean
+ */
+ function fileExists()
+ {
+ return $this->isValid();
+ }
+
+ /**
+ * Test if the template resource exists.
+ *
+ * @return boolean
+ */
+ function isValid()
+ {
+ if (isset($this->_locator)) {
+ return true;
+ }
+
+ // use template resolvers to locate template source data
+ // in most cases, there will be only one resolver in the
+ // resolvers list (the default one) which look on the file
+ // system.
+
+ $i = $this->_resolvers->getNewIterator();
+ while ($i->isValid()) {
+ $resolver =& $i->value();
+ $locator =& $resolver->resolve($this->_sourceFile,
+ $this->_repository,
+ $this->_parentPath);
+ if ($locator && !PEAR::isError($locator)) {
+ $this->_locator =& $locator;
+ $this->_real_path = $this->_locator->realPath();
+ return true;
+ }
+ $i->next();
+ }
+ return false;
+ }
+
+ /**
+ * Add a source resolver to the template.
+ *
+ * @param PHPTAL_SourceResolver $resolver
+ * The source resolver.
+ */
+ function addSourceResolver(&$resolver)
+ {
+ $this->_resolvers->pushRef($resolver);
+ }
+
+ /**
+ * Add filter to this template input filters list.
+ *
+ * @param PHPTAL_Filter $filter
+ * A filter which will be invoked on template source.
+ */
+ function addInputFilter(&$filter)
+ {
+ $this->_inputFilters->pushRef($filter);
+ }
+
+ /**
+ * Add an output filter to this template output filters list.
+ *
+ * @param PHPTAL_Filter $filter
+ * A filter which will be invoked on template output.
+ */
+ function addOutputFilter(&$filter)
+ {
+ $this->_outputFilters->pushRef($filter);
+ }
+
+ /**
+ * Retrieve the source template real path.
+ *
+ * This method store its result internally if no $file attribute is
+ * specified (work on template internals).
+ *
+ * If a file name is specified, this method will try to locate it
+ * exploring current path (PWD), the current template location,
+ * the repository and parent template location.
+ *
+ * @param string $file optional
+ * some file name to locate.
+ *
+ * @throws FileNotFound
+ * @return string
+ */
+ function realpath($file=false)
+ {
+ // real template path
+ if (!$file) {
+ if ($this->isValid()) {
+ return $this->_real_path;
+ } else {
+ $ex = new FileNotFound($this->_sourceFile . ' not found');
+ return PEAR::raiseError($ex);
+ }
+ }
+
+ //
+ // path to some file relative to this template
+ //
+ $i = $this->_resolvers->getNewIterator();
+ while ($i->isValid()) {
+ $resolver =& $i->value();
+ $locator =& $resolver->resolve($file,
+ $this->_repository,
+ $this->_real_path);
+ if ($locator) {
+ return $locator->realPath();
+ }
+ $i->next();
+ }
+
+ $ex = new FileNotFound($file . ' not found');
+ return PEAR::raiseError($ex);
+ }
+
+ /**
+ * Set the template result encoding.
+ *
+ * Changing this encoding will change htmlentities behaviour.
+ *
+ * Example:
+ *
+ * $tpl->setEncoding('ISO-8859-1");
+ *
+ * See http://fr2.php.net/manual/en/function.htmlentities.php for a list of
+ * supported encodings.
+ *
+ * @param $enc string Template encoding
+ */
+ function setEncoding($enc)
+ {
+ $this->_encoding = $enc;
+ }
+
+ /**
+ * Retrieve the template result encoding.
+ *
+ * @return string
+ */
+ function getEncoding()
+ {
+ return $this->_encoding;
+ }
+
+ // ----------------------------------------------------------------------
+ // private / protected methods
+ // ----------------------------------------------------------------------
+
+ /**
+ * Set the called template. (internal)
+ *
+ * @access package
+ */
+ function setParent(&$tpl)
+ {
+ $this->_parent =& $tpl;
+ $this->_resolvers = $tpl->_resolvers;
+ $this->_inputFilters = $tpl->_inputFilters;
+ $this->_parentPath = $tpl->realPath();
+ $this->_cacheManager =& $tpl->getCacheManager();
+ $this->_translator =& $tpl->_translator;
+ $this->setOutputMode($tpl->_outputMode);
+ }
+
+ /**
+ * Prepare template execution.
+ *
+ * @access private
+ */
+ function _prepare()
+ {
+ if ($this->_prepared) return;
+ $this->_sourceFile = $this->realpath();
+
+ // ensure that no error remain
+ if (PEAR::isError($this->_sourceFile)) {
+ return $this->_sourceFile;
+ }
+ $this->_funcName = "tpl_" . PHPTAL_MARK . md5($this->_sourceFile);
+ $this->_codeFile = $this->_cacheDir . $this->_funcName . '.php';
+ $this->_prepared = true;
+ }
+
+ /**
+ * Generate php code from template source
+ *
+ * @access private
+ * @throws PHPTALParseException
+ */
+ function _generateCode()
+ {
+ require_once _phptal_os_path_join(dirname(__FILE__), 'Parser.php');
+
+ $parser = new PHPTAL_Parser();
+ $parser->_outputMode($this->_outputMode);
+ $data = $this->_locator->data();
+
+ // activate prefilters on data
+ $i = $this->_inputFilters->getNewIterator();
+ while ($i->isValid()){
+ $filter =& $i->value();
+ $data = $filter->filter($data);
+ $i->next();
+ }
+
+ // parse source
+ $result = $parser->parse($this->_real_path, $data);
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+
+ // generate and store intermediate php code
+ $this->_code = $parser->generateCode($this->_funcName);
+ if (PEAR::isError($this->_code)) {
+ return $this->_code;
+ }
+ }
+
+ /**
+ * Load cached php code
+ *
+ * @access private
+ */
+ function _loadCachedCode()
+ {
+ include_once($this->_codeFile);
+ $this->_code = "#loaded";
+ }
+
+ /**
+ * Cache generated php code.
+ *
+ * @access private
+ */
+ function _cacheCode()
+ {
+ $fp = @fopen($this->_codeFile, "w");
+ if (!$fp) {
+ return PEAR::raiseError($php_errormsg);
+ }
+ fwrite($fp, $this->_code);
+ fclose($fp);
+ }
+
+ /**
+ * Load or generate php code.
+ *
+ * @access private
+ */
+ function _load()
+ {
+ if (isset($this->_code) && !PEAR::isError($this->_code)) {
+ return;
+ }
+
+ if (!defined('PHPTAL_NO_CACHE')
+ && file_exists($this->_codeFile)
+ && filemtime($this->_codeFile) >= $this->_locator->lastModified()) {
+ return $this->_loadCachedCode();
+ }
+
+ $err = $this->_generateCode();
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+
+ $err = $this->_cacheCode();
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+
+ $err = $this->_loadCachedCode();
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+ }
+
+ /**
+ * Execute template with prepared context.
+ *
+ * This method execute the template file and returns the produced string.
+ *
+ * @return string
+ * @throws
+ */
+ function execute()
+ {
+ $err = $this->_prepare();
+ if (PEAR::isError($err)) {
+ $this->_ctx->_errorRaised = true;
+ return $err;
+ }
+ return $this->_cacheManager->template($this,
+ $this->_sourceFile,
+ $this->_ctx);
+ }
+
+ /**
+ * Really load/parse/execute the template and process output filters.
+ *
+ * This method is called by cache manager to retrieve the real template
+ * execution value.
+ *
+ * IMPORTANT : The result is post-filtered here !
+ *
+ * @return string
+ * @access private
+ */
+ function _process()
+ {
+ $err = $this->_load();
+ if (PEAR::isError($err)) {
+ $this->_ctx->_errorRaised = true;
+ return $err;
+ }
+
+ $this->_ctx->_errorRaised = false;
+ $func = $this->_funcName;
+ if (!function_exists($func)) {
+ $err = "Template function '$func' not found (template source : $this->_sourceFile";
+ return PEAR::raiseError($err);
+ }
+
+ // ensure translator exists
+ if (!isset($this->_translator)) {
+ $this->_translator = new PHPTAL_I18N();
+ }
+
+ $res = $func($this);
+ if ($this->_headers) {
+ $res = $this->_headers . $res;
+ }
+
+ // activate post filters
+ $i = $this->_outputFilters->getNewIterator();
+ while ($i->isValid()) {
+ $filter =& $i->value();
+ $res = $filter->filter($this, $res, PHPTAL_POST_FILTER);
+ $i->next();
+ }
+ return $res;
+ }
+
+ function _translate($key)
+ {
+ return $this->_translator->translate($key);
+ }
+
+ function _setTranslateVar($name, $value)
+ {
+ if (is_object($value)) {
+ $value = $value->toString();
+ }
+ $this->_translator->set($name, $value);
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+require_once "Types/Errors.php";
+
+/**
+ * PHPTAL internal xml parser.
+ *
+ * Note:
+ *
+ * I didn't use the XML/Parser package because of reference problems due to
+ * call_user_func and call_user_method.
+ *
+ * This problem should vanished with automatic object referencing in php4.4
+ * (ZendEngine2) remind me to remove this parser at this time.
+ *
+ *
+ * This class uses "xml_*" php functions to parse xml data.
+ *
+ * To create a new xml parser, extends this class and implements following
+ * methods.
+ *
+ * - onElementStart($tag, $attributes)
+ * - onElementClose($tag)
+ * - onElementData($data)
+ * - onSpecific($data)
+ *
+ * Here's an exemple of xml parser implementation.
+ *
+ * <?php
+ * require_once "PHPTAL/XML_Parser.php";
+ *
+ * class MyParser extends PHPTAL_XML_Parser
+ * {
+ * function onElementStart($tag, $attributes)
+ * {
+ * echo "new tag $tag with attributes :", endl;
+ * print_r($attributes);
+ * }
+ *
+ * function onElementClose($tag)
+ * {
+ * echo "tag ",$tag," is closed", endl;
+ * }
+ *
+ * function onElementData($data)
+ * {
+ * echo "some plain text : ", $data, endl;
+ * }
+ *
+ * function onSpecific($data)
+ * {
+ * echo "non xml data maybe <?xml...?> :", $data, endl;
+ * }
+ * };
+ *
+ * // MyParser usage :
+ * $p = new MyParser();
+ * $p->parse( $myString );
+ *
+ * ?>
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class PHPTAL_XML_Parser
+{
+ var $_file = '#string';
+ var $_tags = array();
+ var $_parser;
+ var $_error;
+ var $_xmlErrors = array(
+ XML_ERROR_NONE => "XML_ERROR_NONE",
+ XML_ERROR_NO_MEMORY => "XML_ERROR_NO_MEMORY",
+ XML_ERROR_SYNTAX => "XML_ERROR_SYNTAX",
+ XML_ERROR_NO_ELEMENTS => "XML_ERROR_NO_ELEMENTS",
+ XML_ERROR_INVALID_TOKEN => "XML_ERROR_INVALID_TOKEN",
+ XML_ERROR_UNCLOSED_TOKEN => "XML_ERROR_UNCLOSED_TOKEN",
+ XML_ERROR_PARTIAL_CHAR => "XML_ERROR_PARTIAL_CHAR",
+ XML_ERROR_TAG_MISMATCH => "XML_ERROR_TAG_MISMATCH",
+ XML_ERROR_DUPLICATE_ATTRIBUTE => "XML_ERROR_DUPLICATE_ATTRIBUTE",
+ XML_ERROR_JUNK_AFTER_DOC_ELEMENT => "XML_ERROR_JUNK_AFTER_DOC_ELEMENT",
+ XML_ERROR_PARAM_ENTITY_REF => "XML_ERROR_PARAM_ENTITY_REF",
+ XML_ERROR_UNDEFINED_ENTITY => "XML_ERROR_UNDEFINED_ENTITY",
+ XML_ERROR_RECURSIVE_ENTITY_REF => "XML_ERROR_RECURSIVE_ENTITY_REF",
+ XML_ERROR_ASYNC_ENTITY => "XML_ERROR_ASYNC_ENTITY",
+ XML_ERROR_BAD_CHAR_REF => "XML_ERROR_BAD_CHAR_REF",
+ XML_ERROR_BINARY_ENTITY_REF => "XML_ERROR_BINARY_ENTITY_REF",
+ XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF => "XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF",
+ XML_ERROR_MISPLACED_XML_PI => "XML_ERROR_MISPLACED_XML_PI",
+ XML_ERROR_UNKNOWN_ENCODING => "XML_ERROR_UNKNOWN_ENCODING",
+ XML_ERROR_INCORRECT_ENCODING => "XML_ERROR_INCORRECT_ENCODING",
+ XML_ERROR_UNCLOSED_CDATA_SECTION => "XML_ERROR_UNCLOSED_CDATA_SECTION",
+ XML_ERROR_EXTERNAL_ENTITY_HANDLING => "XML_ERROR_EXTERNAL_ENTITY_HANDLING",
+ );
+
+ /**
+ * XML parser constructor.
+ */
+ function PHPTAL_XML_Parser()
+ {
+ $this->__construct();
+ }
+
+ /**
+ * XML parser php4.4 constructor.
+ */
+ function __construct()
+ {
+ $this->_parser = xml_parser_create();
+ xml_set_object($this->_parser, $this);
+ xml_set_element_handler($this->_parser, "_onElementStart", "_onElementClose");
+ xml_set_character_data_handler($this->_parser, "_onElementData");
+ xml_set_default_handler($this->_parser, "_onSpecific");
+ xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, 0);
+ }
+
+ /**
+ * Parse specified data and call parser implementation of callback methods.
+ *
+ * @param data string Xml data to parse.
+ */
+ function _parse($data, $eof=true)
+ {
+ $data = str_replace('&','&', $data);
+ if (!xml_parse($this->_parser, $data)) {
+ // PHPTAL errors first
+ if (PEAR::isError($this->_error)) {
+ return $this->_error;
+ }
+ // then look for parser errors
+ $err = xml_get_error_code($this->_parser);
+ return PEAR::raiseError($this->_xmlErrors[$err]
+ .' in '.$this->_file
+ .' around line '.$this->getLineNumber());
+ }
+ if (PEAR::isError($this->_error)) {
+ return $this->_error;
+ }
+ return true;
+ }
+
+ function parseString($data)
+ {
+ return $this->_parse($data, true);
+ }
+
+ function parseFile($path)
+ {
+ $this->_file = $path;
+ $fp = @fopen($path, "r");
+ if (!$fp) {
+ return PEAR::raiseError($php_errormsg);
+ }
+ while ($data = fread($fp, 1024)) {
+ $err = $this->_parse($data, feof($fp));
+ if (PEAR::isError($err)) {
+ fclose($fp);
+ return $err;
+ }
+ }
+ fclose($fp);
+ }
+
+ function _onElementStart($parser, $tag, $attributes)
+ {
+ if (PEAR::isError($this->_error)) return;
+ $this->_error = $this->onElementStart($tag, $attributes);
+ }
+
+ function _onElementClose($parser, $tag)
+ {
+ if (PEAR::isError($this->_error)) return;
+ $this->_error = $this->onElementClose($tag);
+ }
+
+ function _onElementData($parser, $data)
+ {
+ if (PEAR::isError($this->_error)) return;
+ $this->_error = $this->onElementData($data);
+ }
+
+ function _onSpecific($parser, $data)
+ {
+ if (PEAR::isError($this->_error)) return;
+ $this->_error = $this->onSpecific($data);
+ }
+
+ /**
+ * Return current parser line number.
+ *
+ * @return int
+ */
+ function getLineNumber()
+ {
+ return xml_get_current_line_number($this->_parser);
+ }
+
+ //
+ // ABSTRACT METHODS
+ //
+
+ /**
+ * Abstract callback called when a new xml tag is opened.
+ *
+ * @param string tag Tag name
+ * @param hashtable attributes Associative array of attributes
+ */
+ function onElementStart($tag, $attributes){}
+
+ /**
+ * Abstract callback called when a tag is closed.
+ *
+ * @param string tag Tag name
+ */
+ function onElementClose($tag){}
+
+ /**
+ * Abstract callback called when some #cdata is found.
+ *
+ * @param string data Content
+ */
+ function onElementData($data){}
+
+ /**
+ * Abstract callback called when non tags entities appear in the document.
+ *
+ * This method is called by <?xml ...?> <% %> and other specific things like
+ * <?php ?>.
+ *
+ * @param string data strange data content.
+ */
+ function onSpecific($data){}
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+/**
+ * Static class used in common types internals.
+ *
+ * @static
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class Types
+{
+ /**
+ * Generate a string representation of specified variable.
+ *
+ * @param mixed $var -- variable to represent in a string form.
+ * @static
+ */
+ function toString(&$var)
+ {
+ if (is_object($var)) {
+ return Types::_objToString($var);
+ } elseif (is_array($var)) {
+ if (array_key_exists(0, $var) || count($var) == 0) {
+ return Types::_arrayToString($var);
+ } else {
+ return Types::_hashToString($var);
+ }
+ } elseif (is_resource($var)) {
+ return '#'.gettype($var).'#';
+ }
+ return $var;
+ }
+
+ /**
+ * Generate a string representation of an object calling its toString
+ * method of using its class name.
+ *
+ * @access protected
+ * @static
+ */
+ function _objToString(&$var)
+ {
+ if (method_exists($var, "toString")) {
+ return $var->toString();
+ } else {
+ return '<' . get_class($var) . ' instance>';
+ }
+ }
+
+ /**
+ * Generate a string representation of a php array.
+ *
+ * @access protected
+ * @static
+ */
+ function _arrayToString(&$var)
+ {
+ $values = array();
+ foreach ($var as $val) {
+ $values[] = Types::toString($val);
+ }
+ return '[' . join(', ', $values) . ']';
+ }
+
+ /**
+ * Generate a string representation of an associative array.
+ *
+ * @access protected
+ * @static
+ */
+ function _hashToString(&$var)
+ {
+ $values = array();
+ foreach ($var as $key=>$val) {
+ $values[] = '\''. $key . '\': ' . Types::toString($val);
+ }
+ return '{' . join(', ', $values) . '}';
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+// $id$
+
+require_once "Types/OString.php";
+require_once "Types/OHash.php";
+
+/**
+ * Common php string that extract parameters from an associative array.
+ *
+ * This string is appended to the begin of the produced php function. It
+ * takes function context argument and extract it in named variables.
+ *
+ * @access private
+ */
+define('_TYPES_CODE_EXTRACT_CODE_CONTEXT_',
+'// BEGIN EXTRACT __context__
+if (is_array($__context__)) { extract($__context__); }
+if (is_object($__context__)) { extract($__context__->toHash()); }
+// END EXTRACT __context__
+');
+
+/**
+ * Code class handle and evaluate php code.
+ *
+ * The aim of this class is to dynamically generate executable php code from
+ * php string.
+ *
+ * This kind of object can be safely serialized as the code it represents is
+ * stored in the _code member variable.
+ *
+ * When setting code to this object, a new anonymous function is created
+ * waiting to be invoqued using the execute() method.
+ *
+ * As we can't know how many parameters this function should take, a 'context'
+ * hashtable is used as only parameter. This hash may contains any number
+ * of arguments with var name compliant keys.
+ *
+ * Code object automatically append 'extract' code string to produced code.
+ *
+ * It's up to the code to assert parameters using 'isset()' php function.
+ *
+ * <?php
+ * require_once "Types/Code.php";
+ *
+ * $o_code = Code()
+ * $o_code->setCode('return $foo . $bar . $baz;');
+ * $res = $o_code->execute(
+ * 'foo', 'foo value ',
+ * 'baz', 'bar value ',
+ * 'baz', 'baz value'
+ * );
+ *
+ * // will print 'foo value bar value baz value'
+ * echo $res, end;
+ * ?>
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class Code
+{
+ var $_code;
+ var $_function;
+ var $_compiled = false;
+
+ /**
+ * Construct a new code object.
+ *
+ * @param mixed $code (optional)
+ * php code string or object implementing toString method.
+ */
+ function Code($code=false)
+ {
+ $this->__construct($code);
+ }
+
+ function __construct($code=false)
+ {
+ if (!$code) {
+ return $this;
+ }
+ if (is_object($code) && get_class($code) == 'code') {
+ $this->_code = $code->getCode();
+ } else {
+ $this->setCode(Types::toString($code));
+ }
+ }
+
+ /**
+ * Execute code with specific context.
+ *
+ * @param mixed ...
+ * The function execution context. This may be an associative
+ * array, an OHash object, a list of key/value pairs that will
+ * be transformed into an associative array
+ *
+ * @return mixed The execution result.
+ */
+ function &execute()
+ {
+ if (!$this->_compiled) {
+ $err =& $this->compile();
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+ }
+ $argv = func_get_args();
+ $argc = func_num_args();
+ switch ($argc) {
+ case 1:
+ $context = $argv[0];
+ break;
+ default:
+ $context = OHash::ArrayToHash($argv);
+ break;
+ }
+ $func = $this->_function;
+ return $func($context);
+ }
+
+ /**
+ * Compile php code.
+ *
+ * This function may produce parse errors.
+ *
+ * @throws CodeError
+ */
+ function compile()
+ {
+ ob_start();
+ $this->_function = create_function('$__context__', $this->_code);
+ $ret = ob_get_contents();
+ ob_end_clean();
+
+ if (!$this->_function) {
+ return PEAR::raiseError($ret);
+ }
+
+ $this->_compiled = true;
+ }
+
+ /**
+ * Set function code.
+ *
+ * @param string $str
+ * The php code string
+ */
+ function setCode($str)
+ {
+ // add extract code to function code
+ $str = _TYPES_CODE_EXTRACT_CODE_CONTEXT_ . $str;
+ $this->_code = $str;
+ }
+
+ /**
+ * Retrieve code.
+ *
+ * @return string
+ */
+ function getCode()
+ {
+ return $this->_code;
+ }
+
+ /**
+ * On serialization, we store only code, not additional variables.
+ *
+ * @access private
+ */
+ function __sleep()
+ {
+ return array("_code");
+ }
+
+ /**
+ * Make a string representation of this object.
+ *
+ * @return string
+ */
+ function toString()
+ {
+ if ($this->_compiled) {
+ return '<Code \''.$this->_function.'\'>';
+ } else {
+ return '<Code \'not compiled\'>';
+ }
+ }
+}
+
+/**
+ * Code compilation error.
+ *
+ * This error handles parse errors an other problems that may occurs while
+ * compiling a php code.
+ */
+class CodeError extends PEAR_Error
+{
+}
+
+?>
--- /dev/null
+<?php
+
+/**
+ * Container out of bound error.
+ */
+class OutOfBounds extends PEAR_Error {}
+
+/**
+ * Unable to locate some data somewhere.
+ *
+ * This kind of exception was introduced by tof template context.
+ */
+class NameError extends PEAR_Error {}
+
+/**
+ * Mismatched data type.
+ */
+class TypeError extends PEAR_Error {}
+
+/**
+ * Unable to locate a file.
+ */
+class FileNotFound extends PEAR_Error {}
+
+/**
+ * Input output error.
+ */
+class IOException extends PEAR_Error {}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+/**
+ * Iterable interface.
+ *
+ * An iterable object can produce Iterator(s) related to itself.
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class Iterable
+{
+ /**
+ * Retrieve iterable size.
+ */
+ function size(){}
+
+ /**
+ * Get an initialized iterator for this object.
+ */
+ function &getNewIterator(){}
+}
+
+
+/**
+ * Iterator interface.
+ *
+ * This class provides common methods for Iterator objects.
+ *
+ * An iterator is a 'pointer' to a data item extracted from some collection of
+ * resources.
+ *
+ * The aim of Iterator is to allow some abstraction between your program
+ * resources and the way you fetch these resources.
+ *
+ * Thus, you can loop over a file content and later replace this file with a
+ * database backend whithout changing the program logic.
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class Iterator
+{
+ /**
+ * Reset iterator to first item.
+ *
+ * This method should throw an exception for once only iterators.
+ *
+ * @throws ResetFailed
+ */
+ function reset()
+ {
+ return PEAR::raiseError('\'reset\' method not implemented');
+ }
+
+ /**
+ * Test if current item is not the end of iterator.
+ *
+ * @return boolean
+ */
+ function isValid()
+ {
+ return PEAR::raiseError('\'isValid\' method not implemented');
+ }
+
+ /**
+ * Iterate on the next element and returns the next item.
+ *
+ * @return mixed (by reference)
+ */
+ function &next()
+ {
+ return PEAR::raiseError('\'next\' method not implemented');
+ }
+
+ // ----------------------------------------------------------------------
+ // getters
+ // ----------------------------------------------------------------------
+
+ /**
+ * Retrieve the current item index.
+ *
+ * @return int
+ */
+ function index()
+ {
+ return PEAR::raiseError('\'index\' method not implemented');
+ }
+
+ /**
+ * Retrieve current item value.
+ *
+ * @return mixed (by reference)
+ */
+ function &value()
+ {
+ return PEAR::raiseError('\'value\' method not implemented');
+ }
+
+ // ----------------------------------------------------------------------
+ // optional methods
+ // ----------------------------------------------------------------------
+
+ /**
+ * (optional) Additional index for hashes.
+ *
+ * @return string
+ */
+ function key()
+ {
+ return $this->index();
+ }
+
+ /**
+ * (optional) Remove the current value from container.
+ *
+ * Implement this method only when the iterator can do a secured remove
+ * without breaking other iterators works.
+ */
+ function remove()
+ {
+ return PEAR::raiseError('\'remove\' method not implemented');
+ }
+}
+
+/**
+ * Exception thrown by iterators that can be reseted only once.
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class ResetFailed extends PEAR_Error
+{
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+require_once "Types.php";
+require_once "Types/Errors.php";
+require_once "Types/Ref.php";
+require_once "Types/Iterator.php";
+
+/**
+ * Class wrapper for oriented object arrays.
+ *
+ * This class implements a simple interface quite like java to handle array in
+ * a good old oo way.
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class OArray extends Iterable
+{
+ var $_array;
+ var $_size = 0;
+
+ /**
+ * OArray constructor.
+ *
+ * @param array $array optional
+ * The php array this vector will manage.
+ */
+ function OArray($array=array())
+ {
+ $this->__construct($array);
+ }
+
+ /**
+ * OArray php4.4 compliant constructor.
+ *
+ * @param array $array optional
+ * The php array this vector will manage.
+ */
+ function __construct($array=array())
+ {
+ $this->_array = array();
+ for ($i = 0; $i < count($array); $i++) {
+ $this->pushRef($array[$i]);
+ }
+ }
+
+ /**
+ * Retrieve an iterator ready to used.
+ * @return ArrayIterator
+ */
+ function getNewIterator()
+ {
+ return new ArrayIterator($this);
+ }
+
+ /**
+ * Returns the vector number of elements.
+ * @return int
+ */
+ function size()
+ {
+ return count($this->_array);
+ }
+
+ /**
+ * Returns true if this vector is empty, false otherwise.
+ * @return boolean
+ */
+ function isEmpty()
+ {
+ return $this->size() == 0;
+ }
+
+ /**
+ * Retrieve element at specified index.
+ * @return mixed
+ * @throws OutOfBounds
+ */
+ function &get($i)
+ {
+ if ($i > $this->size() || $i < 0) {
+ $err = new OutOfBounds("OArray index out of bounds ($i)");
+ // return EX::raise($err);
+ return PEAR::raiseError($err);
+ }
+ return $this->_array[$i]->obj;
+ }
+
+ /**
+ * Retrieve index of specified element.
+ *
+ * @return int The element index of false if element not in vector.
+ */
+ function indexOf($element)
+ {
+ for ($i=0; $i < count($this->_array); $i++) {
+ if ($this->_array[$i]->obj === $element) {
+ return $i;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Set the item at specified index.
+ *
+ * @throws OutOfBounds if index is greated that vector limit
+ * @return Old element
+ */
+ function &set($i, $item)
+ {
+ return $this->setRef($i, $item);
+ }
+
+ /**
+ * Set the item at specified index (by reference).
+ *
+ * @throws OutOfBounds if index is greated that vector limit
+ * @return Old element
+ */
+ function &setRef($i, &$item)
+ {
+ if ($i > $this->size() || $i < 0) {
+ $err = new OutOfBounds("OArray index out of bounds ($i)");
+ // return EX::raise($err);
+ return PEAR::raiseError($err);
+ }
+ $temp = $this->_array[$i];
+ $this->_array[$i] = Ref($item);
+ return $temp->obj;
+ }
+
+ /**
+ * Test if the vector contains the specified element.
+ *
+ * @param mixed $item The item we look for.
+ */
+ function contains($o)
+ {
+ for ($i = 0; $i < $this->size(); $i++) {
+ if ($this->_array[$i]->obj === $o) return true;
+ }
+ return false;
+ }
+
+ /**
+ * Add an element to the vector.
+ *
+ * @param mixed $o The item to add.
+ */
+ function add($o)
+ {
+ $this->push($o);
+ }
+
+ /**
+ * Remove object from this vector.
+ *
+ * @param mixed $o Object to remove.
+ * @return boolean true if object removed false if not found
+ */
+ function remove($o)
+ {
+ $i = $this->indexOf($o);
+ if ($i !== false) {
+ $this->removeIndex($i);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Remove specified index from vector.
+ *
+ * @param int $i Index
+ * @throws OutOfBounds
+ */
+ function removeIndex($i)
+ {
+ if ($i > $this->size() || $i < 0 ) {
+ $err = new OutOfBounds("OArray index out of bounds ($i)");
+ // return EX::raise($err);
+ return PEAR::raiseError($err);
+ }
+
+ // $this->_array = array_splice($this->_array, $i, 1);
+ array_splice($this->_array, $i, 1);
+ }
+
+ /**
+ * Clear vector.
+ */
+ function clear()
+ {
+ $this->_array = array();
+ }
+
+ /**
+ * Add an element at the end of the vector (same as add()).
+ *
+ * @param mixed $o Item to append to vector.
+ */
+ function push($o)
+ {
+ array_push($this->_array, Ref($o));
+ }
+
+ /**
+ * Add an element at the end of the vector (same as add()).
+ *
+ * @param mixed $o Item to append to vector.
+ */
+ function pushRef(&$o)
+ {
+ $this->_array[] = Ref($o);
+ }
+
+ /**
+ * Retrieve vector values.
+ *
+ * The returned array contains references to internal data.
+ *
+ * @return array
+ */
+ function &values()
+ {
+ $v = array();
+ for ($i = 0; $i < $this->size(); $i++) {
+ $v[] =& $this->_array[$i]->obj;
+ }
+ return $v;
+ }
+
+ /**
+ * Remove the last element of the vector and returns it.
+ *
+ * @return mixed
+ */
+ function &pop()
+ {
+ $ref = array_pop($this->_array);
+ return $ref->obj;
+ }
+
+ /**
+ * Extract and return first element of OArray.
+ * @return mixed
+ */
+ function &shift()
+ {
+ $ref = array_shift($this->_array);
+ return$ $ref->obj;
+ }
+
+ /**
+ * Retrieve a php array for this vector.
+ *
+ * @return array
+ */
+ function &toArray()
+ {
+ return $this->values();
+ }
+
+ /**
+ * Retrieve a string representation of the array.
+ */
+ function toString()
+ {
+ return Types::_arrayToString($this->values());
+ }
+};
+
+/**
+ * OArray itertor class.
+ *
+ * This kind of iterators are used to walk throug a OArray object.
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class ArrayIterator extends Iterator
+{
+ var $_src;
+ var $_values;
+ var $_value;
+ var $_i;
+
+ /**
+ * Iterator constructor.
+ *
+ * @param mixed $oarray OArray object or php array.
+ */
+ function ArrayIterator(&$oarray)
+ {
+ return $this->__construct($oarray);
+ }
+
+ /**
+ * PHP 4.4 compliant constructor.
+ *
+ * @param mixed $oarray OArray object or php array.
+ */
+ function __construct(&$oarray)
+ {
+ if (is_array($oarray)) {
+ $this->_src = new OArray($oarray);
+ } else if (is_a($oarray, 'OArray')) {
+ $this->_src = &$oarray;
+ } else {
+ // ignore error, should throw a bad type error or something like
+ // that but pear does not handle errors in constructors yet
+ $err = new TypeError("ArrayIterator requires OArray object or php array.",
+ PEAR_ERROR_DIE);
+ // return EX::raise($err);
+ return PEAR::raiseError($err);
+ }
+ $this->reset();
+ }
+
+ /**
+ * Reset iterator to first array position.
+ */
+ function reset()
+ {
+ $this->_i = 0;
+ $this->_end = false;
+ $this->_values = $this->_src->_array;
+ if (count($this->_values) == 0) {
+ $this->_end = true;
+ return;
+ }
+
+ $this->_value = $this->_values[0];
+ }
+
+ /**
+ * Returns next vector item value.
+ *
+ * @return mixed
+ * @throws OutOfBounds if iterator overpass the vector size.
+ */
+ function &next()
+ {
+ if ($this->_end || ++$this->_i >= count($this->_values)) {
+ $this->_end = true;
+ return null;
+ }
+
+ $this->_value = $this->_values[$this->_i];
+ return $this->_value->obj;
+ }
+
+ /**
+ * Test if the iteractor has not reached its end.
+ * @return boolean
+ */
+ function isValid()
+ {
+ return !$this->_end;
+ }
+
+ /**
+ * Retrieve current iterator index.
+ * @return int
+ */
+ function index()
+ {
+ return $this->_i;
+ }
+
+ /**
+ * Retrieve the current iterator value.
+ * @return mixed
+ */
+ function &value()
+ {
+ return $this->_value->obj;
+ }
+
+ /**
+ * Delete current index.
+ */
+ function remove()
+ {
+ $this->_src->remove($this->_value->obj);
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+// $id$
+
+/**
+ * Generic date part format.
+ */
+define('TYPES_DATE_FMT_DATE', '%1$04s-%2$02s-%3$02s');
+/**
+ * French date part format.
+ */
+define('TYPES_DATE_FMT_DATE_FR', '%3$02s/%2$02s/%1$04s');
+/**
+ * Hours format.
+ */
+define('TYPES_DATA_FMT_HOUR', '%4$02s:%5$02s:%6$02s');
+
+/**
+ * Simple date manipulation object.
+ *
+ * Note:
+ * -----
+ *
+ * ODate::toString() can take a sprintf() format string as first parameter.
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class ODate
+{
+ var $_year = 0;
+ var $_month = 0;
+ var $_day = 0;
+ var $_hours = 0;
+ var $_minutes = 0;
+ var $_seconds = 0;
+
+ /**
+ * ODate constructor.
+ *
+ * This constructor is for php v4.4 compliance.
+ *
+ * @param int $y optional -- year
+ * @param int $mt optional -- month
+ * @param int $d optional -- day
+ * @param int $h optional -- hour
+ * @param int $m optional -- minutes
+ * @param int $s optional -- seconds
+ */
+ function __construct($y=0, $mt=0, $d=0,
+ $h=0, $m=0, $s=0)
+ {
+ $this->setYear($y);
+ $this->setMonth($mt);
+ $this->setDay($d);
+ $this->setHours($h);
+ $this->setMinutes($m);
+ $this->setSeconds($s);
+ }
+
+ /**
+ * ODate constructor.
+ *
+ * @param int $y optional -- year
+ * @param int $mt optional -- month
+ * @param int $d optional -- day
+ * @param int $h optional -- hour
+ * @param int $m optional -- minutes
+ * @param int $s optional -- seconds
+ */
+ function ODate($y=0, $mt=0, $d=0,
+ $h=0, $m=0, $s=0)
+ {
+ $this->__construct($y, $mt, $d, $h, $m, $s);
+ }
+
+ /**
+ * Add a date to current date.
+ */
+ function add($date)
+ {
+ $this->inc($date->getYear(),
+ $date->getMonth(),
+ $date->getDay(),
+ $date->getHours(),
+ $date->getMinutes(),
+ $date->getSeconds());
+ }
+
+ /**
+ * Add values to this date.
+ *
+ * @param int $y optional -- year
+ * @param int $mt optional -- month
+ * @param int $d optional -- day
+ * @param int $h optional -- hour
+ * @param int $m optional -- minutes
+ * @param int $s optional -- seconds
+ */
+ function inc($y,$mt,$d,$h,$m,$s)
+ {
+ $this->addSeconds($s);
+ $this->addMinutes($m);
+ $this->addHours($h);
+ $this->addDays($d);
+ $this->addMonths($mt);
+ $this->addYears($y);
+ }
+
+ /**
+ * Set date part.
+ *
+ * @param int $y -- year
+ * @param int $mt -- month
+ * @param int $d -- day
+ */
+ function setDate($y,$m,$d)
+ {
+ $this->setYear($y);
+ $this->setMonth($m);
+ $this->setDay($d);
+ }
+
+ /**
+ * Set time part.
+ *
+ * @param int $h -- hour
+ * @param int $m -- minutes
+ * @param int $s -- seconds
+ */
+ function setTime($y,$m,$d)
+ {
+ $this->setHours($h);
+ $this->setMinutes($m);
+ $this->setSeconds($s);
+ }
+
+ /**
+ * Add some years to this date.
+ *
+ * @param int $y -- year
+ */
+ function addYears($y)
+ {
+ $this->_year += $y;
+ }
+
+ /**
+ * Add some months to this date.
+ *
+ * @param int $m -- month
+ */
+ function addMonths($m)
+ {
+ $this->_month += $m;
+ if ($this->_month > 12) {
+ $this->addYears($this->_month / 12);
+ $this->_month = $this->_month % 12;
+ }
+ }
+
+ /**
+ * Add some days to this date.
+ *
+ * @param int $d -- days
+ */
+ function addDays($d)
+ {
+ $m = 0;
+ $this->_day += $d;
+ $m = $this->numberOfMonthDays();
+ while ($this->_day > $m) {
+ $this->addMonths(1);
+ $this->_day -= $m;
+ $m = $this->numberOfMonthDays();
+ }
+ }
+
+ /**
+ * Add some hours to this date.
+ *
+ * @param int $h -- hours
+ */
+ function addHours($h)
+ {
+ $this->_hours += $h;
+ if ($this->_hours >= 24) {
+ $this->addDays($this->_hours / 24);
+ $this->_hours = $this->_hours % 24;
+ }
+ }
+
+ /**
+ * Add minutes to this date.
+ *
+ * @param int $m -- minutes
+ */
+ function addMinutes($m)
+ {
+ $this->_minutes += $m;
+ if ($this->_minutes >= 60) {
+ $this->addHours($this->_minutes / 60);
+ $this->_minutes = $this->_minutes % 60;
+ }
+ }
+
+ /**
+ * Add seconds to this date.
+ *
+ * @param int $s -- seconds
+ */
+ function addSeconds($s)
+ {
+ $this->_seconds += $s;
+ if ($this->_seconds >= 60) {
+ $this->addMinutes($this->_seconds / 60);
+ $this->_seconds = $this->_seconds % 60;
+ }
+ }
+
+ /**
+ * Return true if bissextile year.
+ *
+ * @return boolean
+ */
+ function isBissextile()
+ {
+ if ($this->_year % 400 == 0) return true;
+ if ($this->_year % 100 == 0) return false;
+ if ($this->_year % 4 == 0) return true;
+ return false;
+ }
+
+ /**
+ * Get the number of days in current month
+ *
+ * @return int
+ */
+ function numberOfMonthDays()
+ {
+ if ($this->_month == 2 && $this->isBissextile()) {
+ return 29;
+ }
+ $DOM = array(-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
+ return $DOM[$this->_month];
+ }
+
+ /**
+ * Set now time values to this object.
+ */
+ function setNow()
+ {
+ $d = getdate();
+ $this->setSeconds($d['seconds']);
+ $this->setMinutes($d['minutes']);
+ $this->setHours($d['hours']);
+ $this->setDay($d['mday']);
+ $this->setMonth($d['mon']);
+ $this->setYear($d['year']);
+ }
+
+ /**
+ * Ensure that date is correct.
+ */
+ function cleanup()
+ {
+ $this->add(0,0,0,0,0,0);
+ }
+
+ /**
+ * Compare with another date, return < 0 if less, == 0 if equals, > 0 if
+ * greater.
+ *
+ * @param ODate $date -- date to compare with
+ *
+ * @return int (strcmp)
+ */
+ function compare($date)
+ {
+ return strcmp($this->toNumericString(),
+ $date->toNumericString());
+ }
+
+ /**
+ * Return a concatenation of date fields (numeric date).
+ *
+ * @return string
+ */
+ function toNumericString()
+ {
+ return (int)"$this->_year$this->_month$this->_day".
+ "$this->_hours$this->_minutes$this->_seconds";
+ }
+
+ /**
+ * Simple date format.
+ *
+ * @param string $format optional
+ * Format to pass to sprintf (ex:"%04d-%02d-%02d %02d:%02d:%02d")
+ *
+ * @return string
+ */
+ function toString($format="%04d-%02d-%02d %02d:%02d:%02d")
+ {
+ $this->cleanup();
+ return sprintf($format,
+ $this->_year,
+ $this->_month,
+ $this->_day,
+ $this->_hours,
+ $this->_minutes,
+ $this->_seconds);
+ }
+
+
+ //
+ // OTHER GETTER SETTERS
+ //
+
+ /**
+ * Retrieve year.
+ * @return int
+ */
+ function getYear()
+ {
+ return $this->_year;
+ }
+
+ /**
+ * Retrieve month.
+ * @return int
+ */
+ function getMonth()
+ {
+ return $this->_month;
+ }
+
+ /**
+ * Retrieve day.
+ * @return int
+ */
+ function getDay()
+ {
+ return $this->_day;
+ }
+
+ /**
+ * Retrieve hour.
+ * @return int
+ */
+ function getHours()
+ {
+ return $this->_hours;
+ }
+
+ /**
+ * Retrieve minutes.
+ * @return int
+ */
+ function getMinutes()
+ {
+ return $this->_minutes;
+ }
+
+ /**
+ * Retrieve seconds.
+ * @return int
+ */
+ function getSeconds()
+ {
+ return $this->_seconds;
+ }
+
+ /**
+ * Set year.
+ * @param int $y -- year
+ */
+ function setYear($y)
+ {
+ $this->_year = $y;
+ }
+
+ /**
+ * Set month.
+ * @param int $m -- month
+ */
+ function setMonth($m)
+ {
+ $this->_month = $m;
+ }
+
+ /**
+ * Set day.
+ * @param int $d -- day
+ */
+ function setDay($d)
+ {
+ $this->_day = $d;
+ }
+
+ /**
+ * Set hours.
+ * @param int $h -- hours
+ */
+ function setHours($h)
+ {
+ $this->_hours = $h;
+ }
+
+ /**
+ * Set minutes.
+ * @param int $m -- minutes
+ */
+ function setMinutes($m)
+ {
+ $this->_minutes = $m;
+ }
+
+ /**
+ * Set seconds.
+ * @param int $s -- seconds
+ */
+ function setSeconds($s)
+ {
+ $this->_seconds = $s;
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+require_once "Types.php";
+require_once "Types/Errors.php";
+require_once "Types/Ref.php";
+
+/**
+ * Wrapper for oriented object associative arrays.
+ *
+ * This class implements a simple interface quite like java to handle
+ * associatives arrays (Hashtables).
+ *
+ * Note:
+ *
+ * Some problems may occurs with object references until php4.4 to avoid
+ * unwanted object copy, use setRef() method with objects or pass Ref objects
+ * to set().
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class OHash
+{
+ var $_hash;
+
+ /**
+ * OHash constructor.
+ *
+ * php4.4 compliant constructor
+ *
+ * @param array $array optional -- An associative php array
+ */
+ function __construct($array=array())
+ {
+ $this->_hash = array();
+ $keys = array_keys($array);
+ foreach ($keys as $key) {
+ $this->_hash[$key] = ref($array[$key]);
+ }
+ }
+
+ /**
+ * OHash constructor.
+ *
+ * php4.4 compliant constructor
+ *
+ * @param array $array optional -- An associative php array
+ */
+ function OHash($array=array())
+ {
+ $this->__construct($array);
+ }
+
+ /**
+ * Set a value in this hash.
+ *
+ * @param string $key
+ * @param string $value
+ */
+ function set($key, $value)
+ {
+ $this->_hash[$key] = ref($value);
+ }
+
+ /**
+ * Reference set.
+ *
+ * Until php4.4, it's the only way to avoid object/variable copy.
+ *
+ * @param string $key
+ * @param reference $value
+ */
+ function setRef($key, &$value)
+ {
+ $this->_hash[$key] = ref($value);
+ }
+
+ /**
+ * Set a map of values.
+ *
+ * @param mixed $hash An Hash object or an associative php array.
+ */
+ function setAll($hash)
+ {
+ if (!is_array($hash) && is_a($hash, 'OHash')) {
+ $hash = $hash->toHash();
+ }
+ $keys = array_keys($hash);
+ foreach ($keys as $key) {
+ $this->_hash[$key] = ref($hash[$key]);
+ }
+ }
+
+ /**
+ * Retrieve value associated to specified key.
+ *
+ * @param string $key
+ * @return reference
+ */
+ function &get($key)
+ {
+ if ($this->containsKey($key)) {
+ return $this->_hash[$key]->obj;
+ }
+ return null;
+ }
+
+ /**
+ * Remove element associated to specified key from hash.
+ *
+ * @param string $key
+ */
+ function remove($key)
+ {
+ $keys = $this->keys();
+ $i = array_search($key, $keys);
+ if ($i !== false) {
+ // unset hash element to fix many bugs that should appear while
+ // iterating and using references to this element.
+ unset($this->_hash[$key]);
+ // return array_splice($this->_hash, $i, 1);
+ }
+ }
+
+ /**
+ * Remove an element from the Hash.
+ *
+ * @param mixed $o -- Element to remove from Hash
+ */
+ function removeElement(&$o)
+ {
+ $i = 0;
+ $found = false;
+ foreach ($this->_hash as $key => $value) {
+ if ($value->obj === $o || $value == $o) {
+ $found = $i;
+ break;
+ }
+ $i++;
+ }
+ if ($found !== false) {
+ return array_splice($this->_hash, $found, 1);
+ }
+ }
+
+ /**
+ * Returns true is hashtable empty.
+ *
+ * @return boolean
+ */
+ function isEmpty()
+ {
+ return $this->size() == 0;
+ }
+
+ /**
+ * Retrieve hash size (number of elements).
+ *
+ * @return int
+ */
+ function size()
+ {
+ return count($this->_hash);
+ }
+
+ /**
+ * Retrieve hash values array.
+ *
+ * @return hashtable
+ */
+ function &values()
+ {
+ $v = array();
+ foreach ($this->_hash as $key => $ref) {
+ $v[] =& $ref->obj;
+ }
+ return $v;
+ }
+
+ /**
+ * Retrieve hash keys array.
+ *
+ * @return array
+ */
+ function keys()
+ {
+ return array_keys($this->_hash);
+ }
+
+ /**
+ * Retrieve an hash iterator ready to use.
+ *
+ * @return HashIterator
+ */
+ function getNewIterator()
+ {
+ return new HashIterator($this);
+ }
+
+ /**
+ * Test if this hash contains specified key.
+ *
+ * @param string $key
+ * @return boolean
+ */
+ function containsKey($key)
+ {
+ return array_key_exists($key, $this->_hash);
+ }
+
+ /**
+ * Test if this hash contains specified value.
+ *
+ * @param mixed $value -- The value to search
+ * @return boolean
+ */
+ function containsValue($value)
+ {
+ foreach ($this->_hash as $k => $v) {
+ if ($v->obj === $value || $v == $value) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the php array (hashtable) handled by this object.
+ *
+ * @return hashtable
+ */
+ function &toHash()
+ {
+ $result = array();
+ foreach ($this->_hash as $key => $value) {
+ $result[$key] =& $value->obj;
+ }
+ return $result;
+ }
+
+ /**
+ * Create an Hash object from a simple php array variable.
+ *
+ * This method assumes that the array is composed of serial key, value
+ * elements. For each pair of array element, the former will be used as key
+ * and the latter as value.
+ *
+ * @param array $array -- php array.
+ *
+ * @return Hash
+ * @static 1
+ */
+ function ArrayToHash($array)
+ {
+ $h = new OHash();
+ while (count($array) > 1) {
+ $key = array_shift($array);
+ $h->set($key, array_shift($array));
+ }
+ return $h;
+ }
+
+ /**
+ * Generate and return a string representation of this hashtable.
+ *
+ * @return string
+ */
+ function toString()
+ {
+ return Types::_hashToString($this->toHash());
+ }
+
+ /**
+ * Sort hashtable on its keys.
+ */
+ function sort()
+ {
+ ksort($this->_hash);
+ }
+}
+
+
+/**
+ * Hash iterator.
+ *
+ * This kind of iterators are used to walk throug Hash objects.
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class HashIterator
+{
+ var $_src;
+ var $_values;
+ var $_keys;
+ var $_key;
+ var $_value;
+ var $_i = -1;
+ var $_end = false;
+
+ /**
+ * Iterator constructor.
+ *
+ * @param mixed $hash -- OHash object or associative php array.
+ */
+ function HashIterator(&$hash)
+ {
+ return $this->__construct($hash);
+ }
+
+ /**
+ * Iterator php4.4 compliant constructor.
+ *
+ * @param mixed $hash -- OHash object or associative php array.
+ *
+ * @throws TypeError
+ * When $hash is not an array or an Hash object.
+ */
+ function __construct(&$hash)
+ {
+ if (is_array($hash)) {
+ $this->_src = new OHash($hash);
+ } else if (is_a($hash, 'ohash')) {
+ $this->_src = &$hash;
+ } else {
+ $err = new TypeError('HashIterator requires associative array or OHash');
+ // return EX::raise($err);
+ die($err->toString());
+ return PEAR::raiseError($err);
+ }
+ $this->reset();
+ }
+
+ //
+ // iterator logics
+ //
+
+ /**
+ * Reset iterator to first element.
+ */
+ function reset()
+ {
+ // store a copy of hash references so a modification of source data
+ // won't affect iterator.
+ $this->_values = $this->_src->_hash;
+ $this->_keys = $this->_src->keys();
+
+ if (count($this->_keys) == 0) {
+ $this->_end = true;
+ return;
+ }
+
+ $this->_i = 0;
+ $this->_end = false;
+ $this->_key = $this->_keys[0];
+ $this->_value = $this->_values[$this->_key];
+ }
+
+ /**
+ * Test is end of iterator is not reached.
+ */
+ function isValid()
+ {
+ return !$this->_end;
+ }
+
+ /**
+ * Return next Hash item.
+ *
+ * This method also set _key and _value.
+ *
+ * @return mixed (by reference) or false if end reached
+ */
+ function &next()
+ {
+ if ($this->_end || ++$this->_i >= count($this->_keys)){
+ $this->_end = true;
+ return null;
+ }
+
+ $this->_key = $this->_keys[$this->_i];
+ $this->_value = $this->_values[$this->_key];
+ return $this->_value->obj;
+ }
+
+ //
+ // getters
+ //
+
+ /**
+ * Return current iterator key.
+ *
+ * @return string
+ */
+ function key()
+ {
+ return $this->_key;
+ }
+
+ /**
+ * Return current index (position in iteration).
+ *
+ * @return int
+ */
+ function index()
+ {
+ return $this->_i;
+ }
+
+ /**
+ * Return current iterator value.
+ *
+ * @return mixed
+ */
+ function &value()
+ {
+ return $this->_value->obj;
+ }
+
+ //
+ // optional implementations
+ //
+
+ /**
+ * Remove current iterated item from source.
+ */
+ function remove()
+ {
+ $this->_src->remove($this->_key);
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+
+require_once "Types.php";
+require_once "Types/Errors.php";
+
+/**
+ * Represents end of line string sequence '\n' of '<br/>' depending on
+ * script context.
+ */
+if (!defined('endl')) {
+ if (isset($GLOBALS['REQUEST_URI'])) {
+ define('endl', "<br/>\n");
+ } else {
+ define('endl', "\n");
+ }
+}
+
+/**
+ * Object String wrapper class.
+ *
+ * This class is an attempt to normalize php strings in an oo way.
+ *
+ * PHP string related functions have different arguments scheme that decrease
+ * code readability in OO programs. This class provides some php functions
+ * in a comprehensible String class.
+ *
+ * The second interest reside in the StringIterator class which normalize
+ * iteration over strings just like any other iterable object.
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class OString
+{
+ var $_str;
+
+ /**
+ * String constructor.
+ *
+ * @param mixed $str
+ * May be an object from a class implementing the
+ * 'toString()' method or a php variable that can be
+ * casted into a string.
+ */
+ function OString($str="")
+ {
+ return $this->__construct($str);
+ }
+
+ /**
+ * String php4.4 constructor.
+ *
+ * @param mixed $str
+ * May be an object from a class implementing the
+ * 'toString()' method or a php variable that can be
+ * casted into a string.
+ *
+ * @throws TypeError
+ */
+ function __construct($str="")
+ {
+ if (is_object($str)) {
+ if (!method_exists($str, "toString")) {
+ $err = new TypeError('String constructor requires string or '
+ . ' object implementing toString() method.',
+ PEAR_ERROR_DIE);
+ // return EX::raise($err);
+ return PEAR::raiseError($err);
+ }
+ $this->_str = $str->toString();
+ } else {
+ $this->_str = $str;
+ }
+ }
+
+ /**
+ * Append string values of arguments to this string.
+ *
+ * @param mixed ...
+ * php variables or objects implementing the toString()
+ * method.
+ */
+ function append()
+ {
+ $args = func_get_args();
+ foreach ($args as $arg) {
+ if (is_object($arg)) {
+ $this->_str .= $arg->toString();
+ } else {
+ $this->_str .= $arg;
+ }
+ }
+ }
+
+ /**
+ * Retrieve char at specified position.
+ *
+ * @param int $i Character position
+ * @return char
+ */
+ function charAt($i)
+ {
+ return $this->_str[$i];
+ }
+
+ /**
+ * Find first index of needle in current string.
+ *
+ * Return the first position of needle in string or *false* if
+ * not found.
+ *
+ * @param mixed $needle object implementing toString or php variable.
+ * @param int $pos Search start position
+ * @return int
+ */
+ function indexOf($needle, $pos=0)
+ {
+ if (is_object($needle)) { $needle = $needle->toString(); }
+ return strpos($this->_str, $needle, $pos);
+ }
+
+ /**
+ * Find last occurence of needle in current string.
+ *
+ * Returns the last position of needle in string or *false* if
+ * not found.
+ *
+ * @param mixed needle object implementing toString or php variable.
+ * @return int
+ */
+ function lastIndexOf($needle)
+ {
+ if (is_object($needle)) { $needle = $needle->toString(); }
+ return strrpos($this->_str, $needle);
+ }
+
+ /**
+ * Returns true if the string match the specified regex.
+ *
+ * @param mixed $regex object implementing toString or php string.
+ * @return boolean
+ */
+ function matches($regex)
+ {
+ if (is_object($regex)) { $regex = $regex->toString(); }
+ return preg_match($regex, $this->_str);
+ }
+
+ /**
+ * Returns true if the string contains specified substring.
+ *
+ * @param mixed $str object implementing toString or php string.
+ * @return boolean
+ */
+ function contains($str)
+ {
+ if (is_object($str)){ $str = $str->toString(); }
+ return (strpos($this->_str, $str) !== false);
+ }
+
+ /**
+ * Returns true if the string begins with specified token.
+ *
+ * @param mixed $str object implementing toString or php string.
+ * @return boolean
+ */
+ function startsWith($str)
+ {
+ if (is_object($str)){ $str = $str->toString(); }
+ return preg_match('|^'. preg_quote($str) . '|', $this->_str);
+ }
+
+ /**
+ * Returns true if the string ends with specified token.
+ *
+ * @param mixed $str object implementing toString or php string.
+ * @return boolean
+ */
+ function endsWith($str)
+ {
+ if (is_object($str)){ $str = $str->toString(); }
+ return preg_match('|'. preg_quote($str) . '$|', $this->_str);
+ }
+
+ /**
+ * Replace occurences of 'old' with 'new'.
+ *
+ * @param mixed $old token to replace
+ * @param mixed $new new token
+ * @return string
+ */
+ function replace($old, $new)
+ {
+ if (is_object($old)){ $old = $old->toString(); }
+ if (is_object($new)){ $new = $new->toString(); }
+ return str_replace($old, $new, $this->_str);
+ }
+
+ /**
+ * Split this string using specified separator.
+ *
+ * @param mixed $sep Separator token
+ *
+ * @return array of phpstrings
+ */
+ function split($sep)
+ {
+ if (is_object($sep)){ $sep = $sep->toString(); }
+ return split($sep, $this->_str);
+ }
+
+ /**
+ * Retrieve a sub string.
+ *
+ * @param int $start
+ * start offset
+ *
+ * @param int $end
+ * End offset (up to end if no specified)
+ *
+ * @return string
+ */
+ function substr($start, $end=false)
+ {
+ if ($end === false){ return substr($this->_str, $start); }
+ return substr($this->_str, $start, ($end-$start));
+ }
+
+ /**
+ * Retrieve a sub string giving its length.
+ *
+ * @param int $start
+ * start offset
+ *
+ * @param int $length
+ * length from the start offset
+ *
+ * @return string
+ */
+ function extract($start, $length)
+ {
+ return substr($this->_str, $start, $length);
+ }
+
+ /**
+ * Return this string lower cased.
+ * @return string
+ */
+ function toLowerCase()
+ {
+ return strtolower($this->_str);
+ }
+
+ /**
+ * Return this string upper cased.
+ * @return string
+ */
+ function toUpperCase()
+ {
+ return strtoupper($this->_str);
+ }
+
+ /**
+ * Remove white characters from the beginning and the end of the string.
+ * @return string
+ */
+ function trim()
+ {
+ return trim($this->_str);
+ }
+
+ /**
+ * Test if this string equals specified object.
+ *
+ * @param mixed $o
+ * php string or object implementing the toString() method.
+ * @return boolean
+ */
+ function equals($o)
+ {
+ if (is_object($o)) {
+ return $this->_str == $str->toString();
+ } else {
+ return $this->_str == $o;
+ }
+ }
+
+ /**
+ * Return the length of this string.
+ * @return int
+ */
+ function length()
+ {
+ return strlen($this->_str);
+ }
+
+ /**
+ * Return the php string handled by this object
+ * @return string
+ */
+ function toString()
+ {
+ return $this->_str;
+ }
+
+ /**
+ * Create a string iterator for this String object.
+ *
+ * StringIterators iterate at a character level.
+ *
+ * @return StringIterator
+ */
+ function &getNewIterator()
+ {
+ return new StringIterator($this);
+ }
+}
+
+/**
+ * String buffer class.
+ *
+ * This class represents a buffer that concatenate appended string or objects
+ * into a simple php string.
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class StringBuffer extends OString
+{
+ /**
+ * Append some elements to the buffer with an endl terminator.
+ *
+ * @param mixed ...
+ * List of php variables and/or objects implementing the
+ * toString() method.
+ */
+ function appendln()
+ {
+ $args = func_get_args();
+ foreach ($args as $arg) {
+ if (is_object($arg)) {
+ $this->_str .= $arg->toString();
+ } else {
+ $this->_str .= $arg;
+ }
+ }
+ $this->_str .= endl;
+ }
+}
+
+/**
+ * Iterator for php strings and objects implementing toString method.
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class StringIterator
+{
+ var $_str;
+ var $_index = -1;
+ var $_end = false;
+ var $_value;
+
+ /**
+ * Constructor a string iterator.
+ *
+ * @param mixed $str
+ * String object, string variable, or Object implementing
+ * the toString() method.
+ */
+ function StringIterator(&$str)
+ {
+ $this->__construct($str);
+ }
+
+ /**
+ * Constructor a string iterator.
+ *
+ * @param mixed $str
+ * object implementing toString or php string variable
+ */
+ function __construct(&$str)
+ {
+ if (is_object($str)) {
+ $this->_string = new OString($str->toString());
+ } else if (is_string($str)) {
+ $this->_string = new OString($str);
+ }
+ $this->reset();
+ }
+
+ /**
+ * Reset iterator to the begining of the string.
+ *
+ * If empty string, this iterator assume it is at the end of string.
+ */
+ function reset()
+ {
+ $this->_end = false;
+ $this->_index = 0;
+ if ($this->_string->length() == 0) {
+ $this->_end = true;
+ } else {
+ $this->_value = $this->_string->charAt(0);
+ }
+ }
+
+ /**
+ * Return next character.
+ *
+ * @return char
+ */
+ function next()
+ {
+ if ($this->_end || ++$this->_index >= $this->_string->length()) {
+ $this->_end = true;
+ return null;
+ }
+
+ $this->_value = $this->_string->charAt($this->_index);
+ return $this->_value;
+ }
+
+ /**
+ * Return true if the end of the string is not reached yet.
+ *
+ * @return boolean
+ */
+ function isValid()
+ {
+ return !$this->_end;
+ }
+
+ /**
+ * Retrieve current iterator index.
+ *
+ * @return int
+ */
+ function index()
+ {
+ return $this->_index;
+ }
+
+ /**
+ * Retrieve current iterator value.
+ *
+ * @return char
+ */
+ function value()
+ {
+ return $this->_value;
+ }
+}
+
+?>
--- /dev/null
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+//
+// Copyright (c) 2003 Laurent Bedubourg
+//
+// 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.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
+//
+
+/**
+ * Create a new Ref object referencing specified object or variable.
+ *
+ * Any modification of the source object modify the Refence content, any
+ * modification of the reference obj modify the source object.
+ *
+ * The referenced object can be reached using the reference object using the
+ * 'obj' property.
+ *
+ * <?php
+ *
+ * $a = new Foo();
+ * $ref_a = Ref($a);
+ *
+ * $ref_a->obj->doBar(); // call doBar() on $a
+ *
+ * ?>
+ *
+ * @param mixed $obj -- the object or variable to reference.
+ * @return Ref
+ */
+function ref(&$obj)
+{
+ return new Ref($obj);
+}
+
+/**
+ * Returns true if the object is a Ref object.
+ *
+ * @param mixed $obj -- any variable that must be tested.
+ *
+ * @return boolean
+ */
+function is_ref($obj)
+{
+ return is_object($obj) && get_class($obj) == "ref";
+}
+
+
+/**
+ * Reference class.
+ *
+ * There's a lot to say about references in PHP. The main problem come from the
+ * fact that until ZendEngine2, objects are copied if not referenced.
+ *
+ * This class helps keeping references to objects even while modifying arrays.
+ *
+ * Example :
+ *
+ * If an array stores objects, using array_values will copy its
+ * elements.
+ *
+ * If an array stores Ref objects referencing real objects, array_values copy
+ * Ref objects but these copies still reference the source object.
+ *
+ * Until ZendEngine2, it is safer to use Ref objects in stead of object
+ * references.
+ *
+ * <?php
+ *
+ * $a = new Foo('bar');
+ * $v = new Vector();
+ * $v->push(Ref($a));
+ *
+ * ?>
+ *
+ * Important :
+ *
+ * This object will have no meaning in php 4.4 as PHP will then use references
+ * everywhere objects are involved.
+ *
+ * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
+ */
+class Ref
+{
+ /**
+ * php reference to data.
+ */
+ var $obj;
+
+ /**
+ * Reference constructor.
+ *
+ * Important:
+ *
+ * If $r is a reference, the new reference will just be a copy of the
+ * parameter.
+ *
+ * A Ref cannot reference a Ref.
+ *
+ * @param mixed $r -- the object or data to reference.
+ */
+ function Ref(&$r)
+ {
+ if (is_ref($r)) {
+ $this->obj =& $r->obj;
+ } else {
+ $this->obj =& $r;
+ }
+ }
+
+ /**
+ * Retrieve the type or the referenced variable.
+ *
+ * @return string
+ */
+ function type()
+ {
+ return gettype($this->obj);
+ }
+
+ /**
+ * Return the class name of the referenced variable (if object).
+ *
+ * @return string (empty if not an object)
+ */
+ function getClassName()
+ {
+ return get_class($this->obj);
+ }
+}
+
+?>