Tag Archives: Deployment

Creating deployment packages with Zend Studio 9

Deploying your work in Zend Studio is quite easy.  You have the ability to deploy directly from your IDE, which is, in general, more for local environments, the Zend Developer Cloud or testing.  The reason for this is because generally the developer should not have access to production.  Therefore, the drag and drop deployment that Zend Studio supports is usually not going to be used, though in smaller development shops it may still be quite useful.

The deployment mechanism in Zend Server uses a ZPK file, which contains the source code, assets and the deployment descriptor.  The deployment mechanism on the server side has already been discussed on this blog.

When creating a deployment package the first you need to do is either create a new project with deployment support

or add it by right clicking on your project and selecting “Add Application Deployment Support”

This will allow you to then either drag the project onto a target to deploy it or, as we’re talking about here, allow you to export it as we’re going to show here.

The deployment.xml file is where all of the configuration information resides.  Double click on this file and you will get a display similar to this

Most of the fields are self explanatory but a few might require a few words.

  • Document Root – This is the document root (duh) which is relative to the base directory of the deployment.  Your project, in other words.
  • License – This is the relative path to a text file in the project directory structure that contains the EULA for the project.  During the deployment workflow in the UI the end user will be presented with this if the file is available and will be required to agree to it before proceeding.
  • Persistent Resources – These are items that you don’t want to have overwritten during an upgrade.  For example, cache directories.

Dependencies

There are several different types of dependencies you can specify for your application.
These dependencies will be checked prior to deploying the application.  If they are not satisfied then the application will not deploy.

Triggers

There are several triggers that can be hooked into during the deployment process, each of which has a Pre and Post stage
  • Activate
  • Deactivate
  • Stage
  • Unstage
To set up a trigger simply double click on the stage that you would like to edit and a new file will be created for you.  In that file will be documentation on information on how to retrieve variables and parameters for your deployment scripts.
Speaking of variables and parameters, what is the difference?  There are two differences.
  1. Variables you cannot change during the deployment process.  What the value is in the deployment file is the value that you will get in the deployment script.  Parameters need to be specified during the deployment workflow and also have some validation that you can do on the entered values whereas with variables you do not.
  2. Both are accessible via getenv() during deployment but variables are retrieved with their names “as is” but parameters are upper cased and prepended with “ZS_”.  So if you have a parameter named “ugly_Duckling” it would be accessed via getenv(‘ZS_UGLY_DUCKLING’)

Package

There may be files in your application that you want to include (really!?) or exclude.  You can specify those in the Package panel.

Exporting

The last step is to export your project.  Right click on the project and select Export and choose “Deployment Package”.  This will output the project into a ZPK file that you can then upload to your Zend Server instance or Zend Application Fabric installation where it will be deployed to your website.  Lickety Split.

Conclusion

There are other panels there and other information that I have not included.  The reason for that is that it is either relatively self explanatory on not necessary from an initial “getting started” point of view.  Or because it’s Friday and I’m running out of time.  :-)
The deployment functionality in Zend Server was something that I have personally been waiting for for a long time.  This addition in Zend Studio now makes the circle complete.  Granted, there will always be more that we can do, but this was a feature that has long been needed and I’m quite glad that it’s here.

Enter Deployment: Zend Server 5.5

Today Zend Server 5.5 was released.  This is actually a pretty good sized deal.  The big addition to the Zend Server feature list is deployment.  Now, you might be saying “come on, Kevin, there are many PHP deployment solutions already”.  To which I would say “yes, there are.  But there are NO solutions that work out of the box“.  Ladies and gentlemen, welcome to the box.  If you have Zend Server 5.5, you have deployment.

So what I’m going to do with this post is give you a quick run-through on getting a simple application up and running.  There are a lot of features I am going to skip over (primarily deployment scripts) and show you a simple workflow so you can see how quickly and easily an application can be deployed.

Step 1: Install The Zend SDK

First of all, you need the Zend SDK (and, of course, Zend Server) and set up the tools directory to be in your path.  Actually, you don’t need the SDK since there is a GUI interface.  But I’m showing you a simple workflow that will get your application running very quickly.  We’ll get into more details in a different blog post that demonstrates the full GUI interface.

Step 2: Create an API Key

In the Zend Server UI you need to name and create a new API key so that the SDK can communicate with your instance of Zend Server.

If you click on the “Show full key” link you will get a popup of the full key so you can copy it into your terminal.  The name of the key is also important.

Step 3: Connect the SDK to Zend Server

Once you have your API key you can then associate it with your local SDK instance.  You will need the API key that you generated earlier along with the name of that key.  Provide the hostname and you’re good to go.

D:\workspace>zend add target -s 8a29cd9333ca366af986df69c32b44ed84aff6af0e741209a82cf4a444e64187
   -k deployment -h http://192.168.0.248
 
Target http://192.168.0.248 was added successfully, with id 0

The ID number is important if you are going to have multiple Zend Server clusters to talk to, such as staging and production.  Then you will need to add the -t argument with the target ID.  However, if you have only one installation then the SDK will automatically use that target.

Step 4: Create a deployment scenario for your PHP project

Once your target is set up you need to provide a deployment descriptor file for your project.  For my example project I’m just going to use a blank Hello World application with a /public application/document root.

D:\workspace>zend create project -t simple -n HelloWorld
 
Project resources were created successfully for HelloWorld under D:\workspace\HelloWorld

The -t option is the template of the application.  You have simple, quickstart and zend.  Because my application already existed I specified that it was a simple application so no additional files would be created.  If you are creating a new project from scratch you can quite easily use one of those templates.  If you have an existing application, using the simple template will give you a good starting point.

The deployment.xml file for my application looks something like this (thanks WordPress for not allowing me to post XML, even the code formatters barf on it)

package
 ---> name: HelloWorld
 ---> version
    ---> release: 1.0.0
 ---> appdir: public

Step 5: Deploy!

The next step is to deploy the application.  You can, again use the SDK to do this from the command line.

D:\workspace\HelloWorld>zend deploy application -b /
 
Application HelloWorld (id 9) is deployed to http://<default-server>/

The -b tells the SDK the base URL that we want to install the application at.  If we wanted it at /test, it would be -b /test.

If we want to remove the application we simply tell the SDK to do it.

D:\workspace\HelloWorld>zend remove application -a 9
 
