├── cli ├── stamp └── example.html ├── Examples └── example_dialog.php ├── .travis.yml ├── StampTemplateEngine ├── html5document.html └── StampTE.php ├── composer.json ├── demo.js ├── README.markdown ├── examples.php ├── Templates └── dialog.html ├── Tools └── Dialog.php ├── license.txt ├── phpdialog.php └── tests.php /cli/stamp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from tck.application import Application 3 | Application.run() 4 | -------------------------------------------------------------------------------- /Examples/example_dialog.php: -------------------------------------------------------------------------------- 1 | '/'], ['OK'=>'/confirm'], [ 6 | [ 'name'=>'email_address', 'type'=>'email', 'placeholder'=>'your@email.net', 'autofocus'=>'autofocus', 'title'=>'please enter your email' ]] ); 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | matrix: 4 | include: 5 | - php: 5.3 6 | dist: precise 7 | - php: 5.4 8 | dist: trusty 9 | - php: 5.5 10 | dist: trusty 11 | - php: 5.6 12 | dist: trusty 13 | - php: 7.0 14 | dist: trusty 15 | - php: 7.1 16 | dist: trusty 17 | - php: 7.2 18 | dist: trusty 19 | 20 | script: php tests.php 21 | -------------------------------------------------------------------------------- /cli/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /StampTemplateEngine/html5document.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | #title?# 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gabordemooij/stamp", 3 | "type": "library", 4 | "description": "Clean and powerful template engine for PHP, transforms HTML into object tree for PHP usage without touching design.", 5 | "keywords": ["template","Template Engine"], 6 | "homepage": "http://github.com/gabordemooij/stamp", 7 | "license": "BSD", 8 | "authors": [ 9 | { 10 | "name": "Gabor de Mooij", 11 | "email": "gabordemooij@gaborsoftware.nl", 12 | "homepage": "http://www.stampte.com", 13 | "role": "Developer" 14 | } 15 | ], 16 | "require": { 17 | "php": ">=5.3.0" 18 | }, 19 | "autoload": { 20 | "files" : [ 21 | "StampTemplateEngine/StampTE.php" 22 | ] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /demo.js: -------------------------------------------------------------------------------- 1 | 2 | //A very simple JS function to replace slots for demo purposes. 3 | function replaceAttr(replace, attrTypes) { 4 | if (typeof(attrTypes) === 'undefined') attrTypes = ['src','value','alt','href','title']; 5 | for(var i in attrTypes) { 6 | var query = '['+attrTypes[i]+']'; 7 | var elements = document.querySelectorAll(query); 8 | for(var j=0; j-1) { 24 | eval('replaceAttr('+elements.item(i).innerHTML+');'); 25 | } 26 | } 27 | })() 28 | 29 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | 2 | 3 | STAMP 4 | ===== 5 | 6 | [![Build Status](https://secure.travis-ci.org/gabordemooij/stamp.png)](http://travis-ci.org/gabordemooij/stamp) 7 | 8 | Stamp is micro template library orignally written by Gabor de Mooij. 9 | 10 | Stamp t.e. is a new kind of Template Engine for PHP. 11 | You don't need to learn a new 12 | template language and you get 100% separation 13 | between presentation logic and your HTML templates. 14 | 15 | How it Works 16 | ------------ 17 | 18 | Stamp t.e. is a string 19 | manipulation based template engine. This is a different 20 | approach from most template engines 21 | which use inline templating. In Stamp t.e. 22 | you set markers in your template (HTML comments), 23 | these are then used to manipulate the template from the outside. 24 | 25 | What does it look like 26 | ---------------------- 27 | 28 | 29 | A cut point maker marks a region in the 30 | template that will be cut out from the template 31 | and stored under the specified ID. 32 | 33 |
34 | 35 | 36 | 37 |
38 | 39 | 40 | 41 | 42 | 43 | Now pass the template to StampTE: 44 | 45 | $se = new StampTE($templateHTML); 46 | 47 | To obtain the diamond image: 48 | 49 | $diamond = $se->getDiamond(); 50 | echo $diamond; 51 | 52 | Result: 53 | 54 | 55 | 56 | 57 | And.. to put some diamonds in the jewellery box: 58 | 59 | 60 | $se->jewellery->add($diamond); 61 | 62 | 63 | Easy! 64 | 65 | Notice November 2025: 66 | the Stampte.com domain no longer works. 67 | I am moving this project to a different site and rename it. 68 | 69 | Advantages 70 | ---------- 71 | 72 | * Clean, code-free HTML templates, No PHP in your HTML 73 | * Compact presentation logic free of any HTML 74 | * No new syntax to learn, uses basic HTML markers already in use by many frontend developers to clarify document structure 75 | * Templates do not have to be converted to be used with PHP logic (toll free template upgrades) 76 | * Templates are presentable before integration because they may contain dummy data which is removed by StampTE 77 | * Easy to exchange templates, templates are ready to use 78 | * Very suitable for advanced UI development and complex templates for games 79 | * Templates become self-documenting, PHP code becomes more readable (less bugs) 80 | * Automatically strips HTML comments 81 | * Integrated caching system 82 | * Automatically escapes strings for Unicode (X)HTML documents 83 | * Just ONE little file 84 | * Unit tested, high quality code 85 | * Open Source, BSD license 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /examples.php: -------------------------------------------------------------------------------- 1 | 23 | PizzaPrice 24 | 25 | 26 | #name##price# 27 | 28 | 29 | 30 | '; 31 | 32 | $data = array( 33 | 'Magaritha' => '6.99', 34 | 'Funghi' => '7.50', 35 | 'Tonno' => '7.99' 36 | ); 37 | 38 | $priceList = new StampTE( $t ); 39 | 40 | $dish = $priceList->getPizza(); 41 | 42 | foreach( $data as $name => $price ) { 43 | $pizza = $dish->copy(); 44 | $pizza 45 | ->setName( $name ) 46 | ->setPrice( $price ); 47 | $priceList->add( $pizza ); 48 | } 49 | 50 | echo $priceList; 51 | 52 | /** 53 | * --------------------------------------------------------------------------- 54 | * Example #2: Building a form, the basics 55 | * --------------------------------------------------------------------------- 56 | */ 57 | 58 | $t = ' 59 |
60 | 61 | 62 | 63 |
64 | '; 65 | 66 | $form = new StampTE( $t ); 67 | 68 | $textField = $form->getTextField(); 69 | $textField 70 | ->setLabel( 'Your Name' ) 71 | ->setName( 'person' ) 72 | ->setValue( 'It\'s me!' ); 73 | 74 | $form->add( $textField ); 75 | echo "\n\n\n".$form; 76 | 77 | /** 78 | * --------------------------------------------------------------------------- 79 | * Example #3: Game, multiple templates 80 | * --------------------------------------------------------------------------- 81 | */ 82 | 83 | $vt = '
'; 84 | $bt = ' 85 |
86 | 87 | 88 | 89 |
90 | '; 91 | 92 | $v = new StampTE( $vt ); 93 | $b = new StampTE( $bt ); 94 | $tavern = $b->getTavern(); 95 | $v->village->add( $tavern ); 96 | 97 | echo "\n\n\n".$v; 98 | 99 | exit; 100 | -------------------------------------------------------------------------------- /Templates/dialog.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | #title# 6 | 7 | 60 | 61 | 62 |
63 |

#title#

64 |

#message#

