Archive

Archive for the ‘PHP’ Category

Mac MAMP setup

June 17th, 2011 No comments
  1. install:
    1. mysql: http://www.mysql.com/downloads/mysql/
    2. macports: http://www.macports.org/install.php
  2. run the following:
    1. $> sudo port selfupdate
    2. $> sudo port install apache2 tidy curl curl-ca-bundle php5-apc php5-curl php5-eaccelerator php5-gd php5-iconv php5-imagick php5-mbstring php5-mcrypt php5-mysql php5-soap php5-tidy php5-unit php5-uuid php5-xdebug php5-xmlrpc php5-xsl php5-zip wgetpro
    • Go for a coffee/beer now… this will take a little time.
  3. edit the following files:
    1. edit /opt/local/apache/conf/http.conf
      1. enable mod_rewrite
      2. enable http-vhosts.conf
      3. enable php5
    2. edit /opt/local/apache/conf/extra/httpd-vhosts.conf
      1. ensure that target folder is group with “staff”, and readable.
      2. note the following vhost entry, and the <directory>
      • <VirtualHost *:80>
      • ServerAdmin webmaster@dummy-host2.example.com
      • DocumentRoot “/Users/llleung/Projects/sample”
      • ServerName sample.localhost.com
      • ErrorLog “logs/sample.localhost.com-error_log”
      • CustomLog “logs/sample.localhost.com-access_log” common
      • <Directory /Users/llleung/Projects/sample>
      • Options FollowSymLinks
      • AllowOverride All
      • </Directory>
      • </VirtualHost>
        • Assumptions:
        • llleung is the user home folder
        • Projects, is where you house all the websites you want to deal with.
    3. edit /etc/hosts
      1. add custom urls referenced inside vhosts
      • 127.0.0.1 sample.localhost.com
    4. edit your ~/.profile file, to run your instances of mysql, and apachectl
      • alias mysql=”/usr/local/mysql/bin/mysql”
      • alias apachectl=”sudo /opt/local/apache2/bin/apachectl”
edit:
Had to remove eacellerator, as it was conflicting with the latest APC.
Categories: PHP Tags:

Reusing a edit/create form

March 5th, 2010 No comments

As a web developer, you have to deal with web forms. Why would you make a new form, and an edit form, which is basically the same.

How to handle this would be to use this design pattern:

1.) load the get item to load, if it exists. Lets call it $dataObject
2.) if $dataObject is empty/does not exist, start a new dataObject. Lets call it $dataObject
3.) dataObject is now instantiated.
4.) Do what needs to be done — for example “title” of the page, being “editing XXX”, or “new dataObject”. Whatever frontend reflections need to happen, based on if it’s new object or editing an old object.
5.) populate the form, accordingly. If the object is blank, it’ll just be populated with the default values.

6.) onSubmit()… check to see if an ID value was submitted, and the appropriate security checks, and deal accordingly. If there was no ID value, start a new dataObject, to be populated with the data submitted.

7.) $dataObject->save();

The above described is how symfony admin generator handles form reuse. While the admin generator is useful for most cases, sometimes you need to just do “more” customizations, than using the generator.

For example, you need to handle a collection of both NEW and Prior saved objects… to do batch edits, it’s easier to do it manually, instead of using the symfony-admin-generator.

symfony restful interface setup

March 5th, 2010 1 comment

How to make a restful interface easily with symfony:

# “project_root”/app/”app_name”/config/routing.yml

# default rules
homepage:
  url:   /
  param: { module: test, action: index }

test_get:
  url: /v2/tester
  class: sfRequestRoute
  param: { module: test, action: get }
  requirements:
    sf_method: [get]

test_put:
  url: /v2/tester
  class: sfRequestRoute
  param: { module: test, action: put }
  requirements:
    sf_method: [put]

test_post:
  url: /v2/tester
  class: sfRequestRoute
  param: { module: test, action: post }
  requirements:
    sf_method: [post]

test_head:
  url: /v2/tester
  class: sfRequestRoute
  param: { module: test, action: head }
  requirements:
    sf_method: [head]

# generic rules
# please, remove them by adding more specific rules
default_index:
  url:   /:module
  param: { action: index }

