Oracle 技术网

使用 PHP 和 Oracle Database 11g 开发 Web 2.0 应用程序

<不要删除此文本,因为它是在浏览器中运行时生成的“主要”标题列表的占位符>

目的

本教程介绍如何结合使用 PHP 和 Oracle Database 11g。

所需时间

大约 1 个小时

概述

PHP 是一种流行的 Web 脚本语言,通常用于创建数据库驱动的 Web 站点。如果您要使用 PHP 和 Oracle 数据库开发 Web 应用程序,本教程提供了有关如何在 Oracle 上使用 PHP 的示例,以助您起步。如果您是初次接触 PHP,请参阅附录:PHP 入门,了解 PHP 语言。

前提条件

为了学习该动手实践讲座,需要安装以下软件:

.

Oracle Database 11g,用户名为“phphol”,口令(区分大小写)为“welcome”。

.

带有 OCI8 1.3 扩展的 PHP 5.3。

.

Apache Web 服务器。

.

本教程使用的所有文件都位于 /home/phphol/public_html 目录中。

创建连接

创建标准连接

要创建一个可在 PHP 脚本生命周期内使用的到 Oracle 的连接,执行以下步骤。

.

查看 $HOME/public_html 目录的 connect.php 文件中包含的以下代码。

<?php
// Create connection to Oracle
$conn = oci_connect("phphol", "welcome", "//localhost/orcl");
if (!$conn) {
   $m = oci_error();
   echo $m['message'], "\n";
   exit;
}
else {
   print "Connected to Oracle!";
}
// Close the Oracle connection
oci_close($conn);
?>

oci_connect() 函数包含用户名、口令和连接字符串。在本示例中,使用了 Oracle 的简单连接 (Easy Connect) 连接字符串语法。它由主机名和数据库服务器名组成。

oci_close() 函数关闭连接。脚本结束时,将自动释放所有非显式关闭的标准连接。

 

.

打开一个 Web 浏览器,输入以下 URL 以显示输出:

http://localhost/~phphol/connect.php

如果连接成功,则显示“Connected to Oracle!”。

 

.

查看 $HOME/public_html 目录的 usersess.sql 文件中包含的以下代码。

column username format a30
column logon_time format a18
set pagesize 1000 feedback off echo on

select username, to_char(logon_time, 'DD-MON-YY HH:MI:SS') logon_time
from v$session
where username is not null;

exit

这是一个在 SQL*Plus(Oracle 的命令行 SQL 脚本编写工具)中运行的 SQL 脚本文件。该 SQL*Plus 脚本显示当前的数据库会话以及这些会话登录到该数据库的时间。

 

.

打开一个终端窗口并输入以下命令以运行该 SQL 脚本:注意,您也可以在 SQL Developer 中执行该脚本。

cd $HOME/public_html
sqlplus -l -s phphol/welcome @usersess.sql 

SQL*Plus 脚本列出了当前的数据库会话。只显示对 SQL*Plus 的会话。来自 oci_connect() 函数的 PHP 连接已经关闭。

 

使用数据库驻留连接池

数据库驻留连接池是 Oracle Database 11g 的一个新特性。对 PHP,它允许 Web 应用程序随着站点吞吐量的增长对连接数进行扩充。它还支持多台计算机上的多个 Apache 进程共享一个小规模的数据库服务器进程池。没有 DRCP,标准 PHP 连接必须启动和终止一个服务器进程。一个非 DRCP 持久性连接即使空闲时也将保留数据库服务器资源。本教程介绍新应用程序或现有应用程序如何在不编写和更改任何应用程序逻辑的情况下使用 DRCP。执行以下步骤:

.

检查 php 是否有 oci8.connection_class 设置。打开一个终端窗口,执行以下命令:

php -r 'echo ini_get("oci8.connection_class"), "\n";'

该连接类告诉数据库服务器池这些连接是相关的。不同连接调用之间的会话信息(如默认的数据格式)可能会保留,以改善系统性能。如果之后其他应用程序通过自己的连接类名重用某个池化服务器,则会话信息将作废。

 

.

查看 $HOME/public_html 目录的 query_pooled.php 文件中包含的以下代码。

<?php

$c = oci_pconnect("phphol", "welcome", "//localhost/orcl:pooled");
$s = oci_parse($c, 'select * from employees');
oci_execute($s);
oci_fetch_all($s, $res);
var_dump($res);
?>

将这些代码与 $HOME/public_html 目录的 query_nonpooled.php 文件中的代码进行比较。

<?php

$c = oci_pconnect("phphol", "welcome", "//localhost/orcl");
$s = oci_parse($c, 'select * from employees'); oci_execute($s); oci_fetch_all($s, $res); var_dump($res);
?>

唯一的不同是 query_pooled.php 的简单连接的连接字符串中多了“:pooled”。

请注意,这两个脚本中使用的 oci_pconnect() 调用都会创建到数据库的持久性连接。也就是说,脚本结束后,oci_pconnect() 创建的连接仍然存在,从而避免了为每个脚本建立和断开数据库连接的开销。

 

.

要运行这两个脚本,需要使用 Apache Benchmark 工具。该命令重复加载一个 Web 页面以测量其性能。在一个终端窗口中执行以下命令:

ab -c 150 -t 30 http://localhost/~phphol/query_pooled.php       

