Wednesday, February 7, 2007

Setting up a PHP / MySQL development server


This is a Quick Walkthrough, or whatever of my development server install. Someone might find it useful. Sometime. I hope. This is all The Way I Like Ittm


The initial requirements for my development server this time was; MySQL, Web server, CVS, PHP 5.2 and memcached.


Install Fedora Core 6, packages and partitions as you like. I use /data and /logs partitions, as I have loads of disk and small projects. For software packages, I leave out just about everything except for firewall and emacs (I do love emacs!). Every developer gets his own user.


Log in as root.



Install MySQL:

root@dev# yum install -y mysql-server mysql-devel
(edit config in /etc/my.cnf to your needs)
root@dev# service mysql start


Dump sendmail (just don't like it) for postfix:
root@dev# yum remove sendmail
root@dev# yum install -y postfix

Install packages required for webserver (I use lighttpd, it rocks)
root@dev# yum install -y lighttpd lighttpd-fastcgi

Libraries for memcached:
root@dev# yum install -y libevent libevent-devel

Download and untar memcached and PHP memcache extension:
root@dev# wget http://www.danga.com/memcached/dist/memcached-1.2.1.tar.gz
root@dev# wget http://pecl.php.net/get/memcache-2.1.0.tgz
root@dev# tar xfz memcached-1.2.1.tar.gz
root@dev# tar xfz memcache-2.1.0.tar.gz

Build and install memcached:
root@dev# cd memcached-1.2.1/; ./configure; make install

Install compilers and libraries for PHP (I need freetype, xml & curl, you might not):
root@dev# yum install -y gcc gcc-c++ flex libjpeg libjpeg-devel \
libpng libpng-devel mysql-devel libxml2-devel \
curl-devel freetype-devel

Configure and build PHP (your configure options may vary):
root@dev# cd php-5.2.0
root@dev# ./configure --enable-fastcgi --enable-discard-path \
--enable-force-redirect --with-mysql --with-gd \
--with-curl --enable-gd-native-ttf \
--without-sqlite --with-memcache=../memcache-2.0.1 \
--enable-sockets --with-libjpeg-dir=/usr/lib \
--with-png-dir=/usr/lib --with-zlib-dir=/usr/lib
root@dev# make install

Build and install memcache PHP extension:
root@dev# yum install -y autoconf
root@dev# cd memcache-2.1.0/
root@dev# phpize
root@dev# ./configure
root@dev# make install

Add to / edit /usr/local/lib/php.ini:
extension_dir=/usr/local/lib/php/extensions/no-debug-non-zts-20060613/
extension="memcache.so"

Edit /usr/local/lighttpd/conf/lighttpd.conf to add PHP as FastCGI and user dir support.
Also make sure mod_userdir and mod_fastcgi is enabled in server.modules:

userdir.path = "public_html"

fastcgi.server = ( ".php" => ((
"bin-path" => "/usr/local/bin/php",
"socket" => "/tmp/php.socket",
"max-procs" => 2,
"bin-environment" => (
"PHP_FCGI_CHILDREN" => "8",
"PHP_FCGI_MAX_REQUESTS" => "10000"
),
"bin-copy-environment" => (
"PATH", "SHELL", "USER"
),
"broken-scriptfilename" => "enable"
)))

Open firewall hole for HTTP. Edit /etc/sysconfig/iptables, and add (inbetween the other RH-Firewall-1-INPUT rules):
-A RH-Firewall-1-INPUT -p tcp --dport 80 -j ACCEPT
root@dev# service iptables restart


Then, Fire Up The Webserver!
root@dev# service lighttpd start


Problems? Back-track, read logs and use strace if you need to.

Installing CVS is a cake:
root@dev# yum install -y cvs

For setting up repositories and such, I recommend the CVS Book, just Google it.

XP performance, (maybe) it can be done!

XP tweking night it seems, after building my brothers "delivered-in-pieces-i-hope-everything-is-there" computer successfully (he's online now!), I stumbled across this post, giving you the, imho, best XP performance "tricks" or "hacks", whatever you call them compilations you need to get started in tweaking your XP. I find them most useful, and my XP performs well now even on my, now "old", laptop.


One of my other favourite tweaks are the FireFox network tweaks. Check out about:config in your browser (you're not in IE, are you?) and take a look at these variables:

network.http.max-connections
network.http.max-connections-per-server
network.http.pipelining
network.http.pipelining.maxrequests
There are more, but these are the most interesting ones.

My current values are:

network.http.max-connections = 32
network.http.max-connections-per-server = 12
network.http.pipelining = true
network.http.pipelining.maxrequests = 21
Also, there's similar settings for you that use proxies, I usually dont.

Enjoy your tweaking evening, and stack up with something to snack during the reboots ;=)

Sunday, February 4, 2007

Recursive directory traversal

Today I found myself in need of traversing a directory structure with millions of files and match them against an existing database, in order to free up some storage.

At least one good thing came right out of it;
A nice clean recursive directory traversal function for whenever you need to process a directory tree. It uses hooks so you can implement whatever action you need for each file and directory.

Simple to use:
process_dir("/path/to/dir", "filehook", "dirhook", 2);
Where "filehook" and "dirhook", if set, are arguments to call_user_func (so you can call class methods) and "2" is the max level of directories to descend into.

File- and directory hook function examples:

<?
function dirhook($path$dir)
{
  print 
"dirhook: " $path DIRECTORY_SEPARATOR $dir "\n";
  return 
true;
}

function 
filehook($path$file)
{
  print 
"filehook. " $path DIRECTORY_SEPARATOR $file "\n";
  return 
true;
}
?>



If either dirhook or filehook returns false, processing of the current directory is aborted.

SO, here's the code then. Send me a note if you use it or have suggestions for improvements, ok?



<?
/**
 * Recursive directory traversal function.
 *
 * Author: orIgo (mrorigo@gmail.com)
 * Use, modify and share, but leave my name in here, ok?
 *
 * @param $path      Path of start directory
 * @param $filehook  File callback function
 * @param $dirhook   Directory callback function
 * @param $maxdepth  Max levels of directories to descend into
 */
function process_dir($path,
             
$filehook=null,
             
$dirhook=null,
             
$maxdepth=null,
             
$depth=0)
{
  if(
$maxdepth && 
     
$depth $maxdepth)
    return;

  
$dir opendir($path);
  if(!
$dir)
    return;  
// PHP Generates a warning if opendir fails, no need to print more

  
while (false !== ($file readdir($dir))) 
  {
    if(
$file !== "." && $file !== ".."
    {
      
$fullpath $path DIRECTORY_SEPARATOR $file;
      if(
is_dir($fullpath)) 
      {
    if(
$dirhook)
      if(!
call_user_func($dirhook$path$file))
        break;
    
process_dir($fullpath$filehook$dirhook$maxdepth$depth+1);
      }
      else {
    if(
$filehook)
      if(!
call_user_func($filehook$path$file))
        break;
      }
    }
  }
  
closedir($dir);
}

?>




Immediate update: For better performance under some circumstances, change

if(is_dir($fullpath)) {
to:
if($maxdepth > $depth+1 && is_dir($fullpath)) {

This avoids unnecessary stat() calls when you're not interested in the subdirectories.