Zend Framework on a shared host
When you deploy a Zend Framework website to a shared host, you usually cannot change the DocumentRoot to point at the public/ folder of the website. As a result the URL to the website is now http://www.example.com/public/. This doesn’t look very professional, so we’d like to remove it.
The easiest way, given a ZF project created using Zend_Tool is this:
Create /index.php
< ?php
define('RUNNING_FROM_ROOT', true);
include 'public/index.php';
This uses the index.php already created by Zend_Tool and means that we don't have to change anything if we move to a VPS host where we can set the DocumentRoot directly to public/.
Create /.htaccess
SetEnv APPLICATION_ENV development
RewriteEngine On
RewriteRule .* index.php
We create a .htaccess file that redirects every request to index.php. We want to do this so that no one can try and read application/configs/application.ini. Obviously, set the APPLICATION_ENV to the correct value!
Referencing public facing files
Having created a very aggressive, rewrite rule, what about CSS/JS/image files though?
Fortunately, we already have a .htaccess file in the public/ folder that correctly handles this situation. As Apache will execute the .htaccess files in the deepest directory it finds, any reference to a public facing file within the public/ folder will correctly be served.
You do have to be aware of this when referencing public facing files though and add the /public to the baseUrl yourself.
For example, you may set up your CSS file and other view settings within a Front Controller plugin like this:
class App_Controller_Plugin_View extends Zend_Controller_Plugin_Abstract
{
public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
{
$frontController = Zend_Controller_Front::getInstance();
$view = $frontController->getParam('bootstrap')->getResource('view');
$view->doctype('XHTML1_STRICT');
$baseUrl = $request->getBaseUrl();
if (defined('RUNNING_FROM_ROOT')) {
$baseUrl .= '/public';
$frontController->setBaseUrl($baseUrl);
}
$view->headLink()->appendStylesheet($baseUrl . '/css/main.css');
$view->headLink()->appendStylesheet($baseUrl . '/css/screen.css', 'screen');
$view->headLink()->appendStylesheet($baseUrl . '/css/print.css', 'print');
}
}
(This code assumes you have added a resources.view[] = "" to your application.ini)
As we have a constant that tells us if we’re running from the root, we can dynamically add the /public into the URL. If we change to a host where the DocumentRoot is set directly to the public/ folder, then we don’t need to change our code.
That’s it. Your Zend Framework application works nicely with shared hosts.
Update:
Thomas Dutrion has translated this post into French at http://blog.generation-pc.net/2011/04/zend-framework-sur-un-hebergement-mutualise/. Thank you, Thomas!
Thanks, very helpful!
But still, why use XHTML??:
http://www.456bereastreet.com/archive/200501/the_perils_of_using_xhtml_properly/
http://www.webdevout.net/articles/beware-of-xhtml
http://www.sitepoint.com/forums/showthread.php?t=393445
Thank you for sharing Rob.
This was something that I was thinking of tackling in the the future but you did all the hard work for me :)
Thanks again!
Where is $request defined because in a line you have $baseUrl = $request->getBaseUrl();
but $request doesn't exist at that moment.
Thanks!!
Last year I wrote an article on how to do it with a slightly different .htaccess file, without touching the PHP scripts:
http://www.alberton.info/zend_framework_mod_rewrite_shared_hosting.html
HTH
Fernando,
$request is passed in as a parameter to dispatchLoopStartup()
Lorenzo,
That's an nice solution :)
Regards,
Rob…
Hi Rob,
Why not just rename public to public_html and it just works out of the box?
Steve,
Surely that depends on the shared host?
Rob…
Rob,
What I mean is, whatever the public folder of the hosts is, so for example in your actual zf app, name the "public" folder, public_html or httpdocs, etc, instead of going with the name public, which is the default.
Steve
Steve,
That only works on shared hosts where you are able to store files above publicly accessible folder.
Rob…
Rob,
Thanks for the clarification. Not all shared hosts impose this restriction. We use Heart Internet at work and we can store the application folders, etc, above public_html.
Good to know, thanks!
Steve
this is wonderful tutorial .. i read it 2 times and get a fantastic results and sure i put a
copy of this lesson on my site here
http://www.haaik.com/vb
Great tutorial. Not all hosts have this restriction though. Most hosts that I know use cPanel which allows you to store the application folders in the main directory above public_html.
Most hosts using cPanel don't change the name of the document root and leave the default on.
My shared host does not have this problem, but it does not over memcache or similar. This means that Zend_Cache does not work as well.
Can you write a post about your point of view of using Zend_Cache on a shared host without MemCache?
I think, the simplest solution is using the following .htaccess file in ROOT directory.
RewriteEngine On
RewriteRule ^(.*)$ public/$1 [L,QSA]
Michal,
That's certainly simple :)
Rob…
@Mary, if your hosting provider does not offer memcached or apc, you could use the file system for caching.
You can choose the 'File' backend when instantiating the cache object.
Nice post.
Although I would say that htaccess can be disabled as well – not to mention that they are a performance hit (over apache conf files).
Just a quick question – do you know how to make zend tool use something other than public? i.e. I want to use public_html, or "web" (i.e. something other, so that I can use existing web roots, etc.) – other than symlinking the directories….?
Thanks.
James,
You can call your public folder whatever you like; Zend Framework doesn't care.
Regards,
Rob…
ZF doesn't care, I know that – but using zend tool – create a project, it names it public – how do you change the defaults on that? It's probably obvious, but I can't seem to find it.
Cheers.
Awesome solution! I really like the other two posted by Lorenzo Alberton and Michal Lasak. I came up with my own solution to this one time using only mod_rewrite rules but it was nowhere as elegant as Michal's solution. Thanks for the article Rob and thanks everyone else for the comments.
I just wanted to add that on GoDaddy and maybe some other shared hosts you need to add to the top of the .htaccess file generated by Zend_Tool the following:
Options -MultiViews
I'm not sure why this is needed; if someone else knows please explain.
Eww GoDaddy, eww cooties
hi
did anybody tried this approach on godaddy?
i have a site ready to be uploaded to my domain on godaddy and i`m not sure how to do it.
i will appreciate any detailed help.
best regards
Hi Rob very useful solution for shared hosting. I modified the plugin a bit:
class App_Plugin_BaseUrl extends Zend_Controller_Plugin_Abstract
{
public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
{
$frontController = Zend_Controller_Front::getInstance();
$baseUrl = $request->getBaseUrl();
if (defined('RUNNING_FROM_ROOT')) {
$baseUrl .= '/public';
$frontController->setBaseUrl($baseUrl);
}
}
}
Please be advised that setting an environment variable in .htaccess like
SetEnv APPLICATION_ENV development
will not work if you are on a shared host which is running PHP as CGI ( probably FastCGI with suExec ).
Here is a good explanation ( although in german ) http://www.zfforum.de/showthread.php?t=4822 !
Anyhow – good plugin Rob !
Kind regards,
E:S
hi
i get the css files and images but all links are not working.
i have this link: <a href="/bloging/css/index"
it works great on my local site but on the live site it does not work. it just sends me back to index, even though the url say the right url.
what am i missing here? please help
just hosted my site built on ZF yesterday on godaddy … I did fear this issue as i had never done the ZF setup on a shared server earlier and i did not find this article
so i tried out something different that worked
My folder structure is now
public/
application/
cache/
library/
images/
css/
.
.
.
and edited this in my index.php
set_include_path(implode(PATH_SEPARATOR, array(
realpath(dirname(__FILE__) . '/library'),
get_include_path(),
)));
// Define path to application directory
defined('APPLICATION_PATH')
|| define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/application'));
This worked for me and my site is live and running. Let me know if there are any problems with this folder structure
Rob & Michal, this works perfectly for IIS (in development anyway). Thank you.
just a correction to my comment
application/
cache/
library/
images/
css/
are inside /public
I want to mention that you will have problems using Michal's solution. Simply try to use URL like http://your_site/public-1. If you'll have such route, rewrite rules work incorrect.
And what about Rob's solution: what if you need to give access to some files in the site's root, robots.txt for example? I propose to use these statements in .htaccess:
RewriteCond %{REQUEST_URI} /$
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule .* – [L]
Obviously, you need to protect some of the files in the root
Excuse me for a typo in last comment.
.htaccess:
RewriteCond %{REQUEST_URI} ^/[^/]+$
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule .* – [L]
First statement checks whether URI is like:
["/" (root)] + [anything except "/" (any filename)]
I also did a post on this in my blog.
It is a little bit easier in my opinion. It is only based on file renaming, so very few scripts need to be touched.
check it out at http://michielstaessen.be/garage/2010/05/06/installing-zend-framework-applications-on-a-shared-host/
Ive been wrestling with this for a few days now, perhaps you can help.
1. I am using shared hosting.
This is my directory structure.
public/
application/
library/
css/
images/
js/
index.php
I am trying to get the site to function with the following .htaccess
RewriteEngine on
RewriteRule !.(js|ico|gif|jpg|png|css|swf)$ index.php
Options -Indexes
Nothing fancy but I get the following error 404
The requested URL /about/ was not found on this server.
the main page shows up perfectly, but any other page doesn't work. However, if I create a folder called public/about/index.html the page is rendered.
How can I get the about page to work with the .htacess ans still keep my application in tact?
Rob,
Hi i'm new to ZF, now where should i write your plugin class? Is it under subfolder of Application or i will make new folder as plugin?
Then where do i call it? is it in the index.php file?
Greffin,
App_Controller_Plugin_View lives in lib/App/Controller/Plugin/View.php
you need this in application.ini:
autoloadernamespaces[] = "App_"
Regards,
Rob…
Hi Rob, can you give me some example code for index.php that calls the plugins view.
Thanks.
Do you know of anyway with htaccess to disable someone from using your domain to point to their own website on the same server? Ex: they use YOURDOMAIN.com to promote their PHISHING WEBSITE.COM by using this simple URL to send users : YOURDOMAIN.COM/~phishing/file.html
Any help would be greatly appreciated. Thanks
Hey Rob,
thanks for your solution.
I just noticed that it breaks the redirector.
So I refrained from giving the altered URL to the frontcontoller and applied a quick fix like the following:
$frontController = Zend_Controller_Front::getInstance();
$view = $frontController->getParam('bootstrap')->getResource('view');
$baseUrl = $request->getBaseUrl();
if (defined('RUNNING_FROM_ROOT')) {
$baseUrl .= '/public';
}
$view->baseUrlVar = $baseUrl;
… and than using $view->baseUrlVar in the view-scripts like that:
baseUrlVar ?>/path/to/file
But I am guessing that there's a way more elegant solution out there.
Cheers, Bert
Hmm, the purifier destroyed the last code segment, but I think, you get the drift.
Hi,
I am using the configuration and directory structure from your book ZFIA.
I get the following error when using the above controller:
Fatal error: Call to a member function getResource() on a non-object in /var/www/html/aotf/library/Places/Controller/Plugin/UrlSetup.php on line 7
It may be that I do not see an application.ini file.
Thus, I cannot do:
"(This code assumes you have added a resources.view[] = "" to your application.ini)"
Please advise. Thanks!
I've set up my site according to the more current version of zf, and everything works on a shared host. However, the plugin is not being loaded.
Pics, code and details here:
http://www.zfforums.com/zend-framework-general-discussions-1/general-q-zend-framework-2/plugin-not-loading-4638.html
Thank you so much for this post, it worked like a charm. My css and js file loads fine now. This is however one problem I can't seem to work around.
This is what I am trying to do in my global.css file to load a background image when I am working on my localhost:
background:url("/images/bg2.jpg")
The above line will NOT work on shared host environment. The images folder resides in the public folder, which also has the index.php that starts everything.
I have to change it to
background:url("/public/images/bg2.jpg") in order for the image to load.
Is there anyway to modify the .htaccess file so that
background:url("/images/bg2.jpg") will work just like on my localhost.
It would be nice if I don't have to keep 2 version of css files for localhost and remote server.
Thanks.
qin,
I'd try to use relative URLs for the images in the CSS file. The URL should be relative to the location of the CSS file.
Regards,
Rob…
Rob
Thanks for the quick reply.
I should have tried harder before posting next time :)
I lost track of while moving these files around.
Thanks,
Qin
Hi Rob,
Your solution did solved the referencing public facing files. But what about the hyperlink created by something like this: <a href="url(array('controller'=>'index','action'=>'test'));?>">Test Page ?
It seems the url did not include '/public/' in the above link.
Please advise.
Thanks
James