上面的命令针对该脚本发送了 150 个 Apache 并发请求,每 30 秒重发一次。

 

.

现在,看一下打开的数据库连接的数量。打开另一个终端窗口,执行以下命令:

sqlplus phphol/welcome
select username, program from v$session where username = 'PHPHOL';

DRCP 池默认的 MAXSIZE 为 40。您最多可以看到 40 个用户名为 PHPHOL 的连接,这取决于“ab”请求所能处理的 Apache 进程数。运行“ab”的同时可能还需要执行查询以了解池化服务器的工作情况。

Oracle 管理 DRCP 池,达到指定的超时后将缩小 DRCP 池。

 

.

现在,除了运行非池化脚本外,您将运行同一个命令以比较这两个脚本的不同运行结果。在一个终端窗口中执行以下命令:

ab -c 150 -t 30 http://localhost/~phphol/query_nonpooled.php       

 

.

现在,看一下打开的数据库连接的数量。打开另一个终端窗口,执行以下命令:

sqlplus phphol/welcome
select username, program from v$session where username = 'PHPHOL';

比上个脚本返回的行数多出不少。带有

httpd@localhost (TNS V1-V3) 的行

对应于一个保持数据库连接打开的运行的 Apache 进程。对于 PHP,Apache 以多进程方式运行,它产生的每个子进程都可处理一个 PHP 脚本。根据 Apache 分配这些进程处理“ab”请求的方式的不同,您在 V$SESSION 中看到的行数可能有所不同。

比较每次运行完成的请求数。您也许希望每个脚本都运行几次以预热这些缓存。

这两个脚本的性能大体相似。这两个文件中使用的工作负载较小时,由于池化服务器的切换开销较小,因此 query_pooled.php 比 query_nonpooled.php 速度稍慢。但非池化脚本导致每个 Apache 进程都打开一个到数据库的单独连接。对大型站点或者内存受限的站点来说,DRCP 的整体优势非常明显。

 

获取数据

开发 Web 应用程序时的一个常见任务是,查询一个数据库然后在 Web 浏览器中显示结果。您可以使用许多函数查询一个 Oracle 数据库,但查询的基础始终是相同的:

1. 分析要执行的语句。
2. 绑定数据值(可选)
3. 执行语句。
4. 从数据库中获取结果。

要创建一个简单查询并在 HTML 表格中显示结果,执行以下步骤。

.

查看 $HOME/public_html 目录的 query.php 文件中包含的以下代码。

<?php

// Create connection to Oracle
$conn = oci_connect("phphol", "welcome", "//localhost/orcl");

$query = 'select * from departments';
$stid = oci_parse($conn, $query);
$r = oci_execute($stid);

// Fetch the results in an associative array
print '<table border="1">';
while ($row = oci_fetch_array($stid, OCI_RETURN_NULLS+OCI_ASSOC)) {
   print '<tr>';
   foreach ($row as $item) {
      print '<td>'.($item?htmlentities($item):' ').'</td>';
   }
   print '</tr>';
}
print '</table>';

// Close the Oracle connection
oci_close($conn);

?>

oci_parse() 函数分析语句。

oci_execute() 函数执行经过分析的语句。

oci_fetch_array() 函数将查询结果的一行作为一个关联数组进行检索,包括空值。

htmlentities() 函数将所有类似 HTML 标记的文本进行转义,以便在浏览器中正确显示。

 

.

在 Web 浏览器中,输入以下 URL 以显示输出:

http://localhost/~phphol/query.php

查询结果显示在 Web 浏览器中。

OCI_ASSOC 参数将行作为列名称和列数据的关联数组获取。

或者,将 OCI_NUM 参数传递给 oci_fetch_array() 以便将行作为数字数组获取。

 

使用绑定变量

绑定变量允许您使用新值重新执行语句,避免了重新分析语句的开销。 绑定变量提高了代码可重用性,降低了 SQL 注入攻击的风险。

要在本示例中使用绑定变量,执行以下步骤。

.

查看 $HOME/public_html 目录的 bind.php 文件中包含的以下代码。

<?php

function do_fetch($myeid, $s)
{
  // Fetch the results in an associative array
  print '<p>$myeid is ' . $myeid . '</p>';
  print '<table border="1">';
  while ($row = oci_fetch_array($s, OCI_RETURN_NULLS+OCI_ASSOC)) {
    print '<tr>';
    foreach ($row as $item) {
      print '<td>'.($item?htmlentities($item):'&nbsp;').'</td>';
    }
    print '</tr>';
  }
  print '</table>';
}

// Create connection to Oracle
$c = oci_connect("phphol", "welcome", "//localhost/orcl");

// Use bind variable to improve resuability, 
// and to remove SQL Injection attacks.
$query = 'select * from employees where employee_id = :eidbv';
$s = oci_parse($c, $query);

$myeid = 101;
oci_bind_by_name($s, ":EIDBV", $myeid);
oci_execute($s);
do_fetch($myeid, $s);

// Redo query without reparsing SQL statement
$myeid = 104;
oci_execute($s);
do_fetch($myeid, $s);

// Close the Oracle connection
oci_close($c);

?>

 

.

在 Web 浏览器中,输入以下 URL 以显示输出:

http://localhost/~phphol/bind.php

