Liferay PHP Portlets: a few recommendations prior development
by Stanislav on Wednesday Dec 14, 2016
It’s well known, that Liferay includes special types of portlets called PHP Portlets which allow to use a standard PHP code in order to provide desired functionality. But there are some issues may occur during PHP-portlet development that are not so evident to fix and find a solution on the internet. Such common issues are covered in this post, solutions to them are also provided.
Liferay is shipped with a Quercus library, which is Caucho Technology. This lib is a Java implementation of PHP5, that is able to treat PHP code and PHP modules, such as PDO, MySQL, and JSON, and convert it to Java. In this way Liferay can handle PHP code having Quercus lib entirely written in Java and PHP being compiled into Java by this lib. During the work with Quercus within Liferay the following problems can take place:
1. The PHP-portlet deployable entity is just a single (in the simplest case) php file that is zipped into an archive. And any PHP archive should have a file named index.php in it, it’s a Liferay entry point. Otherwise the portlet silent fails and you’ll see the message “phpapp.zip is temporarily unavailable”, assuming that phpapp.zip is the deployed archive:
2. During the deployment of PHP-portlet, Liferay searches for all URLs within provided php-files and enhances them according to Liferay specification. For example, Liferay will try to find and replace ahrefs, links, form action urls on your php pages. It will convert HTML form tag into Liferay POST/GET url, taking into account method and action attributes provided, e.g. <form action="index.php" method="post"> will look quite different in the end. Also Liferay retrieves url parameters and wraps them according to its notation. A developer should be aware of these points for smoother implementation process.
3. If you define an empty action url within form tag: <form action="" method="post">, then you’ll see an exception in your logs:
java.lang.StringIndexOutOfBoundsException: String index out of range: 0 at java.lang.String.charAt(String.java:658) at com.liferay.util.bridges.common.ScriptPostProcess.doProcessPage(ScriptPostProcess.java:161) at com.liferay.util.bridges.common.ScriptPostProcess.processPage(ScriptPostProcess.java:68) at com.liferay.util.bridges.common.ScriptPostProcess.postProcessPage(ScriptPostProcess.java:58) at com.liferay.util.bridges.php.PHPPortlet.rewriteURLs(PHPPortlet.java:250)
This’s happening because of URL substitution by Liferay as mentioned in 3.
4. To pass a parameter within a POST/GET method on a php-form addressed to Liferay, you’ll need to add a portlet name prefix to each parameter. For instance, let’s assume that portlet name is phpappzip_WAR_phpappzip and parameter name to pass is search, then the result html will look so:
<form action="index.php" method="post"> <input type="text" name="_phpappzip_WAR_phpappzip_search" value=" . $search . "> </form>
And then, to retrieve the parameter in PHP, need to invoke:
$search = isset($_POST['search']) ? $_POST['search'] : '';
5. One more important thing should be noted: SQL queries on PHP-pages. If you have a sql-query on Liferay side and it’s addressed to an external database, e.g. via PHP PDO, then current query may fail if the request is too time consuming (because of its complexity). The following stacktrace will appear:
13:33:16,693 ERROR [RuntimePageImpl-2][PHPPortlet:241] Error processing PHP com.caucho.quercus.QuercusModuleException: java.io.IOException: Stream closed at com.caucho.quercus.env.StringValue.appendReadAll(StringValue.java:1746) at com.caucho.quercus.env.Post.fillPost(Post.java:158) at com.caucho.quercus.env.Post.fillPost(Post.java:78) at com.caucho.quercus.env.Env.fillPost(Env.java:559) at com.caucho.quercus.env.Env.start(Env.java:936) at com.caucho.quercus.servlet.QuercusServletImpl.service(QuercusServletImpl.java:165) at com.caucho.quercus.servlet.QuercusServlet.service(QuercusServlet.java:592) at com.liferay.util.bridges.php.PHPPortlet.processPHP(PHPPortlet.java:224) Caused by: java.io.IOException: Stream closed at org.apache.catalina.connector.InputBuffer.read(InputBuffer.java:312) at org.apache.catalina.connector.CoyoteInputStream.read(CoyoteInputStream.java:200) at com.caucho.quercus.env.StringValue.appendReadAll(StringValue.java:1733) ... 49 more
The simplest way to overcome this, is just to limit the query returning result. In case of MySQL, just append LIMIT 10 to your query. Usually requests from developer PCs can take more time, comparing to the ones deployed to a server (because of network bandwidth as well), so basically it’s worth to limit queries on dev-machines only.
6. When you deploy a PHP-portlet, it goes by default to Undefined category. To fix it, just add liferay-display.xml to deployable archive.
This article covers a few important issues that can be faced at a time of Liferay PHP portlet development. Solutions are provided as well. I hope it is useful.