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 | [](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 |
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 |
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 |
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 = '