$myeid 变量绑定到 :eidbv 绑定变量,因此重新执行该查询时,$myeid 的新值将传递到该查询。这使您可以再次执行该语句,而无需用新值对其进行重新分析,从而可提高代码的性能。

 

创建事务

在 Oracle 数据库中操作数据(插入、更新或删除数据)时,更改的数据或新数据在提交至数据库前仅在数据库会话中可用。更改的数据提交至数据库,然后可供其他用户和会话使用。这是一个数据库事务。

默认情况下,PHP 执行 SQL 语句时会自动提交事务。可以覆盖这一默认设置,使用 oci_commit()oci_rollback() 函数控制事务。在 PHP 脚本结束时,任何未提交的数据将回滚。

单独提交每个更改会额外增加服务器的负载。通常,您希望提交所有数据或者不提交任何数据。进行您自己的事务控制具有性能和数据完整性优势。

要了解在 Oracle 数据库中使用 PHP 时的事务管理,执行以下步骤。

.

启动 SQL*Plus 并创建一个新表:

sqlplus phphol/welcome
create table mytable (col1 date);

 

.

查看 $HOME/public_html 目录的 trans_rollback.php 文件中包含的以下代码。

<?php

$conn = oci_connect("phphol", "welcome", "//localhost/orcl");

// PHP function to get a formatted date
$d = date('j:M:y H:i:s');

// Insert the date into mytable
$s = oci_parse($conn,
		"insert into mytable values (to_date('" . $d . "', 
         'DD:MON:YY HH24:MI:SS'))");

// Use OCI_DEFAULT to insert without committing
$r = oci_execute($s, OCI_DEFAULT);

echo "Previous INSERT rolled back as no commit is done before script ends";

?>

OCI_DEFAULT 参数覆盖 oci_execute() 的基本行为。

 

.

在 Web 浏览器中,输入以下 URL 以显示输出:

http://localhost/~phphol/trans_rollback.php

该脚本在表中插入一行。

 

.

由于没有任何自动的和显式的提交,因此在脚本完成时,PHP 将回滚数据。为了查看是否有尚未提交的数据,查询该表以查看是否有任何插入的行。在 SQL*Plus 会话中,输入以下命令,从 mytable 表中选择任何行:

select to_char(col1, 'DD-MON-YY HH:MI:SS') time from mytable;

 

.

查看 $HOME/public_html 目录的 trans_commit.php 文件中包含的以下代码。

<?php

$conn = oci_connect("phphol", "welcome", "//localhost/orcl");

// PHP function to get a formatted date
$d = date('j:M:y H:i:s');

// Insert the date into mytable
$s = oci_parse($conn,
		"insert into mytable values (to_date('" . $d . "',
          'DD:MON:YY HH24:MI:SS'))");

// Insert & commits
$r = oci_execute($s);

// The rollback does nothing: the data has already been committed
oci_rollback($conn);

echo "Data was committed\n";

?>

该脚本与 trans1.php 的不同之处在于,插入数据时没有 OCI_DEFAULT。这意味着 oci_execute() 调用将提交新数据。

 

.

在 Web 浏览器中,输入以下 URL 以显示输出:

http://localhost/~phphol/trans_commit.php

现在,数据已提交。

 

.

在 SQL*Plus 会话中,输入以下命令,从 mytable 表中选择任何行:

select to_char(col1, 'DD-MON-YY HH:MI:SS') time from mytable;

如果您多次重新加载该 PHP 脚本,每执行一次都将插入一行。

 

.

您可以比较单独提交每行与在事务结束后提交之间的性能差异。

要测试性能差异,查看 $HOME/public_html 目录的 trans_time_commit.php 文件中包含的以下代码。该脚本在每次插入时提交。

<?php

function do_insert($conn)
{
  $stmt = "insert into mytable values (to_date('01-JAN-08 10:20:35', 
       'DD:MON:YY HH24:MI:SS'))";
  $s = oci_parse($conn, $stmt);
  $r = oci_execute($s);  // automatically commit
}
function do_row_check($conn)
{
  $stid = oci_parse($conn, "select count(*) c from mytable");
  oci_execute($stid);
  oci_fetch_all($stid, $res);
  echo "Number of rows: ", $res['C'][0], "<br>";
}
function do_delete($conn)
{
  $stmt = "delete from mytable";
  $s = oci_parse($conn, $stmt);
  $r = oci_execute($s);
}

// Program starts here
$c = oci_connect("phphol", "welcome", "//localhost/orcl");

$starttime = microtime(TRUE);
for ($i = 0; $i < 10000; $i++) {
  do_insert($c);
}
$endtime = microtime(TRUE) - $starttime;
echo "Time was ".round($endtime,3)." seconds<br>";

do_row_check($c);  // Check insert done
do_delete($c);     // cleanup committed rows

?>

将这段代码运行几次,您将了解插入 10,000 行所需的时间。

 

.

现在,运行 trans_time_explicit.php 脚本。该脚本唯一的区别在于 do_insert() 函数中加入了 OCI_DEFAULT,这样就不会自动提交,并且在插入循环末尾添加了显式提交:

...

function do_insert($conn) {
  $stmt = "insert into mytable values (to_date('01-JAN-08 10:20:35', 
      'DD:MON:YY HH24:MI:SS'))";
  $s = oci_parse($conn, $stmt);
  $r = oci_execute($s, OCI_DEFAULT);  // Don't commit
}

