View Javadoc

1   /**
2    * Copyright (C) 2005-2009 Alfresco Software Limited.
3    *
4    * This file is part of the Spring Surf Extension project.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *  http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.springframework.extensions.webscripts.processor;
20  
21  import java.io.BufferedReader;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.Reader;
25  import java.io.Writer;
26  import java.util.HashMap;
27  import java.util.Map;
28  
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  import org.springframework.extensions.surf.core.processor.ProcessorExtension;
32  import org.springframework.extensions.surf.core.scripts.ScriptException;
33  import org.springframework.extensions.webscripts.processor.AbstractTemplateProcessor;
34  
35  import com.caucho.quercus.Quercus;
36  import com.caucho.quercus.env.Env;
37  import com.caucho.quercus.env.StringInputStream;
38  import com.caucho.quercus.env.Value;
39  import com.caucho.quercus.module.QuercusModule;
40  import com.caucho.quercus.page.QuercusPage;
41  import com.caucho.util.CharBuffer;
42  import com.caucho.vfs.ReadStream;
43  import com.caucho.vfs.StringWriter;
44  import com.caucho.vfs.VfsStream;
45  import com.caucho.vfs.WriteStream;
46  
47  /**
48   * PHP Template Processor for Web Framework
49   * 
50   * @author Roy Wetherall
51   * @author muzquiano
52   */
53  public class PHPTemplateProcessor extends AbstractTemplateProcessor
54  {	
55  	private static Log logger = LogFactory.getLog(PHPTemplateProcessor.class);
56  	
57      /** Key to value found in $_SERVER indicating that the Alfresco API is available */
58      public static final String ALF_AVAILABLE = "ALF_AVAILABLE";
59      
60      /** Default template input encoding */
61      private String defaultEncoding;
62      
63      /** Quercus engine */
64      private Quercus quercus; 
65      
66      /**
67       * Constructor
68       */
69      public PHPTemplateProcessor()
70      {
71      	// Create the quercus engine
72      	this.quercus = new Quercus();
73      	
74      	// Set the ALF_AVAILABLE server value to help with script reuse
75      	this.quercus.setServerEnv(PHPTemplateProcessor.ALF_AVAILABLE, "true");
76      }
77      
78      /**
79       * @param defaultEncoding
80       */
81      public void setDefaultEncoding(String defaultEncoding)
82      {
83          this.defaultEncoding = defaultEncoding;
84      }
85          
86      /* (non-Javadoc)
87       * @see org.alfresco.web.scripts.TemplateProcessor#getDefaultEncoding()
88       */
89      public String getDefaultEncoding()
90      {
91          return this.defaultEncoding;
92      }
93      
94      /* (non-Javadoc)
95       * @see org.alfresco.processor.Processor#getExtension()
96       */
97      public String getExtension()
98      {
99          return "php";
100     }    
101     
102     /* (non-Javadoc)
103      * @see org.alfresco.processor.Processor#getName()
104      */
105     public String getName()
106     {
107     	return "php";
108     }
109     
110     /* (non-Javadoc)
111      * @see org.alfresco.web.scripts.processor.AbstractTemplateProcessor#init()
112      */
113     public void init()
114     {
115     	super.init();
116     	
117     	// TODO: any other init?
118     }
119             
120     /* (non-Javadoc)
121      * @see org.alfresco.web.scripts.processor.BaseProcessor#registerProcessorExtension(org.alfresco.processor.ProcessorExtension)
122      */
123     public void registerProcessorExtension(ProcessorExtension processorExtension)
124     {
125         // Call the base implementation
126         super.registerProcessorExtension(processorExtension);
127         
128         // Deal with adding the extension to the quercus engine
129         if (processorExtension instanceof PHPMethodExtension)
130         {
131             this.quercus.addModule((QuercusModule)processorExtension);    
132             ((PHPMethodExtension)processorExtension).initialiseModule(this.quercus.findModule(processorExtension.getClass().getName()));
133         }
134         else if (processorExtension instanceof PHPObjectExtension)
135         {
136             try
137             {
138                 Class clazz = Class.forName(((PHPObjectExtension)processorExtension).getExtensionClass());
139                 this.quercus.addJavaClass(processorExtension.getExtensionName(), clazz);
140             }
141             catch (ClassNotFoundException exception)
142             {
143                 throw new PHPProcessorException("PHP Object Extension class '" + ((PHPObjectExtension)processorExtension).getExtensionClass() + "' could not be found.", exception);
144             }
145         }
146     }
147     
148     /**
149      * Executes a PHP script using the quercus engine.
150      * 
151      * @param script    the script as a string
152      * @param out       the writer to direct the output of the PHP to.  This can be null if no output is required.
153      * @param model     the context model for the script
154      * @return          the return result of the executed PHP
155      */
156     private Object executePHPScript(String script, Writer out, Map<String, Object> model)
157     {
158         return executePHPScript(new StringInputStream(script), out, model);
159     }
160     
161     /**
162      * Executes the PHP script using the quercus engine.
163      * 
164      * @param is        the input stream containing the PHP to execute
165      * @param out       the writer to direct the output of the PHP to.  This can be null if no output is required.
166      * @param model     the context model for the script
167      * @return Object   the return result of the executed PHP
168      */
169     private Object executePHPScript(InputStream is, Writer out, Map<String, Object> model)
170     {
171         try
172         {
173             // Create the string writer
174             StringWriter writer = new StringWriter(new CharBuffer(1024));
175             writer.openWrite();
176             
177             // Parse the page
178             VfsStream stream = new VfsStream(is, null);        
179             QuercusPage page = this.quercus.parse(new ReadStream(stream));
180             
181             // Execute the page
182             WriteStream ws = new WriteStream(writer);
183             Env env = new Env(this.quercus, page, ws, null, null);   
184             env.start();
185                         
186             // Execute the page
187             Value value = page.executeTop(env);
188             
189             // Make sure we flush because otherwise the result does not get written
190             ws.flush();
191            
192             // Write to output
193             String result = ((StringWriter)ws.getSource()).getString();            
194             if (out != null)
195             {
196                 out.write(result);
197                 out.flush();
198             }            
199             
200             // Return the result
201             return value.toJavaObject();
202         }
203         catch (Exception exception)
204         {
205             throw new ScriptException("Error executing script.", exception);
206         }
207     }
208         
209     /**
210      * 
211      * @param model
212      * @return
213      */
214     @SuppressWarnings("unchecked")
215     private Map<String, Object> getModel(Object model)
216     {
217         Map<String, Object> result = null;
218         if (model != null && model instanceof Map)
219         {
220             result = (Map<String, Object>)model;
221         }
222         else
223         {
224             result = new HashMap<String, Object>(2);
225         }
226         return result;
227     }
228     
229     /* (non-Javadoc)
230      * @see org.alfresco.web.scripts.TemplateProcessor#hasTemplate(java.lang.String)
231      */
232     public boolean hasTemplate(String template)
233     {
234     	boolean has = false;
235     	
236     	try
237     	{
238     		Object templateSource = getTemplateLoader().findTemplateSource(template);
239     		has = (templateSource != null);
240     	}
241     	catch (IOException ioe) { }
242     	
243     	return has;
244     }
245     
246     /* (non-Javadoc)
247      * @see org.alfresco.web.scripts.TemplateProcessor#process(java.lang.String, java.lang.Object, java.io.Writer)
248      */
249     public void process(String templatePath, Object model, Writer out)
250     {
251     	// TODO: is there a way to avoid the buffer and process input stream directly?
252     	try
253     	{
254 	    	StringBuilder sb = new StringBuilder();
255 	    	
256 	    	Object templateSource = getTemplateLoader().findTemplateSource(templatePath);
257 	    	Reader reader = getTemplateLoader().getReader(templateSource, this.defaultEncoding);
258 	    	BufferedReader br = new BufferedReader(reader);
259 	
260 	        String eachLine = br.readLine();
261 	        while (eachLine != null) 
262 	        {
263 	          sb.append(eachLine);
264 	          sb.append("\n");
265 	          eachLine = br.readLine();
266 	        }
267 	        
268 	        String template = sb.toString();
269 	        
270 	        processString(template, model, out);
271     	}
272     	catch (IOException ioe)
273     	{
274     		logger.error(ioe);
275     	}
276     }
277     
278     /* (non-Javadoc)
279      * @see org.alfresco.web.scripts.TemplateProcessor#processString(java.lang.String, java.lang.Object, java.io.Writer)
280      */
281     public void processString(String template, Object model, Writer out)
282     {
283     	executePHPScript(template, out, getModel(model));
284     }
285 
286     /* (non-Javadoc)
287      * @see org.alfresco.web.scripts.TemplateProcessor#reset()
288      */
289     public void reset()
290     {
291     	init();
292     }
293     
294 }