Erik's blog

Code, notes, recipes, general musings

Archive for November 2008

installing a custom build of PHP on Dreamhost

with 4 comments

motivation

To run an OpenID consumer library, e.g. JanRain’s PHP lib and Pádraic Brady’s pear lib, on Dreamhost

intended audience

Anyone familiar with:

  • Command-line build
  • shell scripting
  • running PHP as CGI process

references

notes

  • I broke the basic script into 3 pieces: getting, unpacking, and installing.  This way we can re-install a package without re-getting the source code it or re-building it.
  • The syntax highlighting on WordPress does not support shell scripting, so it’s a bit off, but it helps
  • The scripts presented here were used to successfully get, build, and install PHP5, but I have not yet stopped to figure out how to run it, so there may be a huge, as-yet-unknown bug looming in the near future, but for now I’m riding high from the successful build and RUN – it works too! 🙂
  • This build configuration results in the following (non-fatal) error message:
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    + Building in NON-COMPLIANCE with RFC 3501 security requirements:
    + Non-compliant:
    ++ TLS/SSL encryption is NOT supported
    ++ Unencrypted plaintext passwords are permitted
    +
    + In order to rectify this problem, you MUST build with:
    ++ SSLTYPE=nopwd
    + You must also have OpenSSL or equivalent installed.
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    I haven’t sorted this out yet, so please post a comment if your familiar with what this is about.

script

#!/bin/sh

#part 1: get the source code

set -e

# build dir
SRCDIR=${HOME}/src

PHP5="php-5.2.5"
LIBICONV="libiconv-1.11"
LIBMCRYPT="libmcrypt-2.5.7"
LIBXML2="libxml2-2.6.32"
LIBXSLT="libxslt-1.1.24"
MHASH="mhash-0.9.7.1"
ZLIB="zlib-1.2.3"
CURL="curl-7.14.0"
LIBIDN="libidn-0.6.8"
CCLIENT="imap-2004g"
CCLIENT_DIR="imap-2004g" # Another pest!
FREETYPE="freetype-2.2.1"
OPENSSL="openssl-0.9.8i"
GMP="gmp-4.2.4"

# Pre-download clean up!!!!
rm -rf $SRCDIR

mkdir -p ${SRCDIR}

cd ${SRCDIR}

# Get all the required packages
echo "Wgetting ..."

wget -c http://us.php.net/get/${PHP5}.tar.gz/from/this/mirror
wget -c "http://mirrors.usc.edu/pub/gnu/libiconv/${LIBICONV}.tar.gz
wget -c http://easynews.dl.sourceforge.net/sourceforge/mcrypt/${LIBMCRYPT}.tar.gz
wget -c ftp://xmlsoft.org/libxml2/${LIBXML2}.tar.gz
wget -c ftp://xmlsoft.org/libxml2/${LIBXSLT}.tar.gz
wget -c http://superb-west.dl.sourceforge.net/sourceforge/mhash/${MHASH}.tar.gz
wget -c http://www.zlib.net/${ZLIB}.tar.gz
wget -c http://curl.haxx.se/download/${CURL}.tar.gz
wget -c http://kent.dl.sourceforge.net/sourceforge/freetype/${FREETYPE}.tar.gz
wget -c ftp://alpha.gnu.org/pub/gnu/libidn/${LIBIDN}.tar.gz
wget -c ftp://ftp.cac.washington.edu/imap/old/${CCLIENT}.tar.Z
wget -c http://www.openssl.org/source/${OPENSSL}.tar.gz
wget -c http://www.openssl.org/source/${OPENSSL}.tar.gz
wget -c ftp://ftp.gnu.org/gnu/gmp/${GMP}.tar.gz

echo "Done!"
#!/bin/sh

#part 2: unpack

set -e

# build dir
SRCDIR=${HOME}/src
LIBDIR=${HOME}/lib

# Update version information here.
PHP5="php-5.2.5"
LIBICONV="libiconv-1.11"
LIBMCRYPT="libmcrypt-2.5.7"
LIBXML2="libxml2-2.6.32"
LIBXSLT="libxslt-1.1.24"
MHASH="mhash-0.9.7.1"
ZLIB="zlib-1.2.3"
CURL="curl-7.14.0"
LIBIDN="libidn-0.6.8"
CCLIENT="imap-2004g"
CCLIENT_DIR="imap-2004g" # Another pest!
FREETYPE="freetype-2.2.1"
OPENSSL="openssl-0.9.8i"
GMP="gmp-4.2.4"

# Pre-unpack clean up
function cleanup(){
	echo "removing ${LIBDIR}/${1}"
	rm -rf $LIBDIR/$1
	mkdir -p $LIBDIR/$PHP5
	echo Done.
}

#note: rename imap-2004g.tar.Z to imap-2004g.tar.gz in advance
function unpack(){
	echo Extracting $1
	cp $SRCDIR/$1.tar.gz $1.tar.gz
	tar xzf $1.tar.gz
	rm $1.tar.gz
	echo Done.
}

echo "Cleaning up ... "

cleanup $PHP5
cleanup $LIBICONV
cleanup $LIBMCRYPT
cleanup $LIBXML2
cleanup $LIBXSLT
cleanup $MHASH
cleanup $ZLIB
cleanup $CURL
cleanup $LIBIDN
cleanup $CCLIENT
cleanup $CCLIENT_DIR
cleanup $FREETYPE
cleanup $OPENSSL
cleanup $GMP

echo "Unpacking ... "

cd $LIBDIR

unpack ${PHP5}
unpack $LIBICONV
unpack $LIBMCRYPT
unpack $LIBXML2
unpack $LIBXSLT
unpack $MHASH
unpack $ZLIB
unpack $CURL
unpack $LIBIDN
unpack $CCLIENT
unpack $CCLIENT_DIR
unpack $FREETYPE
unpack $OPENSSL
unpack $GMP

echo "Done!"
#!/bin/sh

#part 3: install

set -e

# build dir
SRCDIR=${HOME}/src
INSTALLDIR=${HOME}/bin
DOMAIN="example.com"
LIBDIR=${HOME}/lib

# Update version information here.
PHP5="php-5.2.5"
LIBICONV="libiconv-1.11"
LIBMCRYPT="libmcrypt-2.5.7"
LIBXML2="libxml2-2.6.32"
LIBXSLT="libxslt-1.1.24"
MHASH="mhash-0.9.7.1"
ZLIB="zlib-1.2.3"
CURL="curl-7.14.0"
LIBIDN="libidn-0.6.8"
CCLIENT="imap-2004g"
CCLIENT_DIR="imap-2004g" # Another pest!
FREETYPE="freetype-2.2.1"
OPENSSL="openssl-0.9.8i"
GMP="gmp-4.2.4"

# What PHP features do you want enabled?
PHPFEATURES="--prefix=${INSTALLDIR}/php5 \
 --with-config-file-path=${INSTALLDIR}/conf/php5/${DOMAIN} \
 --enable-fastcgi \
 --enable-force-cgi-redirect \
 --with-xml \
 --with-libxml-dir=${INSTALLDIR} \
 --with-freetype-dir=${INSTALLDIR} \
 --enable-soap \
 --with-openssl=${INSTALLDIR} \
 --with-mhash=${INSTALLDIR} \
 --with-mcrypt=${INSTALLDIR} \
 --with-zlib-dir=${INSTALLDIR} \
 --with-jpeg-dir=/usr \
 --with-png-dir=/usr \
 --with-gd \
 --enable-gd-native-ttf \
 --enable-memory-limit \
 --enable-ftp \
 --enable-exif \
 --enable-sockets \
 --enable-wddx \
 --with-iconv=${INSTALLDIR} \
 --enable-sqlite-utf8 \
 --enable-calendar \
 --with-curl=${INSTALLDIR} \
 --enable-mbstring \
 --enable-mbregex \
 --enable-bcmath \
 --with-mysql=/usr \
 --with-mysqli \
 --with-pear \
 --with-gettext \
 --with-imap=${INSTALLDIR} \
 --without-imap-ssl \
 --with-gmp={INSTALLDIR}"
#the --with-gmp flag is important to avoid this error:
#"GNU MP Library version 4.1.2 or greater required."
#See http://leghumped.com/php_inst.sh
#& DH forum http://discussion.dreamhost.com/showflat.pl?Cat=&Board=forum_programming&Number=78146

echo "Installing ..."

#libiconv
cd ${LIBDIR}/${LIBICONV}
./configure --enable-extra-encodings --prefix=${INSTALLDIR}
# make clean
make
make install

#libxml2
cd ${LIBDIR}/${LIBXML2}
./configure --with-iconv=${INSTALLDIR} --prefix=${INSTALLDIR}
# make clean
make
make install

#libxslt
cd ${LIBDIR}/${LIBXSLT}
./configure --prefix=${INSTALLDIR} \
 --with-libxml-prefix=${INSTALLDIR} \
 --with-libxml-include-prefix=${INSTALLDIR}/include/ \
 --with-libxml-libs-prefix=${INSTALLDIR}/lib/
# make clean
make
make install

#zlib
cd ${LIBDIR}/${ZLIB}
./configure --shared --prefix=${INSTALLDIR}
# make clean
make
make install

#libmcrypt
cd ${LIBDIR}/${LIBMCRYPT}
./configure --disable-posix-threads --prefix=${INSTALLDIR}
# make clean
make
make install

#libmcrypt lltdl issue!!
cd  ${LIBDIR}/${LIBMCRYPT}/libltdl
./configure --prefix=${INSTALLDIR} --enable-ltdl-install
# make clean
make
make install

#mhash
cd ${LIBDIR}/${MHASH}
./configure --prefix=${INSTALLDIR}
# make clean
make
make install

#freetype
cd ${LIBDIR}/${FREETYPE}
./configure --prefix=${INSTALLDIR}
# make clean
make
make install

#libidn
cd ${LIBDIR}/${LIBIDN}
./configure --with-iconv-prefix=${INSTALLDIR} --prefix=${INSTALLDIR}
# make clean
make
make install

#cURL
cd ${LIBDIR}/${CURL}
./configure --with-ssl=${INSTALLDIR} --with-zlib=${INSTALLDIR} \
  --with-libidn=${INSTALLDIR} --enable-ipv6 --enable-cookies \
  --enable-crypto-auth --prefix=${INSTALLDIR}
# make clean
make
make install

