在 WebSphere sMash 中集成 Java 和 PHP

WebSphere® sMash 环境支持基于流行的 Web 技术(如 PHP)快速开发交互式 Web 应用程序,允许从 PHP 脚本重用现有的 Java™ 资产。本文将介绍 Java Bridge 并说明如何从 PHP 访问 Java 类。

Anthony Phillips, 软件开发人员, IBM

照片:Ant PhillipsAnt Phillips 是位于英国 Hursley 的 IBM Java Technology Centre 的一位软件开发人员。他当前的工作重点是 WebSphere sMash(一个用于创建动态 Web 应用程序的简单环境)。在加入 IBM 前,Ant 曾在英国 Newbury 的一家创新型创业公司担任技术负责人。此前,Ant 曾在 Sony® 和 Microsoft® 工作过,先后在东京、西雅图及二者之间的其他几个地方长期呆过。业余时间,除了和妻子及两个孩子在一起外,他还非常喜欢体育运动。



Zoe Slattery, 软件工程师, IBM

照片:Zoe SlatteryZoe Slattery 是在英国 Hursley 工作的一位 IBM 软件工程师。她当前所感兴趣的领域是开源开发和 PHP。过去,Zoe 曾在 IT 行业的很多领域工作过。她最初担任 FORTRAN 程序员,进行用于密集数字计算的并行系统方面的工作,管理欧洲中期天气预报中心 (European Centre for Medium Range Weather Forecasts) 的数据存档。最近,她一直在负责管理进行 IBM 的 Java Virtual Machine 和 Java 类库的开源实现的开发团队。



2008 年 12 月 29 日

开始之前

首先花数分钟细读一下 Project Zero 网站,以对其进行熟悉。您可以加入 Project Zero 社区、参与项目工作或加入论坛,您可以在论坛中对项目的每个开发阶段进行评论。本文假定您已经在计算机上安装了合适的 Java Development Kit (JDK)。您还需要熟悉 PHP 的概念。

建议阅读 Project Zero、WebSphere sMash 和 PHP 入门。其中介绍了如何下载 WebSphere sMash 和创建 PHP 应用程序。这篇文章假定您已经有了能与 PHP 一起工作的 WebSphere sMash。请注意,该文章中只有到“运行应用程序”部分的步骤是有必要进行的工作。

编者注: IBM® WebSphere sMash 和 IBM WebSphere sMash Developer Edition 基于得到高度认可的 Project Zero 孵化器项目。Project Zero 是 WebSphere sMash 的开发社区,并将继续为开发人员提供开发应用程序的免费平台,为开发人员提供最新版本、最新功能和社区支持。


引言

本文说明如何使用 Java Bridge 从 PHP 访问 Java 类。其中将讨论如何调用 Java 方法和访问字段(实例和静态字段)。另外还将讨论异常处理及 PHP 和 Java 之间的类型转换。


ZSL、WebSphere sMash 和 Apache Lucene

有关真实示例,本文逐步说明了使用 PHP 编写的简单搜索引擎的创建工作,此引擎可使用 Apache Lucene 建立文件索引和进行搜索。Apache Lucene 是一个高性能的全功能文本搜索引擎库,完全使用 Java 编写。这是一项适合很多需要全文本搜索的应用程序使用的技术。

ZSL 在其编写的 WebSphere sMash 应用程序中使用 Apache Lucene。ZSL® 希望改进其开发人员之间的信息共享情况。为了解决此问题,他们建立了 Mashup 来对源代码和文档库(PDF、PowerPoint、Word、Excel 和很多其他格式)进行索引。此应用程序提供了从整个企业方便快捷访问代码片段的能力。


在 WebSphere sMash 中创建应用程序

第一步是在 Eclipse 中创建新项目:

  1. 选择 File -> New -> Project...,并在对话框中展开 Zero 目录。
  2. 选择 WebSphere sMash PHP Application,并单击 Next,如图 1 中所示。
  3. 给项目命名(例如 MyJavaProject)并单击 Finish。您的项目现在已创建。
    图 1. “创建新的 WebSphere sMash 项目”对话框
    “创建新的 WebSphere sMash 项目”对话框

创建并调用 Java 对象