...

$starttime = microtime(TRUE);
for ($i = 0; $i < 10000; $i++) {
  do_insert($c);
}
oci_commit($c);
$endtime = microtime(TRUE) - $starttime;

...

重新运行该测试。插入时间明显减少。

通常,您希望提交所有数据或者不提交任何数据。定义您自己的事务控制具有性能和数据完整性优势。

 

使用存储过程

PL/SQL 是 Oracle 对 SQL 的过程语言扩展。PL/SQL 过程和函数存储在数据库中。使用 PL/SQL 可以让所有数据库应用程序重用逻辑,无论应用程序以何种方式访问数据库。许多与数据相关的操作在 PL/SQL 中的执行速度比将数据提取到一个程序中(例如,PHP)然后再进行处理的速度要快。Oracle 还支持 Java 存储过程。

在本教程中,您将创建一个 PL/SQL 存储过程并在一个 PHP 脚本中调用它。执行以下步骤:

.

启动 SQL*Plus,用以下命令创建一个新表 ptab

sqlplus phphol/welcome
create table ptab (mydata varchar(20), myid number);

 

.

在 SQL*Plus 中,使用以下命令创建一个存储过程 myproc,以将数据插入到 ptab 表中:

create or replace procedure
myproc(d_p in varchar2, i_p in number) as
begin
  insert into ptab (mydata, myid) values (d_p, i_p);
end;
/

 

.

查看 $HOME/public_html 目录的 proc.php 文件中包含的以下代码。查看以下代码:

<?php

$c = oci_connect('phphol', 'welcome', '//localhost/orcl');
$s = oci_parse($c, "call myproc('mydata', 123)");
oci_execute($s);
echo "Completed";

?>      

 

.

在 Web 浏览器中,输入以下 URL 以显示输出:

http://localhost/~phphol/proc.php

PHP 脚本通过调用存储过程 myprocptab 表中新建了一行。ptab 表新增了一行,值为“mydata”和 123

Switch to your SQL*Plus session and query the table to show the new row:
select * from ptab; 

 

.

proc.php 进行扩展使其使用绑定变量。将 proc.php 进行如下修改(黑体字表示所作修改):

<?php

$c = oci_connect('phphol', 'welcome', '//localhost/orcl');
$s = oci_parse($c, "call myproc('mydata', :bv)");
$v = 456;
oci_bind_by_name($s, ":bv", $v);
oci_execute($s);
echo "Completed";

?>

oci_bind_by_name() 函数将 PHP 变量 $v 绑定到“:bv”,通过更改 $v 中的值体验插入值的修改。

重新运行以下 URL:

http://localhost/~phphol/proc.php

再次查询该表以显示新行:

select * from ptab;

 

.

PL/SQL 存储函数通常在 Oracle 数据库中使用。在 SQL*Plus 中,创建一个 PL/SQL 存储函数 myfunc(),以便向 ptab 表中插入一行并且返回插入值的两倍

create or replace function
myfunc(d_p in varchar2, i_p in number) return number as
begin
  insert into ptab (mydata, myid) values (d_p, i_p);
  return (i_p * 2);
end;
/

 

.

查看 $HOME/public_html 目录的 func.php 文件中包含的以下代码。查看以下代码:

<?php

$c = oci_connect('phphol', 'welcome', '//localhost/orcl');
$s = oci_parse($c, "begin :bv := myfunc('mydata', 123); end;");
oci_bind_by_name($s, ":bv", $v, 10);
oci_execute($s);
echo $v, "<br>\n";
echo "Completed";

?>

由于要返回一个值,因此将 oci_bind_by_name() 的可选长度参数设为 10,这样 PHP 就可以分配能够存储 10 位的正确内存量了。

重新运行以下 URL:

http://localhost/~phphol/func.php

 

提高查询性能

本节演示一些提高查询性能的方法。执行以下步骤:

.

首先,创建一个包含大量行的表。查看以下 fetch_prefetch.sql 脚本。

set echo on
drop table bigtab;
create table bigtab (mycol varchar2(20));
begin
for i in 1..20000
loop
insert into bigtab (mycol) values (dbms_random.string('A',20));
end loop;
end;
/
show errors
exit

在您的 sqlplus 会话中,运行以下命令:


connect phphol/welcome
@fetch_prefetch

 

.

查看 $HOME/public_html 目录的 fetch_prefetch.php 文件中包含的以下代码。

<?php
require('helper.php');
function do_prefetch($c, $pf)
{
$stid = oci_parse($c, "select mycol from bigtab");
oci_execute($stid);
oci_set_prefetch($stid, $pf);
oci_fetch_all($stid, $res);
return $res;
}
$c = oci_connect("phphol", "welcome", "//localhost/orcl");
$pf_a = array(1, 10, 500, 2000); // Prefetch values to test
foreach ($pf_a as $pf_num)
{
$start = currTime();
$r = do_prefetch($c, $pf_num);
$t = elapsedTime($start);
print "Prefetch $pf_num - Elapsed time is: " . round($t, 3) . " seconds<br>\n";
}
?>

 

.

加载以下 URL 以显示输出:

http://localhost/~phphol/fetch_prefetch.php

重新加载几次,看一下平均时间。

可以在 PHP 初始化文件 php.ini 中设置默认的预取大小。在 PHP 5.3 之前,默认预取大小为 10 行。在 5.3 中,为 100 行。您应该为您的应用程序选择一个合适的默认值,针对需要不同值的特定查询使用 oci_set_prefetch()

 

.

本节介绍允许从 PL/SQL 过程检索 PHP 数组或向 PL/SQL 过程传递 PHP 数组的 oci_bind_array_by_name() 函数。查看以下 fetch_bulk.sql 脚本。

set echo on
create or replace package fetchperfpkg as
type arrtype is table of varchar2(20) index by pls_integer;
procedure selbulk(p1 out arrtype);
end fetchperfpkg;
/
create or replace package body fetchperfpkg as
procedure selbulk(p1 out arrtype) is
begin
select mycol bulk collect
into p1
from bigtab;
end selbulk;
end fetchperfpkg;
/
show errors
exit

该脚本使用 PL/SQL BULK COLLECT 语句创建从 BIGTAB 获取数据的 PL/SQL 程序包,并以 PL/SQL 数组的形式返回结果。在您的 sqlplus 会话中,运行以下命令:

sqlplus phphol/welcome
@fetch_bulk

 

.

查看 $HOME/public_html 目录的 fetch_bulk.php 文件中包含的以下代码。

<?php

require('helper.php');
function do_sel_bulk($c)
{
$s = oci_parse($c, "begin fetchperfpkg.selbulk(:a1); end;");
oci_bind_array_by_name($s, ":a1", $res, 20000, 20, SQLT_CHR);
oci_execute($s);
return($res);
}
$c = oci_connect("phphol", "welcome", "//localhost/orcl");
$start = currTime();
$r = do_sel_bulk($c);
$t = elapsedTime($start);
print "Bulk collect - Elapsed time is: " . round($t, 3) . " seconds\n<br>";
?>

该代码调用 PL/SQL 程序包并绑定一个 PHP 变量以存放返回的数据数组。不需要 OCI8 获取调用。

该示例不输出返回的结果。如果您想看到返回结果,在该函数的返回语句前添加“var_dump($res);”。输出显示了您之前运行的 fetch_prefetch.sql 创建的 20 字符的随机数据字符串。

 

.

加载以下 URL 以显示输出:

http://localhost/~phphol/fetch_bulk.php

重新加载几次,看一下平均时间。

 

.

通过添加 WHERE 子句更改 fetch_bulk.sql 程序包代码以减少获取的行数。

set echo on
create or replace package fetchperfpkg as
type arrtype is table of varchar2(20) index by pls_integer;
procedure selbulk(p1 out arrtype);
end fetchperfpkg;
/
create or replace package body fetchperfpkg as
procedure selbulk(p1 out arrtype) is
begin
select mycol bulk collect
into p1
from bigtab where rownum < 100;
end selbulk;
end fetchperfpkg;
/
show errors
exit

在您的 sqlplus 会话中,运行以下命令:


sqlplus phphol/welcome
@fetch_bulk

 

.

重新加载以下 URL 以显示输出:

http://localhost/~phphol/fetch_bulk.php

尝试不同的行数,观察效果并将结果与之前的 fetch_prefetch.php 脚本进行比较。

根据您的数据、类型、大小及业务需求,测试可帮助您了解哪种获取方法对您的应用程序更快。

 

使用 LOB:上载和查询图像

Oracle 字符大对象 (CLOB) 和二进制大对象 (BLOB) 列(以及 PL/SQL 变量)可包含大量数据。创建这些对象以优化 Oracle 存储的方法有多种。此外,还预先提供了一个程序包 DBMS_LOB,通过它可以轻松地在 PL/SQL 中操作这些对象。

要创建一个小型应用程序以将图像加载并显示到数据库,执行以下步骤。

.

在执行本节之前,先创建一个表来存储 BLOB。在 SQL*Plus 中,以 phphol 用户身份登录,执行以下命令:

sqlplus phphol/welcome
create table btab (blobid number, blobdata blob);

 

.

查看 $HOME/public_html 目录的 blobins.php 文件中包含的以下代码。

<?php
if (!isset($_FILES['lob_upload'])) {
// If nothing uploaded, display the upload form
?>

<form action="<?php echo $_SERVER['PHP_SELF']; ?>"
method="POST" enctype="multipart/form-data">
Image filename: <input type="file" name="lob_upload">
<input type="submit" value="Upload">
</form>
<?php
} // closing brace from 'if' in earlier PHP code
else {
// else script was called with data to upload

$myblobid = 1; // should really be a unique id e.g. a sequence number

$conn = oci_connect("phphol", "welcome", "//localhost/orcl");

// Delete any existing BLOB
$query = 'delete from btab where blobid = :myblobid';
$stmt = oci_parse ($conn, $query);
oci_bind_by_name($stmt, ':myblobid', $myblobid);
$e = oci_execute($stmt);

// Insert the BLOB from PHP's temporary upload area
$lob = oci_new_descriptor($conn, OCI_D_LOB);
$stmt = oci_parse($conn, 'insert into btab (blobid, blobdata) '
.'values(:myblobid, empty_blob()) returning blobdata into :blobdata');
oci_bind_by_name($stmt, ':myblobid', $myblobid);
oci_bind_by_name($stmt, ':blobdata', $lob, -1, OCI_B_BLOB);
oci_execute($stmt, OCI_DEFAULT); // Note OCI_DEFAULT
if ($lob->savefile($_FILES['lob_upload']['tmp_name'])) {
oci_commit($conn);
echo "BLOB uploaded";
}
else {
echo "Couldn't upload BLOB\n";
}
$lob->free();
}