# c-client
cd ${LIBDIR}/${CCLIENT_DIR}
make -i ldb SSLTYPE=none
# Install targets are for wusses!
cp c-client/c-client.a ${INSTALLDIR}/lib/libc-client.a
cp c-client/*.h ${INSTALLDIR}/include

#OpenSSL
cd ${LIBDIR}/${OPENSSL}
./config --prefix=${INSTALLDIR} --openssldir=${INSTALLDIR}
make
make install

# gmp
cd ${LIBDIR}/${GMP}
#--prefix=${INSTALLDIR} --libdir=${INSTALLDIR}/lib req'd to avoid error:
#"configure: error: Unable to locate gmp.h"
#credit: http://leghumped.com/php_inst.sh
./configure --prefix=${INSTALLDIR} --libdir=${INSTALLDIR}/lib
# make clean
make
make install

#PHP 5
cd ${LIBDIR}/${PHP5}
LDFLAGS="-L${INSTALLDIR}"
./configure ${PHPFEATURES}
# make clean
make
make install

#copy config file
mkdir -p ${HOME}/conf/php5/${DOMAIN}
cp ${LIBDIR}/${PHP5}/php.ini-dist ${HOME}/conf/php5/${DOMAIN}/php.ini

#copy PHP CGI
mkdir -p ${HOME}/${DOMAIN}/cgi-bin
chmod 0755 ${HOME}/${DOMAIN}/cgi-bin
cp ${INSTALLDIR}/php5 ${HOME}/${DOMAIN}/cgi-bin/php5.cgi

echo "Done!"

After obtaining and unpacking the requisite files and compiling PHP, tell Apache where to find PHP by putting the following in an .htaccess file located in your domain’s root directory (credit: LegHumped):

Options +ExecCGI
AddHandler php-cgi .php
Action php-cgi /cgi-bin/php.cgi

Good luck!!!

Written by Erik

November 27, 2008 at 8:50 am

Posted in code

Yahoo! Pipes + PHP = blogroll

leave a comment »

motivation:
I have a bunch of services that output rss feeds. I want to consolidate all the feeds into a single feed, but let an external service handle the overhead of the consolidation, i.e. fetching, caching, etc. Then, I want to display the data in a readable way.

intended audience:

  • some experience w/ PHP
  • very little experience w/ Yahoo! Pipes

solution:

  • see my blogroll pipe in Y! Pipes
  • PHP source code below

notes:

  • While you’re at Pipes, search for “blogroll” to see how others solved the problem

//fn to generate readable estimation of pub date
// @credit: http://www.php.net/manual/en/function.time.php#85481
function elapsed_time($date, $show_less_than_a_minute = false) {
    $seconds = round(abs(time() - strtotime($date)));
    $minutes = round($seconds / 60);

        if ( $minutes <= 1 ) {
            if ( !$showLessThanAMinute ) {
                return ($minutes == 0) ? '< 1 min. ago' : '1 min. ago';
            } else {
                if ( $seconds < 5 ) {
                    return 'less than 5 sec. ago';
                }
                if ( $seconds < 10 ) {
                    return '< 10 sec. ago';
                }
                if ( $seconds < 20 ) {
                    return '< 20 sec. ago';
                }
                if ( $seconds < 40 ) {
                    return '~ 30 sec. ago';
                }
                if ( $seconds < 60 ) {
                    return 'less than a min. ago';
                }

                return '1 min. ago';
            }
        }
        if ( $minutes < 45 ) {
            return $minutes . ' min. ago';
        }
        if ( $minutes < 90 ) {
            return '1 hr. ago';
        }
        if ( $minutes < 1440 ) {
            return round(floatval($minutes) / 60.0) . ' hr. ago';
        }
        if ( $minutes < 2880 ) {
            return '1 day ago';
        }
        if ( $minutes < 43200 ) {
            return round(floatval($minutes) / 1440) . ' days ago';
        }
        if ( $minutes < 86400 ) {
            return '1 mo. ago';
        }
        if ( $minutes < 525600 ) {
            return round(floatval($minutes) / 43200) . ' mo. ago';
        }
        if ( $minutes < 1051199 ) {
            return '1 yr. ago';
        }

        return '> ' . round(floatval($minutes) / 525600) . ' yr. ago';
}

//def fn for sorting items in rss
function reverse_cmp_pub_dates($obj1, $obj2)
{
    $time1 = strtotime($obj1->pubDate);
    $time2 = strtotime($obj2->pubDate);

    if($time1 == $time2)
    {
        return 0;
    }
    elseif($time1 > $time2)
    {
        return -1;
    }
    else
    {
        return 1;
    }
}

//load y! pipes pre-consolidated data
$feed = 'http://pipes.yahoo.com/pipes/pipe.run?_id=gIBkHK_V3RGpmv5oBRNMsA&_render=rss';
$sxml = simplexml_load_file($feed);

//extract items array
$items = $sxml->channel->item;

//sort by pub date
usort($items, 'reverse_cmp_pub_dates');

//pattern for determining origin (see below)
$pattern = '@.*(?<host>flickr|upcoming|delicious|wordpress).*@i';

foreach($items as $item)
{
    //determine origin
    preg_match($regex, $item->guid, $matches);
    $host_name = strtolower($matches['host']);

    switch($host_name)
    {
        case 'flickr':

            $img_html = substr($item->description, strpos($item->description, '<img'));//extract html beginning w/ img tag

            $desc = substr($img_html, 0, strpos($img_html, '/>'));//trim trailing html

            $host_url = 'http://www.flickr.com/photos/erikeldridge/';
            break;

        case 'upcoming':
            $desc = substr($item->description, 0, 100);
            $host_url = 'http://upcoming.yahoo.com/user/283279/';
            break;

        case 'delicious':
            $desc = $item->description;
            $host_url = 'http://delicious.com/erikeldridge';
            break;

        case 'wordpress':
            $desc = $item->description;
            $host_url = 'https://erikeldridge.wordpress.com/';
            break;

        default:
    }

    $data_arr[] = array(
        'host' => array(
            'url' => $host_url,
            'name' => $host_name
        ),
        'item' => array(
            'desc' => $desc,
            'title' => $item->title,
            'date' => elapsed_time($item->pubDate),
            'link' => $item->link,
        )
    );
}

//to display, loop through data_arr and print contents

Written by Erik

November 23, 2008 at 8:07 pm

Posted in code

opensocial birthday party 2008 notes: opensocial 0.9 preview

leave a comment »

content proxy

– use Content href=”url” to pull externally-generated html

– all links will be re-written to redirect to the shindig server

template

– html templates that render client-side, eg


<div repeat="${friends}">;

${name}

</div>;

Written by Erik

November 14, 2008 at 2:29 am

Posted in Uncategorized

opensocial birthday party 2008: google client libraries

leave a comment »

-location: code.google.com/p/opensocial-(java|php)-client

-python & ruby are coming

Written by Erik

November 14, 2008 at 12:14 am

Posted in Uncategorized

igoogle tips from opensocial birthday 08

leave a comment »

-focus on content

-no profile page

-maximize info density

-reduce latency

–set headers for css & js

–combine/minify js

Written by Erik

November 13, 2008 at 10:59 pm

appengine unconference discussion notes

leave a comment »

free, scalable hosts:
-joyant/sun
-amazon
-appengine
— free up to 5m page views per mo

–enforces constraints optimized for scalability

—python

—BigTable

—GQL

–features

—memcache

—WSGI-compliant

—uses the same cloud-access points as are available within Google

–moving forward

—map appengine SDK to hadoop

Written by Erik

November 13, 2008 at 10:24 pm