接下来,编写创建和调用 Java 对象的 PHP 脚本:

  1. 右键单击 public 文件夹并选择 New -> File
  2. 给文件命名(例如 Java.php)并单击 Finish
  3. 在文件中添加以下代码:
    <?php
        $file = new Java("java.io.File", __FILE__, FALSE);
        var_dump($file);
        var_dump($file->isDirectory());
    ?>
  4. 在 Eclipse 中右键单击项目名,并选择 Run As -> WebSphere sMash Application,以运行示例代码。
  5. 会在 localhost 的端口 8080 启动 Web 服务器。
  6. 现在可以打开浏览器,指向 http://localhost:8080/Java.php,您将看到以下输出,如图 2 中所示。
    图 2. 调用 Java 对象的 Web 浏览器输出
    调用 Java 对象的 Web 浏览器输出

此示例代码显示了使用内置 Java 类的 PHP 脚本。此 Java 类创建 Java 类的实例,并调用最佳匹配构造函数(从脚本传递任何参数)。在此示例中,根目录为 "/"FALSE。脚本将其存储在名为 $file 的 PHP 变量中。脚本然后像处理普通 PHP 对象一样对该对象调用方法,在此示例中我们将其命名为 isDirectory 方法。

此功能非常强大,允许 PHP 脚本访问任何 Java 类。请注意,该 Java 类必须位于应用程序类路径上,java.io.File 是核心 Java 类库的一部分,因此始终可用。


使用 Java 集合类

Java 提供了丰富的集合类,包括映射、集、列表和队列。此示例代码显示了 PHP 脚本可以如何利用这些类。正如前面所述,创建一个新 PHP 脚本(例如,MoreJava.php)并添加以下代码:

<?php
    $map = new Java("java.util.HashMap");
    
    $map->put("title", "Java Bridge!");
    $array = array(1, 2, 3, 4, 5);
    $map->put("stuff", $array);
    var_dump($map->get("stuff"));
    echo $map->get("title");
?>

现在可以打开浏览器,指向 http://localhost:8080/MoreJava.php,您将看到以下输出,如图 3 中所示。

图 3. 使用 Java 集合类的 Web 浏览器输出
使用 Java 集合类的 Web 浏览器输出

PHP 脚本:

  • 创建 Java HashMap 类的实例。
  • 将包含 Java Bridge! 的字符串存储在映射中。
  • 突出 Java 和 PHP 类型之间的互操作性。
  • 创建 PHP 数组,并将其存储在 Java 映射中,如下面的代码所示。
    $array = array(1, 2, 3, 4, 5);
    $map->put("stuff", $array);

对映射进行 put 调用时,PHP 数组会转换为最接近的 Java 类型,即 Java Map。与此类似,当 get 调用从 $map 读回值时,会将其转换回常规 PHP 数组。这可以在不进行任何复制的情况下进行,因为 PHP 数组具有两个个性类型,即 PHP 数组和 Java 映射。


遍历 Java 集合

尝试使用以下代码替换 MoreJava.php 脚本:

<?php
	$list = new Java("java.util.ArrayList");
	var_dump($list);
	$date = new Java("java.util.Date", 70, 9, 4);
	echo "<br/>";
	
	$list->add("Java Bridge!");
	$list->add($date);
	$list->add(array(1, 2, 3, 4, 5));
	
	$iterator = $list->iterator();
	while ($iterator->hasNext() == TRUE) { 
	     var_dump($iterator->next()); echo "<br/>";
	}
?>

现在可以打开浏览器,指向 http://localhost:8080/MoreJava.php,您将看到以下输出,如图 4 中所示。

图 4. 遍历 Java 集合的 Web 浏览器输出
遍历 Java 集合的 Web 浏览器输出

此示例显示了使用 Java ArrayList 类的 PHP。而且,还从 ArrayList 获得了迭代器,并从头到尾对集合进行了扫描。迭代器的内容按顺序写入,首先是字符串 Java Bridge!,然后是 Java Date 对象,最后是包含五个数字的 PHP 数组。


访问静态方法和字段

静态方法和字段使用 JavaClass 进行访问。对 Java 有一点差异,其中静态方法和字段直接使用类名称进行直接访问。以下代码说明了如何对 java.lang.System 调用 currentTimeMillis

<?php
	$system = new JavaClass("java.lang.System");
	var_dump($system);
	echo("</br>Current time: ".
		$system->currentTimeMillis()."</br>");
?>

图 5 显示了在浏览器中运行此脚本的输出。