65 | 66 | 67 | 68 |
69 | 70 | 71 | 72 | 73 | #label# 74 | 75 |
76 | 77 | 78 | -------------------------------------------------------------------------------- /Tools/Dialog.php: -------------------------------------------------------------------------------- 1 | '/'], ['yes'=>'/proceed'] ); 119 | * 120 | * Prompt: 121 | * Dialog::render( 'Question', 'What is your name?', [], ['register'=>'/register'], ['name'=>'myname', 'type'=>'text'] ); 122 | * 123 | * 'Promptfirm': 124 | * Dialog::render( 'Question', 'What is your name?', ['wont tell'=>'/'], ['register'=>'/register'], ['name'=>'myname', 'type'=>'text'] ); 125 | * 126 | * Custom: 127 | * Dialog::render( some other combination ); 128 | * 129 | * Note: 130 | * Although multiple submit buttons are allowed, only the action of 131 | * the last button will be used. 132 | * 133 | * Note: 134 | * For every field element, at least the properties name and type 135 | * need to be specified. 136 | * 137 | * @param string $title title of the dialog box 138 | * @param string $message message to display in the box 139 | * @param array $links a series of links to display (format is: href => label) 140 | * @param array $buttons a series of submit buttons (format is: name => action) 141 | * @param array $fields additional input fields (format is: [ property => value ]) 142 | * 143 | * @return void 144 | */ 145 | public static function render( $title, $message, $links, $buttons=[], $fields=[] ) { 146 | $dialog = new StampTE( self::$template ); 147 | $dialog->setTitle( $title ); 148 | $dialog->setMessage( $message ); 149 | $dialog->setLanguage( self::$language ); 150 | $dialog->setBackgroundColor( self::$backgroundColor ); 151 | $dialog->setForegroundColor( self::$foregroundColor ); 152 | $dialog->setTextColor( self::$textColor ); 153 | $dialog->setFont(self::$font); 154 | $dialog->setHeaderFont(self::$headerFont); 155 | $dialog->setCss(self::$css); 156 | foreach( $buttons as $name => $action ) { 157 | $dialog->setMethod('POST'); 158 | $dialog->setAction($action); 159 | $button = $dialog->getButton(); 160 | $button->setLabel($name); 161 | $button->setName($name); 162 | $dialog->add( $button ); 163 | } 164 | foreach( $links as $label => $href ) { 165 | $link = $dialog->getLink(); 166 | $link->setHref( $href ); 167 | $link->setLabel( $label ); 168 | $dialog->add( $link ); 169 | } 170 | foreach( $fields as $definition ) { 171 | $field = $dialog->getField(); 172 | foreach( $definition as $key => $value ) { 173 | $field->inject( $key, $value ); 174 | } 175 | $dialog->add( $field ); 176 | } 177 | return $dialog; 178 | } 179 | 180 | /** 181 | * Renders and displays a dialog box. 182 | * For more details see Dialog::render(). 183 | * 184 | * @see Dialog::render() 185 | */ 186 | public static function show( $title, $message, $links, $buttons=[], $fields=[] ) { 187 | echo strval( self::render( $title, $message, $links, $buttons, $fields ) ); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Stamp Template Engine 4 | ===================== 5 | 6 | License 7 | ------- 8 | 9 | StampTE Template Engine - 10 | Written by Gabor de Mooij (c) copyright 2019 11 | 12 | StampTE is DUAL Licensed BSD and GPLv2. You may choose the license that fits 13 | best for your project. 14 | 15 | 16 | BSD/GPLv2 License 17 | 18 | Redistribution and use in source and binary forms, with or without 19 | modification, are permitted provided that the following conditions are met: 20 | * Redistributions of source code must retain the above copyright 21 | notice, this list of conditions and the following disclaimer. 22 | * Redistributions in binary form must reproduce the above copyright 23 | notice, this list of conditions and the following disclaimer in the 24 | documentation and/or other materials provided with the distribution. 25 | * Neither the name of StampTE nor the 26 | names of its contributors may be used to endorse or promote products 27 | derived from this software without specific prior written permission. 28 | 29 | 30 | THIS SOFTWARE IS PROVIDED BY GABOR DE MOOIJ ''AS IS'' AND ANY 31 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 32 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 33 | DISCLAIMED. IN NO EVENT SHALL GABOR DE MOOIJ BE LIABLE FOR ANY 34 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 35 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 36 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 37 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 38 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 39 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 40 | 41 | StampTE is Written by Gabor de Mooij (G.J.G.T de Mooij) Copyright (c) 2019. 42 | 43 | 44 | GPLv2 LICENSE 45 | 46 | 47 | GNU GENERAL PUBLIC LICENSE 48 | Version 2, June 1991 49 | 50 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 52 | Everyone is permitted to copy and distribute verbatim copies 53 | of this license document, but changing it is not allowed. 54 | 55 | Preamble 56 | 57 | The licenses for most software are designed to take away your 58 | freedom to share and change it. By contrast, the GNU General Public 59 | License is intended to guarantee your freedom to share and change free 60 | software--to make sure the software is free for all its users. This 61 | General Public License applies to most of the Free Software 62 | Foundation's software and to any other program whose authors commit to 63 | using it. (Some other Free Software Foundation software is covered by 64 | the GNU Lesser General Public License instead.) You can apply it to 65 | your programs, too. 66 | 67 | When we speak of free software, we are referring to freedom, not 68 | price. Our General Public Licenses are designed to make sure that you 69 | have the freedom to distribute copies of free software (and charge for 70 | this service if you wish), that you receive source code or can get it 71 | if you want it, that you can change the software or use pieces of it 72 | in new free programs; and that you know you can do these things. 73 | 74 | To protect your rights, we need to make restrictions that forbid 75 | anyone to deny you these rights or to ask you to surrender the rights. 76 | These restrictions translate to certain responsibilities for you if you 77 | distribute copies of the software, or if you modify it. 78 | 79 | For example, if you distribute copies of such a program, whether 80 | gratis or for a fee, you must give the recipients all the rights that 81 | you have. You must make sure that they, too, receive or can get the 82 | source code. And you must show them these terms so they know their 83 | rights. 84 | 85 | We protect your rights with two steps: (1) copyright the software, and 86 | (2) offer you this license which gives you legal permission to copy, 87 | distribute and/or modify the software. 88 | 89 | Also, for each author's protection and ours, we want to make certain 90 | that everyone understands that there is no warranty for this free 91 | software. If the software is modified by someone else and passed on, we 92 | want its recipients to know that what they have is not the original, so 93 | that any problems introduced by others will not reflect on the original 94 | authors' reputations. 95 | 96 | Finally, any free program is threatened constantly by software 97 | patents. We wish to avoid the danger that redistributors of a free 98 | program will individually obtain patent licenses, in effect making the 99 | program proprietary. To prevent this, we have made it clear that any 100 | patent must be licensed for everyone's free use or not licensed at all. 101 | 102 | The precise terms and conditions for copying, distribution and 103 | modification follow. 104 | 105 | GNU GENERAL PUBLIC LICENSE 106 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 107 | 108 | 0. This License applies to any program or other work which contains 109 | a notice placed by the copyright holder saying it may be distributed 110 | under the terms of this General Public License. The "Program", below, 111 | refers to any such program or work, and a "work based on the Program" 112 | means either the Program or any derivative work under copyright law: 113 | that is to say, a work containing the Program or a portion of it, 114 | either verbatim or with modifications and/or translated into another 115 | language. (Hereinafter, translation is included without limitation in 116 | the term "modification".) Each licensee is addressed as "you". 117 | 118 | Activities other than copying, distribution and modification are not 119 | covered by this License; they are outside its scope. The act of 120 | running the Program is not restricted, and the output from the Program 121 | is covered only if its contents constitute a work based on the 122 | Program (independent of having been made by running the Program). 123 | Whether that is true depends on what the Program does. 124 | 125 | 1. You may copy and distribute verbatim copies of the Program's 126 | source code as you receive it, in any medium, provided that you 127 | conspicuously and appropriately publish on each copy an appropriate 128 | copyright notice and disclaimer of warranty; keep intact all the 129 | notices that refer to this License and to the absence of any warranty; 130 | and give any other recipients of the Program a copy of this License 131 | along with the Program. 132 | 133 | You may charge a fee for the physical act of transferring a copy, and 134 | you may at your option offer warranty protection in exchange for a fee. 135 | 136 | 2. You may modify your copy or copies of the Program or any portion 137 | of it, thus forming a work based on the Program, and copy and 138 | distribute such modifications or work under the terms of Section 1 139 | above, provided that you also meet all of these conditions: 140 | 141 | a) You must cause the modified files to carry prominent notices 142 | stating that you changed the files and the date of any change. 143 | 144 | b) You must cause any work that you distribute or publish, that in 145 | whole or in part contains or is derived from the Program or any 146 | part thereof, to be licensed as a whole at no charge to all third 147 | parties under the terms of this License. 148 | 149 | c) If the modified program normally reads commands interactively 150 | when run, you must cause it, when started running for such 151 | interactive use in the most ordinary way, to print or display an 152 | announcement including an appropriate copyright notice and a 153 | notice that there is no warranty (or else, saying that you provide 154 | a warranty) and that users may redistribute the program under 155 | these conditions, and telling the user how to view a copy of this 156 | License. (Exception: if the Program itself is interactive but 157 | does not normally print such an announcement, your work based on 158 | the Program is not required to print an announcement.) 159 | 160 | These requirements apply to the modified work as a whole. If 161 | identifiable sections of that work are not derived from the Program, 162 | and can be reasonably considered independent and separate works in 163 | themselves, then this License, and its terms, do not apply to those 164 | sections when you distribute them as separate works. But when you 165 | distribute the same sections as part of a whole which is a work based 166 | on the Program, the distribution of the whole must be on the terms of 167 | this License, whose permissions for other licensees extend to the 168 | entire whole, and thus to each and every part regardless of who wrote it. 169 | 170 | Thus, it is not the intent of this section to claim rights or contest 171 | your rights to work written entirely by you; rather, the intent is to 172 | exercise the right to control the distribution of derivative or 173 | collective works based on the Program. 174 | 175 | In addition, mere aggregation of another work not based on the Program 176 | with the Program (or with a work based on the Program) on a volume of 177 | a storage or distribution medium does not bring the other work under 178 | the scope of this License. 179 | 180 | 3. You may copy and distribute the Program (or a work based on it, 181 | under Section 2) in object code or executable form under the terms of 182 | Sections 1 and 2 above provided that you also do one of the following: 183 | 184 | a) Accompany it with the complete corresponding machine-readable 185 | source code, which must be distributed under the terms of Sections 186 | 1 and 2 above on a medium customarily used for software interchange; or, 187 | 188 | b) Accompany it with a written offer, valid for at least three 189 | years, to give any third party, for a charge no more than your 190 | cost of physically performing source distribution, a complete 191 | machine-readable copy of the corresponding source code, to be 192 | distributed under the terms of Sections 1 and 2 above on a medium 193 | customarily used for software interchange; or, 194 | 195 | c) Accompany it with the information you received as to the offer 196 | to distribute corresponding source code. (This alternative is 197 | allowed only for noncommercial distribution and only if you 198 | received the program in object code or executable form with such 199 | an offer, in accord with Subsection b above.) 200 | 201 | The source code for a work means the preferred form of the work for 202 | making modifications to it. For an executable work, complete source 203 | code means all the source code for all modules it contains, plus any 204 | associated interface definition files, plus the scripts used to 205 | control compilation and installation of the executable. However, as a 206 | special exception, the source code distributed need not include 207 | anything that is normally distributed (in either source or binary 208 | form) with the major components (compiler, kernel, and so on) of the 209 | operating system on which the executable runs, unless that component 210 | itself accompanies the executable. 211 | 212 | If distribution of executable or object code is made by offering 213 | access to copy from a designated place, then offering equivalent 214 | access to copy the source code from the same place counts as 215 | distribution of the source code, even though third parties are not 216 | compelled to copy the source along with the object code. 217 | 218 | 4. You may not copy, modify, sublicense, or distribute the Program 219 | except as expressly provided under this License. Any attempt 220 | otherwise to copy, modify, sublicense or distribute the Program is 221 | void, and will automatically terminate your rights under this License. 222 | However, parties who have received copies, or rights, from you under 223 | this License will not have their licenses terminated so long as such 224 | parties remain in full compliance. 225 | 226 | 5. You are not required to accept this License, since you have not 227 | signed it. However, nothing else grants you permission to modify or 228 | distribute the Program or its derivative works. These actions are 229 | prohibited by law if you do not accept this License. Therefore, by 230 | modifying or distributing the Program (or any work based on the 231 | Program), you indicate your acceptance of this License to do so, and 232 | all its terms and conditions for copying, distributing or modifying 233 | the Program or works based on it. 234 | 235 | 6. Each time you redistribute the Program (or any work based on the 236 | Program), the recipient automatically receives a license from the 237 | original licensor to copy, distribute or modify the Program subject to 238 | these terms and conditions. You may not impose any further 239 | restrictions on the recipients' exercise of the rights granted herein. 240 | You are not responsible for enforcing compliance by third parties to 241 | this License. 242 | 243 | 7. If, as a consequence of a court judgment or allegation of patent 244 | infringement or for any other reason (not limited to patent issues), 245 | conditions are imposed on you (whether by court order, agreement or 246 | otherwise) that contradict the conditions of this License, they do not 247 | excuse you from the conditions of this License. If you cannot 248 | distribute so as to satisfy simultaneously your obligations under this 249 | License and any other pertinent obligations, then as a consequence you 250 | may not distribute the Program at all. For example, if a patent 251 | license would not permit royalty-free redistribution of the Program by 252 | all those who receive copies directly or indirectly through you, then 253 | the only way you could satisfy both it and this License would be to 254 | refrain entirely from distribution of the Program. 255 | 256 | If any portion of this section is held invalid or unenforceable under 257 | any particular circumstance, the balance of the section is intended to 258 | apply and the section as a whole is intended to apply in other 259 | circumstances. 260 | 261 | It is not the purpose of this section to induce you to infringe any 262 | patents or other property right claims or to contest validity of any 263 | such claims; this section has the sole purpose of protecting the 264 | integrity of the free software distribution system, which is 265 | implemented by public license practices. Many people have made 266 | generous contributions to the wide range of software distributed 267 | through that system in reliance on consistent application of that 268 | system; it is up to the author/donor to decide if he or she is willing 269 | to distribute software through any other system and a licensee cannot 270 | impose that choice. 271 | 272 | This section is intended to make thoroughly clear what is believed to 273 | be a consequence of the rest of this License. 274 | 275 | 8. If the distribution and/or use of the Program is restricted in 276 | certain countries either by patents or by copyrighted interfaces, the 277 | original copyright holder who places the Program under this License 278 | may add an explicit geographical distribution limitation excluding 279 | those countries, so that distribution is permitted only in or among 280 | countries not thus excluded. In such case, this License incorporates 281 | the limitation as if written in the body of this License. 282 | 283 | 9. The Free Software Foundation may publish revised and/or new versions 284 | of the General Public License from time to time. Such new versions will 285 | be similar in spirit to the present version, but may differ in detail to 286 | address new problems or concerns. 287 | 288 | Each version is given a distinguishing version number. If the Program 289 | specifies a version number of this License which applies to it and "any 290 | later version", you have the option of following the terms and conditions 291 | either of that version or of any later version published by the Free 292 | Software Foundation. If the Program does not specify a version number of 293 | this License, you may choose any version ever published by the Free Software 294 | Foundation. 295 | 296 | 10. If you wish to incorporate parts of the Program into other free 297 | programs whose distribution conditions are different, write to the author 298 | to ask for permission. For software which is copyrighted by the Free 299 | Software Foundation, write to the Free Software Foundation; we sometimes 300 | make exceptions for this. Our decision will be guided by the two goals 301 | of preserving the free status of all derivatives of our free software and 302 | of promoting the sharing and reuse of software generally. 303 | 304 | NO WARRANTY 305 | 306 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 307 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 308 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 309 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 310 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 311 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 312 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 313 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 314 | REPAIR OR CORRECTION. 315 | 316 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 317 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 318 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 319 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 320 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 321 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 322 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 323 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 324 | POSSIBILITY OF SUCH DAMAGES. 325 | 326 | */ 327 | -------------------------------------------------------------------------------- /StampTemplateEngine/StampTE.php: -------------------------------------------------------------------------------- 1 | 141 | * $tpl = StampTE::createHtml5Utf8Document(); 142 | * $tpl->setTitle('Welcome to StampTE'); //set the title. 143 | * $tpl->head->add( $linkTag ); //Add stylesheets and scripts! 144 | * $tpl->body->add( $myDocument ); //Add your body content! 145 | * 146 | * 147 | * @return StampTE template 148 | */ 149 | public static function createHtml5Utf8Document() 150 | { 151 | return self::fromFile( __dir__ . '/html5document.html' ); 152 | } 153 | 154 | /** 155 | * Returns a Stamp instance using the contents of the specified 156 | * file. 157 | * 158 | * @param string $fname path to file to read 159 | * 160 | * @return StampTE 161 | */ 162 | public static function fromFile( $fname ) 163 | { 164 | return new self( file_get_contents( $fname ) ); 165 | } 166 | 167 | /** 168 | * Constructor. Pass nothing if you plan to use cache. 169 | * Creates a new Stamp object from a string. 170 | * Upon constructing a Stamp object the string will be parsed. 171 | * All cut points, i.e. Y will 172 | * be collected and stored in the internal 'sketchbook'. 173 | * They can now be used as HTML snippets like this: 174 | * 175 | * 176 | * $stpl = new StampTE( $tpl ); 177 | * $stpl->getX(); //where X is cut point 178 | * 179 | * 180 | * @param string $tpl HTML Template 181 | * @param string $id identification string for this template 182 | */ 183 | public function __construct( $tpl='', $id='root' ) 184 | { 185 | if ( is_null( $tpl ) ) $tpl = ''; 186 | 187 | if ( is_object( $tpl ) && !method_exists( $tpl, '__toString' ) ) $tpl = '['.get_class( $tpl ).' instance]'; 188 | 189 | $tpl = (string) $tpl; 190 | 191 | $this->id = strval( $id ); 192 | $this->template = $tpl; 193 | $this->matches = array(); 194 | $pattern = '/\s*(.*)?/sU'; 195 | $me = $this; 196 | $slots = &$this->slots; 197 | 198 | $this->template = preg_replace_callback( $pattern, function( $matches ) use ( $me, &$slots ) { 199 | list( , $type, $id, $snippet ) = $matches; 200 | if ( $type === 'cut' ) { 201 | $me->addToSketchBook( $id, $snippet ); 202 | return ''; 203 | } else { 204 | $slots[$id] = TRUE; 205 | return "#$id#"; 206 | } 207 | }, $this->template ); 208 | 209 | $this->template = preg_replace( '/#([^\?\s#]+)(\?)?#/sU', '#&$1$2#', $this->template ); 210 | } 211 | 212 | /** 213 | * Internal method that needs to be public because PHP is too stupid to understand 214 | * $this in closures. 215 | * 216 | * @param string $id 217 | * @param string $snippet 218 | */ 219 | public function addToSketchBook( $id, $snippet ) 220 | { 221 | $this->catalogue[$id] = count( $this->sketchBook ); 222 | $this->sketchBook[] = $snippet; 223 | } 224 | 225 | /** 226 | * Creates an instance of StampTE template using a file. 227 | * 228 | * @param string $filename file containing HTML input 229 | * 230 | * @return static 231 | */ 232 | public static function load( $filename ) 233 | { 234 | if ( !file_exists( $filename ) ) throw new StampTEException( '[S001] Could not find file: ' . $filename ); 235 | $template = file_get_contents( $filename ); 236 | return new static( $template ); 237 | } 238 | 239 | /** 240 | * Checks whether a snippet with ID $id is in the catalogue. 241 | * 242 | * @param string $id identifier you are looking for 243 | * 244 | * @return boolean $yesNo whether the snippet with this ID is available or not. 245 | */ 246 | public function inCatalogue( $id ) 247 | { 248 | return ( boolean ) ( isset( $this->catalogue[$id] ) ); 249 | } 250 | 251 | /** 252 | * Returns a new instance of StampTE configured with the template 253 | * that corresponds to the specified ID. 254 | * 255 | * @param string $id identifier 256 | * 257 | * @return StampTE $snippet 258 | */ 259 | public function get( $id ) 260 | { 261 | if ( strpos( $id, '.') !== FALSE) { 262 | $parts = ( explode( '.', $id ) ); 263 | $id = reset( $parts ); 264 | array_shift( $parts ); 265 | $rest = implode( '.', $parts ); 266 | } 267 | 268 | if ( $this->inCatalogue( $id ) ) { 269 | $snippet = $this->sketchBook[$this->catalogue[$id]]; 270 | if ( $this->factory ) { 271 | $new = call_user_func_array( $this->factory, array( $snippet, $id ) ); 272 | } else { 273 | $new = new static( $snippet, $id ); 274 | } 275 | //Pass the translator and the factory. 276 | $new->translator = $this->translator; 277 | $new->factory = $this->factory; 278 | 279 | if ( isset( $parts ) ) { 280 | return $new->get( $rest ); 281 | } else { 282 | return $new; 283 | } 284 | } else { 285 | throw new StampTEException( '[S101] Cannot find Stamp Snippet with ID '.preg_replace( '/\W/', '', $id ) ); 286 | } 287 | } 288 | 289 | /** 290 | * Collects snippets from the template. 291 | * $list needs to be a | pipe separated list of snippet IDs. The snippets 292 | * will be returned as an array so you can obtain them using the list() 293 | * statement. 294 | * 295 | * @param string $list List of IDs you want to fetch from template 296 | * 297 | * @return array $snippets Snippets obtained from template 298 | */ 299 | public function collect( $list ) 300 | { 301 | if ( isset( $this->cache[$list] ) ) return $this->cache[$list]; 302 | 303 | $listItems = explode( '|', $list ); 304 | 305 | $collection = array(); 306 | foreach( $listItems as $item ) { 307 | $collection[] = $this->get( $item ); 308 | } 309 | 310 | return $collection; 311 | } 312 | 313 | /** 314 | * Returns a snippet/template as a string. Besides converting the instance 315 | * to a string this function removes all HTML comments and unnecessary space. 316 | * If you don't want this use a different toString method like ->getString() 317 | * 318 | * @return string $string string representation of HTML snippet/template 319 | */ 320 | public function __toString() 321 | { 322 | $template = $this->template; 323 | $template = preg_replace( "/\s*/m", "", $template ); 324 | 325 | if ( strpos($template, '#&' ) !== FALSE ) { 326 | $template = preg_replace( "/data\-stampte=\"#\&\w+\?#\"/m", "", $template ); 327 | $template = preg_replace( "/#\&\w+\?#/m", "", $template ); 328 | } 329 | 330 | if ( self::$clearws ) $template = preg_replace( '/\s*/m', '', $template ); 331 | return $template; 332 | } 333 | 334 | /** 335 | * Returns the template as a string. 336 | * 337 | * @return string $raw raw template 338 | */ 339 | public function getString() { 340 | return $this->template; 341 | } 342 | 343 | /** 344 | * Glues a snippet to a glue point in the current snippet/template. 345 | * The glue() method also accepts raw strings. 346 | * 347 | * The glue method will append the Stamp object or string specified in 348 | * $snippet to the template at the glue point marked by the glue point marker, 349 | * i.e. where X is the name of the glue point. 350 | * A glue point can have conditions, in this case you MUST provide a Stamp 351 | * object rather than a raw string because the ID of the object needs to be checked. 352 | * Conditional glue points have the format: where Y,Z are the 353 | * allowed IDS (from the cut markers). 354 | * 355 | * Note that conditional glue points are rather slow. Consider writing a small shell 356 | * script to remove the conditions before deploying your templates to a production 357 | * environment (assuming they're not needed there). 358 | * 359 | * If you pass a raw string for a conditional glue point you'll get a S003 exception. 360 | * If your Stamp object is rejected by the glue point you'll get a S102 exception. 361 | * 362 | * @throws StampTEException 363 | * 364 | * @param string $what ID of the Glue Point you want to append the contents of the snippet to. 365 | * @param StampTE|string $snippet a StampTE snippet/template to be glued at this point 366 | * 367 | * @return StampTE $snippet self, chainable 368 | */ 369 | public function glue( $what, $snippet ) 370 | { 371 | $matches = array(); 372 | 373 | $pattern = ""; 377 | $clear = (self::$clearws) ? '' : ''; 378 | $replacement = $clear.$snippet.$pattern; 379 | $this->template = str_replace( $pattern, $replacement, $this->template ); 380 | return $this; 381 | } 382 | 383 | $pattern = '/\s*/u'; 384 | 385 | $this->template = preg_replace_callback( $pattern, function( $matches ) use ( $snippet, $what ) { 386 | $copyOrig = $matches[0]; 387 | 388 | if ( isset($matches[2]) ) { 389 | 390 | if ( !is_object( $snippet ) ) throw new StampTEException( '[S003] Snippet is not an object or string. Conditional glue point requires object.' ); 391 | 392 | $allowedSnippets = $matches[2]; 393 | $allowedMap = array_flip( explode( ',', $allowedSnippets ) ); 394 | if ( !isset( $allowedMap[$snippet->getID()] ) ) throw new StampTEException( '[S102] Snippet '.$snippet->getID().' not allowed in slot '.$what ); 395 | } 396 | 397 | return $snippet.$copyOrig; 398 | 399 | }, $this->template ); 400 | 401 | return $this; 402 | } 403 | 404 | /** 405 | * Glues all elements in the specified array. 406 | * This is a quick way to glue multiple elements as well. 407 | * 408 | * @param array $map list of key=>value pairs to glue 409 | * 410 | * @return StampTE $snippet self, chainable 411 | */ 412 | public function glueAll( $map ) 413 | { 414 | foreach( $map as $slot => $value ) { 415 | if ( is_array( $value ) ) { 416 | foreach( $value as $item ) { 417 | $this->glue( $slot, $item ); 418 | } 419 | } else { 420 | $this->glue( $slot, $value ); 421 | } 422 | } 423 | return $this; 424 | } 425 | 426 | /** 427 | * Injects a piece of data into the slot marker in the snippet/template. 428 | * 429 | * @param string $where ID of the slot where to inject the data 430 | * @param string $data the data to inject in the slot 431 | * @param boolean $raw if TRUE output will not be escaped 432 | * 433 | * @return StampTE $snippet self, chainable 434 | */ 435 | public function inject( $slot, $data, $raw = FALSE ) 436 | { 437 | if ( !$raw ) $data = $this->filter( $data ); 438 | 439 | $where = "#&$slot#"; 440 | $whereOpt = "#&$slot?#"; 441 | 442 | $this->template = str_replace( $where, $data, $this->template ); 443 | $this->template = str_replace( $whereOpt, $data, $this->template ); 444 | 445 | return $this; 446 | } 447 | 448 | /** 449 | * Injects a piece of data into an attribute slot marker in the snippet/template. 450 | * 451 | * @param string $slot name of the slot where the data should be injected 452 | * @param string $data the data to be injected in the slot 453 | * @param boolean $raw if TRUE output will not be escaped 454 | * 455 | * @return StampTE 456 | */ 457 | public function injectAttr( $slot, $data, $raw = FALSE ) 458 | { 459 | if ( !$raw ) $data = $this->filter( $data ); 460 | 461 | $where = "data-stampte=\"#&$slot#\""; 462 | $whereOpt = "data-stampte=\"#&$slot?#\""; 463 | 464 | $this->template = str_replace( $where, $data, $this->template ); 465 | $this->template = str_replace( $whereOpt, $data, $this->template ); 466 | 467 | return $this; 468 | } 469 | 470 | /** 471 | * Alias for inject($where,$data,TRUE) 472 | * 473 | * @param string $where ID of the slot where to inject the data 474 | * @param string $data the data to inject in the slot 475 | * 476 | * @return StampTE $snippet self, chainable 477 | */ 478 | public function injectRaw( $where, $data ) 479 | { 480 | return $this->inject( $where, $data, TRUE ); 481 | } 482 | 483 | /** 484 | * Same as inject() but injects an entire array of slot->data pairs. 485 | * 486 | * @param array $array Array of slot->data pairs 487 | * @param boolean $raw if TRUE output will not be escaped 488 | * 489 | * @return StampTE self, chainable 490 | */ 491 | public function injectAll( $array, $raw = FALSE ) 492 | { 493 | foreach( $array as $key => $value ) { 494 | $this->inject( $key, $value, $raw ); 495 | } 496 | return $this; 497 | } 498 | 499 | /** 500 | * Returns the identifier of the current snippet/template. 501 | * 502 | * @return string $id ID of this snippet/template 503 | */ 504 | public function getID() 505 | { 506 | return $this->id; 507 | } 508 | 509 | /** 510 | * Copies the current snippet/template. 511 | * 512 | * @return StampTE $copy Copy of the current template/snippet 513 | */ 514 | public function copy() 515 | { 516 | return clone( $this ); 517 | } 518 | 519 | /** 520 | * Collects a list, just like collect() but stores result in cache array. 521 | * 522 | * @param string $list Pipe separated list of IDs. 523 | * 524 | * @return self 525 | */ 526 | public function writeToCache( $list ) 527 | { 528 | $this->cache[$list] = $this->collect( $list ); 529 | return $this; 530 | } 531 | 532 | /** 533 | * Returns the cache object for storage to disk. 534 | * 535 | * @return string $cache serialized cache object. 536 | */ 537 | public function getCache() 538 | { 539 | return serialize( $this->cache ); 540 | } 541 | 542 | /** 543 | * Loads cache data. 544 | * 545 | * @param string $rawCacheData the serialized cached string as retrieved from getCache(). 546 | * 547 | * @return self 548 | */ 549 | public function loadIntoCache( $rawCacheData ) 550 | { 551 | $this->cache = unserialize( $rawCacheData ); 552 | 553 | if ( !is_array( $this->cache ) ) throw new StampTEException( '[S004] Failed to unserialize cache object.' ); 554 | 555 | return $this; 556 | } 557 | 558 | /** 559 | * Filters data. 560 | * 561 | * @param string $string 562 | * 563 | * @return string $string 564 | */ 565 | protected function filter( $data ) 566 | { 567 | $data = iconv("UTF-8","UTF-8//IGNORE", $data); 568 | $filtered = htmlspecialchars( $data, ENT_QUOTES, 'UTF-8' ); 569 | $filtered = str_replace( '`', '`', $filtered ); //Prevent MSIE backtick XSS hack 570 | return $filtered; 571 | } 572 | 573 | /** 574 | * Selects a Glue Point to attach a Stamp to. 575 | * Note that although this seems like a getter this method 576 | * actually returns the same StampTE. It's both evil and beautiful at the same time. 577 | * 578 | * @param string $gluePoint 579 | * 580 | * @return StampTE 581 | */ 582 | public function &__get( $gluePoint ) 583 | { 584 | $this->select = $gluePoint; 585 | return $this; 586 | } 587 | 588 | /** 589 | * Call interceptor. 590 | * Intercepts: 591 | * - getX(), routes to get('X') 592 | * - setX(Y), routes to inject('X',Y) 593 | */ 594 | public function __call($method, $arguments) 595 | { 596 | if ( strpos( $method, 'get' ) === 0 ) { 597 | return $this->get( lcfirst( substr( $method, 3) ) ); 598 | } elseif ( strpos( $method, 'set' ) === 0 ) { 599 | $this->inject( lcfirst( substr( $method, 3 ) ), $arguments[0] ); 600 | return $this; 601 | } elseif ( strpos( $method, 'say' ) ===0 ) { 602 | $this->inject( lcfirst( substr( $method, 3) ), call_user_func_array( $this->translator, $arguments ) ); 603 | return $this; 604 | } 605 | } 606 | 607 | /** 608 | * Glues the specified Stamp object to the currently selected 609 | * Glue Point. 610 | * 611 | * @param StampTE $stamp 612 | */ 613 | public function add( StampTE $stamp ) 614 | { 615 | if ( $this->select === NULL ) { 616 | $this->select = 'self'.$stamp->getID(); 617 | } 618 | $this->glue( $this->select, $stamp ); 619 | $this->select = NULL; //reset 620 | return $this; 621 | } 622 | 623 | /** 624 | * Sets the translator function to be used for translations. 625 | * The translator function will be called automatically as soon as you invoke the magic 626 | * method: 627 | * 628 | * sayX(Y) where X is the slot you want to inject the contents of Y into. 629 | * 630 | * Note that optional arguments are allowed and passed to the translator. 631 | * 632 | * @param closure $closure 633 | */ 634 | public function setTranslator( $closure ) 635 | { 636 | if ( !is_callable( $closure ) ) throw new StampTEException( '[S005] Invalid Translator. Translator must be callable.' ); 637 | 638 | $this->translator = $closure; 639 | } 640 | 641 | /** 642 | * Sets the object factory. If get(X) gets called StampTE will call the 643 | * factory with template and ID for X to give you the opportunity to 644 | * wrap the template object in your own wrapper class. 645 | * 646 | * @param closure $factory 647 | */ 648 | public function setFactory( $factory ) 649 | { 650 | if ( !is_callable( $factory ) ) throw new StampTEException( '[S006] Invalid Factory. Factory must be callable.' ); 651 | 652 | $this->factory = $factory; 653 | } 654 | 655 | /** 656 | * Attr is a shortcut to quickly set an attribute. 657 | * 658 | * @param string $slot slot 659 | * @param boolean $onOff whether to fill in the slot or not 660 | * 661 | * @return StampTE 662 | */ 663 | public function attr( $slot, $onOff = TRUE ) 664 | { 665 | return ( $onOff ) ? $this->injectAttr( $slot, $slot ) : $this; 666 | } 667 | } 668 | //Stamp Exception 669 | class StampTEException extends \Exception {} 670 | -------------------------------------------------------------------------------- /phpdialog.php: -------------------------------------------------------------------------------- 1 | 141 | * $tpl = StampTE::createHtml5Utf8Document(); 142 | * $tpl->setTitle('Welcome to StampTE'); //set the title. 143 | * $tpl->head->add( $linkTag ); //Add stylesheets and scripts! 144 | * $tpl->body->add( $myDocument ); //Add your body content! 145 | * 146 | * 147 | * @return StampTE template 148 | */ 149 | public static function createHtml5Utf8Document() 150 | { 151 | return self::fromFile( __dir__ . '/html5document.html' ); 152 | } 153 | 154 | /** 155 | * Returns a Stamp instance using the contents of the specified 156 | * file. 157 | * 158 | * @param string $fname path to file to read 159 | * 160 | * @return StampTE 161 | */ 162 | public static function fromFile( $fname ) 163 | { 164 | return new self( file_get_contents( $fname ) ); 165 | } 166 | 167 | /** 168 | * Constructor. Pass nothing if you plan to use cache. 169 | * Creates a new Stamp object from a string. 170 | * Upon constructing a Stamp object the string will be parsed. 171 | * All cut points, i.e. Y will 172 | * be collected and stored in the internal 'sketchbook'. 173 | * They can now be used as HTML snippets like this: 174 | * 175 | * 176 | * $stpl = new StampTE( $tpl ); 177 | * $stpl->getX(); //where X is cut point 178 | * 179 | * 180 | * @param string $tpl HTML Template 181 | * @param string $id identification string for this template 182 | */ 183 | public function __construct( $tpl='', $id='root' ) 184 | { 185 | if ( is_null( $tpl ) ) $tpl = ''; 186 | 187 | if ( is_object( $tpl ) && !method_exists( $tpl, '__toString' ) ) $tpl = '['.get_class( $tpl ).' instance]'; 188 | 189 | $tpl = (string) $tpl; 190 | 191 | $this->id = strval( $id ); 192 | $this->template = $tpl; 193 | $this->matches = array(); 194 | $pattern = '/\s*(.*)?/sU'; 195 | $me = $this; 196 | $slots = &$this->slots; 197 | 198 | $this->template = preg_replace_callback( $pattern, function( $matches ) use ( $me, &$slots ) { 199 | list( , $type, $id, $snippet ) = $matches; 200 | if ( $type === 'cut' ) { 201 | $me->addToSketchBook( $id, $snippet ); 202 | return ''; 203 | } else { 204 | $slots[$id] = TRUE; 205 | return "#$id#"; 206 | } 207 | }, $this->template ); 208 | 209 | $this->template = preg_replace( '/#([^\?\s#]+)(\?)?#/sU', '#&$1$2#', $this->template ); 210 | } 211 | 212 | /** 213 | * Internal method that needs to be public because PHP is too stupid to understand 214 | * $this in closures. 215 | * 216 | * @param string $id 217 | * @param string $snippet 218 | */ 219 | public function addToSketchBook( $id, $snippet ) 220 | { 221 | $this->catalogue[$id] = count( $this->sketchBook ); 222 | $this->sketchBook[] = $snippet; 223 | } 224 | 225 | /** 226 | * Creates an instance of StampTE template using a file. 227 | * 228 | * @param string $filename file containing HTML input 229 | * 230 | * @return static 231 | */ 232 | public static function load( $filename ) 233 | { 234 | if ( !file_exists( $filename ) ) throw new StampTEException( '[S001] Could not find file: ' . $filename ); 235 | $template = file_get_contents( $filename ); 236 | return new static( $template ); 237 | } 238 | 239 | /** 240 | * Checks whether a snippet with ID $id is in the catalogue. 241 | * 242 | * @param string $id identifier you are looking for 243 | * 244 | * @return boolean $yesNo whether the snippet with this ID is available or not. 245 | */ 246 | public function inCatalogue( $id ) 247 | { 248 | return ( boolean ) ( isset( $this->catalogue[$id] ) ); 249 | } 250 | 251 | /** 252 | * Returns a new instance of StampTE configured with the template 253 | * that corresponds to the specified ID. 254 | * 255 | * @param string $id identifier 256 | * 257 | * @return StampTE $snippet 258 | */ 259 | public function get( $id ) 260 | { 261 | if ( strpos( $id, '.') !== FALSE) { 262 | $parts = ( explode( '.', $id ) ); 263 | $id = reset( $parts ); 264 | array_shift( $parts ); 265 | $rest = implode( '.', $parts ); 266 | } 267 | 268 | if ( $this->inCatalogue( $id ) ) { 269 | $snippet = $this->sketchBook[$this->catalogue[$id]]; 270 | if ( $this->factory ) { 271 | $new = call_user_func_array( $this->factory, array( $snippet, $id ) ); 272 | } else { 273 | $new = new static( $snippet, $id ); 274 | } 275 | //Pass the translator and the factory. 276 | $new->translator = $this->translator; 277 | $new->factory = $this->factory; 278 | 279 | if ( isset( $parts ) ) { 280 | return $new->get( $rest ); 281 | } else { 282 | return $new; 283 | } 284 | } else { 285 | throw new StampTEException( '[S101] Cannot find Stamp Snippet with ID '.preg_replace( '/\W/', '', $id ) ); 286 | } 287 | } 288 | 289 | /** 290 | * Collects snippets from the template. 291 | * $list needs to be a | pipe separated list of snippet IDs. The snippets 292 | * will be returned as an array so you can obtain them using the list() 293 | * statement. 294 | * 295 | * @param string $list List of IDs you want to fetch from template 296 | * 297 | * @return array $snippets Snippets obtained from template 298 | */ 299 | public function collect( $list ) 300 | { 301 | if ( isset( $this->cache[$list] ) ) return $this->cache[$list]; 302 | 303 | $listItems = explode( '|', $list ); 304 | 305 | $collection = array(); 306 | foreach( $listItems as $item ) { 307 | $collection[] = $this->get( $item ); 308 | } 309 | 310 | return $collection; 311 | } 312 | 313 | /** 314 | * Returns a snippet/template as a string. Besides converting the instance 315 | * to a string this function removes all HTML comments and unnecessary space. 316 | * If you don't want this use a different toString method like ->getString() 317 | * 318 | * @return string $string string representation of HTML snippet/template 319 | */ 320 | public function __toString() 321 | { 322 | $template = $this->template; 323 | $template = preg_replace( "/\s*/m", "", $template ); 324 | 325 | if ( strpos($template, '#&' ) !== FALSE ) { 326 | $template = preg_replace( "/data\-stampte=\"#\&\w+\?#\"/m", "", $template ); 327 | $template = preg_replace( "/#\&\w+\?#/m", "", $template ); 328 | } 329 | 330 | if ( self::$clearws ) $template = preg_replace( '/\s*/m', '', $template ); 331 | return $template; 332 | } 333 | 334 | /** 335 | * Returns the template as a string. 336 | * 337 | * @return string $raw raw template 338 | */ 339 | public function getString() { 340 | return $this->template; 341 | } 342 | 343 | /** 344 | * Glues a snippet to a glue point in the current snippet/template. 345 | * The glue() method also accepts raw strings. 346 | * 347 | * The glue method will append the Stamp object or string specified in 348 | * $snippet to the template at the glue point marked by the glue point marker, 349 | * i.e. where X is the name of the glue point. 350 | * A glue point can have conditions, in this case you MUST provide a Stamp 351 | * object rather than a raw string because the ID of the object needs to be checked. 352 | * Conditional glue points have the format: where Y,Z are the 353 | * allowed IDS (from the cut markers). 354 | * 355 | * Note that conditional glue points are rather slow. Consider writing a small shell 356 | * script to remove the conditions before deploying your templates to a production 357 | * environment (assuming they're not needed there). 358 | * 359 | * If you pass a raw string for a conditional glue point you'll get a S003 exception. 360 | * If your Stamp object is rejected by the glue point you'll get a S102 exception. 361 | * 362 | * @throws StampTEException 363 | * 364 | * @param string $what ID of the Glue Point you want to append the contents of the snippet to. 365 | * @param StampTE|string $snippet a StampTE snippet/template to be glued at this point 366 | * 367 | * @return StampTE $snippet self, chainable 368 | */ 369 | public function glue( $what, $snippet ) 370 | { 371 | $matches = array(); 372 | 373 | $pattern = ""; 377 | $clear = (self::$clearws) ? '' : ''; 378 | $replacement = $clear.$snippet.$pattern; 379 | $this->template = str_replace( $pattern, $replacement, $this->template ); 380 | return $this; 381 | } 382 | 383 | $pattern = '/\s*/u'; 384 | 385 | $this->template = preg_replace_callback( $pattern, function( $matches ) use ( $snippet, $what ) { 386 | $copyOrig = $matches[0]; 387 | 388 | if ( isset($matches[2]) ) { 389 | 390 | if ( !is_object( $snippet ) ) throw new StampTEException( '[S003] Snippet is not an object or string. Conditional glue point requires object.' ); 391 | 392 | $allowedSnippets = $matches[2]; 393 | $allowedMap = array_flip( explode( ',', $allowedSnippets ) ); 394 | if ( !isset( $allowedMap[$snippet->getID()] ) ) throw new StampTEException( '[S102] Snippet '.$snippet->getID().' not allowed in slot '.$what ); 395 | } 396 | 397 | return $snippet.$copyOrig; 398 | 399 | }, $this->template ); 400 | 401 | return $this; 402 | } 403 | 404 | /** 405 | * Glues all elements in the specified array. 406 | * This is a quick way to glue multiple elements as well. 407 | * 408 | * @param array $map list of key=>value pairs to glue 409 | * 410 | * @return StampTE $snippet self, chainable 411 | */ 412 | public function glueAll( $map ) 413 | { 414 | foreach( $map as $slot => $value ) { 415 | if ( is_array( $value ) ) { 416 | foreach( $value as $item ) { 417 | $this->glue( $slot, $item ); 418 | } 419 | } else { 420 | $this->glue( $slot, $value ); 421 | } 422 | } 423 | return $this; 424 | } 425 | 426 | /** 427 | * Injects a piece of data into the slot marker in the snippet/template. 428 | * 429 | * @param string $where ID of the slot where to inject the data 430 | * @param string $data the data to inject in the slot 431 | * @param boolean $raw if TRUE output will not be escaped 432 | * 433 | * @return StampTE $snippet self, chainable 434 | */ 435 | public function inject( $slot, $data, $raw = FALSE ) 436 | { 437 | if ( !$raw ) $data = $this->filter( $data ); 438 | 439 | $where = "#&$slot#"; 440 | $whereOpt = "#&$slot?#"; 441 | 442 | $this->template = str_replace( $where, $data, $this->template ); 443 | $this->template = str_replace( $whereOpt, $data, $this->template ); 444 | 445 | return $this; 446 | } 447 | 448 | /** 449 | * Injects a piece of data into an attribute slot marker in the snippet/template. 450 | * 451 | * @param string $slot name of the slot where the data should be injected 452 | * @param string $data the data to be injected in the slot 453 | * @param boolean $raw if TRUE output will not be escaped 454 | * 455 | * @return StampTE 456 | */ 457 | public function injectAttr( $slot, $data, $raw = FALSE ) 458 | { 459 | if ( !$raw ) $data = $this->filter( $data ); 460 | 461 | $where = "data-stampte=\"#&$slot#\""; 462 | $whereOpt = "data-stampte=\"#&$slot?#\""; 463 | 464 | $this->template = str_replace( $where, $data, $this->template ); 465 | $this->template = str_replace( $whereOpt, $data, $this->template ); 466 | 467 | return $this; 468 | } 469 | 470 | /** 471 | * Alias for inject($where,$data,TRUE) 472 | * 473 | * @param string $where ID of the slot where to inject the data 474 | * @param string $data the data to inject in the slot 475 | * 476 | * @return StampTE $snippet self, chainable 477 | */ 478 | public function injectRaw( $where, $data ) 479 | { 480 | return $this->inject( $where, $data, TRUE ); 481 | } 482 | 483 | /** 484 | * Same as inject() but injects an entire array of slot->data pairs. 485 | * 486 | * @param array $array Array of slot->data pairs 487 | * @param boolean $raw if TRUE output will not be escaped 488 | * 489 | * @return StampTE self, chainable 490 | */ 491 | public function injectAll( $array, $raw = FALSE ) 492 | { 493 | foreach( $array as $key => $value ) { 494 | $this->inject( $key, $value, $raw ); 495 | } 496 | return $this; 497 | } 498 | 499 | /** 500 | * Returns the identifier of the current snippet/template. 501 | * 502 | * @return string $id ID of this snippet/template 503 | */ 504 | public function getID() 505 | { 506 | return $this->id; 507 | } 508 | 509 | /** 510 | * Copies the current snippet/template. 511 | * 512 | * @return StampTE $copy Copy of the current template/snippet 513 | */ 514 | public function copy() 515 | { 516 | return clone( $this ); 517 | } 518 | 519 | /** 520 | * Collects a list, just like collect() but stores result in cache array. 521 | * 522 | * @param string $list Pipe separated list of IDs. 523 | * 524 | * @return self 525 | */ 526 | public function writeToCache( $list ) 527 | { 528 | $this->cache[$list] = $this->collect( $list ); 529 | return $this; 530 | } 531 | 532 | /** 533 | * Returns the cache object for storage to disk. 534 | * 535 | * @return string $cache serialized cache object. 536 | */ 537 | public function getCache() 538 | { 539 | return serialize( $this->cache ); 540 | } 541 | 542 | /** 543 | * Loads cache data. 544 | * 545 | * @param string $rawCacheData the serialized cached string as retrieved from getCache(). 546 | * 547 | * @return self 548 | */ 549 | public function loadIntoCache( $rawCacheData ) 550 | { 551 | $this->cache = unserialize( $rawCacheData ); 552 | 553 | if ( !is_array( $this->cache ) ) throw new StampTEException( '[S004] Failed to unserialize cache object.' ); 554 | 555 | return $this; 556 | } 557 | 558 | /** 559 | * Filters data. 560 | * 561 | * @param string $string 562 | * 563 | * @return string $string 564 | */ 565 | protected function filter( $data ) 566 | { 567 | $data = iconv("UTF-8","UTF-8//IGNORE", $data); 568 | $filtered = htmlspecialchars( $data, ENT_QUOTES, 'UTF-8' ); 569 | $filtered = str_replace( '`', '`', $filtered ); //Prevent MSIE backtick XSS hack 570 | return $filtered; 571 | } 572 | 573 | /** 574 | * Selects a Glue Point to attach a Stamp to. 575 | * Note that although this seems like a getter this method 576 | * actually returns the same StampTE. It's both evil and beautiful at the same time. 577 | * 578 | * @param string $gluePoint 579 | * 580 | * @return StampTE 581 | */ 582 | public function &__get( $gluePoint ) 583 | { 584 | $this->select = $gluePoint; 585 | return $this; 586 | } 587 | 588 | /** 589 | * Call interceptor. 590 | * Intercepts: 591 | * - getX(), routes to get('X') 592 | * - setX(Y), routes to inject('X',Y) 593 | */ 594 | public function __call($method, $arguments) 595 | { 596 | if ( strpos( $method, 'get' ) === 0 ) { 597 | return $this->get( lcfirst( substr( $method, 3) ) ); 598 | } elseif ( strpos( $method, 'set' ) === 0 ) { 599 | $this->inject( lcfirst( substr( $method, 3 ) ), $arguments[0] ); 600 | return $this; 601 | } elseif ( strpos( $method, 'say' ) ===0 ) { 602 | $this->inject( lcfirst( substr( $method, 3) ), call_user_func_array( $this->translator, $arguments ) ); 603 | return $this; 604 | } 605 | } 606 | 607 | /** 608 | * Glues the specified Stamp object to the currently selected 609 | * Glue Point. 610 | * 611 | * @param StampTE $stamp 612 | */ 613 | public function add( StampTE $stamp ) 614 | { 615 | if ( $this->select === NULL ) { 616 | $this->select = 'self'.$stamp->getID(); 617 | } 618 | $this->glue( $this->select, $stamp ); 619 | $this->select = NULL; //reset 620 | return $this; 621 | } 622 | 623 | /** 624 | * Sets the translator function to be used for translations. 625 | * The translator function will be called automatically as soon as you invoke the magic 626 | * method: 627 | * 628 | * sayX(Y) where X is the slot you want to inject the contents of Y into. 629 | * 630 | * Note that optional arguments are allowed and passed to the translator. 631 | * 632 | * @param closure $closure 633 | */ 634 | public function setTranslator( $closure ) 635 | { 636 | if ( !is_callable( $closure ) ) throw new StampTEException( '[S005] Invalid Translator. Translator must be callable.' ); 637 | 638 | $this->translator = $closure; 639 | } 640 | 641 | /** 642 | * Sets the object factory. If get(X) gets called StampTE will call the 643 | * factory with template and ID for X to give you the opportunity to 644 | * wrap the template object in your own wrapper class. 645 | * 646 | * @param closure $factory 647 | */ 648 | public function setFactory( $factory ) 649 | { 650 | if ( !is_callable( $factory ) ) throw new StampTEException( '[S006] Invalid Factory. Factory must be callable.' ); 651 | 652 | $this->factory = $factory; 653 | } 654 | 655 | /** 656 | * Attr is a shortcut to quickly set an attribute. 657 | * 658 | * @param string $slot slot 659 | * @param boolean $onOff whether to fill in the slot or not 660 | * 661 | * @return StampTE 662 | */ 663 | public function attr( $slot, $onOff = TRUE ) 664 | { 665 | return ( $onOff ) ? $this->injectAttr( $slot, $slot ) : $this; 666 | } 667 | } 668 | //Stamp Exception 669 | class StampTEException extends \Exception {} 670 | } 671 | 672 | namespace PHPDialog { 673 | 674 | use StampTemplateEngine\StampTE; 675 | 676 | /** 677 | * Dialog class. 678 | * 679 | * PHPDialog is a PHP library to display dialog-like HTML documents 680 | * with just one function call: 681 | * 682 | * Usage: 683 | * Dialog::show( 'Hello', 'World' ); 684 | * 685 | */ 686 | class Dialog { 687 | 688 | private static $template = << 690 | 691 | 692 | 693 | #title# 694 | 695 | 748 | 749 | 750 |
751 |

#title#

752 |

#message#

753 | 754 | 755 | 756 |
757 | 758 | 759 | 760 | 761 | #label# 762 | 763 |
764 | 765 | 766 | 767 | HTML 768 | ; 769 | private static $language = 'en'; 770 | private static $backgroundColor = '#ccc'; 771 | private static $foregroundColor = '#fff'; 772 | private static $textColor = '#444'; 773 | private static $font = '18px sans-serif'; 774 | private static $headerFont = '40px Arial'; 775 | private static $css = ''; 776 | 777 | /** 778 | * Sets the template to be used by the Dialog class 779 | * to generate dialog-like HTML documents. 780 | * If you use the all-in-one file, by default the 781 | * 'onboard' template will be used. 782 | * 783 | * @param string $templateParam template (full HTML StampTE template) 784 | * 785 | * @return void 786 | */ 787 | public static function setTemplate( $templateParam ) { 788 | self::$template = $templateParam; 789 | } 790 | 791 | /** 792 | * Sets the language to be used by the Dialog class 793 | * to generate dialog-like HTML documents. 794 | * The default is: 'en'. 795 | * 796 | * @param string $languageParam language code 797 | * 798 | * @return void 799 | */ 800 | public static function setLanguage( $languageParam ) { 801 | self::$language = $languageParam; 802 | } 803 | 804 | /** 805 | * Sets the colors to be used by the Dialog class 806 | * to generate dialog-like HTML documents. 807 | * The defaults are: #ccc #fff #444. 808 | * 809 | * Note: 810 | * RGB(A) notations are also allowed. 811 | * 812 | * @param string $backgroundColor color code 813 | * @param string $foregroundColor color code 814 | * @param string $textColor color code 815 | * 816 | * @return void 817 | */ 818 | public static function colors( $backgroundColor, $foregroundColor = '#fff', $textColor = '#000' ) { 819 | self::$backgroundColor = $backgroundColor; 820 | self::$foregroundColor = $foregroundColor; 821 | self::$textColor = $textColor; 822 | } 823 | 824 | /** 825 | * Sets the theme to be used by the Dialog class 826 | * to generate dialog-like HTML documents. 827 | * The theme consists of the text font and the 828 | * header/title font. 829 | * 830 | * Defaults: 831 | * '18px sans-serif' for text 832 | * '40px Arial' for header 833 | * 834 | * @param string $font font code 835 | * @param string $headerFont font code 836 | * 837 | * @return void 838 | */ 839 | public static function theme( $font, $headerFont = null ) { 840 | if ( !is_null( $font ) ) { 841 | self::$font = $font; 842 | } 843 | if ( !is_null( $headerFont ) ) { 844 | self::$headerFont = $headerFont; 845 | } 846 | } 847 | 848 | /** 849 | * Sets additional CSS rules to be incorporated in the 850 | * resulting HTML document. 851 | * 852 | * @param string $css custom css code 853 | * 854 | * @return void 855 | */ 856 | public static function css( $css ) { 857 | self::$css = $css; 858 | } 859 | 860 | /** 861 | * Renders a dialog-like document. 862 | * 863 | * Alert: 864 | * Dialog::render( 'Notice', 'Mind the gap!' ); 865 | * 866 | * Confirm: 867 | * Dialog::render( 'Confirm', 'Do you agree', ['no'=>'/'], ['yes'=>'/proceed'] ); 868 | * 869 | * Prompt: 870 | * Dialog::render( 'Question', 'What is your name?', [], ['register'=>'/register'], ['name'=>'myname', 'type'=>'text'] ); 871 | * 872 | * 'Promptfirm': 873 | * Dialog::render( 'Question', 'What is your name?', ['wont tell'=>'/'], ['register'=>'/register'], ['name'=>'myname', 'type'=>'text'] ); 874 | * 875 | * Custom: 876 | * Dialog::render( some other combination ); 877 | * 878 | * Note: 879 | * Although multiple submit buttons are allowed, only the action of 880 | * the last button will be used. 881 | * 882 | * Note: 883 | * For every field element, at least the properties name and type 884 | * need to be specified. 885 | * 886 | * @param string $title title of the dialog box 887 | * @param string $message message to display in the box 888 | * @param array $links a series of links to display (format is: href => label) 889 | * @param array $buttons a series of submit buttons (format is: name => action) 890 | * @param array $fields additional input fields (format is: [ property => value ]) 891 | * 892 | * @return void 893 | */ 894 | public static function render( $title, $message, $links, $buttons=[], $fields=[] ) { 895 | $dialog = new StampTE( self::$template ); 896 | $dialog->setTitle( $title ); 897 | $dialog->setMessage( $message ); 898 | $dialog->setLanguage( self::$language ); 899 | $dialog->setBackgroundColor( self::$backgroundColor ); 900 | $dialog->setForegroundColor( self::$foregroundColor ); 901 | $dialog->setTextColor( self::$textColor ); 902 | $dialog->setFont(self::$font); 903 | $dialog->setHeaderFont(self::$headerFont); 904 | $dialog->setCss(self::$css); 905 | foreach( $buttons as $name => $action ) { 906 | $dialog->setMethod('POST'); 907 | $dialog->setAction($action); 908 | $button = $dialog->getButton(); 909 | $button->setLabel($name); 910 | $button->setName($name); 911 | $dialog->add( $button ); 912 | } 913 | foreach( $links as $label => $href ) { 914 | $link = $dialog->getLink(); 915 | $link->setHref( $href ); 916 | $link->setLabel( $label ); 917 | $dialog->add( $link ); 918 | } 919 | foreach( $fields as $definition ) { 920 | $field = $dialog->getField(); 921 | foreach( $definition as $key => $value ) { 922 | $field->inject( $key, $value ); 923 | } 924 | $dialog->add( $field ); 925 | } 926 | return $dialog; 927 | } 928 | 929 | /** 930 | * Renders and displays a dialog box. 931 | * For more details see Dialog::render(). 932 | * 933 | * @see Dialog::render() 934 | */ 935 | public static function show( $title, $message, $links, $buttons=[], $fields=[] ) { 936 | echo strval( self::render( $title, $message, $links, $buttons, $fields ) ); 937 | } 938 | } 939 | } -------------------------------------------------------------------------------- /tests.php: -------------------------------------------------------------------------------- 1 | #greet#'; 59 | $s = new StampTE( $template ); 60 | $s->inject( 'greet','' ); 61 | asrt( '<HELLO>', $s->__toString() ); 62 | 63 | //does the StampTETE class exist? 64 | if ( class_exists( 'StampTE' ) ) pass(); 65 | 66 | testpack( 'Test Whitespace handling' ); 67 | //Can we succesfully create an instance of the StampTETE class? 68 | $StampTE = new StampTE(''); 69 | 70 | //Test the wash() function 71 | $template = 'HELLOWORLD'; 72 | $StampTE = new StampTE( $template ); 73 | asrt( 'HELLOWORLD', trim( $StampTE ) ); 74 | 75 | $template = 'HELLOWORLD'; 76 | $StampTE = new StampTE( $template ); 77 | asrt( 'HELLOWORLD', trim( $StampTE ) ); 78 | 79 | $template = 'HELLOWORLD'; 80 | $StampTE = new StampTE( $template ); 81 | asrt( 'HELLOWORLD', trim( $StampTE ) ); 82 | 83 | $template = 'HELLOWORLD'; 84 | $StampTE = new StampTE( $template ); 85 | asrt( 'HELLORLD', trim( $StampTE ) ); 86 | 87 | $template = 'HELLO 88 | 89 | 90 | 91 | WORLD'; 92 | $StampTE = new StampTE( $template ); 93 | asrt('HELLO 94 | 95 | 96 | 97 | WORLD', trim( $StampTE ) ); 98 | asrt( $template, trim( $StampTE ) ); 99 | 100 | //test whitespace 101 | $template = '
102 | 103 |
region
104 | 105 | 106 |
'; 107 | 108 | $expect = '
109 |
'; 110 | 111 | $stampTE = new StampTE( $template ); 112 | asrt( (string) $stampTE, $expect ); 113 | 114 | $template = '
115 | 116 |
region
117 | 118 | 119 |
'; 120 | 121 | $expect = '
122 |
region
123 |
'; 124 | 125 | $stampTE = new StampTE( $template ); 126 | $stampTE->add( $stampTE->getRegion() ); 127 | asrt( (string) $stampTE, $expect ); 128 | 129 | $template = '
130 | 131 |
region
132 | 133 | 134 |
'; 135 | 136 | $expect = '
137 |
region
138 |
'; 139 | 140 | $stampTE = new StampTE( $template ); 141 | $stampTE->regions->add( $stampTE->getRegion() ); 142 | asrt( (string) $stampTE, $expect ); 143 | 144 | $template = '
145 | 146 |
region
147 | 148 | 149 | 150 | 151 | 152 |
region
153 | 154 | 155 |
'; 156 | 157 | $expect = '
158 |
region
159 |
'; 160 | 161 | $stampTE = new StampTE( $template ); 162 | $stampTE->add( $stampTE->getRegion() ); 163 | asrt( (string) $stampTE, $expect ); 164 | 165 | 166 | $template = '
167 | 168 |
|#value#|
169 | 170 | 171 |
'; 172 | 173 | $expect = '
174 |
|lorem ipsum|
175 |
'; 176 | 177 | $stampTE = new StampTE( $template ); 178 | $stampTE->regions->add( $stampTE->getRegion()->setValue( 'lorem ipsum' ) ); 179 | asrt( (string) $stampTE, $expect ); 180 | 181 | $template = '
182 | 183 |
| 184 | #value#|
185 | 186 | 187 |
'; 188 | 189 | $expect = '
190 |
| 191 | lorem ipsum|
192 |
'; 193 | 194 | $stampTE = new StampTE( $template ); 195 | $stampTE->regions->add( $stampTE->getRegion()->setValue( 'lorem ipsum' ) ); 196 | asrt( (string) $stampTE, $expect ); 197 | 198 | 199 | $template = '
200 | 201 |
||
202 | 203 | 204 |
'; 205 | 206 | $expect = '
207 |
|lorem ipsum|
208 |
'; 209 | 210 | $stampTE = new StampTE( $template ); 211 | $stampTE->regions->add( $stampTE->getRegion()->setValue( 'lorem ipsum' ) ); 212 | asrt( (string) $stampTE, $expect ); 213 | 214 | 215 | $template = '
216 | 217 |
|DEMO|
218 | 219 | 220 |
'; 221 | 222 | $expect = '
223 |
|lorem ipsum|
224 |
'; 225 | 226 | $stampTE = new StampTE( $template ); 227 | $stampTE->regions->add( $stampTE->getRegion()->setValue( 'lorem ipsum' ) ); 228 | asrt( (string) $stampTE, $expect ); 229 | 230 | $template = '
231 | 232 |
|#value?#|
233 | 234 | 235 |
'; 236 | 237 | $expect = '
238 |
|lorem ipsum|
239 |
'; 240 | 241 | $stampTE = new StampTE( $template ); 242 | $stampTE->regions->add( $stampTE->getRegion()->setValue( 'lorem ipsum' ) ); 243 | asrt( (string) $stampTE, $expect ); 244 | 245 | $template = '
246 | 247 |
|#value?#|
248 | 249 | 250 | 251 |
'; 252 | 253 | $expect = '
254 |
||
255 |
'; 256 | 257 | $stampTE = new StampTE( $template ); 258 | $stampTE->regions->add( $stampTE->getRegion() ); 259 | asrt( (string) $stampTE, $expect ); 260 | 261 | $template = '
262 | 263 | 264 |
region
265 | 266 | 267 |
'; 268 | 269 | $expect = '
270 | 271 |
region
272 |
'; 273 | 274 | $stampTE = new StampTE( $template ); 275 | $stampTE->regions->add( $stampTE->getRegion() ); 276 | asrt( (string) $stampTE, $expect ); 277 | 278 | testpack( 'Test UTF-8.' ); 279 | 280 | $template = '
281 | 282 |
象形字
283 | 284 | 285 |
'; 286 | 287 | $expect = '
288 |
象形字
289 |
'; 290 | 291 | $stampTE = new StampTE( $template ); 292 | $stampTE->regions->add( $stampTE->getRegion() ); 293 | asrt( (string) $stampTE, $expect ); 294 | 295 | $template = '
296 | 297 |
#value#
298 | 299 | 300 |
'; 301 | 302 | $expect = '
303 |
象形字
304 |
'; 305 | 306 | $stampTE = new StampTE( $template ); 307 | $stampTE->regions->add( $stampTE->getRegion()->setValue( '象形字' ) ); 308 | asrt( (string) $stampTE, $expect ); 309 | 310 | $template = '
象形字 311 | 312 |
象形字#value#
313 | 314 | 315 |
'; 316 | 317 | $expect = '
象形字 318 |
象形字象形字
319 |
'; 320 | 321 | $stampTE = new StampTE( $template ); 322 | $stampTE->regions->add( $stampTE->getRegion()->setValue( '象形字' ) ); 323 | asrt( (string) $stampTE, $expect ); 324 | 325 | //even the definitions of the cut/paste markers may be UTF-8 326 | $template = '
象形字 327 | 328 |
象形字#value#
329 | 330 | 331 |
'; 332 | 333 | $expect = '
象形字 334 |
象形字象形字
335 |
'; 336 | 337 | $stampTE = new StampTE( $template ); 338 | $region = $stampTE->get( '測試' ); 339 | $region->setValue( '象形字' ); 340 | $stampTE->glue( '測試2', $region ); 341 | asrt( (string) $stampTE, $expect ); 342 | 343 | testpack('Test Cut and Paste Metaphor'); 344 | 345 | $template = ' 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | '; 358 | 359 | $StampTE = new StampTE( $template ); 360 | $fish = $StampTE->get( 'fish' ); 361 | $StampTE->glue( 'water', $fish ); 362 | 363 | $expectation = ' 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | '; 373 | 374 | asrt( clean( $StampTE ),clean( $expectation ) ); 375 | 376 | //Does it work with more than one cut area? 377 | $template = ' 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | '; 393 | 394 | $StampTE = new StampTE( $template ); 395 | $fish = $StampTE->get( 'fish' ); 396 | $StampTE->glue( 'water', $fish ); 397 | 398 | $expectation = ' 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | '; 408 | 409 | asrt( clean( $StampTE ), clean( $expectation ) ); 410 | 411 | //Can we put more than one fish in the bowl? 412 | $StampTE = new StampTE( $template ); 413 | $fish = $StampTE->get( 'fish' ); 414 | $StampTE->glue( 'water',$fish ); 415 | $StampTE->glue( 'water',$fish ); 416 | 417 | $expectation = ' 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | '; 431 | 432 | asrt( clean( $StampTE ), clean( $expectation ) ); 433 | 434 | //What about multiple slots? 435 | $template = ' 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | '; 454 | 455 | $StampTE = new StampTE( $template ); 456 | $redfish = $StampTE->get( 'redfish' ); 457 | $yellowfish = $StampTE->get( 'yellowfish' ); 458 | $StampTE->glue( 'bowl1', $redfish ); 459 | $StampTE->glue( 'bowl2', $yellowfish ); 460 | 461 | $expectation = ' 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | '; 471 | 472 | asrt( clean( $StampTE ), clean( $expectation ) ); 473 | 474 | //now with glueAll, to do it all in one run... 475 | $StampTE = new StampTE( $template ); 476 | $redfish = $StampTE->get( 'redfish' ); 477 | $yellowfish = $StampTE->get( 'yellowfish' ); 478 | $StampTE->glueAll( array( 'bowl1' => $redfish, 'bowl2' => $yellowfish ) ); 479 | 480 | asrt( clean( $StampTE ), clean( $expectation ) ); 481 | 482 | //Now put the castle from the bowl in the box as well. 483 | $template = ' 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | '; 503 | 504 | $StampTE = new StampTE( $template ); 505 | $redfish = $StampTE->get( 'redfish' ); 506 | $yellowfish = $StampTE->get( 'yellowfish' ); 507 | $StampTE->glue( 'bowl1', $redfish ); 508 | $StampTE->glue( 'bowl2', $yellowfish ); 509 | $castle = $StampTE->get( 'castle' ); 510 | $StampTE->glue( 'box', $castle ); 511 | 512 | $expectation = ' 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | '; 523 | 524 | asrt( clean( $StampTE ), clean( $expectation ) ); 525 | 526 | //Test same, in combination with slots (complex template) 527 | $template = ' 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | '; 544 | 545 | $StampTE = new StampTE( $template ); 546 | $redfish = $StampTE->get( 'fish' ); 547 | $redfish->inject( 'color', 'red' ); 548 | $yellowfish = $StampTE->get( 'fish' ); 549 | $yellowfish->inject( 'color','yellow' ); 550 | $StampTE->glue( 'bowl1', $redfish ); 551 | $StampTE->glue( 'bowl2', $yellowfish ); 552 | $castle = $StampTE->get( 'castle' ); 553 | $StampTE->glue( 'box', $castle ); 554 | $StampTE->injectAll( array( 'liters1' => '50', 'liters2' => '100' ) ); 555 | 556 | $expectation = ' 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | '; 567 | 568 | asrt( clean( $StampTE ), clean( $expectation ) ); 569 | 570 | //Nest and restrictions 571 | $template = ' 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | '; 593 | 594 | $StampTE = new StampTE( $template ); 595 | $redfish = $StampTE->get( 'fish' ); 596 | $redfish->inject( 'color', 'red' ); 597 | $greenfish = $StampTE->get( 'fish' ); 598 | $greenfish->inject( 'color', 'green' ); 599 | $yellowfish = $StampTE->get( 'fish' ); 600 | $yellowfish->inject( 'color', 'yellow' ); 601 | $StampTE->glue( 'bowl1', $redfish ); 602 | $StampTE->glue( 'bowl2', $yellowfish ); 603 | $castle = $StampTE->get( 'castle' ); 604 | $castle->glue( 'incastle', $greenfish ); 605 | $jelly = $StampTE->get( 'jellyfish' ); 606 | try { 607 | $castle->glue( 'incastle', $jelly ); //jellyfish not allowed in castle 608 | fail(); 609 | } catch( Exception $e ) { 610 | pass(); 611 | } 612 | $StampTE->glue( 'box', $castle ); 613 | $StampTE->inject( 'liters1', '50' ); 614 | $StampTE->inject( 'liters2', '100' ); 615 | 616 | $expectation = ' 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | '; 629 | 630 | asrt( clean( $StampTE ), clean( $expectation ) ); 631 | 632 | //Nest and restrictions part 2 633 | $template = ' 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | '; 655 | 656 | $StampTE = new StampTE( $template ); 657 | $redfish = $StampTE->get( 'fish' ); 658 | $redfish->inject( 'color', 'red' ); 659 | $greenfish = $StampTE->get( 'fish' ); 660 | $greenfish->inject( 'color', 'green' ); 661 | $yellowfish = $StampTE->get( 'fish' ); 662 | $yellowfish->inject( 'color', 'yellow' ); 663 | $StampTE->glue( 'bowl1', $redfish ); 664 | $StampTE->glue( 'bowl2', $yellowfish ); 665 | $castle = $StampTE->get( 'castle' ); 666 | $castle->glue( 'incastle', $greenfish ); 667 | $jelly = $StampTE->get( 'jellyfish' ); 668 | $castle->glue( 'incastle', $jelly ); //jellyfish IS allowed in castle 669 | $StampTE->glue( 'box', $castle ); 670 | $StampTE->inject( 'liters1', '50' ); 671 | $StampTE->inject( 'liters2', '100' ); 672 | 673 | $expectation = ' 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | '; 687 | 688 | asrt( clean( $StampTE ), clean( $expectation ) ); 689 | 690 | testpack( 'Test StampTETE metaphor' ); 691 | 692 | $template = ' 693 | 694 | 695 | 696 | 697 | 698 | 699 | '; 700 | 701 | $StampTE = new StampTE( $template ); 702 | $flower1 = $StampTE->get( 'flower' )->copy(); 703 | $flower2 = $StampTE->get( 'flower' )->copy(); 704 | $flowers = array( 'flowers' =>array( $flower1, $flower2 ) ); 705 | $StampTE->glueAll( $flowers ); 706 | 707 | $expectation = ' 708 | 709 | 710 | 711 | 712 | '; 713 | 714 | asrt( clean( $StampTE ), clean( $expectation ) ); 715 | 716 | //StampTE and slots 717 | 718 | $template = ' 719 | 720 | 721 | 722 | 723 | 724 | 725 | '; 726 | 727 | $StampTE = new StampTE( $template ); 728 | $flower = $StampTE->get( 'flower' )->copy(); 729 | $flower2 = $flower->copy(); 730 | $flower->inject( 'type', 'lily' ); 731 | $flower2->inject( 'type', 'phlox' ); 732 | $flowers = $flower . $flower2; 733 | $StampTE->glue( 'flowers', $flowers ); 734 | 735 | $expectation = ' 736 | 737 | 738 | 739 | 740 | '; 741 | 742 | asrt( clean( $StampTE ), clean( $expectation ) ); 743 | 744 | //Complex, put lily in pond 745 | $template = ' 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | '; 756 | 757 | $StampTE = new StampTE( $template ); 758 | $flower = $StampTE->get( 'flower' )->copy(); 759 | $flower2 = $flower->copy(); 760 | $flower->inject( 'type', 'lily' ); 761 | $pond = $StampTE->glue( 'pond', $flower ); 762 | $flower2->inject( 'type', 'phlox' ); 763 | $flowers = $flower2; 764 | $StampTE->glue( 'flowers', $flowers ); 765 | 766 | $expectation = ' 767 | 768 | 769 | 770 | 771 | 772 | 773 | '; 774 | 775 | asrt( clean( $StampTE ), clean( $expectation ) ); 776 | 777 | testpack( 'Infinte loop - no longer an issue, preserving tests.' ); 778 | $template = 'hello there'; 779 | $StampTE = new StampTE( $template ); 780 | asrt( strval( $StampTE ), 'hello there' ); 781 | 782 | $StampTE = new StampTE( ' without a beginning.' ); 789 | pass(); 790 | $StampTE = new StampTE( '--!>' ); 791 | pass(); 792 | $StampTE = new StampTE( '' ); 795 | pass(); 796 | $StampTE = new StampTE( '' ); 797 | pass(); 798 | $StampTE = new StampTE( 'atreasureb' ); 799 | asrt( strval( $StampTE->get( 'chest' ) ), 'treasure' ); 800 | pass(); 801 | $StampTE = new StampTE( 'atreasureb' ); 802 | asrt( strval( $StampTE->get( 'chest' ) ), 'treasure' ); 803 | pass(); 804 | $StampTE = new StampTE( 'atreasureb' ); 805 | asrt( strval( $StampTE ), 'atreasureb' ); 806 | 807 | testpack( 'Test Self-Replace' ); 808 | $stampTE = new StampTE(' 809 | 814 | '); 815 | //echo $stampTE->getTodo();exit; 816 | $todoItem = $stampTE->get( 'todo' ); 817 | $todoItem->inject( 'todo', 'Make Coffee' ); 818 | $stampTE->add( $todoItem ); 819 | $expectation = ' 822 | '; 823 | 824 | asrt( trim( strval( $stampTE ) ), trim( $expectation ) ); 825 | 826 | //Now with two lists 827 | $stampTE = new StampTE(' 828 | 836 | '); 837 | $todoItem = $stampTE->get( 'todo' ); 838 | $todoItem->inject( 'todo', 'Make Coffee' ); 839 | $stampTE->add( $todoItem ); 840 | $expectation = ' 843 | '; 844 | asrt( trim( strval( $stampTE ) ), trim( $expectation ) ); 845 | 846 | $stampTE = new StampTE(' 847 | 855 | '); 856 | $todoItem2 = $stampTE->get( 'todo2' ); 857 | $todoItem2->inject( 'todo', 'Clean the house' ); 858 | $todoItem = $stampTE->get( 'todo' ); 859 | $todoItem->inject( 'todo', 'Make Coffee' ); 860 | $stampTE->add( $todoItem2 ); 861 | $stampTE->add( $todoItem ); 862 | $expectation = ' 863 | 867 | '; 868 | asrt( trim( strval( $stampTE ) ), trim( $expectation ) ); 869 | 870 | testpack( 'Test Dummy Slots' ); 871 | 872 | $stampTE = new StampTE( 'ipsum' ); 873 | $stampTE->inject( 'lorem', 'Hello' ); 874 | asrt( trim( strval( $stampTE ) ), 'Hello' ); 875 | 876 | testpack( 'Test Magic API' ); 877 | 878 | //Complex, put lily in pond 879 | $template = ' 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | '; 890 | 891 | $StampTE = new StampTE( $template ); 892 | $flower = $StampTE->getFlower()->copy(); 893 | $flower2 = $flower->copy(); 894 | $flower->setType( 'lily' ); 895 | $pond = $StampTE->pond->add( $flower ); 896 | $flower2->setType( 'phlox' ); 897 | $StampTE->flowers->add( $flower2 ); 898 | $expectation = ' 899 | 900 | 901 | 902 | 903 | 904 | 905 | '; 906 | 907 | asrt( clean( $StampTE ), clean( $expectation ) ); 908 | 909 | class StampTEProxy extends StampTE { 910 | public static function getSlots( $object ) { 911 | return $object->slots; 912 | } 913 | } 914 | 915 | $stampTE = new StampTE( '' ); 916 | $slots = StampTEProxy::getSlots( $stampTE ); 917 | asrt( isset( $slots['castle'] ) , TRUE ); 918 | 919 | testpack( 'Test Filters' ); 920 | $template = '#test#'; 921 | 922 | class InternationalStampTE extends StampTE { 923 | protected function filter( $data ) { 924 | $data = DICT( $data ); 925 | $data = parent::filter( $data ); 926 | return $data; 927 | } 928 | } 929 | function DICT($text) { 930 | if ( $text == 'hello' ) return 'Allo'; 931 | } 932 | 933 | $StampTE = new InternationalStampTE( $template ); 934 | $StampTE->inject( 'test', 'hello' ); 935 | asrt( strval( $StampTE ), 'Allo' ); 936 | 937 | testpack( 'Test Cleaning' ); 938 | 939 | $StampTE = new StampTE( 'Test test piece' ); 940 | $str = strval( $StampTE ); 941 | asrt( strpos( ' 951 | 952 | #blub# 953 | 954 | #diamond# 955 | 956 | 957 | 958 | 959 | '; 960 | $stampTE = new StampTE( $template ); 961 | $dict = array( 962 | 'Fish.Sound' => 'Blub Blub %s', 963 | 'Diamond' => 'Wanda' 964 | ); 965 | $stampTE->setTranslator( function( $word, $params = array() ){ 966 | global $dict; 967 | return vsprintf( $dict[$word], $params ); 968 | }); 969 | $bowl = $stampTE->getFishBowl(); 970 | asrt( clean( $bowl ), '#&blub#' ); 971 | $bowl->sayBlub( 'Fish.Sound', array( 'says the fish' ) ); 972 | asrt( clean( $bowl ), 'BlubBlubsaysthefish' ); 973 | $castle = $bowl->getCastle(); 974 | $castle->sayDiamond( 'Diamond' ); 975 | asrt( clean( $castle ), 'Wanda' ); 976 | $castle = $stampTE->get( 'fishBowl.castle' ); 977 | $castle->setDiamond( 'Diamond' ); 978 | asrt( clean( $castle ), 'Diamond' ); 979 | $castle = $stampTE->get( 'fishBowl.castle' ); 980 | $castle->sayDiamond( 'Diamond' ); 981 | asrt( clean( $castle ), 'Wanda' ); 982 | 983 | testpack(' Test Factory' ); 984 | 985 | class Pirahna extends StampTE { 986 | public function isHungry() { 987 | return 'Sure'; 988 | } 989 | } 990 | 991 | $stampTE->setFactory( function( $stamp, $id ) { 992 | if ( $id == 'fishBowl' ) { 993 | return new Pirahna( $stamp, $id ); 994 | } else { 995 | return new StampTE( $stamp, $id ); 996 | } 997 | }); 998 | 999 | $fish = $stampTE->getFishBowl(); 1000 | asrt( 'Sure', $fish->isHungry() ); 1001 | $castle = $stampTE->get( 'fishBowl.castle' ); 1002 | asrt( NULL, $castle->isHungry() ); 1003 | 1004 | class Castle extends StampTE { 1005 | public function isCastle() { return 'Yes'; } 1006 | } 1007 | 1008 | $stampTE->setFactory( function( $stamp, $id ) { 1009 | if ( $id === 'castle' ) { 1010 | return new Castle( $stamp, $id ); 1011 | } else { 1012 | return new StampTE( $stamp, $id ); 1013 | } 1014 | }); 1015 | 1016 | $castle = $stampTE->get( 'fishBowl.castle' ); 1017 | asrt( 'Yes', $castle->isCastle() ); 1018 | $fish = $stampTE->getFishBowl(); 1019 | asrt( NULL, $fish->isHungry() ); 1020 | $castle = $fish->getCastle(); 1021 | asrt( 'Yes', $castle->isCastle() ); 1022 | 1023 | testpack( 'Test error handling' ); 1024 | 1025 | try{ $stampTE->get( 'unknown' ); fail(); } catch( StampTEException $e ) { pass(); } 1026 | try{ $stampTE->getUnknown(); fail(); } catch( StampTEException $e ) { pass(); } 1027 | try{ $stampTE->get( 'unknown.unknown' ); fail(); } catch( StampTEException $e ) { pass(); } 1028 | try{ $stampTE->get( 'fishBowl.unknown' ); fail(); } catch( StampTEException $e ) { pass(); } 1029 | try{ $stampTE->get( 'fishBowl.castle.unknown' ); fail(); } catch( StampTEException $e ) { pass(); } 1030 | 1031 | testpack( 'Test strtolower issue with magic setter' ); 1032 | 1033 | $template = '#helloWorld#'; 1034 | $stampTE = new StampTE( $template ); 1035 | $stampTE->setHelloWorld( 'Hi' ); 1036 | asrt( strval( $stampTE ), 'Hi' ); 1037 | 1038 | $template = '#helloWorld#'; 1039 | $stampTE = new StampTE( $template ); 1040 | $stampTE->setTranslator( function( $a ) { return $a; } ); 1041 | $stampTE->sayHelloWorld( 'Hi' ); 1042 | asrt( strval( $stampTE ), 'Hi' ); 1043 | 1044 | testpack( 'Security Test' ); 1045 | $t = ''; 1046 | $s = new StampTE( $t ); 1047 | $s->setValue( '"' ); 1048 | $s->setType( 'text' ); 1049 | asrt( trim( $s ), '' ); 1050 | 1051 | $t = ''; 1052 | $s = new StampTE( $t ); 1053 | $s->setValue( "'" ); 1054 | $s->setType( 'text' ); 1055 | asrt( trim( $s ), '' ); 1056 | 1057 | $t = ''; 1058 | $s = new StampTE($t); 1059 | $s->setValue( '#&type#' ); 1060 | $s->setType( 'text' ); 1061 | asrt( trim( $s ), '' ); 1062 | 1063 | $t = ''; 1064 | $s = new StampTE( $t ); 1065 | $s->setValue( '#type#' ); 1066 | $s->setType( 'text' ); 1067 | asrt( trim( $s ), '' ); 1068 | 1069 | $t = ''; 1070 | $s = new StampTE( $t ); 1071 | $s->injectRaw( 'value', '#&type#' ); 1072 | $s->setType( 'text' ); 1073 | asrt( trim( $s ), '' ); 1074 | 1075 | $t = '
#slot#
'; 1076 | $s = new StampTE( $t ); 1077 | $s->inject( 'slot', '' ); 1078 | $s->hello->add( new StampTE( '' ) ); 1079 | asrt( trim( $s ), '
<b><!-- paste:hello --></b>
' ); 1080 | 1081 | $t = '
#slot#
'; 1082 | $s = new StampTE( $t ); 1083 | $s->injectRaw( 'slot', '' ); 1084 | $s->hello->add( new StampTE( '' ) ); 1085 | asrt( trim( $s ), '
' ); 1086 | 1087 | testpack( 'Test optional slot marker.' ); 1088 | 1089 | $t = '
#slot?#
'; 1090 | $s = new StampTE( $t ); 1091 | asrt( trim( $s ),'
' ); 1092 | 1093 | $t = '
#slot?#
#slot2?#
'; 1094 | $s = new StampTE( $t ); 1095 | asrt( trim( $s ), '
' ); 1096 | 1097 | $t = '
#slot?#
'; 1098 | $s = new StampTE( $t ); 1099 | $s->setSlot( 'Boo!' ); 1100 | asrt( trim( $s ), '
Boo!
' ); 1101 | 1102 | $t = '
#slot?#
#slot?#
'; 1103 | $s = new StampTE( $t ); 1104 | $s->setSlot( 'Boo!' ); 1105 | asrt( trim( $s ), '
Boo!
Boo!
' ); 1106 | 1107 | $t = '
#slot?#
'; 1108 | $s = new StampTE( $t ); 1109 | $s->setSlot( '#Boo?#' ); 1110 | $s->setBoo( 'Baa' ); 1111 | asrt( trim( $s ), '
#Boo?#
' ); 1112 | 1113 | $t = '
#slot?#
'; 1114 | $s = new StampTE( $t ); 1115 | $s->setSlot( '#&Boo?#' ); 1116 | $s->setBoo( 'Baa' ); 1117 | asrt( trim( $s ), '
#&Boo?#
' ); 1118 | 1119 | $t = '
#slot?#
'; 1120 | $s = new StampTE( $t ); 1121 | $s->setSlot( '#&Boo#' ); 1122 | $s->setBoo( 'Baa' ); 1123 | asrt( trim( $s ), '
#&Boo#
' ); 1124 | 1125 | testpack( 'Test backtick XSS filter for MSIE.' ); 1126 | 1127 | $t = ''; 1128 | $s = new StampTE( $t ); 1129 | $s->setSlot( '`' ); 1130 | asrt( trim( $s ), '' ); 1131 | 1132 | $t = ''; 1133 | $s = new StampTE( $t ); 1134 | $s->setSlot( '``' ); 1135 | asrt( trim( $s ), '' ); 1136 | 1137 | testpack( 'Test attribute injection' ); 1138 | 1139 | $t = ''; 1140 | $s = new StampTE( $t ); 1141 | $s->injectAttr( 'state', 'checked' ); 1142 | asrt( trim( $s ), '' ); 1143 | 1144 | $t = ''; 1145 | $s = new StampTE( $t ); 1146 | $s->injectAttr( 'state', '<' ); 1147 | asrt( trim( $s ), '' ); 1148 | 1149 | $t = ''; 1150 | $s = new StampTE( $t ); 1151 | $x = $s->injectAttr( 'state', '<', TRUE ); 1152 | asrt( ( $x instanceof StampTE ), TRUE ); 1153 | asrt( trim( $s ), '' ); 1154 | 1155 | $t = ''; 1156 | $s = new StampTE( $t ); 1157 | asrt( trim( $s ), '' ); 1158 | 1159 | $t = ''; 1160 | $s = new StampTE( $t ); 1161 | asrt( trim( $s ), '' ); 1162 | 1163 | testpack( 'Test Attr-toggler.' ); 1164 | 1165 | $t = ''; 1166 | $s = new StampTE( $t ); 1167 | $x = $s->attr( 'checked', TRUE ); 1168 | asrt( ( $x instanceof StampTE ), TRUE ); 1169 | asrt( trim( $s ), '' ); 1170 | 1171 | $t = ''; 1172 | $s = new StampTE( $t ); 1173 | $x = $s->attr( 'checked', FALSE ); 1174 | asrt( ( $x instanceof StampTE ), TRUE ); 1175 | asrt( trim( $s ), '' ); 1176 | 1177 | $t = ''; 1178 | $s = new StampTE( $t ); 1179 | asrt( trim( $s ), '' ); 1180 | 1181 | testpack('Test escaping issue.'); 1182 | 1183 | $s = new StampTE( '#slot#' ); 1184 | $s->add( $s->getItem()->setSlot( '/' ) ); 1185 | asrt( trim( $s ), '/' ); 1186 | 1187 | $s = new StampTE('#slot#'); 1188 | $s->add($s->getItem()->setSlot('$1')); 1189 | asrt(trim($s),'$1'); 1190 | 1191 | //Test cache & collect 1192 | testpack(' Test Cache and Collect' ); 1193 | 1194 | $s = new StampTE(' 1195 |
1196 | 1197 | 1198 |
1199 |
1200 | 1201 |
1202 | '); 1203 | 1204 | $cacheObject = $s->writeToCache( 'button|textfield' )->getCache(); 1205 | 1206 | //make sure cache is used, remove elements from new template... 1207 | $s = new StampTE(' 1208 |
1209 |
1210 |
1211 | 1212 |
1213 | '); 1214 | 1215 | $s->loadIntoCache( $cacheObject ); 1216 | $items = $s->collect( 'button|textfield' ); 1217 | asrt( count( $items ), 2 ); 1218 | 1219 | try { 1220 | $s->collect( 'button|textfield|misc' ); 1221 | fail(); 1222 | } catch( Exception $e ) { 1223 | pass(); 1224 | } 1225 | 1226 | $button = $items[0]; 1227 | $textField = $items[1]; 1228 | 1229 | $s->form->add( $button->copy() ); 1230 | $s->form->add( $textField->copy() ); 1231 | 1232 | asrt( trim( '
1233 |
1234 |
1235 |
1236 | ' ), trim( strval( $s ) ) ); 1237 | 1238 | 1239 | //Test template loader 1240 | testpack( 'Test Template Loader' ); 1241 | 1242 | $path = '/tmp/dummy.tpl'; 1243 | $template = '
1244 | Dummy Template for StampTE Unit Test 1245 |
'; 1246 | file_put_contents( $path, $template ); 1247 | $s = StampTE::load( $path ); 1248 | 1249 | asrt( strval( $s->getMessage() ), 'Dummy Template for StampTE Unit Test' ); 1250 | asrt( $s->getString(), '
1251 |
' ); 1252 | 1253 | try { 1254 | StampTE::load( '/non/existant/file/nowhere.nothing' ); 1255 | fail(); 1256 | } catch ( Exception $e ) { 1257 | pass(); 1258 | } 1259 | 1260 | //test exceptions 1261 | testpack(' Test exceptions.' ); 1262 | 1263 | try { 1264 | StampTE::load( '/non/existant/file/nowhere.nothing' ); 1265 | fail(); 1266 | } catch( StampTEException $e ) { 1267 | asrt( strpos( $e->getMessage(), '[S001]' ) === 0, TRUE ); 1268 | } 1269 | 1270 | 1271 | //Null is allowed, becomes empty string ''. 1272 | $s = new StampTE( NULL ); 1273 | pass(); 1274 | 1275 | //non-string values get converted to string 1276 | $s = new StampTE( 123 ); 1277 | asrt( strval($s), '123' ); 1278 | 1279 | $s = new StampTE( FALSE ); 1280 | asrt( strval($s), '' ); 1281 | 1282 | $s = new StampTE( TRUE ); 1283 | asrt( strval($s), '1' ); 1284 | 1285 | $s = new StampTE( new StampTE('hello') ); 1286 | asrt( strval($s), 'hello' ); 1287 | 1288 | $s = new StampTE( new StdClass ); 1289 | asrt( strval($s), '[stdClass instance]' ); 1290 | 1291 | try { 1292 | $s = new StampTE( '' ); 1293 | $s->getGhost(); 1294 | fail(); 1295 | } catch( StampTEException $e ) { 1296 | asrt( strpos( $e->getMessage(), '[S101]' ) === 0, TRUE ); 1297 | } 1298 | 1299 | try { 1300 | $s = new StampTE( '' ); 1301 | $s->get('test'); 1302 | fail(); 1303 | } catch( StampTEException $e ) { 1304 | asrt( strpos( $e->getMessage(), '[S101]' ) === 0, TRUE ); 1305 | } 1306 | 1307 | try { 1308 | $s = new StampTE( '' ); 1309 | $s->get('test.test'); 1310 | fail(); 1311 | } catch( StampTEException $e ) { 1312 | asrt( strpos( $e->getMessage(), '[S101]' ) === 0, TRUE ); 1313 | } 1314 | 1315 | try { 1316 | $s = new StampTE( '' ); 1317 | $s->loadIntoCache( NULL ); 1318 | fail(); 1319 | } catch( StampTEException $e ) { 1320 | asrt( strpos( $e->getMessage(), '[S004]' ) === 0, TRUE ); 1321 | } 1322 | 1323 | try { 1324 | $s = new StampTE( '' ); 1325 | $s->setTranslator( NULL ); 1326 | fail(); 1327 | } catch( StampTEException $e ) { 1328 | asrt( strpos( $e->getMessage(), '[S005]' ) === 0, TRUE ); 1329 | } 1330 | 1331 | try { 1332 | $s = new StampTE( '' ); 1333 | $s->setTranslator( 'translator' ); 1334 | fail(); 1335 | } catch( StampTEException $e ) { 1336 | asrt( strpos( $e->getMessage(), '[S005]' ) === 0, TRUE ); 1337 | } 1338 | 1339 | 1340 | try { 1341 | $s = new StampTE( '' ); 1342 | $s->setFactory( NULL ); 1343 | fail(); 1344 | } catch( StampTEException $e ) { 1345 | asrt( strpos( $e->getMessage(), '[S006]' ) === 0, TRUE ); 1346 | } 1347 | 1348 | try { 1349 | $s = new StampTE( '' ); 1350 | $s->setFactory( 'factory' ); 1351 | fail(); 1352 | } catch( StampTEException $e ) { 1353 | asrt( strpos( $e->getMessage(), '[S006]' ) === 0, TRUE ); 1354 | } 1355 | 1356 | try { 1357 | $s = new StampTE( '' ); 1358 | $s->setFactory( 'factory' ); 1359 | fail(); 1360 | } catch( StampTEException $e ) { 1361 | asrt( strpos( $e->getMessage(), '[S006]' ) === 0, TRUE ); 1362 | } 1363 | 1364 | try { 1365 | $s = new StampTE( '' ); 1366 | $s->glue( 'here', new StampTE ); 1367 | fail(); 1368 | } catch( StampTEException $e ) { 1369 | asrt( strpos( $e->getMessage(), '[S102]' ) === 0, TRUE ); 1370 | } 1371 | 1372 | try { 1373 | $s = new StampTE( '' ); 1374 | $s->glue( 'here', 'x' ); 1375 | fail(); 1376 | } catch( StampTEException $e ) { 1377 | asrt( strpos( $e->getMessage(), '[S003]' ) === 0, TRUE ); 1378 | } 1379 | 1380 | //fake possible. 1381 | $s = new StampTE( '' ); 1382 | $s->here->add( new StampTE( '', 'c' ) ); 1383 | pass(); 1384 | 1385 | //test html5 document creator 1386 | testpack( 'Test HTML5 document creator.' ); 1387 | $tpl = StampTE::createHtml5Utf8Document(); 1388 | 1389 | $expectedHTML = ' 1390 | 1391 | 1392 | 1393 | 1394 | 1395 | 1396 | 1397 | '; 1398 | asrt( clean( strval( $tpl ) ), clean( $expectedHTML ) ); 1399 | 1400 | $tpl->setTitle( 'Welcome to StampTE' ); 1401 | $tpl->add( $tpl 1402 | ->getLink() 1403 | ->copy() 1404 | ->setRel( 'stylesheet' ) 1405 | ->setType( 'text/css' ) 1406 | ->setHref( 'style.css' ) ); 1407 | $tpl->add( $tpl 1408 | ->getScript() 1409 | ->copy() 1410 | ->setSrc( 'js/script.js' )); 1411 | $myTemplateStr = '#greeting#'; 1412 | $myTemplate = new StampTE( $myTemplateStr ); 1413 | $myTemplate->setGreeting( 'Hello World!' ); 1414 | $tpl->body->add( $myTemplate ); 1415 | $expectedHTML = ' 1416 | 1417 | 1418 | 1419 | Welcome to StampTE 1420 | Hello World! 1421 | '; 1422 | asrt( clean( strval( $tpl ) ), clean( $expectedHTML ) ); 1423 | asrt( ( strlen( clean( strval( $tpl ) ) ) > 0 ), TRUE ); 1424 | 1425 | testpack( 'Test white space handling options.' ); 1426 | 1427 | $expectedDinnerWithoutSpace = ' 1428 | 1429 | 1430 | 1431 | 1432 | '; 1433 | 1434 | $expectedDinnerWithSpace = ' 1435 | 1436 | 1437 | 1438 | 1439 | 1440 | 1441 | '; 1442 | 1443 | $dinner = new StampTE( ' 1444 | 1445 | 1446 | 1447 | 1448 | 1449 | 1450 | ' ); 1451 | 1452 | $dinner->setClearWS( TRUE ); 1453 | $steak = $dinner->getSteak(); 1454 | $dinner->plate->add( $steak ); 1455 | 1456 | asrt( strval( $dinner ), $expectedDinnerWithoutSpace ); 1457 | 1458 | $dinner = new StampTE(' 1459 | 1460 | 1461 | 1462 | 1463 | 1464 | 1465 | 1466 | 1467 | '); 1468 | 1469 | $dinner->setClearWS( FALSE ); 1470 | $steak = $dinner->getSteak(); 1471 | $dinner->plate->add( $steak ); 1472 | 1473 | asrt( strval( $dinner ), $expectedDinnerWithSpace ); 1474 | 1475 | $html = ' 1481 |
#name#
'; 1482 | $se = new StampTE($html); 1483 | $se->setName('Melville'); 1484 | asrt($se->__toString(), ' 1490 |
Melville
'); 1491 | 1492 | testpack('Test high UTF-8 and invalid UTF-8'); 1493 | $se = new StampTE(''); 1494 | $heart = $se->get('爱'); 1495 | $se->add( $heart ); 1496 | $se->add( $heart ); 1497 | asrt( $se->__toString(), '♥♥' ); 1498 | 1499 | $se = new StampTE('#test#'); 1500 | $se->setTest( '𐀀' ); 1501 | asrt( $se->__toString(), '𐀀' ); 1502 | 1503 | $se = new StampTE('#test#'); 1504 | $invalid = chr('129') . chr('129'); 1505 | $se->setTest( $invalid ); 1506 | asrt( $se->__toString(), '' ); 1507 | 1508 | if ( !$XDEBUG ) exit(0); 1509 | 1510 | $report = xdebug_get_code_coverage(); 1511 | $misses = 0; 1512 | $hits = 0; 1513 | 1514 | $covLines = array(); 1515 | foreach( $report as $file => $lines ) { 1516 | $pi = pathinfo( $file ); 1517 | 1518 | if ( strpos( $file, 'Stamp' ) === false ) continue; 1519 | $covLines[] = '***** File:'.$file.' ******'; 1520 | 1521 | $fileData = file_get_contents( $file ); 1522 | $fileLines = explode( "\n", $fileData ); 1523 | $i = 1; 1524 | foreach( $fileLines as $covLine ) { 1525 | if ( isset( $lines [$i] ) ) { 1526 | if ( $lines[$i] === 1 ) { 1527 | $covLines[] = '[ OK ] '.$covLine; 1528 | $hits ++; 1529 | } else if ( $lines[$i] === -1 ){ 1530 | $covLines[] = '[ MISSED! ] '.$covLine; 1531 | $misses ++; 1532 | } else { 1533 | $covLines[] = '[ - ] '.$covLine; 1534 | } 1535 | } else { 1536 | $covLines[] = '[ - ] '.$covLine; 1537 | } 1538 | $i ++; 1539 | } 1540 | } 1541 | $covFile = implode( "\n", $covLines ); 1542 | @file_put_contents( 'coverage_log.txt', $covFile ); 1543 | 1544 | if ( $hits > 0 ) { 1545 | $perc = ( $hits / ( $hits + $misses ) ) * 100; 1546 | } else { 1547 | $perc = 0; 1548 | } 1549 | 1550 | echo PHP_EOL; 1551 | echo 'Code Coverage: '.PHP_EOL; 1552 | echo 'Hits: '.$hits.PHP_EOL; 1553 | echo 'Misses: '.$misses.PHP_EOL; 1554 | echo 'Percentage: '.$perc.' %'.PHP_EOL; 1555 | exit( 0 ); 1556 | --------------------------------------------------------------------------------