More WoW

Can’t have a post about PHP at the top of the page for more that an couple of days. So on with the WoW gloating and moaning.

  • Karazhan — Nothing at all to report. Haven’t run for weeks, no one seems interested (two that were interested left yesterday. Kara group one leader, and Kara group two leader). Would be seriously looking for another guild, but I feel bad now that I have highest guild rank next to the GM
  • 3 more epics since last post
  • Eternium Runed Blade – Highest DPS crafted dagger (168 spell damage)
  • Icon of the Silver Crescent – Finally, took a long time to get all 41 badges
  • Bands of Rarefied Magic – Actually slightly lower damage than the Bracers of Havoc, but much more balanced. Now with a +15 spell damage enchant too

Stats are looking pretty good. Hit is suffering a little though (says someone who can count his Kara raids on one hand :-(

  • Health: 6643
  • Mana: 7616
  • Hit: 10.70% (+3% from Talents)
  • Fire crit: 24.40% (+3% from Molten Armor)
  • Fire damage: 970 (944 excluding Spellfire set bonus)

Overall, very happy with my mage… Just running out of things to aim for, there is one more item I want for badges, but I’m only 5 / 60 so far. I would really like to run Kara on a regular basis with a group of dedicated people, I don’t think it’s possible with this guild, and who on earth wants a Mage for Kara :-(

Posted on

Very Simple (Pretend) SQL Paramaters

<?php

/*

    Some hacked up code for Very Simple (Pretend) SQL Paramaters
    -- For when you don't have PDO, or just can't be bothered

    Examples:

        >>> SQL("SELECT * FROM users WHERE id=? AND name LIKE ?", 123, '"123\"');
        SELECT * FROM users WHERE id=123 AND name LIKE "\"123\\\""

        >>> SQL("INSERT INTO user (nameFirst, nameLast) VALUES (?, ?)", "Fred", "Nurk");
        INSERT INTO user (nameFirst, nameLast) VALUES ("Fred", "Nurk")

*/

function SQL($sql)
{
    $params = func_get_args();
    $params = array_map('mysql_real_escape_string', $params);
    $sql    = explode('?', $sql);

    if(count($params) != (count($sql)-1))
    {
        throw new Exception(sprintf("Incorrect number of paramaters.  Expected %d got %d", count($sql)-1, count($params)));
    }

    $newSql = '';

    for($i = 0; $i < count($params); $i++)
    {
        $newSql .= $sql[$i] . (is_numeric($params[$i]) ? $params[$i] : '"' . $params[$i] . '"');
    }

    return $newSql . $sql[$i];
}

?>

Update: This is not only the wrong way to escaping, but introduces more bugs. Don’t ever use.

Posted on

PHP interface for the DecentUrl API

PHP version of the Python script located at http://decenturl.com/tools#api.

Update: Thanks to a quick update by Ben, DecentURL now returns valid JSON, so the quote workaround is no longer needed.

<?php

/*

    PHP interface for the DecentUrl service
        by Matthew D (project-2501.net)

    Based on the Python script by Ben Hoyt
    See http://decenturl.com/tools#api

    Example:


    <?php

        require_once("decenturl_interface.php");

        //  returns: "youtube/medieval-helpdesk-with-english"
        DecentUrl::Get('http://youtube.com/watch?v=pQHX-SjgQvQ');

        //  returns: "http://youtube.com/watch?v=pQHX-SjgQvQ"
        DecentUrl::Resolve('youtube/medieval-helpdesk-with-english');

        //  returns: array("DecentURL - Making ugly URLs decent", "making-ugly-urls-decent")
        DecentUrl::Title('http://decenturl.com/');

        //  returns: "brush.co.nz"
        DecentUrl::Domain('http://brush.co.nz');

        //  throws a DecentUrlException("resolve request failed:  notfound")
        DecentUrl::Resolve('baddy');
    ?>

*/

    class DecentUrlException extends Exception {}
    class DecentUrl
    {
        const BASE_URL = 'http://decenturl.com/api-';

        //  Create or get decent URL from given ugly one and return it
        public static function Get($url, $title="")
        {
            $response = self::Request("get", array("u" => $url, "t" => $title), 2);

            return $response[0];
        }

        //  Resolve decent URL and return ugly original
        public static function Resolve($decentUrl)
        {
            $response = self::Request("resolve", array("d" => $decentUrl), 2);

            return $response[0];
        }

        //  Return tuple of full <title> and decent title for given URL
        public static function Title($url, $maxLength = 1000)
        {
            return self::Request("title", array("u" => $url, "l" => $maxLength), 2);
        }

        //  Return URL's base domain
        public static function Domain($url, $maxLength = 1000)
        {
            $response = self::Request("domain", array("u" => $url, "l" => $maxLength), 1);

            return $response[0];
        }

        //  Call generic DecentUul API function, throw DecentUrlException if not ok
        private static function Request($type, $parameters, $expectedResponseSize = false)
        {
            //  Take a key value pair, and join them together, making sure to
            //  urlencode the value
            foreach($parameters as $key => $value)
            {
                $parameters[$key] = sprintf("%s=%s", $key, urlencode($value));
            }

            $queryString = implode("&", array_values($parameters));

            $url = sprintf("%s%s?%s", self::BASE_URL, $type, $queryString);

            $contents = file_get_contents($url);

            $response = json_decode($contents);

            if($response == null)
            {
                throw new DecentUrlException("Response was not valid JSON");
            }

            if($response[0] != "ok")
            {
                throw new DecentUrlException("{$type} request failed:  {$response[0]}");
            }

            //  Remove the first element (which we proved was 'ok' above)
            $trimedResponse = array_slice($response, 1);

            //  If requested, we check to see that the response is the correct
            //  size.
            if(($expectedResponseSize !== false) && (count($trimedResponse) != $expectedResponseSize))
            {
                throw new DecentUrlException("{$type} request failed:  expected {$expectedResponseSize} fields, got " . count($trimedResponse));
            }

            return $trimedResponse;
        }
    }

?>

Posted on

The timewaster

Yep, another WoW post.

  • Karazhan is still at exactly the same place it was last time… That is pretty embarrassing :-)
  • I’ve respec’d yet again. Now 10/48/3 Fire
  • My health and mana are now tiny, at 6463 / 7166 unbuffed
  • Spell damage is now: +885 fire unbuffed (Including Wrath of Spellfire Set bonus)
  • I think most impressively upgrade is my hit: +129 (10.23%) not including the 3% bonus from Elemental Precision :D

Future Upgrades

November 21st Update

I’m not even going to mention Kara today. As good as my current guild is, I’m seriously thinking about moving (but then again, no one really wants a level 70 mage)

  • +915 Fire damage (Unbuffed but with Spellfire Set bonus)
  • 6513 / 7421 Health/Mana
  • 23.38% Fire crit (Unbuffed)
  • 12.13% Spell hit (Before +3% Talent bonus) – For someone who doesn’t actually raid, I think this is a pretty good achievement
  • I spent all my badges on a Carved Witch Doctor’s Stick as Quagmirran’s Eye finally dropped.
  • Still trying to collect badges for the Icon of the Silver Crescent and the Runed Spell-cuffs. Going to be a while though
  • I’m really running out of practicable things to buy/craft (an Epic Flying Mount is completely out of the question) :-(

Posted on

5 Things I hate about PHP

Articles on why PHP sucks are fairly common (PHP is a really easy target). So rather than repeat what’s already been said (lack of namespaces, function naming, and argument positions, etc), I though I’d try and come up some more obscure ‘features’ and ‘bugs’ in PHP that I hate.

create_function()

//  Actually creates a new named function, in the global namespace every
//  time it's called, even if it's called with the exact same arguments.
//  Syntax is pretty bad too, need to pay close attention to quoting strings

$args = '$r';
$body = 'return $r;'

$f1 = create_function($args, $body);
$f2 = create_function($args, $body);

// Displays false
var_dump($f1 == $f2);

Automatic creation of class members

//  This should throw a E_NOTICE, or E_STRICT (though I do believe that PHP needs a
//  way to add members,  as it is sometimes is useful.  Maybe allow it on stdclass,
//  and not user defined classes?)

// Completely valid code
$foo = new stdClass();
$foo->bar = "abc"

//  You can take this one step further (still valid PHP)
$bar->xyzzy = "abc";
echo $bar->xyzzy;

Scope, wonderful scope

$numbers = array(1,2,3);
foreach($numbers as $number)
{
    echo $number . "\n";
}

//  This should throw a error notice of Undefined variable
//  instead is prints 3
echo $number . "\n";

extract()

extract() works in a similar method to register.globals, while it’s safer (you can make sure it never overwrites existing variables) it still is a really bad idea.

Late static binding

class aParent
{
    static $phrase = "Goodbye";

    public static function SayHello()
    {
        printf("%s %s from class %s\n", self::$phrase, self::GetWorld(), get_class());
    }

    public static function GetWorld()
    {
        return "Universe";
    }
}

class aChild extends aParent
{
    static $phrase = "Hello";

    public static function GetWorld()
    {
        return "World";
    }
}

//  All good here...
//  Displays:       'Goodbye Universe from class aParent'
//  Should display: 'Goodbye Universe from class aParent'
aParent::SayHello();

//  Not so good here though...
//  Displays:       'Goodbye Universe from class aParent'
//  Should display: 'Hello World from class aChild'
aChild::SayHello();

Posted on

WoW Update

Wow, almost two weeks since my last WoW post. In short:

  • Respect to Arcane/Fire, for (hopfully) better mana management
  • Frostsabre mount from Darnassus
  • Picked up Boots of Blasphemy
  • Went to Karazhan with my guild Gankers Inc. Wasn’t a great result, but was fun

Posted on

Fun with ‘anonymous’ functions in PHP

Just some fun with anonymous functions in PHP (which are surprisingly rare due to silly syntax, and a buggy implementation)

<?php

    class LambdaException extends Exception {}
    class Lambda
    {
        private static $cache;

        public static function Create($functionString)
        {
            if(!isset(self::$cache[$functionString]))
            {
                list($args, $body) = self::BreakupFunctionString($functionString);
                self::$cache[$functionString] = new self($args, $body);
            }

            return self::$cache[$functionString];
        }

        private static function BreakupFunctionString($functionString)
        {
            $errorMessage = "Unable to understand the function: '$functionString'." .
                            "Format should follow function (\$arg1, \$arg2) { return rand(\$arg1, \$arg2); }";

            $regex = "^function\s*\(([^\)]*)\)\s*\{(.*?)\}$";
            if(preg_match("#$regex#si", trim($functionString), $matches) !== 1)
            {
                throw new LambdaException($errorMessage);
            }

            list(, $args, $body) = $matches;

            $args = explode(',', $args);
            $args = array_map('trim', $args);
            $args = array_diff($args, array(''));

            return array($args, $body);
        }

        //--------------------------------------------------------------------


        private $args;
        private $body;
        private $function;

        private function __construct($args, $body)
        {
            assert('is_array($args)');
            assert('is_string($body)');

            $this->args = $args;
            $this->body = $body;
            $this->function = create_function(implode(',', $this->args), $this->body);
        }

        public function map($array)
        {
            //  We could actually test if the number of args is two
            //  and do a array_map with keys as well
            assert('count($this->args) == 1');
            return array_map($this->function, $array);
        }

        public function filter($array)
        {
            assert('count($this->args) == 1');
            return array_filter($array, $this->function);
        }

        public function sort($array)
        {
            assert('count($this->args) == 2');
            usort($array, $this->function);
            return $array;
        }

        public function call()
        {
            $args = func_get_args();

            return call_user_func_array($this->function, $args);
        }

        public function getFunction()
        {
            return $this->function;
        }

        public function __toString()
        {
            return $this->function;
        }
    }

    $extractRowId = Lambda::Create('function ($row) { return $row["id"]; }');
    print_r($extractRowId->map(array(array('id' => 'a'), array('id' => 'b'))));

    $randomise = Lambda::Create('function ($v, $v) { return rand(0,1) == 0 ? -1 : 1; }');
    print_r($randomise->sort(array(1, 2, 3, 4, 5, 6, 7, 8, 9)));

    $greaterThanFive = Lambda::Create('function ($var) { return $var > 5; }');
    print_r(array_filter(array(1,2,3,4,5,6,7,8,9), $greaterThanFive->getFunction()));
    print_r(array_filter(array(1,2,3,4,5,6,7,8,9), (string)$greaterThanFive));

    $printString = Lambda::Create('function($str) { echo $str . "\n"; }');
    $printString->call("Hello World");


    function formatString($s)
    {
        $args = func_get_args();

        for($i = 1; $i < count($args); $i++)
        {
            $s = $args[$i]->call($s);
        }

        return $s;
    }


    $italic = Lambda::Create('function($s) { return "<i>" . $s . "</i>"; }');
    $bold   = Lambda::Create('function($s) { return "<bold>" . $s . "</bold>"; }');

    $tag        = Lambda::Create('function($t) { return Lambda::Create("function(\$s) { return \"<$t>\" . \$s . \"</$t>\"; }"); }');
    $heading    = $tag->call('h1');
    $para       = $tag->call('p');

    echo formatString('Weird Formatting', $italic, $bold, $heading) . "\n";


    $content    = Lambda::Create('function($s) { return "Hello Lambda!" . $s; }');
    $newLine    = Lambda::Create('function($s) { return $s . "<br/>"; }');

    echo formatString('', $content, $bold, $italic, $newLine, $para) . "\n";

?>

Output

Array
(
    [0] => a
    [1] => b
)
Array
(
    [0] => 7
    [1] => 1
    [2] => 5
    [3] => 4
    [4] => 3
    [5] => 6
    [6] => 2
    [7] => 8
    [8] => 9
)
Array
(
    [5] => 6
    [6] => 7
    [7] => 8
    [8] => 9
)
Array
(
    [5] => 6
    [6] => 7
    [7] => 8
    [8] => 9
)
Hello World
<h1><bold><i>Weird Formatting</i></bold></h1>
<p><i><bold>Hello Lambda!</bold></i><br/></p>

**Note:**Please don’t actually use this code for anything important :-)

Posted on

explodeTree

While reading a blog post by Kevin Van Zonneveld’s on converting arrays to trees in PHP, I was shocked to see his approach to the problem. To paraphrase a popular quote:

Some people, when confronted with a problem, think
“I know, I’ll use eval.” Now they have two problems.

Below is the same function, but using a reference rather than eval to keep track of the current branch. (After reading the comments on the blog, my code is surprisingly similar to lachlan’s. Great minds, or something like that I guess :))

function explodeTree($array, $delim = '/')
{
    $tree = array();

    foreach($array as $elem)
    {
        //  Split our string up, and remove any blank items
        $items = explode($delim, $elem);
        $items = array_diff($items, array(''));

        //  current holds the current position in the tree
        $current = &$tree;

        foreach($items as $item)
        {
            //  If we've not created this branch before, or there is
            //  a leaf with the same name, then turn it into a branch
            if(!isset($current[$item]) || !is_array($current[$item]))
            {
                $current[$item] = array();
            }

            //  Update our current position to the branch we entered
            //  (or created, depending on the above if statement)
            $current = &$current[$item];
        }

        //  If the last value in this row is an array with 0 elements
        //  then (for now) we will consider it a leaf node, and set it
        //  to be equal to the string representation that got us here.
        if(count($current) == 0)
        {
            $current = $elem;
        }
    }

    return $tree;
}

Update: To quickly clarify, I didn’’’t mean the post to come over as arrogant, I think using eval() like that is ingenious. It’s just (IMHO) not the best way as it suffers greatly with readability, and have to be extra careful using eval especially with user data.

Posted on