图 5. 访问静态方法的 Web 浏览器输出
访问静态方法的 Web 浏览器输出

访问静态字段与此类似。下面的代码显示 java.lang.Integer 类中的 MIN_VALUE 静态字段:

<?php
	$integerClass = new JavaClass("java.lang.Integer");
	var_dump($integerClass->MIN_VALUE);
?>

图 6 显示了在浏览器中运行此脚本的输出。

图 6. 访问静态字段的 Web 浏览器输出
访问静态字段的 Web 浏览器输出

在 PHP 中捕获 Java 异常

Java Bridge 将 Java 异常转换为 JavaException 的实例。这是在 PHP 脚本中捕获的通用 PHP 异常。以下代码片段显示了对 java.lang.System 上的 getProperty 的无效调用:

<?php
	try { 
		$system = new JavaClass("java.lang.System");
    		$system->getProperty(FALSE);
	} catch (JavaException $exception) { 
	    echo "Cause: ".$exception->getCause();
	}
?>

图 7 显示了在浏览器中运行此脚本的输出。

图 7. 捕获 Java 异常的 Web 浏览器输出
捕获 Java 异常的 Web 浏览器输出

请注意,在 WebSphere sMash 1.0 中,getCause 方法将返回基础 Java 异常的类名称,而不是 所导致的异常本身。在最新的 Project Zero 构建版本中,这个奇怪的行为已经得到修复,将返回实际的 Java 异常。


从 Java 到 PHP 的类型转换

表 1 显示了 Java 类型如何转换为 PHP 类型。常规的做法是转换为尽可能减少潜在损失的类型(例如,将 int 转换为 byte)。另请注意,转换同样适用于受限和不受限 Java 类型,例如 Integerint

表 1. 从 Java 到 PHP 的类型转换
Java 类型 PHP 类型注释
nullnull 
Integer/intint 
Double/doubledouble 
Boolean/booleanbool 
Byte/byte int 
Character/charint 
Short/shortint 
Long/longint 
Float/floatdouble 
byte[]string 
Stringstring此 PHP 字符串使用运行时编码进行编码。
Maparray各个元素的类型按照此表进行转换,包括嵌套映射。
Object[]array请参见数组转换
Anything Else! n/a 这使用 Java Bridge 包装,成为通用 PHP 对象。

Project Zero 网站提供了关于转换的更多信息。


Java Bridge 局限性

Java Bridge 旨在作为让 PHP 脚本使用 Java 类的简单方法使用。考虑这一点,有几个其中未包含的更为高级的功能。其中最重要的是可靠地调用重载方法。

Java Bridge 完全 根据提供的参数数量选择方法或构造函数。如果存在多个可能性,则 Java Bridge 将选择第一个方法或构造函数并试用。这极为简单,使用错误的参数类型调用构造函数或方法时,会引发异常。

使用签名选择重载

选择合适的重载的问题已经在最新的 Project Zero 构建版本(在 WebSphere sMash 1.0 中 可用)中通过添加新的 JavaSignature 类得到了解决。JavaSignature 允许脚本通过定义参数类型查找以下内容来明确指定调用哪个构造函数或方法:

<?php
	$signature = new JavaSignature(JAVA_STRING);
	$string = new Java("java.lang.String", 
          $signature, "Hello World!");

	var_dump($string->toLowerCase());
	var_dump($string->split(" "));
	var_dump($string->toUpperCase());
?>

JavaSignature 的参数来自以下 PHP 常量:

  • JAVA_BOOLEAN
  • JAVA_BYTE
  • JAVA_CHAR
  • JAVA_SHORT
  • JAVA_INT
  • JAVA_LONG
  • JAVA_FLOAT
  • JAVA_DOUBLE
  • JAVA_STRING
  • JAVA_OBJECT

在前面的示例中,示例选择了 java.lang.String 上的构造函数,此构造函数接受单个 Java String 作为其参数 (JAVA_STRING)。多个参数使用逗号分隔,例如,newJavaSignature(JAVA_STRING, JAVA_INT)。可以使用 JAVA_ARRAY 修饰符指定 Java 类型的数组。例如,以下代码将选择字符串数组: newJavaSignature(JAVA_STRING | JAVA_ARRAY).

以下代码片段显示了一个 JavaSignature,它选择 java.lang.String 上的 valueOf 方法的重载。请注意签名作为第一个参数如何传递到方法调用。Java Bridge 知道对其进行检查来获取签名。

<?php
	$class = new JavaClass("java.lang.String");
	$signature = new JavaSignature(JAVA_INT);
	var_dump($class->valueOf($signature, 1234567890));
?>

方法名称区分大小写

PHP 中的方法不区分大小写,而 Java 区分大小写。Java Bridge 区分大小写,因此 PHP 方法名称必须与 Java 方法名称完全匹配。

静态方法和字段

Java 开发人员习惯使用类名称调用静态方法和字段(例如,Integer.MAX_VALUE)。这在 PHP 中尚不可能,因此必须使用 JavaClass。脚本创建 JavaClass 的实例,并使用此实例调用静态方法和访问静态字段。这种做法并不常见,因为需要开发人员创建仅用于访问非实例(静态)方法和字段的对象的实例。

遍历集合

前面的示例代码说明了如何遍历 Java 集合。这相当冗长,比 PHP foreach 语句的开销少。目前,Java Bridge 并未将 Java 迭代器和 PHP foreach 语句集成。以下代码说明了如何在 PHP 中使用 Java 迭代器:

$iterator = $list->iterator();
while ($iterator->hasNext() == TRUE) { 
	 var_dump($iterator->next()); echo "<br/>";
}

将所有这些应用到实际示例中

下一部分将前面部分所讨论的内容集中到 Java Bridge 的真实使用示例中。此示例创建了使用 PHP 编写的简单搜索引擎,可以使用 Apache Lucene 建立文件索引和进行搜索。Apache Lucene 是一个高性能的全功能文本搜索引擎库,完全使用 Java 编写。它适合于需要全文本搜索(特别是跨平台)的几乎任何应用程序。有关更多信息,请参见 Apache Lucene 站点。

创建索引

第一步是获得 Lucene。我们要使用最新版本的 Lucene(尽管其工作并不完美),因为我们希望与 Lucene 的 PHP 实现(基于 Lucene 2.2.0)进行比较。

  1. 下载 lucene-2.2.0.tar.gz。例如,从以下镜像位置下载: http://mirror.cc.columbia.edu/pub/software/apache/lucene/java/archive/.
  2. 解压缩文件(或运行 tar -xvzf lucene-2.2.0.tar.gz)。
  3. 找到两个 JAR 文件,lucene-core-2.2.0.jarlucene-demos-2.2.0.jar