Application HelloWorld (id 9) was removed from http://<default-server>/

The -a tells the SDK the application ID of the application that we want to remove.  We got the application ID from the return message when we originally deployed it.

Step 6 (optional): Rollback

Every once in a while you deploy an application that wasn’t quite as baked as you thought it was.  With the new deployment feature you can log in to your Zend Server GUI and do a one-click rollback of your application (well, two clicks once you click the “are you sure button”).

Conclusion

That is how easy it is to deploy a (very) simple application using the new deployment techniques.  This works both for Zend Server and Zend Server Cluster Manager installations, so you can deploy to a single machine or let ZSCM push your application to your entire cluster.

Later on I will go through using the Zend Server GUI to set up more complex applications requiring configuration parameters or deployment scripts and also go over the zdpack command which is the package builder that you use if you are not using command line based deployments.

Where to go from here?

It’s simple.  Go to our download site, or set up your test machine to access our yum or deb repositories.  In other words, try it out.

Deployment beta for Zend Server 5.5 – Getting Started

Deployment for PHP applications is one of those things where it’s tough to get a general reading on where things are.  On one hand you have people who have deployment mechanisms that seemingly automate the rotation of the earth and on the other hand you have people who jump up and down on their code trying to force it into a small hole (visions of Wile E. Coyote come to mind).  Others set up complex build procedures that has more processing on it than Cheez-Whiz (now I’m hungry for a grilled cheese sandwich).  My own thought on the matter is that for most PHP implementations a lot of the build tools out there are probably overkill for a lot of PHP applications.

Given all that, we have announced that we are making the beta for our new deployment feature in Zend Server 5.5 beta, available for download.  It’s not feature complete (there are some new features being worked on), but it provides the functionality needed for the 90% of us who do not have the need to have crazy complex deployment scenarios.

This begs the question, under what circumstances would the new deployment feature in Zend Server benefit you?

  • You simply copy files into production as part of your existing deployment mechanism using FTP (gasp), SFTP or rsync.
  • You do NOT want to maintain the infrastructure or tooling needed to deploy your application to production boxes (singly or in a cluster)

Zend Server deployment will be available for both Zend Server and Zend Server Cluster Manager meaning that you will be able to deploy applications to either a single server or to a cluster of servers with just a few clicks of the mouse.  We would like your help in looking at what works and what doesn’t.  Not just in the “what breaks” sense, but also in terms of the workflow and also the integration points, both in the Zend Server UI and also using the deployment API.

We would like to see innovative ways that you could use it, but it would probably help for you to see a quick example of how to do it to get you up and running quickly.  For that I have a short video that how you can utilize the deployment feature .


Download Video from YouTube | YouTube to MP3

With that, let’s take a look at some of the things that you need to work with the deployment beta.

The first thing you need to be aware of is a program called zdpack.  This program helps to create the package to deploy to your Zend Server instance or cluster.  You can easily script it into an existing packaging routine or your can run it manually.  How you get it integrated into your own environment is up to you, but here are the steps that I used for my own testing.  These are not “best practices” necessarily.  There are variations in how you can do this, but if you want to start with a clean project (probably the easiest way to get familiar with it) this is a good way of doing it.

Step 0: Download the Zend Server 5.5 beta

Windows or Linux

Step 1: Create a Zend Framework project

The reason I chose Zend Framework instead of a PHP project was because a Zend Framework project (in Zend Studio) has an internal document root folder called “public” where the public files are stored and the source code of the application, configuration files and such are stored outside of the document root.  Those are some of the reasons why I prefer using a ZF application for this.  The other reason is that when we build out the deployment skeleton, having the document root one directory lower allows you to write your deployment hooks easily in the same project as well as restricting access to the deployment config file.

Step 2: Create a deployment skeleton

Open up a CLI and go to the main workspace and type

1
zdpack create ProjectName

replacing “ProjectName” with the name of your project.  This will create a few files and directories in your project.  You can do this outside of a project, or anywhere on your computer for that matter.  But this just makes it easy to get started.  Your project should look something like the image on the right.  The red highlighted areas are files that the zdpack application created.

In the root directory of your application it will have created the directories “data” and “scripts”.  Inside of scripts you will see templates for individual files that allow you to run deployment commands at various points along the deployment process.  One that I created was a hook that would send an email each time the application was deployed.  I put the following code into the post_activate.php

1
2
3
4
5
6
7
8
9
10
11
12
require_once 'Zend/Mail.php';
$mail = new Zend_Mail();
$mail->addTo('[email protected]');
$mail->setSubject('New deployment');
$currentAppVersion = getenv('ZS_CURRENT_APP_VERSION');
$mail->setBodyText(
"
New application has been deployed.
Version: {$currentAppVersion}
"
);
$mail->send();

The most important line is the $currentAppVersion.  Any data or meta data that you want will want will be made available via environment variables.

Step 3: Modify the deployment.xml file

The deployment.xml file in the main directory contains the meta information that the deployer needs to get your application up and running.  Here is a screenshot of what that might look like.  Most the elements are self descriptive and so I won’t spend much time talking about them, but there are a few things I would like to highlight.

First of all, the highlighted elements on the bottom-right side of the page.  This is the XML editor in Zend Studio and as you can see you are limited to the names of the nodes that you are able to enter into the deployment file at various node positions due to the validation that occurs.  This actually makes it easy for you to create a deployment file in a simple XML editor.

The second (more important) thing I want to direct your attention to are the three nodes on the left.  The first is the “eula” node.  If you have an end user license agreement you put the location of it there.  The second one is “appdir”.  When you first create the deployment.xml file the directory “data” will be in there, which is where the deployer is expecting the base of the application to be.  But if you are following this example then the base of the project is technically the base of your application.  That is why I put the value “public” in the “docroot” node.  These are all relative paths to the base of the application directory.

Step 4: Create the deployment package

Going back to your CLI in the main workspace directory type in

zdpack pack <ProjectName>

This will create what is basically a zip file with an expected file structure. The file is called a zpk, but it’s really just a zip file.

Step 5: Deploy

Log in to the Zend Server GUI and click on the new “Applications” tab.  On the right hand side of the screen you will see a button that says “Deploy Application”.  Click on that button and follow the instructions.  When you get to the end of the wizard click “Done” and your application will start to deploy.

That’s it.

Where to go from here

Try it.  Go to the download page and install it.

Check to see if there are better ways of integrating it with your application than what I have noted here.

Go to the forum.  Report bugs.  Download sample deployment applications.

As I said earlier there are more features coming in the near future but there is definitely enough for you to get cracking on it.  I’m actually quite happy to see this new feature being added.  Yes there are definitely alternate ways of deploying your applications.  But in the experimentation I have done I have found that this is a nice, simple, easy to use method for deploying your PHP based applications.

PHP Deployment: RPM/yum, whatever your OS uses

This is an article that is based off of a talk I did covering various deployment mechanisms.  The slides can be found at Slideshare.

Our final examination is going to be my prefered method, which, ironically, I don’t actually use… yet.  That is, the operating system specific method.  Because I’m using CentoOS that means using RPM and yum for me.  I don’t always deploy my applications, but when I do, I prefer yum.  OK, that sounded funnier in my head.

The reason I like this method is because it blurs the line between the application and the operating system.  Consider for a moment a goal that needs more than one person to be achieved.  Can the goal be achieved without the two of them working closely together?  Yes, but it will be less likely than if they know what each other is doing and how it is going to affect progress to the goal.  The application and the operating system cannot be thought of as two separate entities.  Also, most developers know squat about how to administer on operating system.  There’s nothing wrong with that, but it’s mostly true.  Yes, they may know about things like symbolic links and that log files exist but my experience has shown that a lot of developers, especially if they are not directly building onto the features of an OS, are not in-depth experts about the OS.  Some developers are, but they are not the majority.

Do you need to update an operating system?  Yes.  Do you need to update an application?  Yes.  Do your administrators need to do both?  Yes.  (Developers shouldn’t have access to systems that could run away, frightened that a developer might look at them) So why not use what is already there that your admins already know.

rpm.spec

This file is the basis for the whole thing.  They can be relatively complex when you’re dealing with compiled files, but with PHP it can actually be relatively simple.  So let’s take a look at what one of these looks like.

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
%define version 0.0.1
%define release 1
%define name HelloWorld
 
Name: %{name}
Summary: Our HelloWorld application
Vendor: OurOrg
Release: %{release}
License: GPL
Group: Applications/ExampleOrg
Version: %{version}
Source: %{name}-%{version}-%{release}
BuildArch: noarch
Requires: zend-server >= 5.0, zend-server-framework > 1.10.0
 
%description
This is a basic application for demonstrating yum-based repositories
 
%build
 
%install
rm -rf /var/www/application
svn export svn://localhost/repos/HelloWorld/tags/%{version} /var/www/application
 
%clean
mkdir -p /var/www/repos/noarch
cp /usr/src/redhat/RPMS/noarch/%{name}-%{version}-%{release}.noarch.rpm /var/www/repos/noarch
createrepo /var/www/repos
 
%files
/var/www/application
 
%post
service httpd restart


So let’s go through these lines.

The first three are just some constant definitions.  All 3 of them are required for rpmbuild (the package builder).  They don’t have to be defined there, but the values do need to be in that header section.  Name and version are self-explanatory.  Revision is simply a number that you are supposed to increment when you create a new package for the same release number.  What you see in that header section is about the minimum that you require, with the exception of the Requires.  That’s not required.  That’s optional.  But here’s the good thing about using Requires.  It does not separate the application from the environment.  So when you install the application, you are also installing the environment.  Kinda a neat idea.

%description is required.  Nuff sed.

After that we have a series of macros that need are going to be run.  That’s what the %build, %install, etc. all are is macros.  And order is sometimes important, especially with the %files macro.  %build is empty because we’re not building anything.

%files lists the locations of all the files that you want to include in the package.  Directories will be included recursively, though you can specify individual files as well.  You can also exclude files by setting up an %exclude list.

%install is where we script how it is that we want the deployment box to create the installation.  What that means is that we set up the application’s structure as we want it to be on the production systems.  I am installing my application in /var/www/application.  So what I do is first remove that directory and then export the version that I had tagged during my testing process.

Then what I want to do is automatically take the resulting RPM file and place it in a yum repository so I can deploy it to my production machines.  Why do it during %clean?  Because %clean is run after the RPM is written.  So while I’m technically not doing an actual clean I am able to hook in there and copy the built RPM file from its build location to the yum repository.  After that I run createrepo to yumify it.

There are also several hooks that can be set up that can be run pre and post installation, or unintallation.  So what I do in mine is, for the macro %post, restart Apache to make sure that my opcode cache is cleared.

Building the RPM file

To create the RPM file I run the following command (I have version 0.0.3 tagged as my most current one).

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
[root@deploy ~]# rpmbuild -bb /build-rpm/rpm.spec
Executing(%build): /bin/sh -e /var/tmp/rpm-tmp.16948
+ umask 022
+ cd /usr/src/redhat/BUILD
+ exit 0
Executing(%install): /bin/sh -e /var/tmp/rpm-tmp.16948
+ umask 022
+ cd /usr/src/redhat/BUILD
+ rm -rf /var/www/application
+ svn export svn://localhost/repos/HelloWorld/tags/0.0.3 /var/www/application
A    /var/www/application
A    /var/www/application/tests
A    /var/www/application/tests/application
A    /var/www/application/tests/application/bootstrap.php
/snip/
A    /var/www/application/public/index.php
Exported revision 23.
+ /usr/lib/rpm/brp-compress
+ /usr/lib/rpm/brp-strip
+ /usr/lib/rpm/brp-strip-static-archive
+ /usr/lib/rpm/brp-strip-comment-note
Processing files: HelloWorld-0.0.3-1
Requires(interp): /bin/sh
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1
Requires(post): /bin/sh
Requires: zend-server >= 5.0 zend-server-framework > 1.10.0
Checking for unpackaged file(s): /usr/lib/rpm/check-files %{buildroot}
Wrote: /usr/src/redhat/RPMS/noarch/HelloWorld-0.0.3-1.noarch.rpm
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.16948
+ umask 022
+ cd /usr/src/redhat/BUILD
+ mkdir -p /var/www/repos/noarch
+ cp /usr/src/redhat/RPMS/noarch/HelloWorld-0.0.3-1.noarch.rpm /var/www/repos/noarch
+ createrepo /var/www/repos
3/3 - noarch/HelloWorld-0.0.2-1.noarch.rpm                                      
Saving Primary metadata
Saving file lists metadata
Saving other metadata
+ exit 0

If we look in our /var/ww/repos directory (which is where our yum repository lies) we see several files.

1
2
3
4
5
6
7
8
9
10
11
[root@deploy ~]# find /var/www/repos/
/var/www/repos/
/var/www/repos/noarch
/var/www/repos/noarch/HelloWorld-0.0.3-1.noarch.rpm
/var/www/repos/noarch/HelloWorld-0.0.1-1.noarch.rpm
/var/www/repos/noarch/HelloWorld-0.0.2-1.noarch.rpm
/var/www/repos/repodata
/var/www/repos/repodata/other.xml.gz
/var/www/repos/repodata/primary.xml.gz
/var/www/repos/repodata/filelists.xml.gz
/var/www/repos/repodata/repomd.xml

That is our repository.

Setting up Production

Setting up your production environment is really easy.  All you need to do is create a new file in /etc/yum.repos.d.  I named mine org.repo.

1
2
3
4
5
[OurOrg]
name=OurOrg
baseurl=http://deploy/repos
enabled=1
gpgcheck=0

Makes sense.

Now to install our application.

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
[root@xen1 ~]# yum install HelloWorld
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * addons: centos.omnispring.com
 * base: mirror.steadfast.net
 * extras: mirror.team-cymru.org
 * updates: centos.mirror.netriplex.com
Setting up Install Process
Resolving Dependencies
--> Running transaction check
---> Package HelloWorld.noarch 0:0.0.3-1 set to be updated
--> Finished Dependency Resolution
 
Dependencies Resolved
 
====================================================================================================================================================================================================================
 Package                                               Arch                                              Version                                            Repository                                         Size
====================================================================================================================================================================================================================
Installing:
 HelloWorld                                            noarch                                            0.0.3-1                                            OurOrg                                             10 k
 
Transaction Summary
====================================================================================================================================================================================================================
Install       1 Package(s)
Upgrade       0 Package(s)
 
Total download size: 10 k
Is this ok [y/N]: y
Downloading Packages:
HelloWorld-0.0.3-1.noarch.rpm                                                                                                                                                                |  10 kB     00:00     
Running rpm_check_debug
Running Transaction Test
Finished Transaction Test
Transaction Test Succeeded
Running Transaction
  Installing     : HelloWorld                                                                                                                                                                                   1/1
Stopping httpd: [FAILED]
Starting httpd: httpd: Could not reliably determine the server's fully qualified domain name, using 192.168.0.77 for ServerName
[  OK  ]
 
Installed:
  HelloWorld.noarch 0:0.0.3-1                                                                                                                                                                                       
 
Complete!


Easy.

Rollbacks

Rollbacks are a snap.

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
[root@xen1 ~]# yum downgrade HelloWorld
Loaded plugins: fastestmirror
Setting up Downgrade Process
Loading mirror speeds from cached hostfile
 * addons: centos.omnispring.com
 * base: mirror.steadfast.net
 * extras: mirror.team-cymru.org
 * updates: centos.mirror.netriplex.com
Resolving Dependencies
--> Running transaction check
---> Package HelloWorld.noarch 0:0.0.2-1 set to be updated
---> Package HelloWorld.noarch 0:0.0.3-1 set to be erased
--> Finished Dependency Resolution
 
Dependencies Resolved
 
====================================================================================================================================================================================================================
 Package                                               Arch                                              Version                                            Repository                                         Size
====================================================================================================================================================================================================================
Downgrading:
 HelloWorld                                            noarch                                            0.0.2-1                                            OurOrg                                             10 k
 
Transaction Summary
====================================================================================================================================================================================================================
Remove        0 Package(s)
Reinstall     0 Package(s)
Downgrade     1 Package(s)
 
Total download size: 10 k
Is this ok [y/N]: y
Downloading Packages:
HelloWorld-0.0.2-1.noarch.rpm                                                                                                                                                                |  10 kB     00:00     
Running rpm_check_debug
Running Transaction Test
Finished Transaction Test
Transaction Test Succeeded
Running Transaction
  Installing     : HelloWorld                                                                                                                                                                                   1/2
Stopping httpd: [  OK  ]
Starting httpd: httpd: Could not reliably determine the server's fully qualified domain name, using 192.168.0.77 for ServerName
[  OK  ]
  Cleanup        : HelloWorld                                                                                                                                                                                   2/2
 
Removed:
  HelloWorld.noarch 0:0.0.3-1                                                                                                                                                                                       
 
Installed:
  HelloWorld.noarch 0:0.0.2-1                                                                                                                                                                                       
 
Complete!


Conclusion

This is probably my personal favorite out of all the deployment mechanisms for reasons that I stated earlier.

Anyway, that’s it for deployment.  Thanks for reading all of these articles (or at least this one).  If you have any thoughts feel free to post them here, Twitter, Facebook or wherever.

PHP Deployment: Source Control

This is an article that is based off of a talk I did covering various deployment mechanisms.  The slides can be found at Slideshare

The next deployment option that we're going to look at is source control.  You're using source control, yes?  There are arguments as to which is the best.  Git seems to be winning that war in the open source world, but what it comes down to is that the source control you use is less important than whether or not you're using source control.

The example that we're going to use is Subversion.  Git may work really well and it might be the future, but Subversion is the now.  So we'll use that.

Like most deployment mechanisms there are a lot of ways that you could approach this.  I am going to go with the tagging approach.  Some people might be tempted to use trunk as their deployment source since trunk is supposed to contain pristine code.  However, as with all things, rollbacks are a difficult issue to resolve.  If trunk is your source you will need to know the revision of the code that you want to roll back to.  And it might not be immediately clear which revision you need to roll back to.  You will either need to have the previous version revision number written down somewhere or recorded as part of your deployment process.

Why is that?  It's because your revision numbers on trunk are based off of the entire repository, not just trunk.  If you need to roll back your application from revision 1853 to its previous release it that revision number will probably not be 1852.  That's not to say that you can't build ways around it.  What I'm saying is why bother with building ways around it when you can just tag it?  Then when you need to roll back your app you just take the previous tag number and switch your source tree to it.

So how do you do it?  First let's check out our source code locally

[root@dev ~]# cd /var/www/
[root@dev www]# svn co svn://testing/repos/HelloWorld/branches/1.0 application
A    application/tests
A    application/tests/application
A    application/tests/application/bootstrap.php
/snip/
A    application/public/index.php
 U   application
Checked out revision 13.

Then we'll change a line of code in the application, using vi.

[root@dev www]# vi application/application/views/scripts/index/index.phtml

And we commit our code

[root@dev www]# cd application/
[root@dev application]# svn ci -m 'Changed version number'
Sending        application/views/scripts/index/index.phtml
Transmitting file data .
Committed revision 14.

Now that we've committed our code we go to our testing server and test it.  Once we have tested it we need to merge that into trunk, which can sometimes be a bit of a bear.  I actually do it differently in Zend Studio.  In Zend Studio all I do is switch to trunk and replace trunk with the branch.  It's much easier.  Maybe lazy.  But easier.  However, if you're going to merge the branch into trunk you will need to do something like this.

[root@dev tmp]# cd /var/www/
[root@dev www]# mkdir /tmp/HelloWorld && cd /tmp/HelloWorld
[root@dev HelloWorld]# svn co svn://testing/repos/HelloWorld/trunk
A    trunk/tests
A    trunk/tests/application
A    trunk/tests/application/bootstrap.php
/snip/
A    trunk/public/index.php
 U   trunk
Checked out revision 21.
[root@dev HelloWorld]# cd trunk/
[root@dev trunk]# svn log svn://testing/repos/HelloWorld/branches/1.0/

Moving branch 1.0 to trunk
------------------------------------------------------------------------
r12 | (no author) | 2010-06-17 06:20:42 -0500 (Thu, 17 Jun 2010) | 1 line
/snip/
For 0.0.2
------------------------------------------------------------------------
r2 | (no author) | 2010-06-16 15:28:24 -0500 (Wed, 16 Jun 2010) | 1 line

Initial Commit
------------------------------------------------------------------------
r1 | (no author) | 2010-06-16 15:28:05 -0500 (Wed, 16 Jun 2010) | 1 line

Share project "HelloWorld" into "svn://testing/repos"
------------------------------------------------------------------------
[root@dev trunk]# svn merge -r 2:HEAD svn://testing/repos/HelloWorld/branches/1.0
D    application/controllers/ServiceController.php
 G   .
[root@dev trunk]# svn ci -m 'Commiting new 1.0 changes to trunk'
Deleting       application/controllers/ServiceController.php

Committed revision 22.

A few things to note.  What we're doing here checking out the trunk first.  Then we go into the trunk and do a log of the 1.0 branch.  The reason we do this is to find out the revision number of when we branched the source code.  In this case it was r2.  Then I do the svn merge against the branch, stating the revision of when the branch was made and doing it against HEAD.  To me this doesn't make sense, but it works, so I won't complain.

The next thing we have to do is tag the current version of trunk (which we just merged) as the new release.  To do that we simply copy trunk into the tags directory.

[root@dev trunk]# svn copy -m 'Tagging for 0.0.3' svn://testing/repos/HelloWorld/trunk svn://testing/repos/HelloWorld/tags/0.0.3

Committed revision 23.

At this point we are ready to deploy to production.  To do that we need to log into our production server and simply check out the tagged version into our application directory.

[root@prod www]# svn co svn://testing/repos/HelloWorld/tags/0.0.3 application
A    application/tests
A    application/tests/application
A    application/tests/application/bootstrap.php
/snip/
A    application/public/.htaccess
A    application/public/index.php
 U   application
Checked out revision 23.

Then if we need to roll back the installation, rather than re-checking it out we simply switch it.

[root@prod www]# cd application/
[root@prod application]# svn switch svn://testing/repos/HelloWorld/tags/0.0.2
A    application/controllers/ServiceController.php
Updated to revision 23.

And there you have it.  On top of this you can also set up pre and post install scripts.  But there are a couple of problems.  The first one is the .svn directories.  Well, actually the only one is the .svn directories, but there are two problems with it.  The first problem is that you are putting information on how to access your repository on a production box.  A web-facing box.  I personally don't like the sound of that.  There is a fix, I suppose.  Simply disallow access to URLs with .svn in there.  However, that's the second problem.  That's assuming that the ONLY way an attacker would gain access to it is by directly accessing it via the URL.  There are other ways that could be vulnerable and the last thing you want is to have two vulnerabilities to work through at the same time.  But overall it's not really a bad solution.

 


 

PHP Deployment: PEAR

This is an article that is based off of a talk I did covering various deployment mechanisms.  The slides can be found at Slideshare

Before I get into this one I would like to note that while I have presented several options (and will presenting one more after this) that none of these are given with the assumption that they are the only way or even the best way to do things.  Each of these options is provided as a starting point.  What that means is that you need to try it yourself and modify what I present here to fit what you need.

With that; PEAR.  PEAR stands for PHP Extension and Application Repository.  What it does is allow you to maintain very PHP-friendly packages in a manner that is native for PHP developers.  What that means is that you are able to package up your application in a way that is easy for you to understand.  But in order to deploy your application we need to discuss a few concepts.

Concepts

First PEAR2.  There is a PEAR 2.  It looks a little light right now in terms of packages, documentation and links that work on the site. So for that reason I will stick with PEAR 1 as an example.

Next, channels.  In order to make your application available to your production servers you need a channel.  A channel is just an HTTP server that has some data in pre-defined areas so the PEAR binary knows where to look for it.  There are several options that you can have to setting up a PEAR channel. Chiara and Pirum are the two that I have most heard about.  I chose Pirum because it is really easy to get up and running.

The first thing we will do is get our channel up and running.  To do that we need to follow the instructions on the Pirum page, which I won't bother going over (since they're already there).  But I will show how to set up the channel.

Creating the Channel

First create the directory on your deployment server where you want the repository to be located.  Then you need to place a file called pirum.xml in that directory, which has a simple XML format that is used to initialize the channel.

<server>
     <name>testing/pear</name>
     <summary>OurOrg PEAR channel</summary>
     <alias>helloworld</alias>
     <url>http://testing/pear/</url>
</server>

Then you need to run the "pirum build" command.

[root@xen2 www]# pirum build /var/www/pear
Pirum 0.9.9 by Fabien Potencier
Available commands:
  pirum build target_dir
  pirum add target_dir Pirum-1.0.0.tgz

Running the build command:
   INFO   Building channel
   INFO   Building maintainers
   INFO   Building categories
   INFO   Building packages
   INFO   Building releases
   INFO   Building index
   INFO   Building feed
   INFO   Updating PEAR server files
   INFO   Command build run successfully

If we look at the directory structure we see several new files added.

[root@xen2 www]# find pear
pear
pear/feed.xml
pear/channel.xml
pear/pirum.css
pear/get
pear/rest
pear/rest/r
pear/rest/c
pear/rest/c/Default
pear/rest/c/Default/packages.xml
pear/rest/c/Default/packagesinfo.xml
pear/rest/c/Default/info.xml
pear/rest/c/categories.xml
pear/rest/p
pear/rest/p/packages.xml
pear/rest/m
pear/rest/m/allmaintainers.xml
pear/pirum.xml
pear/index.html

These are the files needed to have a basic PEAR repository set up.

But a repository is useless without something to repos… er.. distribute.  For that what we will do is simply export our Subversion repository to a directory somewhere where we can start building our package.

[root@xen2 www]# svn export svn://testing/repos/HelloWorld/tags/0.0.3 application
A    application
A    application/tests
A    application/tests/application
A    application/tests/application/bootstrap.php
/snip/
A    application/public
A    application/public/.htaccess
A    application/public/index.php
Exported revision 23.

Building the Package

The next thing we need to do is build the package.  But how does one do that with PEAR?  With a package.xml file.  That's how.  The problem is that a package.xml file can actually be a little lengthy.  Another problem is that it needs to have very exact details about each and every file that you want to deply.  The way I worked around that is to create a base package.xml file that I use as a template and then use a simple script to find all the files and add them in there.  First the package.xml.

<?xml version="1.0" encoding="UTF-8"?>
<package packagerversion="1.9.0" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0     http://pear.php.net/dtd/tasks-1.0.xsd     http://pear.php.net/dtd/package-2.0     http://pear.php.net/dtd/package-2.0.xsd">
 <name>HelloWorld</name>
 <channel>xen2/pear</channel>
 <summary>HelloWorld</summary>
 <description>This is a description</description>
 <lead>
  <name>Kevin</name>
  <user>kevin</user>
  <email>[email protected]</email>
  <active>yes</active>
 </lead>
 <date>2010-06-15</date>
 <time>14:27:49</time>
 <version>
  <release>0.0.3</release>
  <api>0.0.3</api>
 </version>
 <stability>
  <release>stable</release>
  <api>stable</api>
 </stability>
 <license uri="http://www.apache.org/licenses/">Apache</license>
 <notes>
No notes
 </notes>
 <contents>
        <dir baseinstalldir="/" name="/">
                <file baseinstalldir="/application"  role="www" name="/ignore.html" />
        </dir>
 </contents>

 <dependencies>
  <required>
   <php>
    <min>5</min>
   </php>
   <pearinstaller>
    <min>1.4.0</min>
   </pearinstaller>
  </required>
 </dependencies>
 <phprelease />
 <changelog>
  <release>
   <version>
    <release>0.0.3</release>
    <api>0.0.3</api>
   </version>
   <stability>
    <release>stable</release>
    <api>stable</api>
   </stability>
   <date>2010-06-15</date>
   <license uri="http://www.apache.org/licenses/">Apache</license>
   <notes>
No notes
   </notes>
  </release>
 </changelog>
</package>

Like I said.  Long.  However, I've highlighted the interesting part.  That is where we are going to place our file listing.  How, you ask?  With a simple script.


$deployDirs = array(
        'application',
        'library',
        'public'
);

$simpleXml = simplexml_load_file('package.xml');
$dirEntry = $simpleXml->contents->dir;
unset($dirEntry->file); // Get rid of all the files
foreach ($deployDirs as $dir) {
        foreach (glob("$dir") as $dirNode) {
                addDirectory($dirEntry, $dirNode);
        }
}
file_put_contents('package.xml', $simpleXml->asXML());

function addDirectory(SimpleXMLElement $dirEntry, $dirNode)
{
        echo "Reading directory $dirNoden";
        foreach (glob("$dirNode/*") as $fileName) {

                if (is_dir($fileName)) {
                        addDirectory($dirEntry, $fileName);
                } else {

                        $file = $dirEntry->addChild('file');
                        $file['baseinstalldir'] = '/application';
                        $file['name'] = $fileName;
                        $file['role'] = 'www';
                }
        }
}

What this script does is state which directories to search, loads up the package.xml file into a simpleXML object and iterates over every file and adds it to the content node.  If we run this code we get the following output.

[root@xen2 application]# php build-pear.php
Reading directory application
Reading directory application/configs
Reading directory application/controllers
Reading directory application/models
Reading directory application/views
Reading directory application/views/helpers
Reading directory application/views/scripts
Reading directory application/views/scripts/error
Reading directory application/views/scripts/index
Reading directory library
Reading directory public

Deploying to the Repository

If we look at our package.xml file now we will now see a list of all of our files.  Now what we need to do is have pear package it.  How do we do that?  With "pear package".  However, before we actually build the package we need to tell pear that our repository exists.  "pear package" will refuse to create a package for a repository that it does not know about.  So we need to tell PEAR about our repository.  We do that by issuing the following command.

[root@xen2 application]# pear channel-discover xen2/pear
Adding Channel "ourour" succeeded
Discovery of channel "xen2/pear" succeeded

What?  What's this "xen2/pear" thing?  Think of it like http://xen2/pear.  Or, with a fully qualified domain name xen2.eschrade.com/pear.  It's basically the URL that is used to locate the repository.  Now that we have our channel discovered we can package our package.

[root@xen2 application]# pear package

Package HelloWorld-0.0.3.tgz done

Now we need to tell Pirum to install it in the repository

[root@xen2 application]# pirum add /var/www/pear/ HelloWorld-0.0.3.tgz
Pirum 0.9.9 by Fabien Potencier
Available commands:
  pirum build target_dir
  pirum add target_dir Pirum-1.0.0.tgz

Running the add command:
   INFO   Parsing package 0.0.3 for HelloWorld
   INFO   Building channel
   INFO   Building maintainers
   INFO   Building categories
   INFO   Building packages
   INFO   Building package HelloWorld
   INFO   Building releases
   INFO   Building releases for HelloWorld
   INFO   Building release 0.0.3 for HelloWorld
   INFO   Building index
   INFO   Building feed
   INFO   Updating PEAR server files
   INFO   Command add run successfully

Installation

We now have our PEAR channel set up to deploy our application.  Now all that's left is to set up production server.

The first thing to do is discover the channel, just like we did before.  However, we need to do something before we actually do the deployment.  If you look back at our build-pear.php code there was a line where I stated the role of the files.  While you can have multiple roles I defined all of mine as being www.  That's important because PEAR likes to deploy files to the PEAR root, which is probably not where you want them to go.  To define that, change the following setting for PEAR.

[root@xen1 www]# pear config-set www_dir /var/www
config-set succeeded

Now that we've done that we can deploy our application quite easily.

[root@xen1 www]# pear install helloworld/HelloWorld
downloading HelloWorld-0.0.3.tgz ...
Starting to download HelloWorld-0.0.3.tgz (2,971 bytes)
....done: 2,971 bytes
install ok: channel://xen2/pear/HelloWorld-0.0.3

With that, we're installed.

Rolling Back

With PEAR you don't have the option of rolling back your installation.  What you need to do is uninstall your current installation and then install an older one.

[root@xen1 www]# pear uninstall helloworld/HelloWorld
uninstall ok: channel://xen2/pear/HelloWorld-0.0.3
[root@xen1 www]# pear install helloworld/HelloWorld-0.0.2
downloading HelloWorld-0.0.2.tgz ...
Starting to download HelloWorld-0.0.2.tgz (2,971 bytes)
....done: 2,971 bytes
install ok: channel://xen2/pear/HelloWorld-0.0.2

Conclusion

PEAR is a pretty good option for you to deploy your PHP applications with.  It takes a fair amount of work up front (perhaps PEAR2 will make this easier) but all of that is scriptable.  And since it's written in PHP you, as a developer, you have a lot of control.  The problem is that you, as a developer, should not have access to the production environment.  That is almost always the domain of system administrators. Why?  You might be tempted to fix something.  But PEAR is really good when you need cross-platform deployment and a lot of developer control over the deployment.

PHP Deployment: rsync

This is an article that is based off of a talk I did covering various deployment mechanisms.  The slides can be found at Slideshare

The first one that we're going to look at is rsync.  What rsync does is maintain synchronization between an individual machine and another master machine.  Updates are made by checking the differences between the files on the local machine and the files on the remote server and copying the changes over.  It's relatively easy to use.

The first thing you need to do is set up your server.  So say you have your application deployed on your staging server, or some kind of production mirror that holds the standard by which the production servers are going to be.  You will need to add the following lines to your /etc/rsyncd.conf file, which may or may not exist.

[shares]
comment = Our Org
path = /var/www/application
list = yes
uid = nobody
gid = nobody

Set the path to the directory where your application resides.  Rsyncd runs using xinetd so you will need to edit your /etc/xinetd.d/rsync file by "disable = no" and restarting the xinetd service.

Then on the production machine, once you are ready to deploy your code run the following command

rsync -av --delete testing::shares /var/www/application

"testing" is the name of the server.  "shares" is the name of the, well, share. –av first sets it into verbose mode (so you can see what you're doing) and then uses the -a option, for archive.  Archiving will transfer symlinks, permissions, time stamps, user and group ownership and any device data, and will do so recursively.  It's really nice and easy.

There are a couple of drawbacks.  Rsync does not natively have the ability to run post deployment scripts.  You will need to run that your self.  Also, if you need to roll back any changes you will need to roll back the source server.  Rsync does not understand versioning.

But while those are limitations of rsync, there are ways around them.  Shawn Stratton emailed me after the webinar giving me a bit of (deserved) grief for not addressing the workarounds.  I agree with Shawn's remarks, but due to time and the fact that I had 3 other deployment mechanisms I needed to go through I had opted for a simpler explanation of rsync.  However, the email that Shawn wrote had a lot of really good details that I would like to share with you.  There is some conversational stuff that I have removed but here is Shawn's response to me.

Your rsync command used the r flag which does not preserve timestamps/permissions/etc but more importantly does not transfer symlinks which is a big part of a deployment process using rsync.  Let me show you a directory sample of how I mean that:

current -> releases/v1.0.1
next    -> releases/v1.0.2
prev    -> releases/v1.0.0a
maint  -> maintenance/
releases/
releases/v1.0.0
releases/v1.0.0a
releases/v1.0.1
releases/v1.0.2
maintenance/
maintenance/index.html

This setup allows me keep multiple versions of the application on the remote server (disk space is cheap,) which I can change by simply changing a symlink.  Primarily the process for deployment would be: 

  1. export/build a tag from your vcs/dvcs system
  2. rsync -az –delete from a deployment box (local machine, dev utility server, etc) to the production server/servers
  3. alter next symlink from current value (in my case if nothing is staged next points to maintenance)
  4. proceed to test at a staging level (Apache vhosts in my case are setup as staging = next, production = current, and previous is a vhost that only links to a entry in my host file)
  5. remove prev symlink, move current symlink to maint, move maint symlink to current (maintenance being optional along with the next 3 steps).
  6. rsync -az –delete from a deployment box (local machine, dev utility server, etc) to the production server/servers, which changes the symlink, at this point your website is in "Maintenance Mode."
  7. do a database backup
  8. clear caches
  9. make database changes.
  10. swap current and next, rsync up.

Granted, most of these steps for me are scripted out in bash script and in the past we had setup a build system to do all the work up to step 5. because we felt it was better if human hands handled the finalization of deployment.  But there are several problems doing a deployment using rsync like this fixes, first it allows for a fast transition using symlinks (deployment on a 3 node cluster of the symlinks takes < 1 second,) second it can ensure that multiple servers are in complete sync (nothing worse than one server being out of sync on one file and one file only or worse permissions be off on one server) and lastly I can roll back rapidly.  I generally use migrations but the database backup exists also because it creates a snapshot of the database just prior to release and I can tell you from experience that little code changes can adversely affect database entries in ways that are not easily repaired without a dump.

There you have it.  If you have any additional comments do leave them below and come back soon since I have 3 more deployment options I need to write up.

PHP Deployment: Application considerations and process

It's been a while since I've posted anything of any real significance.  Part of that is because I have been working pretty hard on a webinar for Zend regarding deployment.  Deployment is no small subject and testing your deployment options is no small undertaking.  Then add other responsibilities and you end hav…. blah blah blah.

So, I have a lot to write about and with some of the things coming up I don't know how much of what I want to write will be written.  But let's start with the basics.  If you want to see the slides from the webinar here they are for your enjoyment.

Process

The first thing to look at when considering deployment is not to look at deployment.  The first thing you want to do is look at process.  Adherence to process, even bad process, is better than no process at all.  This is because if you have a bad process you at least have some idea of where things are.  If you have no process you have no idea where things are because you have no idea of who did what.

In traditional development/deployment shops there are 4 stages of getting your software from the developer to the production environment.  It's probably one of the few traditions that has mostly stood the test of time in IT, though a lot of people are trying to automate it pretty significantly, speaking about Continuous Deployment.  I haven't personally made up my mind on it yet.  What Continous Deployment is is that if source code checked into your repository passes all of its tests then it is immediately deployed into production.  Sounds nice but one of the things that things like TDD or Selenium do is make repeatable tests.  Having humans in the process means that there will be entropy.  Entropy is good for testing.  But so is predictability.  That's why I'm on the fence about Continuous Deployment.

In terms of the 4 stages, here they are, more or less copied and pasted from my slide deck, because I'm lazy.

  • Development

    • Purpose

      • To provide developers an environment to write their code
    • Characteristics

      • Should be similar to production, though it usually isn’t
      • Often more open security
      • Usually local to the developer, though not required
  • Testing

    • Purpose

      • To provide a non-programming environment to test functionality
    • Characteristics

      • Continuous Integration could be included
      • Generally no development occurs, only testing
      • Developers should not do the testing, if possible
      • Restricted outbound networking

        • Use Zend Server Monitoring and Code Tracing to help reproduce errors

One of the things that developers are going to be tempted to do is to fix what is wrong in testing.  Instead, even if the developers themselves are testing the app, issues should be registered in an issue tracker so that it can be replicated, opened, closed and checked off the list.

  • Staging

    • Purpose

      • To test your deployment process/scripting (not your code)
    • Characteristics

      • Developers generally do not have access, unless they are also the sysadmin
      • Very restricted outbound networking
      • Mirrors production as best as possible

To re-iterate, staging is only about testing to make sure that your deployment process will work when you go to production. If there is a bug it will need to be determined if it is a show stopper or if you can live with it until the next iteration.

  • Pre-Production (Optional)

    • Purpose

      • Test the code in the production environment without impacting customers
    • Characteristics

      • Not likely to have use in the cloud or large scale deployments
      • Deployed in production immediately prior to making it live
      • Test the application with production settings without customer interaction

Some additional notes on pre-production.  I really want to highlight me saying that it's optional.  I personally like the ability to put the application somewhere and be able to interact with it prior to making my customers touch it.  That said, it's completely redundant with a properly set up staging environment.

  • Production

    • Purpose

      • Do whatever it is that it’s supposed to be doing
    • Characteristics

      • Developers do not have access (as they might be tempted to fix something)
      • Deployment should be done without requiring developer input
      • Very limited inbound traffic – generally only the service that is being served. i.e. HTTP


Application Considerations

Having looked, quite briefly, at the process we need to look at the application.  Again, briefly.  Since we've already talked about the process I would like to show you how you should make your application aware of the different environments.

One of the things that developers have sometimes done is hard coded their environment logic into their application.  So something like this:

$env = 'production';
if (substr($_SERVER['HTTP_HOST'], -6) == '.local') {
    $env = 'development';
} else if (substr($_SERVER['HTTP_HOST'], -8) == '.testing') {
    $env = 'testing';
}
define('APPLICATION_ENV', $env);

or even worse

if (strpos(__FILE__, '/home/kevin') === 0) {
    $env = 'development';
} else if (strpos(__FILE__, '/home/bob') === 0) {
    $env = 'development';
} else {
    $env = 'production';
}
define('APPLICATION_ENV', $env);

One of the problems with the second one is that you need to modify your code to have someone else work on it, or when someone else leaves the company.  The problem with the first one is that you can't always trust $_SERVER['HTTP_HOST'].  Plus what if you have to change your domain name for some reason.  Then you have to refactor.

What you can trust is your server configuration.  Usually.  So in my httpd.conf file I put the following line.

SetEnv APPLICATION_ENV testing

And then in my application

define('APPLICATION_ENV', (getenv('APPLICATION_ENV'));

The reason I like this is because rather than having the application guess which environment it's supposed to be in, the web server tells it which environment it's supposed to be in.

Well, that's it for now.  In the webinar there were 4 different types of deployment options I looked at, which we will cover in the upcoming days.  So I have plenty more to write about but I'm hungry.  Later all.

Setting up a maintenance page with Zend Framework

One of the things that smaller sites who don't have a fully redundant setup or a relatively minimal deployment mechanism need to do when doing some kind of maintenance is put up an "Under Maintenance" page.  That or there was some massive problem and you need to just shut down access to the site while you fix the problem.  With that in mind I have written a very simple example that allows you to create a maintenance page that is configurable and requires no changes to your existing site.  This example uses Zend_Application, but all of the code can be used in a pure Zend_Controller application by adding the plugin however you normally add plugins.

First let's set up a maintenance controller.

MaintenanceController.php

class MaintenanceController extends Zend_Controller_Action
{
    public function indexAction(){} 
}

and a view.

scripts/maintenance/index.phtml

<h1> We are currently under maintenance</h1>

<h2> Please check back soon</h2>

Now the fun part.  Create a simple plugin that is called during routeShutdown() and redirect all requests to the maintenance page.

class Esc_Application_Plugin_Maintenance extends Zend_Controller_Plugin_Abstract
{
    public function routeShutdown(Zend_Controller_Request_Abstract $request)
    {
        $request->setActionName('index');
        $request->setModuleName('default');
        $request->setControllerName('maintenance');
    } 
}

One of the nice things about this method is that if you have any parts of your application that you want to keep open you can implement the code to handle that in here.

The last thing you need to do is add this plugin to your application configuration.  If you are using Zend_Config_Ini you can simply do this:

resources.frontController.plugins[] = "Esc_Application_Plugin_Maintenance"

Whenever you need to put your site into maintenance mode all you need to do is add that line and it will be done as soon as you save the file.

Web Analytics