Archive for the ‘PHP’ Category
Open Fedora Port 80
After installing the LAMP stack on Fedora, you need to open port 80 in the Firewall to access the PHP programs on the Fedora instance from external servers. You can open a firewall port by launching the firewall-config application as the root
user with the following syntax:
firewall-config |
The firewall-config
utility opens the following dialog:
Click on the Ports tab, and you’ll see the following:
Skip this step if you only want to set the runtime privilege to the port. Click on the Runtime tab and change it to Permanent if you want the port to be accessible when you reboot your OS.
Click on Add button to add a port exception, and you’ll see the following:
Enter Port 80 for the Apache server unless you used a different value for the Apache server’s listener port. If you’re not sure open the /etc/httpd/conf/httpd.conf
file and check for the following line (default value shown):
Listen 80 |
Click the OK button to set the port exception. Then, you can connect to the Linux machine with the IP address, a DNS name, or a name you resolve in your local hosts
file, like:
http://192.168.2.1/query.php |
You can find the IP address of your Fedora image by inspecting the /etc/hosts
file or capture a DHCP assigned address with the following command as the root
user (or with sudo
as a valid sudoer user):
ifconfig -a |
It should return the following image, which is based on the data stored in MySQL’s studentdb
database, as qualified in yesterday’s blog post:
I hope this helps those setting up a LAMP instance to work with the MySQL database.
Fedora Install LAMP
My students wanted an extra credit assignment, so I thought a LAMP configuration and test would be appropriate. The only problem was I hadn’t added it to their course VMware instance. So, here are the instructions to install Apache2, PHP, and MySQLi for a complete LAMP stack when MySQL is already installed.
The post builds on my Fedora Install of MySQL and MySQL Workbench on Fedora posts from last year. It also presumes that you’ve installed a studentdb
database but you need to know how to do that let me know (but it hasn’t changed much from the example at the bottom of this old MySQL 5.1 blog post).
You install Apache2 with the following command as the root
user, or with the sudo
command as a sudoer
-list user:
yum install httpd |
The following displays the results of starting the yum
utility to install httpd
, and you need to reply with a y
to complete the installation:
Loaded plugins: langpacks, refresh-packagekit mysql-connectors-community | 2.5 kB 00:00 mysql-tools-community | 2.5 kB 00:00 mysql56-community | 2.5 kB 00:00 pgdg93 | 3.6 kB 00:00 updates/20/x86_64/metalink | 16 kB 00:00 updates | 4.9 kB 00:00 updates/20/x86_64/primary_db | 13 MB 00:04 (1/2): updates/20/x86_64/updateinfo | 1.9 MB 00:02 (2/2): updates/20/x86_64/pkgtags | 1.4 MB 00:01 Resolving Dependencies --> Running transaction check ---> Package httpd.x86_64 0:2.4.10-2.fc20 will be installed --> Processing Dependency: httpd-tools = 2.4.10-2.fc20 for package: httpd-2.4.10-2.fc20.x86_64 --> Processing Dependency: system-logos-httpd for package: httpd-2.4.10-2.fc20.x86_64 --> Running transaction check ---> Package fedora-logos-httpd.noarch 0:21.0.1-1.fc20 will be installed ---> Package httpd-tools.x86_64 0:2.4.10-2.fc20 will be installed --> Finished Dependency Resolution Dependencies Resolved ================================================================================ Package Arch Version Repository Size ================================================================================ Installing: httpd x86_64 2.4.10-2.fc20 updates 1.2 M Installing for dependencies: fedora-logos-httpd noarch 21.0.1-1.fc20 fedora 28 k httpd-tools x86_64 2.4.10-2.fc20 updates 79 k Transaction Summary ================================================================================ Install 1 Package (+2 Dependent packages) Total download size: 1.3 M Installed size: 4.0 M Is this ok [y/d/N]: y Downloading packages: (1/3): fedora-logos-httpd-21.0.1-1.fc20.noarch.rpm | 28 kB 00:00 (2/3): httpd-2.4.10-2.fc20.x86_64.rpm | 1.2 MB 00:01 (3/3): httpd-tools-2.4.10-2.fc20.x86_64.rpm | 79 kB 00:00 -------------------------------------------------------------------------------- Total 815 kB/s | 1.3 MB 00:01 Running transaction check Running transaction test Transaction test succeeded Running transaction (shutdown inhibited) Installing : httpd-tools-2.4.10-2.fc20.x86_64 1/3 Installing : fedora-logos-httpd-21.0.1-1.fc20.noarch 2/3 Installing : httpd-2.4.10-2.fc20.x86_64 3/3 Verifying : httpd-2.4.10-2.fc20.x86_64 1/3 Verifying : fedora-logos-httpd-21.0.1-1.fc20.noarch 2/3 Verifying : httpd-tools-2.4.10-2.fc20.x86_64 3/3 Installed: httpd.x86_64 0:2.4.10-2.fc20 Dependency Installed: fedora-logos-httpd.noarch 0:21.0.1-1.fc20 httpd-tools.x86_64 0:2.4.10-2.fc20 Complete! |
Next, you install php
as the root
user with the following command:
yum install php |
The following displays when you install php
, and you need to reply with a y
to complete the installation:
Loaded plugins: langpacks, refresh-packagekit Resolving Dependencies --> Running transaction check ---> Package php.x86_64 0:5.5.22-1.fc20 will be installed --> Processing Dependency: php-common(x86-64) = 5.5.22-1.fc20 for package: php-5.5.22-1.fc20.x86_64 --> Processing Dependency: php-cli(x86-64) = 5.5.22-1.fc20 for package: php-5.5.22-1.fc20.x86_64 --> Running transaction check ---> Package php-cli.x86_64 0:5.5.22-1.fc20 will be installed ---> Package php-common.x86_64 0:5.5.22-1.fc20 will be installed --> Processing Dependency: php-pecl-jsonc(x86-64) for package: php-common-5.5.22-1.fc20.x86_64 --> Running transaction check ---> Package php-pecl-jsonc.x86_64 0:1.3.6-1.fc20 will be installed --> Processing Dependency: /usr/bin/pecl for package: php-pecl-jsonc-1.3.6-1.fc20.x86_64 --> Processing Dependency: /usr/bin/pecl for package: php-pecl-jsonc-1.3.6-1.fc20.x86_64 --> Running transaction check ---> Package php-pear.noarch 1:1.9.5-6.fc20 will be installed --> Processing Dependency: php-xml for package: 1:php-pear-1.9.5-6.fc20.noarch --> Processing Dependency: php-posix for package: 1:php-pear-1.9.5-6.fc20.noarch --> Running transaction check ---> Package php-process.x86_64 0:5.5.22-1.fc20 will be installed ---> Package php-xml.x86_64 0:5.5.22-1.fc20 will be installed --> Finished Dependency Resolution Dependencies Resolved ================================================================================ Package Arch Version Repository Size ================================================================================ Installing: php x86_64 5.5.22-1.fc20 updates 2.6 M Installing for dependencies: php-cli x86_64 5.5.22-1.fc20 updates 3.9 M php-common x86_64 5.5.22-1.fc20 updates 1.0 M php-pear noarch 1:1.9.5-6.fc20 updates 343 k php-pecl-jsonc x86_64 1.3.6-1.fc20 updates 34 k php-process x86_64 5.5.22-1.fc20 updates 77 k php-xml x86_64 5.5.22-1.fc20 updates 247 k Transaction Summary ================================================================================ Install 1 Package (+6 Dependent packages) Total download size: 8.2 M Installed size: 32 M Is this ok [y/d/N]: y Downloading packages: (1/7): php-5.5.22-1.fc20.x86_64.rpm | 2.6 MB 00:03 (2/7): php-cli-5.5.22-1.fc20.x86_64.rpm | 3.9 MB 00:03 (3/7): php-common-5.5.22-1.fc20.x86_64.rpm | 1.0 MB 00:00 (4/7): php-pear-1.9.5-6.fc20.noarch.rpm | 343 kB 00:00 (5/7): php-pecl-jsonc-1.3.6-1.fc20.x86_64.rpm | 34 kB 00:00 (6/7): php-process-5.5.22-1.fc20.x86_64.rpm | 77 kB 00:00 (7/7): php-xml-5.5.22-1.fc20.x86_64.rpm | 247 kB 00:00 -------------------------------------------------------------------------------- Total 1.1 MB/s | 8.2 MB 00:07 Running transaction check Running transaction test Transaction test succeeded Running transaction (shutdown inhibited) Installing : php-cli-5.5.22-1.fc20.x86_64 1/7 Installing : php-process-5.5.22-1.fc20.x86_64 2/7 Installing : php-xml-5.5.22-1.fc20.x86_64 3/7 Installing : 1:php-pear-1.9.5-6.fc20.noarch 4/7 Installing : php-common-5.5.22-1.fc20.x86_64 5/7 Installing : php-pecl-jsonc-1.3.6-1.fc20.x86_64 6/7 Installing : php-5.5.22-1.fc20.x86_64 7/7 Verifying : php-5.5.22-1.fc20.x86_64 1/7 Verifying : php-common-5.5.22-1.fc20.x86_64 2/7 Verifying : php-cli-5.5.22-1.fc20.x86_64 3/7 Verifying : 1:php-pear-1.9.5-6.fc20.noarch 4/7 Verifying : php-process-5.5.22-1.fc20.x86_64 5/7 Verifying : php-xml-5.5.22-1.fc20.x86_64 6/7 Verifying : php-pecl-jsonc-1.3.6-1.fc20.x86_64 7/7 Installed: php.x86_64 0:5.5.22-1.fc20 Dependency Installed: php-cli.x86_64 0:5.5.22-1.fc20 php-common.x86_64 0:5.5.22-1.fc20 php-pear.noarch 1:1.9.5-6.fc20 php-pecl-jsonc.x86_64 0:1.3.6-1.fc20 php-process.x86_64 0:5.5.22-1.fc20 php-xml.x86_64 0:5.5.22-1.fc20 Complete! |
After installing the software, you can set the Apache server to start automatically with the following command:
chkconfig httpd on |
However, that command only starts the Apache server the next time you boot the server. You use the following command as the root
user to start the Apache server:
apachectl start |
You can verify the installation with the following command as the root
user:
ps -ef | grep httpd | grep -v grep |
It should return:
root 5433 1 0 17:03 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND apache 5434 5433 0 17:03 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND apache 5435 5433 0 17:03 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND apache 5436 5433 0 17:03 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND apache 5437 5433 0 17:03 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND apache 5438 5433 0 17:03 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND apache 5442 5433 0 17:03 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND |
and, then verify the listening port with the following command as the root
user:
netstat -tulpn | grep :80 |
It should return the following when both the Apache server is listening on port 80 and the Oracle multi-protocol server is listening on port 8080:
tcp6 0 0 :::80 :::* LISTEN 5433/httpd tcp6 0 0 :::8080 :::* LISTEN 1505/tnslsnr |
After verifying the connection, you can test it by creating the traditional info.php
program file in the /var/www/http
directory. The file should contain the following:
1 2 3 | <?php phpinfo(); ?> |
You can test it by opening the Firefox browser and entering the following URL from the Fedora Linux image:
http://localhost/info.php |
It should display the typical diagnostic page. This verifies the configuration of the Apache and PHP servers. The next step verifies whether you have the mysqli
library to connect to the MySQL database.
You create a mysqli_check.php
script, like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <html> <header> <title>Static Query Object Sample</title> <style type="text/css"> /* HTML element styles. */ table {background:white;border-style:solid;border-width:3px;border-color:black;border-collapse:collapse;} th {text-align:center;font-style:bold;background:lightgray;border:solid 1px gray;} td {border:solid 1px gray;} /* Class tag element styles. */ .ID {min-width:50px;text-align:right;} .Label {min-width:200px;text-align:left;} </style> </header> <body> <?php if (!function_exists('mysqli_init') && !extension_loaded('mysqli')) { print 'mysqli not installed.'; } else { print 'mysqli installed.'; } ?> </script> </body> </html> |
You can test it with the following URL from the local browser:
http://localhost/mysqli_check.php |
If it’s installed you can skip the next step, but if not you need to run yum
in expert mode as follows (the check for php-mysql
isn’t really necessary because it’s too old a version but good practice):
[root@localhost etc]# yum shell Loaded plugins: langpacks, refresh-packagekit > remove php-mysql No Match for argument: php-mysql > install php-mysqlnd > run --> Running transaction check ---> Package php-mysqlnd.x86_64 0:5.5.22-1.fc20 will be installed --> Processing Dependency: php-pdo(x86-64) = 5.5.22-1.fc20 for package: php-mysqlnd-5.5.22-1.fc20.x86_64 --> Running transaction check ---> Package php-pdo.x86_64 0:5.5.22-1.fc20 will be installed --> Finished Dependency Resolution ================================================================================ Package Arch Version Repository Size ================================================================================ Installing: php-mysqlnd x86_64 5.5.22-1.fc20 updates 293 k Installing for dependencies: php-pdo x86_64 5.5.22-1.fc20 updates 141 k Transaction Summary ================================================================================ Install 1 Package (+1 Dependent package) Total download size: 433 k Installed size: 1.4 M Is this ok [y/d/N]: y Downloading packages: (1/2): php-mysqlnd-5.5.22-1.fc20.x86_64.rpm | 293 kB 00:00 (2/2): php-pdo-5.5.22-1.fc20.x86_64.rpm | 141 kB 00:00 -------------------------------------------------------------------------------- Total 427 kB/s | 433 kB 00:01 Running transaction check Running transaction test Transaction test succeeded Running transaction (shutdown inhibited) Installing : php-pdo-5.5.22-1.fc20.x86_64 1/2 Installing : php-mysqlnd-5.5.22-1.fc20.x86_64 2/2 Verifying : php-pdo-5.5.22-1.fc20.x86_64 1/2 Verifying : php-mysqlnd-5.5.22-1.fc20.x86_64 2/2 Installed: php-mysqlnd.x86_64 0:5.5.22-1.fc20 Dependency Installed: php-pdo.x86_64 0:5.5.22-1.fc20 Finished Transaction > quit |
You should note that this also installed PDO. One caveat, before you rerun the mysqli_check.php
script from a browser, you need to restart the Apache server. You can do that as the root
user with the following syntax:
apachectl restart |
You can retest it with the following URL from the local browser:
http://localhost/mysqli_check.php |
At this point you should have everything installed to test your connection the MySQL database. As mentioned, this example extends my instructions for installing MySQL on the Fedora instance.
The following query.php
file tests your ability to connect to the MySQL database with the mysqli driver, and it uses the studentdb and video store example from my Oracle Database 11g and MySQL 5.6 Developer Handbook:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | <html> <header> <title>Static Query Object Sample</title> <style type="text/css"> /* HTML element styles. */ table {background:white;border-style:solid;border-width:3px;border-color:black;border-collapse:collapse;} th {text-align:center;font-style:bold;background:lightgray;border:solid 1px gray;} td {border:solid 1px gray;} /* Class tag element styles. */ .ID {min-width:50px;text-align:right;} .Label {min-width:200px;text-align:left;} </style> </header> <body> <?php // Assign credentials to connection. $mysqli = new mysqli("localhost", "student", "student", "studentdb"); // Check for connection error and print message. if ($mysqli->connect_errno) { print $mysqli->connect_error."<br />"; print "Connection not established ...<br />"; } else { // Declare a static query. $query = "SELECT au.system_user_id, au.system_user_name FROM system_user au" ; // Loop through a result set until completed. do { // Attempt query and exit with failure before processing. if (!$stmt = $mysqli->query($query)) { // Print failure to resolve query message. print $mysqli->error."<br />"; print "Failed to resolve query ...<br />"; } else { // Print the opening HTML table tag. print '<table><tr><th class="ID">ID</th><th class="Label">User Role Name</th></tr>'; // Fetch a row for processing. while( $row = $stmt->fetch_row() ) { // Print the opening HTML row tag. print "<tr>"; // Loop through the row's columns. for ($i = 0;$i < $mysqli->field_count;$i++) { // Handle column one differently. if ($i == 0) print '<td class="ID">'.$row[$i]."</td>"; else print '<td class="Label">'.$row[$i]."</td>"; } // Print the closing HTML row tag. print "</tr>"; } } } while( $mysqli->next_result()); // Print the closing HTML table tag. print "</table>"; // Release connection resource. $mysqli->close(); } ?> </script> </body> </html> |
This should display the following in the browser:
You can see how to open port 80 for the Apache server in this blog post. If you want to work with blob data types, you’ll also need to use yum
to install the php-gd
library. You can read my LAMP php-gd
library blog post to learn how to install the libraries. As always, I hope a step-by-step approach without assumptions helps those learning MySQL.
Popular Programming Languages
First of all, Happy New Year!
IEEE Spectrum published a ranking of the most popular programming languages. Computational journalist Nick Diakopoulos wrote the article. While it may surprise some, I wasn’t surprised to find SQL in the top ten.
Nick weighted and combined 12 metrics from 10 sources (including IEEE Xplore, Google, and GitHub) to rank the most popular programming languages.
- Compiled programming languages (Java [#1], C [#2], C++ [#3], C# [#4], Objective-C [#16])
- Interpreted programming languages (Python [#5], JavaScript [#6], PHP [#7], Ruby [#8], Perl [#11], HTML [#12])
- Data languages (SQL [#9], MATLAB [#10], R [#13])
I couldn’t resist including Objective-C because it shows how the iPhone, iPad, and Mac OS impact our daily lives. At the same time, Assembly [#15] is actually more popular than Objective-C. Shell [#17] follows Objective-C. While the Visual Basic [#14] programming language still remains very popular.
There are many “why” questions raised by this list of popular programming languages. The “why” from my perspective deals with what are the market drivers for their popularity. The money drivers I see are as follows:
- Business software: Java, C++, C#, and AIDE – Android IDE (works with Java and C++ source code)
- OS X and iOS Development: Objective-C
- Development Tools: Java, C, C++, and Python
- System Admin/Utilities Tools: C, Perl, and Shell
- Web Development: Python, PHP, Ruby, and Perl
- Data Analysis: SQL, MATLAB, and R
Business Intelligence (BI) software manages most high-level data analysis tools and they’ll continue to get better over time. However, if SQL has shown us anything over 30 years it’s that ultimately we revert to it to solve problems. The conclusion from the reality of BI probably means the programming languages that develop those tools will continue to rise and so will the underlying data languages.
It’s also interesting to note that nine out of ten of the popular programming languages work with databases, like Oracle, MySQL, PostgreSQL, or SQL Server. While JavaScript doesn’t access the database typically, it’s JSON (JavaScript Object Notation) is supported in all the databases.
Popular PHP Frameworks
My students often ask me about popular PHP frameworks and MySQL approaches. I wish a PHP framework choice was as clear as a JavaScript framework, like AngularJS. Unfortunately, PHP frameworks aren’t that clear cut.
It seems that the most popular PHP frameworks are: Laravel (1st), Phalcon (2nd), and Symphony2 (3rd). I found the following graphic (from December 2013) that highlights popularity by percentage of the market (though I can’t guarantee its accuracy). As far as jobs go, on Dice.com only Laravel had more than 3 positions referring to the framework by name. There were actually 42 PHP developer positions that mention Laravel out of 2,115 PHP developer positions. So, it seems learning a specific framework for PHP doesn’t yet have much market appeal.
While learning the basics of PHP are generic, frameworks expedite process and control pattern implementation. My suggestion to students is to target three to five employers that use a specific framework and start learning how to use the framework. I’d like to get opinions from those in the field whether this is the best advice.
As to MySQL, I suggest they learn native Mysqli and MySQL PDO. Any other suggestions on that from readers? Thanks as always.
Deprecated mysqli Functions
mysqli
functions web page. There weren’t any deprecated by 5.5. Unfortuantely, there were six mysqli
functions deprecated in 5.3 and removed in 5.4. Unfortunately, many of my posted code examples use 5.2 or 5.3 where they would have worked. The deprecated mysqli
functions are:
mysqli_bind_param
mysqli_bind_result
mysqli_client_encoding
mysqli_fetch
mysqli_param_count
mysqli_send_long_data
Unfortunately, that puts a lot of updates on my queue of things to do. I imagine it likewise imposes limits on those running commercially on PHP 5.3 or older that plan an upgrade.
It does remind me that I need to pay close attention to the deprecation of mysqli
functions with each release. These are actually the largest change since PHP 5.0.
MySQL Image Architecture
The LinkedIn MySQL DB Development group posed a questions on how to handle images. Naturally, the argument always goes: Should images be deployed in the database or the file system? I believe they should be stored in the database because the cost and time associated is too high with regard to managing files, a file naming schema, and backing up the file system discretely from the database.
Since there’s a significant difference between the backup of transactional data and image data, they should be placed in different databases. The imagedb
database is where you would place the images and large text descriptions, as shown in the MySQL Workbench ERD:
The imagedb ERD splits the foreign key references back to the system_user
table, which contains the individual user credentials. The system_user
table serves as the Access Control List (ACL) for the application.
Until I get a chance to write the code for this model, you can refer to the generic PHP/MySQL solution from several years back (its code source was last tested with PHP 5.3). As always, I hope this helps.
Oracle 12c & PHP
This answers “How you connect PHP programs to an Oracle 12c multitenant database. This shows you how to connect your PHP programs to a user-defined Container Database (CDB) and Pluggable Database (PDB). It presupposes you know how to provision a PDB, and configure your Oracle listener.ora
and tnsnames.ora
files.
CDB Connection:
This assumes you already created a user-defined c##plsql
CDB user, and granted an appropriate role or set of privileges to the user. Assuming the demonstration database Oracle TNS Service Name of orcl
, you would test your connection with this script:
PDB Connection:
This assumes you already created a user-defined videodb
PDB, and video
user in the PDB, and granted an appropriate role or set of privileges to the video
user. Assuming the user-defined videodb PDB uses an Oracle TNS Service Name of videodb
, you would test your connection with this script:
Line 3 above uses the TNS Service Name from the tnsnames.ora
file, which is also the SID Name from the listener.ora
file after the slash that follows the localhost
. That’s the only trick you should need.
You should note that because the tnsnames.ora
file uses a video service name, the connection from the command line differs:
sqlplus video@video/video |
Hope this helps those trying to sort it out.
PHP PL/SQL Function Return
Somebody wanted an example of how to capture the return value of a PL/SQL function in a PHP script running against the Oracle Database. The trick is embedding the call inside an anonymous block program unit, like a prior example that I posted on handling an OUT mode variable in a PL/SQL Procedure.
Here’s the sample PHP code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <?php // Attempt to connect to your database. $c = @oci_connect("student", "student", "localhost/orcl"); if (!$c) { print "Sorry! The connection to the database failed. Please try again later."; die(); } else { // Initialize incoming message whether or not parameter sent. $msg_in = (isset($_GET['msg'])) ? $_GET['msg'] : "Cat got your keyboard?"; // Set the call statement, like a SQL statement. $sql = "BEGIN :b := echo(:a); END;"; // Prepare the statement and bind the two strings. $stmt = oci_parse($c,$sql); // Bind local variables into PHP statement, you MUST size OUT only variables. oci_bind_by_name($stmt, ":a", $msg_in); oci_bind_by_name($stmt, ":b", $msg_out, 80, SQLT_CHR); // Execute it and print success or failure message. if (oci_execute($stmt)) { print $msg_out; } else { print "Sorry, I can't do that Dave..."; } // Free resources. oci_free_statement($stmt); oci_close($c); } ?> |
As noted by Chris, you should size input parameters too. It could be qualified as a best practice when code re-executes with different values without rebinding.
It depends on this echo function:
1 2 3 4 5 6 | CREATE OR REPLACE FUNCTION echo ( message IN VARCHAR2 ) RETURN VARCHAR2 IS BEGIN RETURN 'Message ['||message||'] received.'; END; / |
Hope that this is what they needed, or you’re looking for as an answer to a search.
PHP DB Connection Class
PHP namespaces are new to PHP 5.3 (2012), but PHP class files have been around since PHP 5 was introduced. However, a couple students had problems creating working code from the many fragments published else where. Here’s my attempt to qualify it in a single post, running Zend Server Community Edition 6 and Oracle Database 11g.
The first thing you need to understand is a namespace. Namespaces exist to disambiguate (tell the difference between) class files that share the same name. After all, there are only so many obvious things to call class files. 😉 You can put classes, interfaces, functions, and constants in namespaces.
Let’s say you qualify your namespace as:
namespace Oracle\Db; |
You would make that the first thing in a PHP file, and shouldn’t include any HTML. You would then use a require()
, require_once()
, include()
, or include_once()
to add the class to a PHP file that uses the namespace qualified file. Then, you would construct a new instance of your PHP class. Together, these two steps would look like this:
require_once('Db.php'); $db = new \Oracle\Db\Db("Test Example","Author"); |
Notice the back slash in front of the Oracle namespace, and then you provide the namespace qualified file name (minus the file extension) and the class name. Since the namespace qualified file name and class name are the same, you see the double Db
.
Here is a basic (starter) Oracle database connection class file, which you should store as Db.php
in the Apache’s htdocs\Oracle
directory:
<?php /* Declare a namespace, available from PHP 5.3 forward. */ namespace Oracle\Db; /* Create a Database Connection class. */ class Db { /* Declare class variables. */ protected $conn = null; protected $stmt = null; protected $prefetch = 100; /* Declare the default construction function. */ function __construct($module, $cid) { // Construct a connection and suppress errors and warnings. $this->conn = @oci_connect(SCHEMA, PASSWD, TNS_ID, CHARSET); // Check for a connection, and process the work. if (!$this->conn) { // Assign Oracle error message. $msg = oci_error(); /* The \ preceding Exception is necessary because of the introduction of namespaces in PHP 5.3. Without it, the program would attempt to call \Oracle\Exception rather than our little runtime example. */ throw new \Exception('Cannot connect to database: '.$msg['message']); } /* Set Oracle Call Interface parameters. * ========================================================= * - The oci_set_client_info() function replaces a call * to the DBMS_APPLICATION_INFO package, and much more * efficient. * - The oci_set_module_name() function allows you to * register the function name that calls the Db class. * - The oci_set_client_identifier() function and you * use it with DBMS_MONITOR.CLIENT_ID_TRACE_ENABLE, * which can be enabled with a call to the * DBMS_MONITOR.SERV_MOD_ACT_STAT_ENABLE. * ========================================================= */ oci_set_client_info($this->conn, "Administrator"); oci_set_module_name($this->conn, $module); oci_set_client_identifier($this->conn, $cid); } /* Declare execute function. */ public function execute($sql, $action, $bindvars = array()) { // Parse statement. $this->stmt = oci_parse($this->conn, $sql); // Check for a prefetch value greater than zero. if ($this->prefetch >= 0) { oci_set_prefetch($this->stmt, $this->prefetch); } // Read the list of bind variables and bind them. foreach ($bindvars as $bv) { oci_bind_by_name($this->stmt, $bv[0], $bv[1], $bv[2]); } // Set the action name for Oracle tracing and execute statement. oci_set_action($this->conn, $action); // Set to auto commit. oci_execute($this->stmt); } /* Declare function that fetches all. */ public function execFetchAll($sql, $action, $bindvars = array()) { $this->execute($sql, $action, $bindvars); oci_fetch_all($this->stmt, $res, 0, -1, OCI_FETCHSTATEMENT_BY_ROW); // Free statement resources. $this->stmt = null; return($res); } /* Declare the default destructor function. */ function __destruct() { if ($this->stmt) oci_free_statement($this->stmt); if ($this->conn) oci_close($this->conn); } } ?> |
Here is a credential file for Oracle, where the network SID is orcl
(change orcl
to xe
when using the Oracle Express Edition):
1 2 3 4 5 6 7 | <?php // Connection variables. define('SCHEMA',"student"); define('PASSWD',"student"); define('TNS_ID',"localhost/orcl"); define('CHARSET',"AL32UTF8"); ?> |
If you do not know your the character set of your database, you can find it by logging in as the SYSTEM
user, and running this query:
SELECT VALUE$ FROM sys.props$ WHERE name = 'NLS_CHARACTERSET'; |
Here’s the test program for the database connection class, save it as TestDb.php
in your Apache’s htdocs\Oracle
directory:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | <?php // Require once the namespace identified class and credentials files. require_once('Db.php'); require_once('credentials.php'); // Process the input parameter, which REALLY should be through a $_POST argument. (isset($_GET['last_name'])) ? $input = $_GET['last_name'] : $input = ''; /* Establish new connection. * ====================================================== * The namespace (PHP 5.3) is set in Db.php as follows: * namespace Oracle\Db; * * The namespace syntax needs to qualify the following * when you call it: * - A \ (back slash) before the namespace. * - The file name but not the file extension. * - The class name from the Db.php file. */ $db = new \Oracle\Db\Db("Test Example","Author"); // Assign query. $sql = "SELECT * FROM contact c WHERE c.last_name = :bv"; // Assign fetch to a result array. $result = $db->execFetchAll($sql, "Query Example", array(array(":bv", $input, -1))); // Open table and provide headers. print "<table border='1'>\n"; print "<tr><th>First Name</th><th>Last Name</th></tr>\n"; // Iterate through the rows. foreach ($result as $row) { $fname = htmlspecialchars($row['FIRST_NAME'], ENT_NOQUOTES, 'UTF-8'); $lname = htmlspecialchars($row['LAST_NAME'], ENT_NOQUOTES, 'UTF-8'); print "<tr><td>$fname</td><td>$lname</td></tr>\n"; } // Close the table. print "</table>"; ?> |
If you get the call to the namespace wrong, you’ll get a strange set of errors. Just make sure you understand the differences between declaring a namespace and calling a namespace.
You test the database connection class with the following URL on your localhost
(substitute a server name if it’s not a development environment), provided you’ve created a table contact with a row where the last_name
equals 'Sweeney'
:
http://localhost/Oracle/TestDb.php?last_name=Sweeney |
The following creates and seeds the contact table:
CREATE TABLE contact ( contact_id NUMBER , first_name VARCHAR2(10) , last_name VARCHAR2(10)); INSERT INTO contact VALUES (1,'Meghan','Sweeney'); INSERT INTO contact VALUES (2,'Matthew','Sweeney'); INSERT INTO contact VALUES (3,'Ian','Sweeney'); |
Zend 6 & Timezones
Just went through all my PHP testing against a fresh instance of Oracle with Zend Server Community Edition 6, and found these warnings, guess that’s pretty clean for the Oracle part of the installation. I didn’t notice it before because generally I do most of my PHP development against a MySQL database. I should have been configuring the php.ini
file routinely, as qualified in this PHP forum discussion.
Warning: oci_set_client_info(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected the timezone 'UTC' for now, but please set date.timezone to select your timezone. in C:\Program Files (x86)\Zend\Apache2\htdocs\Oracle\Db.php on line 47 Warning: oci_set_module_name(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected the timezone 'UTC' for now, but please set date.timezone to select your timezone. in C:\Program Files (x86)\Zend\Apache2\htdocs\Oracle\Db.php on line 48 Warning: oci_set_action(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected the timezone 'UTC' for now, but please set date.timezone to select your timezone. in C:\Program Files (x86)\Zend\Apache2\htdocs\Oracle\Db.php on line 69 |
Turns out Zend 6 doesn’t automatically set the [Date]
elements in the php.ini
file, which is required for the oci_set_client_info()
, oci_set_module_name()
, and oci_set_action()
functions of the OCI. You can find the php.ini
file in the C:\Program Files (x86)\Zend\ZendServer\etc
folder on Windows:
[Date] ; Defines the default timezone used by the date functions ; http://php.net/date.timezone ;date.timezone = ; http://php.net/date.default-latitude ;date.default_latitude = 31.7667 ; http://php.net/date.default-longitude ;date.default_longitude = 35.2333 ; http://php.net/date.sunrise-zenith ;date.sunrise_zenith = 90.583333 ; http://php.net/date.sunset-zenith ;date.sunset_zenith = 90.583333 |
You can find the values for date.timezone
here. Update the date.timezone
as follows:
date.timezone = America/Denver |
Then, reboot the Zend Server, and it fixes the warning messages.