default:
  url:   /:module/:action/*

Remember, order DOES matter… if you have the default one at the top, the more specific matches to be overlooked. So move more specific items to the top, and your routing will be fine.

Summary:
post to http://localhost/sf_project/frontend_dev.php/test, will call frontend->test->post

delete to http://localhost/sf_project/frontend_dev.php/test, will call frontend->test->delete

put to http://localhost/sf_project/frontend_dev.php/test, will call frontend->test->put

head to http://localhost/sf_project/frontend_dev.php/test, will call frontend->test->head

you get the point…

Symfony Batch Delete while using Doctrine Soft Delete

March 5th, 2010 2 comments

Doctrine Soft Delete is a fabulous method that will set a deleted_at column when the user deletes a record. So it doesn’t really delete the record but timestamps it as deleted.

This offers wonderful functionality to you admin generated app. But there’s a catch!

If you’re using a batch delete you’ll notice that the code uses DQL (Doctrine Query Language) to make the delete and not actually calling the delete() method from the class.

I fixed this quickly by actually time stamping deleted_at field:

$count = Doctrine_Query::create()
      ->update('myclass')
      ->set('deleted_at','now()')
      ->whereIn('id', $ids)
      ->execute();

Symfony should account for this so that soft delete will work properly with admin generated code.

Clean example of using APC caching

December 3rd, 2009 No comments

A nice clean example of using APC.

class bar{
    private $ttl = 300;  // number of seconds time to live.

    public function foo($val) {

        $key = sprintf('%s-%s-%s', __CLASS__, __FUNCTION__, $val);
        $result = apc_fetch($key);

        if (!$result) {
            // do logic here;
            $result = 'something';

            apc_store($result, $key, $this->ttl);
        }

        return $result;
    }
}

The inclusion of the class, and function name, is to help organize your cache. Thus giving you the ability to clear only part of your cache.

Hope this helps…

Categories: PHP Tags:

phpdocs sniplet

September 3rd, 2009 No comments

Quick way of using phpDocs to generate php programmer manuals

$> phpdoc -f file1,file2,…,fileX -t manual -s -pp

or

$> phpdoc -d dir1,dir2,…,dirX -t manual -s -pp

$> phpdoc -h
#always works too…

Categories: PHP Tags:

Unsigned and Delivered

September 2nd, 2009 No comments

This isn’t botched code per se, but it’s something I’ve implemented for several projects, and then had to go back and fix later every time. I’m putting it here so others can learn from my apparent inability to.

Say you have a list of IP addresses that you need to store in a database:


$ips = array('74.28.103.255', '192.168.0.3', '15.47.199.20');

Storing each number in a separate field would be dumb. So you store them as a formatted string, and then realize that you can’t sort them the way you need to!


$sorted_ips = $sort($ips);
$sorted_ips = Array(
  [0] =>; '15.47.199.20',
  [1] => '192.168.0.3', <- WRONG!
  [2] => '74.28.103.255'
)

That’s the correct alphabetical ordering, but you don’t want that; you want the numeric ordering. Digging through the PHP documentation, you find the ip2long() function that converts IP addresses to integers between 0 and 232-1. Perfect!


foreach ($ips as &$ip) {
  $ip = ip2long($ip);
}
$sorted_ips = sort($ips);
foreach ($sorted_ips as &$sorted_ip) {
  $sorted_ip = long2ip($sorted_ip);
}

$sorted_ips = Array(
  [0] => '192.168.0.3', <- STILL WRONG!
  [1] => '15.47.199.20',
  [2] => '74.28.103.255'
)

But now your problem gets even stranger. When you try to look up entries by IP, you can’t find them even though you know they’re in the database. And even weirder, some of the numbers being stored are negative! And it’s only happening with IP addresses that start with 128 or higher! What’s going on here?

Sound familiar? Congratulations, you’re the victim of a type mismatch! See, most computers are still 32-bit, and can store any number between 0 and 232-1 in a single register. But if you might need to store a negative number, you lose a bit for the sign, and can only store between -216 and 216-1 instead. In languages where data types are enforced, these are treated as “unsigned” vs. “signed” integers.

PHP, however, treats all integers as signed, which means any integers larger than 216-1 are interpreted incorrectly. And not all MySQL data types can handle values that big either, for the exact same reason. If you’re seeing negative numbers in your database, you need to change the data type of that field to one that supports numbers up to 232-1, such as BIGINT. If you’re seeing them in your business logic, you need to use the %u option of sprintf, which casts the value as an unsigned integer.


foreach ($ips as &$ip) {
  $ip = floatval(sprintf("%u",ip2long($ip)));
}
$sorted_ips = sort($ips);
foreach ($sorted_ips as &$sorted_ip) {
  $sorted_ip = long2ip($sorted_ip);
}

$sorted_ips = Array(
  [0] => '15.47.199.20',
  [1] => '74.28.103.255'
  [2] => '192.168.0.3', <- RIGHT!
)

The best part is that 64-bit computing won’t fix this; it’ll just increase the range of numbers that we can get away with using before this problem emerges.

Categories: PHP Tags: ,

When Loops Attack

August 17th, 2009 1 comment

Let’s say you have an array of values, and you want to loop through those values. You might do something like this:


$values = array(...);

foreach ($values as $value) { ... }

Simple, right? So simple, in fact, that I can’t figure out why I keep seeing this instead:


$tmp = array(...);

foreach ($tmp as $t) {

$values[$t] = 1;

}

foreach (array_keys($values) as $value) { ... }

This takes the values of one array and uses them as the keys for a second array, but then pulls out the array keys and operates on them alone, disregarding the value! It’s not an isolated incident, either, as it’s come up in numerous spots in the code. If anyone can think of why it would be done this way, please let me know because I’ve got nothing.

Categories: General Programming, PHP Tags:

redundant code redundant

August 12th, 2009 1 comment
function ifFileExists($file)
{
if(!file_exists($file))
{
return 0;
}
else
{
return 1;
}
}

function isWritable($file)
{
if(!is_writable($file))
{
return 0;
}
else
{
return 1;
}
}
Categories: General Programming, PHP Tags:

PHP5 simple type hinting

February 16th, 2009 No comments

The symptom error messages may include:

must be an instance of string, string given
must be an instance of integer, integer given

The cause:

“Type Hints can only be of the object and array (since PHP 5.1) type. Traditional type hinting with int and string isn’t supported.” — http://ca.php.net/oop5.typehinting

Summary:

You can’t type hint STRING or INT, at least as of PHP 5.2.8, the current release at the time of this post.

Categories: PHP Tags: