Redirect sub-sub-domains to dynamically configured virtual hosts on the localhost.
0A colleague (thanks Danny) pointed me to this solution to host multiple development environments under a single sub-domain name.
Step 1. Configure a type A domain record by using a wildcard and a fixed sub-domain name like:
*.lab A 127.0.0.1
( why didn’t I think of that x_X )
Step 2. Enable mod_vhost_alias in Apache if not already active.
Step 3. Configure a virtual host and use the wildcards from mod_vhost_alias to your liking, for example
<VirtualHost 127.0.0.1:80>
ServerName lab.yourdomain.com
VirtualDocumentRoot /var/my_flexible_friends/%1
</VirtualHost>
would redirect myproject.lab.yourdomain.com to 127.0.0.1 and serve pages from /var/my_flexible_friends/myproject but would also redirect jquery.lab.yourdomain.com to 127.0.0.1 and serve pages from /var/my_flexible_friends/jquery.
Just look at the mod_vhost_alias documentation for more possibilities.
Also a nice consistent setup to share in development teams.
If you happen to stumble upon apache error messages like ‘Request exceeded the limit of 10 internal redirects due to probable configuration error.’, just change your .htaccess file by added the rule RewriteBase /. This will most likely happen when you are using the windows hosts file instead of a real domain.
See also Stackoverflow Infinite Rewrite Loop
SetEnv APPLICATION_ENV development
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteBase /
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
I guess PhpStorm does not love OpenJDK
0OpenJDK Runtime Environment (IcedTea6 1.9.10) (6b20-1.9.10-0ubuntu1~10.04.2)
OpenJDK Client VM (build 19.0-b09, mixed mode, sharing)
WARNING: You are launching IDE using OpenJDK Java runtimeTHIS IS STRICTLY UNSUPPORTED DUE TO KNOWN PERFORMANCE AND GRAPHICS PROBLEMS
NOTE: If you have both Sun JDK and OpenJDK installed
please validate either WEBIDE_JDK or JDK_HOME environment variable points to valid Sun JDK installation
Ubuntu Sun (Oracle) Java Installation Guide
sudo add-apt-repository "deb http://archive.canonical.com/ lucid partner" sudo apt-get update aptitude install sun-java6-jdk sudo update-alternatives --config java
Disable Apache 404 ErrorDocument HTML
0While implementing a REST service, Apache started to append HTML 404 error HTML to my custom 404 application/json response.
It seems the Windows Apache version which comes with Zend Server requires a httpd.conf entry to disable the HTML to be appended to the response:
To disable it for a 404 error:
ErrorDocument 404 ” ”
So in general:
ErrorDocument
It’s weird though that the Linux version does not append this HTML error code by default.
PHPDoc an Array Param (in Zend Framework)
0I found this code snippet which shows how array based parameters should be documented with PHPDoc (in ZF projects). The topic came up because I’m always nagging about those magic functions and magic properties in ZF and how poorly they are documented. It seems we’ve been neglecting adding PHPDoc tags a bit because PHPDoc does have support to document magic methods and magic class properties:
/** * $args may contain the following keys: * - var1: * - var2: * * @param array @args * @return void */
Items prefixed with a ‘- ‘ will be treated as bullets by phpDocumentor.
source: zend framework community
I’m currently piloting PhpStorm because I had some issues with Zend Studio, it’s interesting to notice that a phpdoc line like:
/*
* @return Foo[]
*/
function getList() {
$f = array();
$f[] = new Foo();
...
return $f;
}
triggers auto-completion in PhpStorm when looping over the return value like:
$list = $this->getList();
foreach($list as $l) {
$l->(starts auto-completion ...)
}
Zend Tool Cheat Sheet (v1.11.10)
1Usage:
zf [--global-opts] action-name [--action-opts] provider-name [--provider-opts] [provider parameters ...]
Note: You may use “?” in any place of the above usage string to ask for more specific help information.
Example: “zf ? version” will list all available actions for the version provider.Providers and their actions:
| Version zf show version mode[=mini] name-included[=1] Note: There are specialties, use zf show version.? to get specific help on them.Config zf create config zf show config zf enable config Note: There are specialties, use zf enable config.? to get specific help on them. zf disable config Note: There are specialties, use zf disable config.? to get specific help on them. Phpinfo Manifest Profile Project Application Model |
View Controller Action Module Form Layout DbAdapter DbTable ProjectProvider |
Examples (Windows): zf create project ./ zf create module test zf create controller Test index-action-included=1 test
Remarks:
- The Zend Tool will maintain a xml based configuration file called ‘.zfproject.xml’, do not remove!
- Zend Tool is case sensitive, thus “zf create module blog” and “zf create module Blog” are different commands, running them both will create 2 modules (in Windows it will only result in 1 module directory though!)
- The create module command did not create a module Bootstrap file
- The bootstrap file in the tests directory has a lowercase ‘b’ which probably needs to be an uppercase like ‘Bootstrap.php’
- Note: PHPUnit is required in order to generate controller test stubs.
- For a module Bootstrap to work, the application.ini needs resources.modules = ""
A simple module Bootstrap looks like:
<?php
class Yourmodulename_Bootstrap extends Zend_Application_Module_Bootstrap {
protected function _initSomething () {
}
}
ZF ReCaptcha Inline Configuration Code Snippet
0Somehow it’s always a pain to find the right configuration parameters and proper format in ZF which makes me reverse engineer them.
$this->captcha = new Zend_Form_Element_Captcha('captcha',
array(
'captcha'=>array ('ReCaptcha'),
'captchaOptions'=>array (
'pubkey'=>'...',
'privkey'=>'...'
)
)
);
Show the LIVE Status from an OWN3D.TV Livestream with a jQuery Plugin
4Demo
jQuery plug-in source at BitBucket
Examples of how to use the plug-in.
// global configuration options
$.own3d.liveurl = 'http://ingol.nl/own3d/live.php';
$.own3d.channelurl = 'http://ingol.nl/own3d/channel.php';
// A single stream
$('#own3d-cowclan').own3d({liveid:'1213', title:'COWCLAN'});
// A single stream with HD
$('#own3d-rebootgpf').own3d({liveid:'1208', title:'REBOOTGPF', hd:true});
// A single stream without title
$('#own3d-1208').own3d({liveid:'1208', hd:true, embed:true, channelid: 'orlissenberg'});
// A single channel, live streams only
$('#channel').own3d({channelid:'clgame', hd:true, embed:true});
// A single channel, live and offline streams
$('#channel-all').own3d({channelid:'clgame', hd:true, showall:true});
The jQuery OWN3D Plug-in Code.
(function($){
$.own3d = {
liveurl : 'live.php',
channelurl : 'channel.php'
};
$.fn.own3d = function(options) {
return this.each(function(){
var self = $(this);
self.data('liveid', options.liveid);
if (options &amp;&amp; options.liveid) {
$.ajax({
type: &quot;GET&quot;,
async: true,
data: {live_id:options.liveid},
url: $.own3d.liveurl,
dataType: &quot;jsonp&quot;,
success: function(data){
var title = (options.title) ? options.title : 'OWN3D CHANNEL ['+options.liveid+']';
var liveclass = (options.hd) ? 'own3d-live-hd' : 'own3d-live';
self.empty();
self.addClass('own3d');
var link = $('&lt;a/&gt;')
.attr('href','http://www.own3d.tv/live/'+options.liveid)
.attr('target','_blank');
if (data.isLive &amp;&amp; data.isLive == 'true') {
link.html(title+' - LIVE (viewers: '+data.liveViewers+' - duration: '+Math.round(parseInt(data.liveDuration) / 60)+' minutes)');
if ((options.showthumbnail || options.embed) &amp;&amp; options.channelid) {
$.ajax({
type: &quot;GET&quot;,
async: true,
data: {channel_id : options.channelid, stream_guid : 'http://www.own3d.tv/live/'+options.liveid},
url: $.own3d.channelurl,
dataType: &quot;jsonp&quot;,
success: function(data){
if (options.showthumbnail &amp;&amp; data.thumbnail) {
var table = $('&lt;table/&gt;');
var status = $('&lt;td/&gt;').append($('&lt;div/&gt;').addClass(liveclass).append(link));
table.append($('&lt;tr/&gt;').append(status));
var thumbnail = $('&lt;td/&gt;').append($('&lt;img/&gt;').attr('src',data.thumbnail));
table.append($('&lt;tr/&gt;').append(thumbnail));
self.append(table);
} else if (options.embed &amp;&amp; data.title) {
var e = '&lt;iframe height=&quot;360&quot; width=&quot;640&quot; frameborder=&quot;0&quot; src=&quot;http://www.own3d.tv/liveembed/'+self.data('liveid')+'&quot;&gt;&lt;/iframe&gt;';
var table = $('&lt;table/&gt;');
var status = $('&lt;td/&gt;').append($('&lt;div/&gt;').addClass(liveclass).append(link));
table.append($('&lt;tr/&gt;').append(status));
var embeddedplayer = $('&lt;td/&gt;').html(e);
table.append($('&lt;tr/&gt;').append(embeddedplayer));
self.append(table);
};
},
error: function(XMLHttpRequest, textStatus, errorThrown){
// console.debug(errorThrown);
}
});
} else {
self.append(link);
self.addClass(liveclass);
}
} else {
link.html(title+' - OFFLINE');
self.removeClass(liveclass);
self.append(link);
}
},
error: function(XMLHttpRequest, textStatus, errorThrown){
// console.debug(errorThrown);
}
});
} else if (options &amp;&amp; options.channelid) {
var ajaxdata = {
channel_id : options.channelid
};
if (options.showall) { ajaxdata.showall = 1; };
$.ajax({
type: &quot;GET&quot;,
async: true,
data: ajaxdata,
url: $.own3d.channelurl,
dataType: &quot;jsonp&quot;,
success: function(data){
self.empty();
var embedded = true;
for(var i=0;i&lt;data.streams.length;i++) {
var stream = data.streams[i];
var plugindata = {
liveid : stream.liveid,
title : stream.title,
channelid : options.channelid,
// embed the first, thumbnail the rest
showthumbnail : (options.showthumbnail) || (!(options.showthumbnail) &amp;&amp; options.embed &amp;&amp; !embedded) ? true : false,
embed : (options.embed) ? true &amp;&amp; embedded : false
};
// prevent opening multiple embedded players
embedded = false;
self.append($('&lt;div/&gt;').own3d(plugindata));
}
},
error: function(XMLHttpRequest, textStatus, errorThrown){
// console.debug(errorThrown);
}
});
}
});
}
})(jQuery)
A simple PHP – Zend Framework script to enable a cross domain request to the OWN3D API (the full project contains two, one for the live status and one for the channel information).
&lt;?php
// Define path to application directory
define('APPLICATION_PATH', realpath(dirname(__FILE__)));
// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
realpath(APPLICATION_PATH . '/../../apps/library'),
get_include_path(),
)));
$liveid = isset($_GET['live_id']) ? intval($_GET['live_id']) : 0;
require_once 'Zend/Http/Client.php';
$client = new Zend_Http_Client('http://api.own3d.tv/liveCheck.php?live_id='.$liveid, array('adapter' =&gt; 'Zend_Http_Client_Adapter_Socket'));
$xmlstr = $client-&gt;request()-&gt;getBody();
$xml = new SimpleXMLElement($xmlstr);
require_once 'Zend/Json/Encoder.php';
header('Content-Type: application/json');
// JSONP
if (isset($_GET['callback'])) {
echo $_GET['callback'].'('. Zend_Json_Encoder::encode(array(
'isLive'=&gt;(string)$xml-&gt;liveEvent-&gt;isLive,
'liveViewers'=&gt;(string)$xml-&gt;liveEvent-&gt;liveViewers,
'liveDuration'=&gt;(string)$xml-&gt;liveEvent-&gt;liveDuration,
)).')';
}
// JSON
else {
echo Zend_Json_Encoder::encode(array(
'isLive'=&gt;(string)$xml-&gt;liveEvent-&gt;isLive,
'liveViewers'=&gt;(string)$xml-&gt;liveEvent-&gt;liveViewers,
'liveDuration'=&gt;(string)$xml-&gt;liveEvent-&gt;liveDuration,
));
}