下一步编写创建 Lucene 搜索引擎的 PHP 脚本:

  1. 在 Java 透视图中,通过选择 File -> New -> Other 来创建新应用程序。选择 WebSphere Smash PHP Application 并将其命名为 Lucene
  2. 右键单击 public 文件夹并选择 New -> File
  3. 将文件命名为 index.php 并单击 Finish
  4. 将前面的两个 Lucene JAR 文件复制到 Lucene/lib 目录中。
  5. 要确保 WebSphere sMash 使用 Lucene Java 库,请右键单击项目名称 Lucene 并选择 WebSphere sMash Tools -> Resolve
  6. 在文件中添加以下代码:
    <html>
    <head>
       <title>Search Index</title>
    </head>
    <body>
    	<form name="input" action="/index.php" method="POST">
    		<label for="directory">Directory:</label>
       	    <input type="text" name="directory">
    		<label for="extension">File Extension:</label>
        	<input type="text" name="extension">
          	<input type="submit" name="action" value="Index!">
       	</form>   
    </body>
    </html>
  7. 右键单击项目名称 Lucene 并选择 WebSphere sMash Application -> Run,以运行应用程序。将 Web 浏览器指向本地服务器,如 http://localhost:8080/index.php。其外观与图 8 中所示类似。
    图 8. “选择目录和文件扩展名”页
    “选择目录和文件扩展名”页
  8. 不要尝试建立任何内容的索引,因为还需要添加其他代码。最后,提交表单时,PHP 脚本将创建 Lucene 搜索索引,并使用目录中具有匹配扩展名的所有文件对其进行填充。也会向下递归到开始目录,并在此过程中添加相应的文件。
  9. 接下来,将以下 PHP 代码添加到 index.php 中:
    <?php
    
    $directory = dirname(__FILE__)."/../index";
    if (file_exists($directory) === FALSE) {
    	mkdir($directory);
    }
    
    define("INDEX_DIRECTORY", $directory);
    
    try {	
    	$extension = zget('/request/params/extension');
    	if (strlen($extension) > 0) {
    		$directory = zget('/request/params/directory');
    		if (strlen($directory) > 0) {
    			index_directory($directory, $extension);
    		}
    	}
    } catch (JavaException $exception) {
    	echo "Index creation failed [".
    	$exception->getMessage()."]</br>";	
    }
    ?>
  10. 尚不要运行,因为还没有完成!代码从全局上下文获取表单变量,并检查是否已经填充。如果已经跳出,则会调用 index_directory 函数。此函数将在后面进行说明,负责将任何匹配的文件添加到 Lucene 搜索引擎。
  11. 接下来,将以下 PHP 代码添加到 index.php 中:
    /**
     * This creates an index from scratch and adds all the documents
     * by recursing from the directory passed in. It also checks
     * each candidate file to see if it matches the file extension.
     */
    function index_directory($path, $extension) {
        	echo "Indexing! [".$path.",".$extension."]</br>";
        	
    	// Uses the SimpleAnalyzer because we will do a performance comparison 
              with the PHP 
    	// implementation of Lucene in the Zend Framework and it is the closest match	
    	$analyser = new Java("org.apache.lucene.analysis.SimpleAnalyzer");
    	$policy = new Java("org.apache.lucene.index.KeepOnlyLastCommitDeletionPolicy");
    	
    	$file = new Java("java.io.File", INDEX_DIRECTORY, FALSE);
    	$file_directory = new JavaClass("org.apache.lucene.store.FSDirectory");
    	$directory = $file_directory->getDirectory($file);
    	
    	$writer = new Java("org.apache.lucene.index.IndexWriter", 
    		$directory, TRUE, $analyser, TRUE, $policy);
    	
    	$writer->setUseCompoundFile(FALSE);
    
    	// Insert some calls to microtime() for comparison
    	$start_time = get_microtime();	
    	recursive_index_directory($writer, $path, $extension);
    	$count = $writer->docCount();
    
    	// Lucene only matches the first 10,000 tokens by default
    	$writer->setMaxFieldLength(1000000);
    	$end_index_time = get_microtime();
    	
    	$writer->optimize();
    	$end_time = get_microtime();
    	$writer->close();	
    	
    	echo "Finished indexing [".$count." documents]</br>";
    	$t1 = $end_index_time - $start_time;
    	$t2 = $end_time - $end_index_time;
    	echo "Time to index  = $t1 </br>";
    	echo "Time to optimize  = $t2 </br>";
    }

    本文将不会介绍 Java Lucene API 的详细信息。简单来说,此代码用于创建 IndexWriter 对象。这是键索引对象,当脚本在目录中进行递归时,文件添加到其中。请注意,可以根据很多不同的源(例如,RAM 磁盘)进行反向索引。在此示例中,从常规文件系统读取文件,因此将使用 FSDirectory 类。

    一旦设置 IndexWriter,脚本将调用 recursive_index_directory 来实际进行索引工作。此函数传递 IndexWriter,即起始目录以及候选文件要匹配的文件扩展名。

    以下部分的代码是索引脚本的最后一部分。其中的大部分代码都是通用 PHP 脚本,枚举了目录中的所有文件,并按顺序逐一处理。确定了要索引的文件后,将会创建 FileDocument。需要使用文件的完全限定路径建立此对象,然后将其添加到 IndexWriter
    /**
     * Processes a file by adding it to the indexer.
     */
    function index_file($writer, $path) {
    	echo "Indexing file [".$path."]</br>";
    
    	try {		
    		// A few of the files we indexed in the examples have non
    		// UTF-8 characters so we just skip indexing those files!
    		
    		$file = new Java("java.io.File", $path, FALSE);
    		$file_document = new JavaClass("org.apache.lucene.demo.FileDocument");
    		$document = $file_document->Document($file);
    		$writer->addDocument($document);
    
    	} catch (JavaException $exception) {
    		echo "Invalid characters in file!\n";
    	}	
    }
    
    function get_microtime(){
    	list($part_one,$part_two) = explode(' ',microtime());
    	return ((float) $part_one + (float) $part_two);
    }
    
    /**
     * Indexes all matching files (by extension) in the directory tree. 
     */
    function recursive_index_directory($writer, $path, $extension) {
        echo "Indexing directory [".$path."]</br>";
    	
        // Remove any trailing slash first
        if (substr($path, -1) == '/') {
            $path = substr($path, 0, -1);
        }
        
        // Make sure the directory is valid
        if (is_dir($path) == TRUE) {
    	    if (is_readable($path) == TRUE) {
    		 $handle = opendir($path);
    		
    		  // Scan through the directory contents
        		  $extension_length = strlen($extension);
    		  while (FALSE !== ($item = readdir($handle))) {
    		    if ($item != '.') {
    		        if ($item != '..') {
    		        $index_path = ($path.'/'.$item);
    		        if (is_dir($index_path) == TRUE) {
    		             recursive_index_directory(
    			        $writer, $index_path, $extension);
    		   } else {
    
    		         $position = strpos(strtolower($index_path), $extension);
    		                	
    		         // Very rough and ready way to check for trailing extension!
    		         if ($position == (strlen($index_path)-$extension_length)) {
    			  index_file($writer, $index_path, $extension);
    		                	}
    		                }
    		            }
    		        }
    		    }		
    		    closedir($handle);
    	    }
        }
        return TRUE;
    }
  12. 将 Web 浏览器指向脚本,并填写表单变量,如图 9 中所示。
    图 9. 建立目录索引时的 Web 浏览器输出
    建立目录索引时的 Web 浏览器输出
  13. 单击 Index!,脚本将对所选文件进行索引。在上面的示例中,脚本指向一段 C 源代码,对五个源文件进行了索引。如果刷新 Eclipse 项目,会发现一个名为 Index 的新目录。此目录包含 Lucene 搜索引擎产生的搜索索引文件,如图 10 中所示。
    图 10. WebSphere sMash 应用程序的目录结构
    WebSphere sMash 应用程序的目录结构