?>

该代码显示了嵌入在多个 PHP 块中的 HTML 代码。尤其是,PHP“if”语句包含了 HTML 代码。首次加载该脚本时,显示 HTML 上载表单。PHP 已经填充了 form action 名称以便再次调用同一个脚本。

HTML 表单名称 name="lob_upload" 与特定的 PHP 变量 $_FILES['lob_upload'] 之间存在直接关系。通过数据调用该表单时,该脚本从该表中删除所有现有的图像,然后再插入新图片。

该脚本说明了 oci_new_descriptor() 的使用,它绑定到 empty_blob() 位置。LOB->savefile() 方法向新建的行中插入图片。注意,针对 oci_execute() 选择 OCI_DEFAULT 选项是后续的 LOB 方法所必需的。

 

.

在 Web 浏览器中,输入以下 URL 以显示输出:

http://localhost/~phphol/blobins.php

它显示一个包括 Browse 和 Upload 按钮的 Web 表单。单击 Browse

 

.

/home/phphol/public_html 目录中选择 oracle.jpg,然后单击 Open

 

.

单击 Upload

form action 再次调用该脚本,但现在设置特殊的变量 $_FILES['lob_upload'] 并上载图片。显示成功的 echo 消息。

图像已上载到 Web 服务器。

 

.

要显示图像,查看 $HOME/public_html 目录的 blobview.php 文件中包含的以下代码。

<?php

$conn = oci_connect("phphol", "welcome", "//localhost/orcl");

$query = 'SELECT BLOBDATA FROM BTAB WHERE BLOBID = :MYBLOBID';
$stmt = oci_parse ($conn, $query);
$myblobid = 1;
oci_bind_by_name($stmt, ':MYBLOBID', $myblobid);
oci_execute($stmt);
$arr = oci_fetch_array($stmt, OCI_ASSOC);
$result = $arr['BLOBDATA']->load();

header("Content-type: image/JPEG");
echo $result;

oci_close($conn);

?>

 

.

在 Web 浏览器中,输入以下 URL 以显示输出:

http://localhost/~phphol/blobview.php

确保脚本中没有 echo 语句且“<?php”前面没有任何空格,否则将发送错误的 HTTP 头,造成浏览器无法正确显示图像。如果有问题,将 header() 函数调用注释掉,看看会显示什么。

 

将 AJAX 用于您的页面

本节介绍在不重新加载整个页面内容的情况下更新部分页面的基本技巧。执行以下任务:

您可以使用 XmlHttpRequest 在不重新加载整个页面内容的情况下更新部分页面。执行以下步骤:

.

查看 $HOME/public_html 目录的 ajax_id.php 文件中包含的以下代码。该文件只回显传入的参数。

<?php

if (!isset($_GET['id'])) {
$id = 'No id passed';
}
else {
$id = $_GET['id'];
}
echo "Id was: ", htmlentities($id);
?>

 

.

在浏览器中,输入以下 URL 以显示输出:

http://localhost/~phphol/ajax_id.php?id=185

 

.

查看 $HOME/public_html 目录的 ajax_id.html 文件中包含的以下代码。该文件包含 JavaScript 函数 makeRequest()

<html>
<head>
<script type="text/javascript">
function makeRequest(id)
{
httpRequest = new XMLHttpRequest();
httpRequest.open('GET', 'http://localhost/~phphol/ajax_id.php?id=' + id);
httpRequest.onreadystatechange = function()
{
if (httpRequest.readyState == 4) { // The request is complete
alert(httpRequest.responseText); // Display the result
}
}
httpRequest.send(null);
}
</script>
</head>
<body onload="makeRequest(185)">
<h1>Employee Lookup</h2>
<div id="descriptionNode">This page finds an employee</div>
</body>
</html>

 

.

在浏览器中,输入以下 URL 以显示输出:

http://localhost/~phphol/ajax_id.html

单击 OK 关闭警报窗口。

注意:如果您使用 Internet Explorer,将需要编辑 ajax_id.html,将 XMLHttpRequest() 调用更改为 ActiveXObject("Msxml2.XMLHTTP")ActiveXObject("Microsoft.XMLHTTP")

加载该 HTML 页面时,将调用 makeRequest() javascript 函数。它准备了一个 XMLHttpRequest 请求以调用 ajax_id.php。设置回调函数 onreadystatechange。最后,将该请求异步发送给 Web 服务器。

当回调函数收到 Web 服务器请求已返回的通知时,alert() 函数将显示来自 ajax_query.php 的输出。

 

.

编辑 ajax_id.html,将 185 更改为 186

 

.

在浏览器中重新加载该文件。显示新值。单击 OK 关闭警报窗口。

 

使用 PHP 框架数据库组件

PHP 框架(例如 Symfony、Cake 和 Zend Framework)将结构引入 PHP 项目中,从而为 PHP 项目提供了整体体系结构。它们与 eZ Components 之类的组件库一起还提供了丰富的预先构建组件体验,例如,使用 Web 服务或身份验证。

