This is the online home of From A to Zoop: a beginners guide to Zoop. This is an online book in that it is intended to be read in order. as each chapter builds on the chapters before it. It is also a handy reference when you want to know how to do something.
This book will be kept current with the current stable version of Zoop. It is authored by the creators and maintainers of Zoop and can be considered the greatest book of it's kind (or perhaps the only).
It is a work in progress, but quite useful. Please aid in the process by placing helpful comments on any page.
The idea behind a framework is to offer a design you can use across multiple applications. All applications have a number of basic things in common. A framework is designed to provide a structure for those common elements (database interaction, presentation layer, application logic) so you spend less time writing up database interface code or presentation-layer interfaces and more time writing the application itself. The architecture represented by breaking an application up in this fashion is referred to as Model-View-Controller (MVC). Model refers to your data, View to your presentation layer, and Controller refers to the application or business logic.
-IBM
In general, a framework is a defined set of libraries, classes or structure which provides the foundation for a software development project. The purpose of a framework is to provide both usability and consistency between projects. It is almost always more efficient to use a framework geared for your specific task than writing the entire code yourself. Popular frameworks include Ruby on Rails (ruby), Apache Struts (java) and TurboGears (python).
The benefit of using a framework is to handle the more tedious programming tasks. Additionally, structure based frameworks provide a consistent and organized manner for more elegant development. This means that anyone familiar with the framework could look at another project written with the same framework and be able to navigate the code easily.
Zoop is a framework in every sense of the word, in that it provides both structure and libraries. To understand this better we will examine how it achieves this.
The core structure to a Zoop Application is a zone. This is probably a new term to you if you are using Zoop for the first time. Zones are unique to Zoop and provide a very organized structure for the programmer to work in. Each separate major function of the program is placed into a zone. We will discuss more about zones later. The major point is that Zoop is very organized, which translates into a more manageable codebase. In fact with Zoop you actually have to make an effort to be unorganized, because it is much easier to follow the standard.
Because of the structure of Zoop, Zoop can provide functionality in two different ways. For simplicities sake we will distinguish them as lower level and higher level functionality. Lower level functions can be used regardless of the structure of the application and could even be used in a non Zoop based application. A good example of this is the function append_to_file($file,$content) which opens a file and appends content to it. That function can be used anywhere, even outside of Zoop. Higher level functions are those that take advantage of the Zone structure provided by Zoop. Examples of these higher level functions are guiControls. GuiControls are more or less form widgets with integrated validation. They are incredibly simple to use and the validation occurs before you even see $_POST. As a programmer that is a very nice function to have. These higher level functions cannot be used without using the zone structure. Zoop has a nice balance of both higher and lower level functions.
Part of the advantage of using Zoop is that it has a very clear and understandable URL mapping structure. Given a url the programmer knows exactly which function is being run in which file and which parameters are being passed to that function. This makes Zoop one of the best environments to develop in especially for groups of programmers working on the same code base.
Zoop PHP Framework is quite easy to install and deploy. This guide expects you to be familiar with such concepts as uncompressing files and editing files.
Download Zoop. We also recommend downloading the Zoop Skeleton, particularly if you are creating an application from scratch.
Uncompress the tarball into it's own directory. We recommend placing Zoop in a separate location from your program. Perhaps a place like /usr/local/zoop if you or on a unix machine. Zoop can be located pretty much anywhere that is accessible by php. Remember where you place Zoop because we are going to need that information in our program. The framework, and this guide references the location where Zoop is located on your system as "zoop_dir".
Uncompress the skeleton tarball into a directory for your project. The Skeleton should look like this screenshot does.
We need to edit the config.php file and tell our application where to find zoop. There is one definition:
define('zoop_dir','/path/to/zoop');
A few more things are involved in starting to program with the Zoop Skeleton, so we wrote a separate tutorial about that called Beginning Zoop.
The Zoop framework has a few dependencies on some Pear libraries. Starting with Zoop 1.2, Zoop now packages the necessary PEAR libraries and makes them available to download. These should be uncompressed and placed into zoop_dir/lib. This package should be used only when a standard Pear setup is not capable (such as on a shared hosting environment).
To Setup the necessary Pear dependencies you will need to copy and paste this into a terminal:
pear install DB pear install --force VFS pear install --force Log pear install --force Validate pear install --force XML_Util pear install --force XML_Parser pear install --force XML_Serializer pear install --force XML_RPC pear install --force Mail
It is likely you may already have some (most) of these. You also may not need them all, since many people probably won't be using all the components of Zoop, but there is no harm in installing them all. If you are on a shared system then you may have to ask someone else to do this for you. If you are upgrading zoop or using the zoop/lib for the first time, make sure the following line is in your config file below the line where "zoop_dir" is defined:
Since Zoop uses caching and other temporary files the webserver needs a place to store them. By default that is in your application directory (app_dir for short) in a subdirectory tmp. Please make sure that this directory is writable by the server. In a Unix Environment you would do something like
# cd /path/to/app_dir # sudo chgrp -R apache tmp # sudo chmod -R g+w tmp
Zoop does not require, but largely benefits from using it's own programming structure of zones.
To understand zones we will describe what a program would look like without zones. A plain php program would consist of the following:
http://example.com/program/file.phpThis approach has a number of disadvantages.
This plain php approach works perfect if all you are doing is writing a script to handle the post from a form, but it falls short when using php to develop an application.
Note: There are multiple approaches to solving this issue with php and zones is a very elegant one, but certainly not the only one.
A zone is a group of pages with a similar function or subject. For example most applications have a set of pages for administration. In our traditional PHP example, they would be a number of php files organized in a single /admin directory. In a Zoop application, the logic ( in the MVC model for applications, the controller portion) for the page is contained within a method called pageXXXX in the zone_admin class, which is generally defined in a file called zone_admin.php.
So inside the single zone_admin.php file is an object (class in this case) called zone_admin. Inside of that object exist many functions, one for each separate page. These functions are called page functions.
Because of the zone architecture, going to a page in Zoop is a bit different than standard php. In our standard php example, say there is a page to edit a user. This page is in the admin section, eg. application_directory/admin/edituser.php. To go to this page in a web browser you would go to the url http://example.com/application_directory/admin/edituser.php
In Zoop things would be done a bit differently. First the same function would be located in a different place. In the file zone_admin.php, in a function called pageEditUser inside of the zone_admin class. To view this page in a web browser you would go to http://example.com/application_directory/index.php/admin/editUser
It is very similar to a standard php approach, but has subtle differences. Notice the additional index.php there. There are many advantages to using zones beyond organization, and zones provide a lot of functionality that could not be practically accomplished any other way. Later we will revisit zones and provide more in depth information on some of these benefits.
Also available for download from the Zoop site (or SourceForge) is the Zoop Skeleton. This is a barebones application provided so that a programmer could have a starting point when beginning a new application. A new skeleton would likely be released with each new Zoop release so it is usually a good idea to get the skeleton that corresponds with your Zoop release.
The skeleton contains a number of files and directories of which some files are worth mentioning here * READ: THESE ARE IMPORTANT *.
config.php includes.php templates/default config/
We will look at a few of these files and directories individually to ensure our application turns out as well as it should.
This is your primary configuration file for your application. It contains a lot of defines to setup the options Zoop will use for your application. Mostly the defaults will work for you until you know enough to change them.
There is one setting however that will prevent your entire application from working until it is set properly. This is the location of Zoop on your system. It looks like this:
define('zoop_dir', '/home/steve/Projects/zoop');
That is where Zoop exists on my system and would be very unlikely in the same place on yours. Please change this to the appropriate location. Another important one is the app_status definition. Though we won't use it for the hello world application, it is one to make a note of. It can be dev, test or live and Zoop will handle things like errors and caching differently depending on the app_status.
The next most important file is the includes.php file. A lot of things won't work if this file isn't used properly, luckily it is really easy to use. There are five sections of this file, configuration, zones, objects, misc, and pear.
This section includes files like config.php and zoop.php. After that it sets up the zoop object which will be used for the rest of the file. The first thing we do after we have the Zoop object is use it to include the different components of zoop that our application will use. By only including the parts we use we help to eliminate application bloat a problem that many frameworks suffer from (most think of it as a necessary evil). Zoop based application can actually run really lean by simply only including the components you plan on using. For our hello world application we will only include the gui component. This is done with the function call:
$zoop->addComponent('gui');
Make sure all other addComponent calls are commented out and this call for gui isn't.
This section includes all the zones that we will be using in our application. *This is important* When you create a new zone don't forget to include it here. This is done with the function call:
$zoop->addZone('zonename');
For our hello world application we will only be using the default zone so make sure that only that line is uncommented.
This section is used to include objects. Objects are libraries that you use for your application. These could be objects (classes) or static functions (though it is usually a good idea to classify everything to avoid collisions in the global namespace). A good example of this would be if you were to write a webmail application you would want to create an object containing some IMAP or POP functions that could be used by your page functions. These objects are located in app_dir/objects and can exist in subdirectories. The are included with the function call:
$zoop->addObject('objectname');
Notice that we do not put the .php after the object name in the function call.
This section is up to you and is put here for organizational purposes. Just use standard php include or include_once here.
This section is for pear libraries that you need for your application. Zoop also uses pear for different things, but each component includes it's necessary pear libraries itself. It is always a good idea to use include_once when including pear libraries as you are not sure which pear libs Zoop will include (also using include_once) in the future. As a general rule Zoop only uses pear libraries when there is a compelling reason to do so. A good example of this is pear_DB an excellent database abstraction library which quickly surpassed the one we wrote in Zoop previously.
In a Zoop application, when a web browser visits the http://example.com/application_dir/
Zoop will always execute the same page function. pageDefault inside of zone_default.php. The skeleton actually already has a welcome page there setup so lets go there in our web browser and make sure that we see what we want to see (it should be a welcome screen of some sort). If you see an error message instead retrace your steps. One step that many people forget is to make sure that the web server can write to the app_dir/tmp directory. That is where the template caching files are located and Zoop will display an error message if it cannot create them.
If you are reading this it is safe to assume that you have come thus far unscathed. You are almost there with your first Zoop application.
Edit zone_default.php with your favorite PHP editor. If you don't have a favorite PHP editor allow me to recommend some that I have enjoyed using. On Linux or Mac I love Quanta Plus. On Windows notepad++ is pretty nice and so is PHP Designer 2006. Of course there is always vim for any os. So back to zone_default.php, edit it and find the function called pageDefault. It already has some content in it, but we don't need to use that content, so comment it out or delete it. We should be left with a pageDefault function looking like this:
pageDefault($inPath) { }
Now lets place something into that function. Since this is a "hello world" app, we obviously want to echo ("hello world");. Your pageDefault should look like:
pageDefault($inPath) { echo("Hello World"); }
Now to view your application. In your web browser go to http://example.com/app_dir/
Zoop uses the excellent Smarty templating system. Zoop's Gui component provides some extra functionality not provided in the core Smarty system, but for the most part it is Smarty. One of the large differences is that Smarty uses a Smarty object called $smarty by default, in Zoop that object is called $gui. For the most part it is just a little easier to type $gui than $smarty as it is fewer characters.
Zoop is completely usable without the gui component (Smarty) and you are welcome to echo all of your content to the browser inside of your page functions, however we strongly recommend using the gui component for most projects as there are so many advantages to doing so.
The usage of a templating system such as Smarty helps to promote MVC or a separation of logic and layout.
In Zoop by default the template files are located in app_dir/templates/default.
Settings for the gui component are defined in app_dir/config/gui.php.
Also by default gui compiles templates and caches them. The temporary cached files are created in app_dir/tmp/gui which must be writable by the web server. Substantial speed benefits can be achieved by using this compile and cache method and we recommend using it (it is enabled by default).
The Zoop Gui Component uses the standard Smarty template file format.
Smarty template files are a mixture of HTML and Smarty tags. A Smarty tag looks like {$var}. Smarty template files have the extension .tpl.
Let's use some templates in our Hello World application. First step is to create a template file. Create the file app_dir/templates/default/helloworld.tpl and place the following content into it.
<html> <head><title>{$hw}</title></head> <body> <h1>{$hw}</h1> </body> </html>
Now lets return to the zone_default.php file and edit the pageDefault function we used previously.
The $gui object is a global object already instantiated for us. All we need to do is to declare it as global to be able to access it in our page function. Once we have access to the $gui object we need to tell it what to set the {$hw} variable to that we referenced in the tpl file. Lastly we need to tell the $gui object to display this tpl.
Our pageDefault function should look like this:
pageDefault($inPath) { global $gui; $gui->assign("hw", "Hello World"); $gui->display("helloworld.tpl"); }
Lets visit the same page in the web browser as we did previously, http://example.com/app_dir/ . We should see something similar to the screenshot below.
Moving on with the tutorial..
Now we are going to do a little more with the zones and acutally make this application usuable...
First step is that we want to make a landing page that links to our different pages and zones.
Lets create a new file called menu.tpl and place it in app_dir/templates/default/ (the standard location for templates).
place the following contents into menu.tpl
{include file="head.tpl"} <body> <h1>Our Menu</h1> <a href="{$BASE_HREF}/email/send">Send An Email</a><br> </body> </html>
Notice we are doing a gui include. By including an already existing tpl file that is packaged with the skeleton we will load up all the javascript we will use in the next section. Of course since the skeleton is now your application you can modify it to do whatever you like.
We are going to change the pageDefault function of zone_default (the one we used in the last section of this book) where we just put the hello world to now display this file instead. The pageDefault function inside of zone_default should look as follows:
function pageDefault($inPath) { global $gui; $gui->display("menu.tpl"); }
Now lets create our very own zone... we will call it email. The easiest way to do this is to copy the Template zone_name.php file and call it zone_email.php.
We need to change all references from zone_name to zone_email. A simple search and replace will do it.
Our file should have the following contents:
<? class zone_email extends zone { function zone_email() { //$this->setUrlVarNames(array("Id", "Id")); } function makePath() { return "/name"; } function initZone($inPath) { // do things here that you want to happen on every // request that includes this zone } function initPages($inPath) { // do things here that you want to happen for // every request that ends with a page in this zone, // including posts } function pageDefault($inPath) { global $gui; $gui->display("default.tpl"); } function postDefault($inPath) { // this is for posts, it handles when // forms get submitted. } }
We also need to make sure that our new zone is included. This occurs in the includes.php file. There is a section dedicated to adding Zones... There should already be a couple there just place this line after them .
$zoop->addZone("email");
Notice how we used the zone name, not the file name.
Now in the zone_email.php file we are going to add a new function.
We will call it pageSend. It can be placed anywhere within the class definition. I usually add it to the end.
For the time being we will just put an echo into that function so that we can test everything up till this point.
function pageSend($inPath) { echo("here at pageSend"); }
Now lets fire up our favorite browser and goto our skeleton. Since we are now displaying template in the pageDefault function we should see the output from our menu.tpl.
One of Zoop's unique features is guicontrols. Zoop's Guicontrol component uses the gui component and the validation component to provide excellent form controls. Guicontrols add validation to html's form controls and extend them to contain more controls than found in standard html (while remaining standards compliant).
Since most every webapplication uses forms to collect data from the users, learning how to use guicontrols effectively can save the programmer time and resources. They also provide security features that would be difficult to implement otherwise. Using Guicontrols.
Let's add some guicontrols to our Hello World application.
Our first step is to make sure that the guicontrol component is being included.
Open includes.php and make sure that
$zoop->addComponent('guicontrol');
is in there.
We are going to continue to work with the email_zone we created in last section. Create a new directory inside template/default called email. Now create a file in that directory called send.tpl with the following contents.
{include file="head.tpl"} <body> <h1>Sending an email</h1> <form method="POST"> <p> From: {guicontrol type='text' name='from'}<br> To: {guicontrol type='text' name='to'}<br> Subject: {guicontrol type='text' name='subject'}<br> Message:<br> {guicontrol type='textarea' name='message'}<br> <input type="submit"> </form> </body> </html>
Something noteworty is that the form tag is missing an action. When the form tag doesn't have an action, the browser submits the POST to the same url as the page the form appears on. Also notice that the method is POST. Guicontrols require form data submitted through a POST to work properly.
Now lets return to zone_email and edit it to display this template and recieve to post.
New Concept:
ZooP has a special way of handling requests that have a POST in them. Instead of running the pageDefault function as we used previously, ZooP will run the postDefault function. So given the same url, Zoop will execute one of two different functions depending on if the request contains a POST or not.
Our pageSend function should now look like
function pageSend($inPath) { $this->guidisplay("send.tpl"); }
Note: notice here we didn't use the $gui object as we did in previous instances. Zoop has a shortcut function called $this->guidisplay which will automatically look in the directory with the same name as your zone.. in our case it will look in "templates/default/email/".
We will create a new function called postSend which will recieve the post from the form we created.
! Important !
In Zoop any request containing a post should be send to a post function.
Given the same url, if there isn't a post attached, zoop will run the page function if there is a post attached it will first try to run the post function using the page function as a fallback.
The postSend function should look like this:
postSend($inPath) { $post = getRawPost(); echo_r($post); }
Lets visit the same page in the web browser as we did previously, http://hostname/app_dir/email/send . We should see something similar to the screenshot below.
It isn't necessarily stylized very attractively, but it is a function form. Lets fill it out and see what happens when we hit submit... The postSend function should have echo'ed exactally what we entered as we asked it to. Unfortunately you may have noticed that the person that filled this form out doesn't like to play by the rules. Had we been sending a real email from this data it could have been fatal.
Validation made easy with guiControls
Now comes the best part about guiControls and Zoop, integrated validation. Zoop supports many different validation formats and this is just a simple introduction into using them... More information is available by looking through the validation component of zoop or looking through the autogen documentation.
Since we can't send an email from or to a first name or pronoun, we should probably ask for an email address and make sure we recieve one.
Additionally people like their emails to have a subject, so while we are at it, lets require a subject that is at least 5 characters long and no longer than 25.
Lastly an email should have a message with it so lets require that too.
Lets edit our send.tpl file again and place the following content into it.
{include file="head.tpl"} <body> <h1>Sending an email</h1> <div id='errorsbx'></div> <form method="POST"> <p> <label for="controls[text][from][value]">From:</label> {guicontrol type='text' name='from' _validate_type='email' _validate_required='true'}<br> <label for="controls[text][to][value]">To:</label> {guicontrol type='text' name='to' _validate_type='email' _validate_required='true'}<br> <label for="controls[text][subject][value]">Subject:</label> {guicontrol type='text' name='subject' _validate_type='length' _validate_min=5 _validate_max=25 _validate_required='true'}<br> <label for="controls[text][message][value]">Message:</label> <br> {guicontrol type='textarea' name='message' _validate_required='true'}<br> <br> <input type="submit" onclick="return submitForm(form);"> </form> </body> </html>
There are quite a few changes from before, but they are simple ones... Lets talk about each one and the conventions used.
First of all we added label tags and assigned them to their corresponding control (or form element).
Next we added validation to each of the controls. Since smarty doesn't provide a method of passing an array, we devised our own. Essentially we are defining a validate array using the "_" character as a seperator. We also start the parameter name with an underscore ("_") so as to distinguish it from other parameters which just have "_" in their name.
validate requires "type" to be set, but all other parameters are optional (though specific types may require parameters themselves).
Lastly we added a javascript function call to the submit button and an empty div above the form with the id "errorsbx". This is for the javascript validation. Zoop also provides server side validation... Lets try this form again and show you what we mean.
Example 1: With Javascript Enabled.
Notice the javascript validation steps through the form and shows you each part that is wrong. If a label exists, it highlights and and prevents the form from going forward until every field validates.
Your thinking, so what anyone can disable javascript and then our validation is worthless.... Perhaps in other frameworks but not in Zoop. Here is that same page with javascript disabled.
Naturally it validates the entire form at once since we don't want a lot of server trips.
Did you notice the best part... We didn't have to change our postSend function to handle the validation. Zoop does all the validation behind the scenes so when we access the post in our postSend function it has already been validated. postSend will never run unless the guiControls all pass validation.
The form also remembers the values we entered so the users don't have to enter them again. One final try... with the correct format and ...
The Zoop Mail component is a powerful tool usable to send emails from within php. It supports templates (yes the same templates as gui). It also has a modular mailing engine and supports SMTP (authenticated or non authenticated) and sendmail.
WARNING: Zoop Mail uses a number of pear libraries to work with the smtp server, authentication, mail setup and socket connecting. We recommend if you use this component that you use the Zoop Lib package and install it in your zoop_dir/lib. We even recommend this over using your own systems pear packages because the stable mail_mime package is broken and hasn't been updated in nearly a year. We package the cvs one and it works quite well.
The Mail component follows Zoops standard config format. The config file can be found in the app_dir/config/mail.php
Here is a list of the important directives in that file
define("mail_type", "smtp"); define("mail_smtp_host", "mail.yourserver.com"); define("mail_smtp_port", 25); define("mail_smtp_auth_use", false); define("mail_smtp_auth_username", ""); define("mail_smtp_auth_password", ""); define("gui_messages", "messages");
Most should be self explanatory, but please take note that the default settings may not work at all on your system and will likely need to be configured.
mail_type can be smtp or sendmail.
gui_messages is the directory inside of app_dir/templates/default/ that will contain your email templates.
We will begin by creating a template file in gui_messages as defined in our config file. For our purposes we will call the file notify.tpl and place the following contents into it:
{$message} <br> <hr> The above message was sent by {$from} using our new Zoop based emailer!
We should now modify our postSend function that we have created to send this email from. The postSend function will look like:
function postSend($inPath) { $post = getRawPost(); $message = new message(); $message->assign("message", $post['message']); $message->assign("from", $post['from']); $message->display( $post['from'], $post['to'], "", $post['subject'], "notify.tpl", "multipart"); /* $message->send( $post['from'], $post['to'], "", $post['subject'], "notify.tpl", "multipart"); */ }
Now to explain what it is doing.
We need to instantiate a new instance of message and assign it the variable we will be using. This works just like the gui object does.
The message object has a very nice function for development, the display function. It takes the same parameters at the send function, but instead of sending the email, it renders it and displays it to the browser. This enables us to create our email and preview it without sending a lot of emails in the process.
The send and the display functions take the following parameters (from, to, cc, subject, template, type).
There are three types of emails ( text, html and multipart ). Text sends a plain text email that is readable by all email readers. The zoop mail component will automatically convert any html formatting to plain text if you use this option. The html type sends an html email that is readable by most email readers and can have much richer formatting than the plain text one. Multipart is a special type that sends out an email that contains both a plain text part and a html part and lets the email reader decide which one to display. This one should be fairly universally supported. It's major downside is that the byte size of the email is approximately double the others, since it sends the content of both text and html.
Once the email is to our liking we simply switch the function call from display to send, cross our fingers and hope for the best. Obviously we should send an email to ourselves first (at an external email address) and make sure that everything is working properly with our connection to either the smtp server or the server's sendmail program.
When sending mail from a PHP script, you have two general choices, the UNIX "sendmail" function, and a SMTP server.
Sendmail is what PHP uses by default. This is primarily due to the fact that php does not have built in support for connecting to an SMTP server. When you use sendmail from a web server, the server itself attempts to deliver the mail by connecting to the destination system using the SMTP protocol.
SMTP stands for Simple Mail Transfer Protocol. Using this option permits you to connect to a local or remote SMTP server using a socket based connection. This is usually a more efficient means of sending email since it has less overhead then sendmail.
Zoop has it's own database abstraction layer, built upon the excellent pear::DB class. Zoop makes it easy to connect to multiple databases, of different types in the same application and is quite powerful. This is only meant to be an introduction into the database component of Zoop.
Settings for the db component are defined in app_dir/config/db.php.
The file should look like:
define('db_RDBMS', 'mysql'); define('db_Username', ''); define('db_Password', ''); define('db_Server', 'localhost'); define('db_Port', '3306'); define('db_Database', '');
Lets extend the hello World application to use a database. I am going to assume you have a working knowledge of database administration and php's integration with databases.
I am most familar with MySQL so I will use that for this tutorial, but there is no reason you need to.. The sql I use will be close enough so you can tweak it to your database if you are using something else. So if you aren't using mysql, this tutorial won't be copy paste, but will inform you just the same.
Create a database and make sure to config the application as shown above.
Lets setup a table within that database to house the users of our mock application.
I created a new zone to house this in, but you could do it in zoneDefault as well.
I made a new page function called pageInitDB and put the following into it.
function pageDBinit($inPath)
{
sql_query("DROP TABLE IF EXISTS users");
sql_query("CREATE TABLE users2 (
userid int(25) NOT NULL auto_increment,
first_name varchar(25) NOT NULL default '',
last_name varchar(25) NOT NULL default '',
email_address varchar(55) NOT NULL default '',
username varchar(25) NOT NULL default '',
password varchar(255) NOT NULL default '',
info text NOT NULL,
last_login datetime NOT NULL default '0000-00-00 00:00:00',
activated enum('0','1') NOT NULL default '0',
PRIMARY KEY (userid)
) TYPE=MyISAM COMMENT='Membership Information'
");
$id = sql_insert("INSERT into users2 values ('',
'Steve',
'Tester',
'test@testing.com',
'Tester',
'IloveTesting3',
'I am L33t',
'',
'0')
");
$id2 = db_insert_array(
array(
'first_name' => "John",
"last_name" => "Tester",
'email_address' => "testingrocks@testing.com",
"username" => "test4me",
"password" => "TestingIsInMYBlood",
"activated" => "1"),
"users2");
}For the rest of this section we will show you different database functions in Zoop and how they work. We will continue to use this record set we setup here. This is not intended to be an all incompasing guide, but rather an introduction to the more commonly used functions.
In general there are two types of database functions in Zoop, sql_ and db_. sql_ functions are passed sql statements and do different things with the result of those statements. These functions are database independent, however the sql that they pass to the database is not, so your code won't necessarily be completly portable. Using our record set setup below, here are some of the more commonly used functions.
Takes a sql statement and passes it to the database.. Returns Boolean depending on success, nothing to see here.
Returns the primary_key id of the created record.
Boolean return, if it finds anything, it will return true.
For these I will show you the call and then the echo'ed result.
Only expects one record to be found.
Array
(
[userid] => 1
[first_name] => Steve
[last_name] => Tester
[email_address] => test@testing.com
[username] => Tester
[password] => IloveTesting3
[info] => I am L33t
[last_login] => 0000-00-00 00:00:00
[activated] => 0
)Expects only one cell to be returned.
Tester
Array
(
[0] => Array
(
[userid] => 1
[first_name] => Steve
[last_name] => Tester
[email_address] => test@testing.com
[username] => Tester
[password] => IloveTesting3
[info] => I am L33t
[last_login] => 0000-00-00 00:00:00
[activated] => 0
)
[1] => Array
(
[userid] => 2
[first_name] => John
[last_name] => Tester
[email_address] => testingrocks@testing.com
[username] => test4me
[password] => TestingIsInMYBlood
[info] =>
[last_login] => 0000-00-00 00:00:00
[activated] => 1
)
)Expects one field to be selected
Array
(
[0] => Tester
[1] => test4me
)Similar to fetch rows, except the key matches the primary key id
Array
(
[1] => Array
(
[first_name] => Steve
[last_name] => Tester
[email_address] => test@testing.com
[username] => Tester
[password] => IloveTesting3
[info] => I am L33t
[last_login] => 0000-00-00 00:00:00
[activated] => 0
)
[2] => Array
(
[first_name] => John
[last_name] => Tester
[email_address] => testingrocks@testing.com
[username] => test4me
[password] => TestingIsInMYBlood
[info] =>
[last_login] => 0000-00-00 00:00:00
[activated] => 1
)
)makes the value of the field passed in the key of the array.
Array
(
[Tester] => Array
(
[userid] => 1
[first_name] => Steve
[last_name] => Tester
[email_address] => test@testing.com
[username] => Tester
[password] => IloveTesting3
[info] => I am L33t
[last_login] => 0000-00-00 00:00:00
[activated] => 0
)
[test4me] => Array
(
[userid] => 2
[first_name] => John
[last_name] => Tester
[email_address] => testingrocks@testing.com
[username] => test4me
[password] => TestingIsInMYBlood
[info] =>
[last_login] => 0000-00-00 00:00:00
[activated] => 1
)
)The db functions do not accept sql as parameters (in general). They are a newer addition to Zoop (starting with 1.2).
The following functions are expected to take their inputted values unescaped!! They are designed to interoperate with the sql_fetch_* functions. If you have are using them with post data and have magic_quotes_gpc on in your php.ini file please make sure to strip_slashes() before using these functions.
The Input array should look like the result of sql_fetch_one.
The Input array should look like the result of sql_fetch_one. Updates instead of inserting.
This one will update if the primary key is != "new", if it is new, it will insert a new record.
Zoop as a framework provides both lower level and more advanced functionality. Forms is one of the more advanced functions within Zoop. It is built on top of the gui, guicontrol and db components and provides a bridge between them.
For the 1.2 Release of Zoop, Forms was redone to provide both better efficiency and an easier interface for the programmer.
Forms integrates the database with the gui offering a very nice answer to database driven forms, searching, and lists.
We are going to pickup right where the database section left off. Make sure you have your database and table setup properly.
We are now going to create a few page functions with the intention of
We are also going to validate our user inputted data to ensure that proper information is received... Oh and we aren't going to touch html or sql for any of this.
Create a new page function called pageListUsers and put the following into it. I created a new zone (zone_users) to put it in, but you could put it in any zone you want.
function pageListUsers($inPath)
{
global $gui;
$form = new form2("users", "list");
$form->setParam("zone", $this->zonename);
$form->setTitle("Viewing Users");
$form->setParam("listlink", "EditUser");
$form->setFieldParam(array("password", "info"), "listshow", false);
$form->setFieldParam(
array("first_name", "username", "last_name"),
"clickable", true);
$form->setFieldIndex("activated", array(1 => "y", 0 => ""));
$form->setParam("deleteColumn", true);
$form->setParam("deletelink", "DeleteUser");
$form->guiAssign();
$gui->generate("forms/form2.tpl", "blank.tpl", "mainmenu.tpl", "Users");
}First the screenshot of the output then a discussion on what's going on here.
[???We need to get a screenshot, and put it here???]
What you are seeing in that screen shot is acutally only the relevant part of that page... The $gui->generate function includes the menu's, headers, css & js as well.
Now lets break down what we did.
We start out by instantiating our form object... and pass along the table and the type of form we want generated... a list in this case.
$form = new form2("users", "list");We then tell the object which zone we will be linking things to(creating urls), in this case, $this->zonename.
$form->setParam("zone", $this->zonename);The next line sets the title of the list, as seen in the screenshot.
$form->setTitle("Viewing Users");Next we tell forms2 where to link the clickable fields to.. In this case we want them to goto the pageEditUser function in this same zone.
$form->setParam("listlink", "EditUser");And now which Fields are shown in the list (by default all, so we turn a few to false).
$form->setFieldParam(array("password", "info"), "listshow", false);The function setFieldParam can take either an array of fields or a single field as the first value.
We want to tell the object which fields are clickable. If we didn't do this the primary_key field would be clickable by default.
$form->setFieldParam(
array("first_name", "username", "last_name"),
"clickable", true);$form->setFieldIndex("activated", array(1 => "y", 0 => ""));We tell it we want a delete column and which function to link it to.
$form->setParam("deleteColumn", true);
$form->setParam("deletelink", "DeleteUser");Lastly we assign and display it (display is done using the generate function).
$form->guiAssign(); $gui->generate( "forms/form2.tpl", "blank.tpl", "mainmenu.tpl", "Users");
And to show you what is in the relevant template file, here is the form2.tpl file:
{forms2 form=$form}We are doing to do these steps together, because they are the exact same function.
We are going to create two functions, a page function and a post function.. We will call them EditUser, since that is what we setup the list to link to.
function pageEditUser($inPath)
{
global $gui;
$id = $inPath[1];
$form = new form2("users", "record", $id);
$form->setTitle("Editing Contact:" . $form->getValue("username"));
$form->setValidationOptions("email_address",
array("type" => "email"));
$form->required(array("email_address", "username"));
$form->setFieldParam(array("password"), "formshow", false);
$form->setHTMLoption('activated', 'type', 'checkbox');
$form->setHTMLoption('info', 'type', 'textarea');
$form->guiAssign();
$this->session("form", $form);
$gui->generate(
"forms/form2.tpl", "blank.tpl", "mainmenu.tpl", "Users");
}
function postEditUser($inPath)
{
$form = $this->session("form");
$POST = getRawPost();
$form->saveRecord($POST);
zoneRedirect("/ListUsers");
}Here I am going to show you two screenshots. The first come from going to [???some unknown link???]. The second [???some other uknown link???]. Which explains the difference between editing and creating a record using forms. The code is the exact same and knows how to handle a "new" record.


So lets break down what we did.
First we grab the passed $id from the path
$id = $inPath[1];
Next we instantiate the form object just like before, except this time of type "record" and pass in an id.
$form = new form2("users", "record", $id);Title is the same as before, so after that we setup some validation for the fields. First make sure email is a validily formatted email address.
$form->setValidationOptions("email_address",
array("type" => "email"));The validation type can be any validation found in the validator component of zoop.
And make sure email and username are inputted.
$form->required(array("email_address", "username"));For whatever reason, we don't want to be able to view or edit the passwords, so we tell forms not to place that field in the form.
$form->setFieldParam(array("password"), "formshow", false);For ease of use, we want to use a checkbox for the activated field, and a text area for info field.
$form->setHTMLoption('activated', 'type', 'checkbox');
$form->setHTMLoption('info', 'type', 'textarea');The HTML type can be any valid guicontrol.
guiAssign as before then we want to store the form in the session. An easy way of doing that is with the zone session function. This needs to happen so the post function can pickup where we leave off.
$this->session("form", $form);Generate as before... Onto the post function.
Here we start by retrieving the form object from the session.
$form = $this->session("form");Next we get the post using getRawPost. We could use any of the post functions, but this one serves our purposes for an example.
$POST = getRawPost();
We tell the form object to save it's data
$form->saveRecord($POST);
And redirect to back to the list.
zoneRedirect("/ListUsers");This is the easiest step, not that any were hard. Remember back in step 1 we told the form object what to link the delete link to.. Lets create that function.
function pageDeleteUser($inPath)
{
$id = $inPath[1];
$form = &new form2("users");
$form->deleteRecord($id);
zoneRedirect("/ListUsers");
}Zoop 1.2 adds caching support to Zoop. The Caching component using the zcache object is also integrated into the zones to provide integrated caching support everywhere in your application.
Caching is probably the single best way to improve the speed of an application. If used properly you could increase the speed of your application many many times. A simple example, we had a script that took about 3 seconds to run. After we wrote caching support into it that same script took about .03 seconds to run. That is not an uncommon increase in speed thanks to caching.
One of the biggest advantages of caching is storing data. Common uses are for things that take a while to render or generate, or for remote things such as an rss feed. Here's why to use caching. Simple math if you have a RSS feed that your site displays that is updated, lets say hourly. Without caching, every single user would request that remote file every time a page was visited. 5 users a second times 60 seconds times 60 minutes, that is 1800 downloads of the exact same file. Throw in caching and the file is only downloaded once. Perhaps you are worried about having the file be out of date for too long a time, then only cache it for 10 minutes. That is still 6 vs 1800.
The best part is that Zoop makes using caching super easy! Lets see how...
Zoop uses the zcache object to do all of it's caching. This object is a special object that has functions that can be called statically or as an instantiated object. There are instances when one way is better than the other. Here I will try to demonstrate examples in both ways. General rule would be, use it statically unless you are planning on caching a number of things, and then instantiate the object with the settings for those things.
Zcache handles raw data (like an array, or object) differently than strings, but can cache both. In this section we will provide four examples, data, strings, intantiated, statically. Here we go. These examples will be in sudo code, so it won't be a complete cut and paste solution, because we won't provide the string or data or what $id to assign, but enough is there that you can modify it for your needs pretty easily.
Static:
if (!$XML = zcache::getString($id, array('base' => "MyCacheDirectory"))) { $XML = // function call to get XML zcache::cacheString($id, $XML, array('base' => "MyCacheDirectory", 'lifeTime' => 60*60*24*7) ); } echo($XML);
Instantiated:
$zcache = new zcache( array('base'=> "MyCacheDirectory", 'lifeTime' => 60*60*24*7) ); if (!$XML = $zcache->getString($id)) { $XML = // function call to get XML $zcache->cacheString($id, $XML); } echo($XML);
Static:
if (!$array = zcache::getData($id, array('base' => "MyCacheDirectory"))) { $array = array("1", "2", "3"); zcache::cacheData($id, $array, array('base' => "MyCacheDirectory", 'lifeTime' => 60*60*24*7) ); } echo_r($array);
Instantiated:
$zcache = new zcache( array('base'=> "MyCacheDirectory", 'lifeTime' => 60*60*24*7) ); if (!$array = $zcache->getData($id)) { $array = array("1", "2", "3"); $zcache->cacheData($id, $array); } echo($array);
In the same base and group no two items can share the same id, even if on is data and the other a string. base and group are good ways of organizing the cache logically.
Directories are automatically created recursively, so don't worry about making your base /some/really/long/path/ if it helps you organize things.
Caching is particularly helpful in speeding up sql based applications. The less times you request the sql server for the same information, the better. The best part is that an application will know when the sql data changes so it can cache items indefinately and then delete the cache after a sql_insert or sql_update. Caching works best on items that change infrequently… for this example we will cache a settings table. Most people rarely change their settings in an application, so this makes it a great example for using caching. I would create a function to get the settings which could be used anywhere, then In a zoop application you would likely have two different functions, a pageEditSettings and a postEditSettings you would want to write the clear cache function in the later. Some more pseudo code here.
function getSettings($id) { if (!$settings = zcache::getData($id, array('base' => "Settings"))) { $settings = sql_fetch_one("select * from settings where id=$id"); zcache::cacheData($id, $settings, array('base' => "Settings", 'lifeTime' => 60*60*24*7*365) ); } return $settings; }
After you saved the data you would place the following line in postEditSettings:
zcache::clear($id, array('base' => "Settings"));
clear clears both data and string type cache.
There are four functions for clearing cache.
clear : clears or removes one id is a specific base (and group)
zcache::clear($id, $options_array);
clearGroup : clears all cache of a group in a specific base
zcache::clearGroup($group, $options_array);
clearBase : clears all cache in a passed base
zcache::clearBase($base);
clearAllCache : clears all cache in app_cache_dir (recursively)
zcache::clearAllCache();
Each zone you create has a built in zcache object $this->zcache. It is already instantiated with the base set to the name of the zone. This aids in organization since all your cached items are seperated by zones.
The Zoop Validator Component is used by a number of the other components, particularly guiControl and Forms.
It provides type based validation that can be used to validate user_input, or anything else you can think of.
Here is a table of the current validators:
| Type | PHP | JS | Notes |
|---|---|---|---|
| Phone | x | x | US phone only. |
| Length | x | x | Accepts min and max params. |
| Quantity | x | For quantity in an array (like boxes checked). Accepts min and max params. | |
| EqualTo | x | Requires equal_id param (id of another field). | |
| Int | x | x | Accepts min and max params. |
| Float | x | x | Accepts min and max params. |
| Numeric | x | x | Accepts min and max params. |
| AlphaNumeric | x | x | |
| Money | x | x | |
| Zip | x | x | Canadian or US formatted Zip (postal) code. |
| x | x | ||
| Domain | x | doesn't start with http:// or https:// | |
| Url | x | x | starts with http:// or https:// |
| IP | x | ||
| Password | x | Validates for a secure password... 1 UC, 2 LC and 1 Num and Length > 6 | |
| SSN | x | ||
| RegExp | x | Requires Parameter RegExp Which needs to be a valid preg style Expression | |
| DbUnique | x | Requires Parameters table and field. It will check the table in the default sql connection to see if a duplicate value exists in that table. | |
| Date | x | Accepts Parameter format. Accepted values for format are "writeout", "us", "european", "univeral". |
All the validation functions are statically callable. They follow the following pattern, using Int as an example.
The Call:
Validator::validateInt($value, array('min' => 3, 'max' => 7));
Returns an array ($result), with the following structure:
$result['result'] = false; // or true depending on validation $result['message'] = "Message to Display to the User when False";
GuiControls and Forms both use integrate the use of validator. Simple by defining the validate parameters of either, you will be using validator.
There are also a handful of special functions to help you in implementing validation in your code.
validate wrapper for the functions in this class.
The validate array must have 'type' => $type set for this to work. The type is any found in the above table (and php able).
This function also permits 'required' as a parameter in the array(). Without required set to true, a value will validate as true if it is either empty, null, or validates.
Example:
Validator::validate($value, array('type' => 'email', 'required' => 'true'));
validate wrapper for the functions in this class. Works like validate, but returns a boolean (true / false) value rather than an array.
The validate array must have 'type' => $type set for this to work. The type is any found in the above table (and php able).
Example:
Validator::boolValidate($value, array('type' => 'email'));
This function will validate more than one validator, stacked in order one at a time. Permits validating things like Alpahnumeric & Length together for example.
It will return as soon as one of the validators fails. This is particularly useful if one of the validators performs a sql check.
The validate array must have 'validators' => array of individual "validate" arrays set for this to work.validate wrapper for the functions in this class.
Example:
Validator::validateStack($value, array('required' => true, 'validators' => array( array('type' => 'AlphaNumeric'), array('type' => 'length', "min" => 4, "max" => 20), array('type' => 'dbUnique', 'field' => 'username', 'table' => 'users') )));
This function will validate more than one validator, stacked in order one at a time. Permits validating things like Alpahnumeric & Length together for example.
It will perform all validations before it returns the combined results.
The validate array must have 'validators' => array of individual "validate" arrays set for this to work.validate wrapper for the functions in this class.
Example:
Now, we’ll make another example using templates. A little bit more work in HTML and we can get a better visual output.
Open the last project - Gui : The Zoop View (powered by Smarty). Using your favorite HTML editor browse to app_path/templates/default folder and open helloword.tpl file. Our intention is to create three separate sessions: top, middle and bottom. You can do this using tables, it’s the easier way, but div tags is recommended, actually. Div tags allow detailed formatting with CSS, and deliver HTML code with better semantic.
Use two smarty tags: {$lbl1} and {$lbl2}. $lbl1 at top and $lbl2 at bottom section:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta content="pt-br" http-equiv="Content-Language" /> <meta content="text/html; charset=utf-8" http-equiv="Content-Type" /> <title>Ola_Mundo_com_Template</title> </head> <body> <div align="center" style="background-color: #FFE2C6; font-family: Tahoma; font-weight: bold"> <br/>{$lbl1}<br /> </div> <div style="background-color: #FFF4EA"> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> <br/> </div> <div align="center" style="background-color: #FFE2C6"> {$lbl2} </div> </body> </html>
Our next step is to edit zone_default.php file. In that file we can find the functions – one to each page in our project. Find pageDefault function and change it. Use the code below:
pageDefault($inPath) { global $gui; $gui->assign("lbl1", "Hello World"); $gui->assign("lbl2", "Vespersoft Desenvolvimentos - www.vespersoft.com.br"); $gui->display("helloworld.tpl"); }
To remember: the assign commands above will put the second argument at first argument variables. The $gui->display command will show the template. Just run your project to see the changes.