最后一步是写入一个允许用户针对索引运行搜索的表单。

  1. 右键单击 public 文件夹并选择 New -> File
  2. 将文件命名为 search.php 并单击 Finish
  3. 在文件中添加以下代码:
    <html>
    <head>
       <title>Query</title>
    </head>
    <body>
    	<form name="input" action="/search.php" method="POST">
    		<label for="query">Search Query:</label>
       	    <input type="text" name="query">
          	<input type="submit" name="action" value="Search!">
       	</form>   
    </body>
    </html>
  4. 运行此脚本,Web 浏览器中的情况将与图 11 所示类似。
    图 11. 搜索查询页
    搜索查询页
  5. 接下来,将以下 PHP 代码添加到 search.php 中:
    <?php
    
    /**
     * This runs a search through an index already created.
     */
    function search_index($path, $query) {
    	echo "Searching for [".$query."]</br>";
    
    	$file = new Java("java.io.File", $path, FALSE);
    	$file_directory = new JavaClass("org.apache.lucene.store.FSDirectory");
    	$directory = $file_directory->getDirectory($file);
    	$searcher = new Java("org.apache.lucene.search.IndexSearcher", $directory);	
    	$analyser = new Java("org.apache.lucene.analysis.SimpleAnalyzer");
    	$parser = new Java("org.apache.lucene.queryParser.QueryParser", 
    		"contents", $analyser);
    		
    	$parsed_query = $parser->parse($query);	
    	$hits = $searcher->search($parsed_query);	
    	$count = $hits->length();
    	for ($index = 0; $index < $count; $index++) {
    		$document = $hits->doc($index);
    		echo $index.") ".$document->get("path")."</br>";
    	}
    	echo "</br>Finished searching [".$count." hits]</br>";
    }
    
    try {	
    	$directory = dirname(__FILE__)."/../index";
    	define("INDEX_DIRECTORY", $directory);
    	$query = zget('/request/params/query');
    	if (strlen($query) > 0) {
    		search_index($directory, $query);
    	}
    } catch (JavaException $exception) {
    	echo "Index search failed [".$exception->getMessage()."]</br>";	
    }
    ?>

    和前面一样,此脚本使用了多个 Lucene 类。此脚本的要点在于,没有使用 index.php 之类的 IndexWriter 类,而使用了 IndexSearcher。其配置的目录与前面创建索引文件的目录相同。用户在该表单中输入的字符串将随后用于创建查询对象。Lucene QueryParser 提供了解析查询字符串的简单方法。

    有了经过解析的查询后,脚本就准备好在 IndexSearcher 上运行搜索了。这将返回脚本所枚举的命中列表,显示每个项目的路径。
  6. 将 Web 浏览器指向 search.php 并输入一些搜索术语,如图 12 中所示。
    图 12. 运行搜索查询的 Web 浏览器输出
    运行搜索查询的 Web 浏览器输出