可以框架模型-视图-控制器风格使用 ZFW,也可使用单独的模块。本节介绍 Zend Framework (ZFW) 的数据库组件。

执行以下任务:

创建 Zend_Db 查询

您可以使用 Zend 执行一个数据库查询,其结果将显示在页面上。执行以下步骤:

.

查看 $HOME/public_html 目录的 zfwdb.php 文件中包含的以下代码。该文件只显示一个员工的有关信息。

<?php

require_once 'Zend/Db.php';
$db = Zend_Db::factory('Oracle', array(
'username' => 'phphol',
'password' => 'welcome',
'dbname' => 'localhost/orcl'
));
$result = $db->fetchAll("select * from employees where employee_id = 185");
echo "<pre>"; // make var_dump output formatted.
var_dump($result);
?>

它包括来自 Web 服务器文档根目录的 Zend/Db.php。该脚本声明了 Zend_DB 类。然后创建一个 Zend_Db Adapter 类的实例。这可以将实际连接推迟到首次使用,因此可以使您在常用初始块中编写能够初始化连接的应用程序,但是并没有物理上打开数据库连接,除非实际发出了数据库请求。这只是在 Zend_Db 中打开连接的一种方法。fetchAll() 方法返回由查询产生的所有结果。如果只需返回一部分行,则使用相应的 WHERE 子句。

 

.

在浏览器中,输入以下 URL 以显示输出:执行查询时,将返回员工详细信息。

http://localhost/~phphol/zfwdb.php

 

.

可以使用各种查询模式。编辑 zfwdb.php 文件,在 fetchAll() 方法前添加以下代码行。添加下面黑体的语句。

<?php

require_once 'Zend/Db.php';
$db = Zend_Db::factory('Oracle', array(
'username' => 'phphol',
'password' => 'welcome',
'dbname' => 'localhost/orcl' ));
$db->setFetchMode(Zend_Db::FETCH_NUM);
$result = $db->fetchAll("select * from employees where employee_id = 185");
echo "<pre>"; // make var_dump output formatted.
var_dump($result);
?>

 

.

重新加载 zfwdb.php 文件可看到不同的格式。

 

.

要将绑定名称的和值的数组传递给 fetchAll(),按照下面的黑体编辑 zfwdb.php 文件:

<?php


require_once 'Zend/Db.php';
$db = Zend_Db::factory('Oracle', array(
'username' => 'phphol',
'password' => 'welcome',
'dbname' => 'localhost/orcl'
));
$bind = array(':id' => 186 );
$result = $db->fetchAll("select * from employees where employee_id = :id", $bind);

echo "<pre>"; // make var_dump output formatted.
var_dump($result);
?>

 

.

重新加载 zfwdb.php 文件验证是否返回了正确的用户值。

 

有条件地构建查询

可以使用 Zend_Db_Select 类构建一个基于条件逻辑的查询。执行以下步骤:

.

查看 zfwdb_3.php 文件。将要插入的数据行封装在一个 PHP 数组中。

<?php

require_once 'Zend/Db.php';
$db = Zend_Db::factory('Oracle', array(
'username' => 'phphol',
'password' => 'welcome',
'dbname' => 'localhost/orcl'
));
$select = new Zend_Db_Select($db);
$select->from("EMPLOYEES");
$select->where('employee_id = 185');
$stmt = $db->query($select);
$result = $stmt->fetchAll();
echo "<pre>";
var_dump($result);
?>

该查询的每部分都是分别添加的。

 $select->from("EMPLOYEES");
 $select->where('employee_id = 185');

您甚至可以将方法链接在一起:

 $select->from("EMPLOYEES")->where('employee_id = 185');

如果只想选择一列,FROM 条件将为:

 $select->from("EMPLOYEES", "LAST_NAME")

要选择两列,FROM 条件将为:

 $select->from("EMPLOYEES", array("LAST_NAME", "FIRST_NAME"))

 

.

在浏览器中加载 zfwdb_3.php 文件。

编辑 zfwdb_3.php,体验一下不同的子句。

本节只介绍了 Zend_Db 提供的部分功能,这些功能本身只是 ZFW 的一部分。Zend_Db 与其他组件以及模型-视图-控制器 (MVC) 框架结合使用时,可以更快地构建应用程序。

在使用框架或数据库抽象层实现项目前,您应该考虑它的成熟度、支持和性能特征,从而能够亲自决定适合您业务需要的最佳解决方案。

 

总结

在本教程中,您学习了如何:

附录:PHP 入门

PHP 是一种动态类型的脚本语言。它在 Web 应用程序中很常见,但也可用于运行命令行脚本。基本的 PHP 语法简单易学。它具有熟悉的循环、测试和赋值结构。每行以分号结束。

字符串可以包含在单引号或双引号中:

'A string constant'
"another constant"

变量名以美元符号为前缀。类似双引号字符串内的变量将扩展为:

"A value appears here: $v1"

也可使用句点将字符串和变量连接在一起。

'Employee ' . $ename . ' is in department ' . $dept

变量不需要声明类型:

$count = 1;
$ename = 'Arnie';

数组可以具有数字索引或关联索引:

$a1[1] = 3.1415;
$a2['PI'] = 3.1415;

可以用 echoprint 语句显示字符串和变量。使用 printf() 还可以实现格式化输出。

echo 'Hello, World!';
echo $v, $x;
print 'Hello, World!';
printf("There is %d %s", $v1, $v2);

var_dump() 函数对于调试很有帮助。

var_dump($a2);

假定上面指定的值为 $a2,输出如下所示:

array(1) {
  ["PI"]=>
  float(3.1415)
}

可以通过测试和循环来控制代码流。PHP 还具有一个 switch 语句。if/elseif/else 语句如下所示:

if ($sal > 900000) {
  echo 'Salary is way too big';
}
elseif ($sal > 500000) {
  echo 'Salary is huge';
}
  else {
  echo 'Salary might be OK';
}

这还会显示代码块是如何包含在括号中。

传统的循环为:

for ($i = 0; $i < 10; $i++) {
  echo $i;
}

这将输出数字 0 到 9。$i 的值在每次迭代后递增。当测试条件值为 false 时,循环停止。您也可以使用 whiledo while 结构进行循环。

foreach 命令对于数组迭代很有帮助:

$a3 = array('Aa', 'Bb', 'Cc');
foreach ($a3 as $v) {
  echo $v;
}

这会依次将 $v 设置为数组中的每个元素。

可能会定义如下所示的函数:

function myfunc($p1, $p2) {
  echo $p1, $p2;
  return $p1 + $p2;
}

函数可能具有可变数量的参数,可能返回值,也可能不返回值。可以使用以下代码调用该函数:

$v3 = myfunc(1, 3);

函数调用可能会出现在函数定义之前。

可以使用 include()require() 语句将子文件包括在 PHP 脚本中。

include("foo.php");
require("bar.php");

如果没找到该脚本,require() 将生成严重错误。

注释要么是一行:

// a short comment

要么是多行:

/*
  A longer comment
*/

PHP 脚本包含在 <?php?> 标记中。

<?php
  echo 'Hello, World!';
?>

当 Web 服务器配置为通过 PHP 解释程序运行 PHP 文件时,在浏览器中加载脚本将导致执行 PHP 代码,所有输出将传输到浏览器。

PHP 代码块和 HTML 代码块可能是交替的。PHP 代码还可以显式输出 HTML 标记。

<?php
  require('foo.php');
  echo '<h3>';
  echo 'Full Results';
  echo '</h3>';
  $output = bar(123);
?>
<table border="1">
  <tr>
    <td>
     <?php echo $output ?>
    </td>
  </tr>
</table>

PHP 的许多方面由 php.ini 配置文件中的设置控制。该文件的位置取决于系统。使用 phpinfo() 函数,可以找到其位置、
加载的扩展名列表以及所有初始化设置的值:

<?php
  phpinfo();
?>

可以通过编辑 phpl.ini 来更改值,然后重启 Web 服务器。通过使用 ini_set() 函数,还可以在脚本中改变某些设置值。

各种 oci 函数的列表显示如下:

oci_bind_array_by_name

通过名称将 PHP 数组绑定到 Oracle PL/SQL 数组

oci_bind_by_name

将 PHP 变量绑定到 Oracle 占位符

oci_cancel

取消从游标的读取

oci_close

关闭 Oracle 连接

oci_commit

提交未完成的语句

oci_connect

建立到 Oracle 服务器的连接

oci_define_by_name

在 SELECT 期间将 PHP 变量用于步进定义

oci_error

返回最后发现的错误

oci_execute

执行一条语句

oci_fetch_all

将结果数据的所有行获取到一个数组中

oci_fetch_array

将结果数据中的下一行作为关联数组和/或数字数组返回

oci_fetch_assoc

将结果数据中的下一行作为关联数组返回

oci_fetch_object

将结果数据中的下一行作为对象返回

oci_fetch_row

将结果数据中的下一行作为数字数组返回

oci_fetch

将下一行获取到结果缓冲区中

oci_field_is_null

检查字段是否为 NULL

oci_field_name

从语句返回字段的名称

oci_field_precision

告诉字段的精度

oci_field_scale

告诉字段的数值范围

oci_field_size

返回字段的大小

oci_field_type_raw

告诉字段的原始 Oracle 数据类型

oci_field_type

返回字段的数据类型

oci_free_statement

释放与语句或游标关联的所有资源

oci_internal_debug

启用或禁用内部调试输出

oci_new_collection

分配新的集合对象

oci_new_connect

建立到 Oracle 服务器的新连接

oci_new_cursor

分配和返回新游标(语句句柄)

oci_new_descriptor

对新的空 LOB 或 FILE 描述文件进行初始化

oci_num_fields

返回语句中的结果列数

oci_num_rows

返回语句执行期间受影响的行数

oci_parse

准备要执行的 Oracle 语句

oci_password_change

更改 Oracle 用户的口令

oci_pconnect

使用持久性连接连接到 Oracle 数据库

oci_result

从获取的行返回字段的值

oci_rollback

回滚未完成的事务

oci_server_version

返回服务器版本

oci_set_prefetch

设置要预取的行数

oci_statement_type

返回 OCI 语句的类型

Oracle Is The Information Company 关于 Oracle | Oracle RSS 信源 | 招聘 | 联系我们 | 网站地图 | 法律声明 | 使用条款 | 您的隐私权利