在此示例中,找到匹配关键字“TSRM”和“int”的五个匹配项。Lucene 提供了强大的查询语法,能够支持各种搜索术语。关于可能的搜索查询的更多信息,请访问 Apache Lucene 站点。

性能比较

如果仔细分析一下我们添加到 index.php 的源代码,则您会看到对 microtime 的一些调用和一些注释,其中表明我们希望检查性能。

我们执行的检查是简单计时检查。我们非常有兴趣比较一下使用三个不同的软件创建索引的时间:

  • 通过 WebSphere sMash Java Bridge 调用的 Lucene 的 Java 实现。
  • 从 Java 应用程序调用的 Java Lucene。
  • Zend Framework 中的 Lucene 的 PHP 实现。

为了进行公平的比较,我们使用了 Lucene Version 2.2.0,即 Zend 实现所基于的版本。我们还使用了 Lucene SimpleAnalyser。有关 Zend 实现的详细讨论超出了本文的范围。不过,它是 Lucene 代码的可靠端口,会生成与 Java 版本生成的格式完全相同的格式。

性能比较所执行的是对 PHP 5.3 源树下的所有 PHP 测试脚本(*.phpt 文件)进行索引。创建和优化索引所花费的时间如表 2 中所示。

表 2. Lucene 搜索的性能比较
技术时间(秒)
WebSpere sMash Java Bridge 9
Java Lucene 8
Zend Search Lucene 200

这可以让我们简要了解使用这些现成技术的时间比较情况。所有这些计时都打开了 Java JIT,这在 Lucene 之类的应用程序中的执行时间方面会产生巨大的差异。

这些原因都不能作为不使用 Zend 实现的原因。事实上,如果不使用 Java,而您主要的开发语言是 PHP,则使用同样是 PHP 编写的搜索引擎会带来很多优势。方便地理解和修改代码之类的考虑事项可能会比唯一的性能优势更为重要。

更有意思的是使用 PHP 及 Java Bridge 与使用 Java 应用程序间的比较。二者时间接近的事实表明,我们不会在 Java Bridge 中或在 Java VM 上运行 PHP 浪费太多时间。

当然,还有其他通过 PHP 使用 Java Bridge 的实现。例如,Zend Platform 中有一个商业实现,而 sourceforge.net 提供了一个开源实现。虽然我们没有使用这些实现,但它们的存在为将 Java 用于最适用的场合(算法性能)并同时充分利用 PHP 的易用性提供了支持。

如果您重复进行这些实验,可能会发现创建索引的过程中存在着细微的差别。Zend 实现的较为有用的功能之一是,能够创建与 Java 实现格式完全相同的索引,而这就意味着可以使用标准 Java 工具对其进行检查(例如,可以从 Luke 站点下载的 Luke)。这些差异都相对比较容易解释,不会影响耗时比较的结果。例如,PHP 和 Java 分析工具之间就存在细微差别。


结束语

在本文中,您进行了以下工作:

  • 使用 PHP 和 WebSphere sMash 创建应用程序。
  • 使用 Java Bridge 创建和调用 Java 对象。
  • 探索从 PHP 脚本使用 Java 集合的方法。
  • 了解 Java Bridge 如何进行类型强制转换和异常处理。
  • 开发基于 Java Lucene 库的搜索引擎。
  • 了解 Java Lucene 库的性能。

完成本文的学习后,您可以将 Java 库和 PHP 脚本用于更广泛的用途。为什么不在 WebSphere sMash 中将更多的 Java 库与 PHP 结合使用?通过Project Zero 论坛让我们了解您的情况。如果您希望了解关于 Zero Global Context 和其他相关主题的更多信息,请参见下面参考资料部分列出的 WebSphere sMash Developer's Guide

致谢

作者要感谢 ZSL Inc. 的 Naveen Noel Jakkamsetti 在撰写本文过程中提供的帮助。

参考资料

学习

获得产品和技术

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=WebSphere, Java technology, Web development
ArticleID=361327
ArticleTitle=在 WebSphere sMash 中集成 Java 和 PHP
publish-date=12292008