All entries tagged "php".

Page 1 of 5 older »

PHP snippet: Circular shifting of an array

<?php // This code is public domain. The original author is Marc Ermshaus.
 
error_reporting(-1);
 
/**
 * Circularly shifts an array
 *
 * Shifts to right for $steps > 0. Shifts to left for $steps < 0. Keys are
 * preserved.
 *
 * @param  array $array Array to shift
 * @param  int   $steps Steps to shift array by
 * @return array Resulting array
 */
function array_shift_circular(array $array, $steps = 1)
{
    if (!is_int($steps)) {
        throw new InvalidArgumentException(
                'steps has to be an (int)');
    }
 
    if ($steps === 0) {
        return $array;
    }
 
    $l = count($array);
 
    if ($l === 0) {
        return $array;
    }
 
    $steps = $steps % $l;
    $steps *= -1;
 
    return array_merge(array_slice($array, $steps),
                       array_slice($array, 0, $steps));
}
 
 
 
header('content-type: text/plain');
 
$a = range(0, 9);
 
$l = count($a);
 
for ($i = $l * -2; $i <= $l * 2; $i++) {
    printf("% 3s  :  %s\n", $i, implode(', ', array_shift_circular($a, $i)));
}

Apr 25 2011 • by Marc Ermshaus • type=post language=en php 1 comments

 

PHP snippet: Iterative calculation of permutations

This function calculates the n-th permutation of the elements in an array using an iterative approach. It does not rely on recursion.

<?php // This code is public domain. The original author is Marc Ermshaus.
 
error_reporting(-1);
 
/**
 * Calculates the factorial of a given non-negative integer
 *
 * @param  int $n Non-negative integer
 * @return int Factorial of $n
 */
function fac($n)
{
    if (!is_int($n) || $n < 0) {
        throw new InvalidArgumentException(
                'n has to be a non-negative (int)');
    }
 
    for ($f = 1; $n > 1; $n--) {
        $f *= $n;
    }
 
    return $f;
};
 
/**
 * Returns a specific permutation of an array
 *
 * This function does not preserve key/value pairs.
 * 
 * Permutation table for input (a, b, c):
 *
 *    n   result
 *   -------------
 *   ...
 *   -6   a, b, c    initial state
 *   -5   a, c, b
 *   -4   b, a, c
 *   -3   b, c, a
 *   -2   c, a, b
 *   -1   c, b, a
 *    0   a, b, c    initial state
 *    1   a, c, b
 *    2   b, a, c
 *    3   b, c, a
 *    4   c, a, b
 *    5   c, b, a
 *    6   a, b, c    initial state
 *   ...
 *
 * @param  array $items Array to calculate permutation of
 * @param  int   $n     Index of wanted permutation
 * @return array Resulting array
 */
function getNthPermutation(array $items, $n)
{
    if (!is_int($n)) {
        throw new InvalidArgumentException(
                'n has to be an (int)');
    }
 
    $n      = (int) $n;
    $result = array();
    $l      = count($items);
 
    $facL = fac($l);
 
    while ($n < 0) {
        $n += $facL;
    }
 
    $n = $n % $facL;
 
    for ($i = 1; $l >= $i; $i++) {
        $facLI = fac($l - $i);
 
        // Calculate current index in $items
        $k = (int) ($n / $facLI);
 
        // Remove element from items and push to result
        $curItem = array_splice($items, $k, 1);
        $result[] = $curItem[0];
 
        $n -= $k * $facLI;
    }
 
    return $result;
}
 
 
 
header('content-type: text/plain');
 
$items = range('a', 'd');
 
$f = fac(count($items));
 
for ($i = $f * -2; $i <= $f * 2; $i++) {
    printf("% 3d  :  %s\n", $i, join(', ', getNthPermutation($items, $i)));
}

Apr 22 2011 • by Marc Ermshaus • type=post language=en php 0 comments

 

Algorithmic Advent: 24 – Value object stack parser (PHP)

<?php
 
/**
 * A simple value object (VO)
 *
 */
class MyVO
{
    // Section title
    public $title;
 
    // Array of key->value mappings
    public $data;
}
 
/**
 * Simple tree structure to hold arbitrary data
 *
 */
class TreeNode
{
    protected $_data;
    protected $_children;
 
    public function __construct()
    {
        $this->_children = array();
        $this->_data = array();
    }
 
    public function addChild(TreeNode $child)
    {
        $this->_children[] = $child;
    }
 
    public function getChildren()
    {
        return $this->_children;
    }
 
    public function setValue($key, $value)
    {
        $this->_data[$key] = $value;
    }
 
    public function getValue($key)
    {
        return (isset($this->_data[$key]) ? $this->_data[$key] : null);
    }
}
 
/**
 * Standard PHP doesn't seem to contain a peek function for arrays
 *
 * @param array $array
 * @return mixed Last element from the array
 */
function array_peek(array &$array)
{
    $o = array_pop($array);
    array_push($array, $o);
    return $o;
}
 
/**
 * Creates a tree structure from file content
 *
 * @param string $file File to parse
 * @return TreeNode Root element of created tree
 */
function parseFileIntoTree($file)
{
    $lines = file($file);
    $root = new TreeNode();
 
    // Stack of TreeNode instances
    $stack = array();
 
    array_push($stack, $root);
 
    foreach ($lines as $line) {
        $line = trim($line);
 
        // Skip empty lines
        if ($line == '') {
            continue;
        }
 
        switch (substr($line, 0, 1)) {
            case '{':
                // Functionality is implemented in default case; ignore
                break;
            case '}':
                // Pop stack
                array_pop($stack);
                break;
            case '"':
                // Add new entry to last section on stack
                $node = array_peek($stack);
                $vo = $node->getValue('data');
                $parts = explode(' ', $line);
                $parts[0] = substr($parts[0], 1, strlen($parts[0]) - 2);
                $parts[1] = substr($parts[1], 1, strlen($parts[1]) - 2);
                $vo->data[$parts[0]] = $parts[1];
                break;
            default:
                // Initialize new section
                $vo = new MyVO();
                $vo->title = $line;
                $node = new TreeNode();
                $node->setValue('data', $vo);
 
                // Add new section to tree structure
                $parent = array_peek($stack);
                $parent->addChild($node);
 
                // Push new section on stack
                array_push($stack, $node);
        }
    }
 
    return $root;
}
 
/**
 * Displays the content of a tree structure
 *
 * @param TreeNode $node Root element
 * @param int $depth Recursion depth
 */
function showRec(TreeNode $node, $depth = 0)
{
    foreach ($node->getChildren() as $child) {
        $vo = $child->getValue('data');
        echo str_repeat('    ', $depth) . $vo->title . "\n";
 
        if ($vo->data != null) {
            foreach ($vo->data as $key => $value) {
                echo str_repeat('    ', $depth) . '  ' . $key . ' => ' . $value . "\n";
            }
        }
 
        showRec($child, $depth + 1);
    }
}
 
$root = parseFileIntoTree('input.txt');
 
echo '<pre>';
showRec($root);
echo '</pre>';

Example input:

Text1
{
   Colours
   {
     "1" "red"
     "2" "yellow"
     "3" "green"
   }
 
   Animals
   {
      "6" "Cat"
      "3" "Dog"
   }
 
}

Output:

Text1
    Colours
      1 => red
      2 => yellow
      3 => green
    Animals
      6 => Cat
      3 => Dog

Dec 24 2010 • by Marc Ermshaus • type=post language=en php algorithmicadvent 0 comments

 

Algorithmic Advent: 23 – Frequency of words in a text

<?php
 
$text = <<<EOT
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur augue risus,
sollicitudin nec convallis sit amet, tristique sit amet elit. Nam mauris turpis,
pulvinar sed commodo ac, luctus quis erat. Nulla vitae velit tellus. Etiam non
eros quis quam fringilla dapibus. Cras nec risus lorem. Phasellus cursus, magna
nec elementum porttitor, risus mauris ultricies turpis, varius interdum nisi leo
vitae risus. Donec non justo vitae est condimentum varius at fermentum risus.
Proin volutpat dignissim arcu eu bibendum. Vivamus eget feugiat eros. Cum sociis
natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nam
varius sem sed mi pulvinar commodo. Ut lorem enim, tincidunt eu condimentum
vitae, gravida sit amet magna. Fusce blandit viverra convallis. Etiam egestas
neque vitae leo mattis placerat. Pellentesque hendrerit odio ac ligula eleifend
vel iaculis neque euismod. Nunc nec malesuada mauris. Duis non urna nec sem
porta bibendum sit amet in enim. Nam consequat elit vel purus porta ut euismod
sapien consectetur.
 
Donec venenatis pellentesque massa, eget mollis magna congue quis. Fusce eu
gravida magna. Phasellus quis eros enim. Nulla facilisi. In eu risus eu orci
sodales laoreet. Nunc tellus tellus, porttitor nec interdum in, suscipit et
risus. Ut adipiscing adipiscing molestie. Praesent nec nisl nulla, et congue
tellus. Etiam ornare, metus ac tristique euismod, elit orci pharetra neque,
consectetur feugiat orci nulla ac sem. Nullam suscipit, turpis quis mollis
cursus, ipsum ligula rutrum arcu, ac commodo eros neque non purus. Vestibulum
ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed
vestibulum, nunc non consectetur ullamcorper, justo mi pulvinar dolor, vel
viverra quam elit fringilla enim. Nam varius eros vel velit dignissim vitae
mollis risus aliquam. Sed accumsan, nunc sodales elementum consectetur, sapien
leo egestas orci, id commodo felis eros ac nisl. Nulla ut lectus eu felis
scelerisque ullamcorper pharetra at tellus. Nunc pellentesque rutrum velit id
pharetra. Etiam ac imperdiet nisi.
EOT;
 
header('Content-Type: text/plain; charset=UTF-8');
 
echo $text;
 
echo "\n--------------\n";
 
// Ignore case
 
$text = mb_strtolower($text);
 
// Everything consisting of 3+ letters shall be indexed as a word
 
$cleaned = trim(preg_replace('/(.+?)(?:(\p{L}{3,})|$)/su', ' $2', $text));
 
// Let's calculate some word frequencies
 
$words = explode(' ', $cleaned);
 
$map = array();
 
foreach ($words as $word) {
    if (array_key_exists($word, $map)) {
        $map[$word]++;
    } else {
        $map[$word] = 1;
    }
}
 
// Group by number of occurrences
 
$grouped = array();
 
foreach ($map as $word => $count) {
    $grouped[$count][] = $word;
}
 
krsort($grouped);
 
// Sort words per group alphabetically
 
foreach ($grouped as $count => &$words) {
    sort($words);
}
unset($words);
 
print_r($grouped);

Dec 23 2010 • by Marc Ermshaus • type=post language=en php algorithmicadvent 0 comments

 
 

Algorithmic Advent: 20 – League tables with PHP

index.php

<?php
 
require_once './League.php';
require_once './Match.php';
require_once './Team.php';
 
$league = new League();
 
$teams = array(
    0 => $league->createTeam('Manchester City'),
    1 => $league->createTeam('Chelsea'),
    2 => $league->createTeam('Arsenal'),
    3 => $league->createTeam('Blackburn Rovers')
);
 
$league->addMatch(new Match($teams[0], $teams[1], 2, 0))
       ->addMatch(new Match($teams[0], $teams[2], 2, 1))
       ->addMatch(new Match($teams[0], $teams[3], 2, 0))
 
       ->addMatch(new Match($teams[1], $teams[0], 2, 0))
       ->addMatch(new Match($teams[1], $teams[2], 3, 5))
       ->addMatch(new Match($teams[1], $teams[3], 2, 0))
 
       ->addMatch(new Match($teams[2], $teams[0], 2, 0))
       ->addMatch(new Match($teams[2], $teams[1], 3, 0))
       ->addMatch(new Match($teams[2], $teams[3], 2, 0))
 
       ->addMatch(new Match($teams[3], $teams[0], 2, 0))
       ->addMatch(new Match($teams[3], $teams[1], 1, 1))
       ->addMatch(new Match($teams[3], $teams[2], 2, 0));
 
$table = $league->getTable();
 
 
echo '<table border="1">';
 
echo '<tr>';
echo '<td>Rank</td>';
echo '<td>Team</td>';
echo '<td>Games</td>';
echo '<td>W</td>';
echo '<td>D</td>';
echo '<td>L</td>';
echo '<td>Goals</td>';
echo '<td>Goals Diff</td>';
echo '<td>Points</td>';
 
echo '</tr>';
$i = 1;
foreach ($table as $team) {
    echo '<tr>';
    echo '<td>' . $i++ . '</td>';
    echo '<td>' . $team->teamname . '</td>';
    echo '<td>' . $team->gamesPlayed . '</td>';
    echo '<td>' . $team->wins . '</td>';
    echo '<td>' . $team->draws . '</td>';
    echo '<td>' . $team->losses . '</td>';
    echo '<td>' . ($team->hgoals + $team->agoals) . ':' . ($team->hgoalsAgainst + $team->agoalsAgainst) . '</td>';
    echo '<td>' . (($team->hgoals + $team->agoals) - ($team->hgoalsAgainst + $team->agoalsAgainst)) . '</td>';
    echo '<td>' . $team->points . '</td>';
    echo '</tr>';
}
 
echo '</table>';

Team.php

<?php
 
class Team
{
    public $teamname;
    public $agoals = 0;
    public $hgoals = 0;
    public $agoalsAgainst = 0;
    public $hgoalsAgainst = 0;
 
    public $gamesPlayed = 0;
 
    public $wins = 0;
    public $draws = 0;
    public $losses = 0;
 
    public $points = 0;
 
    public function __construct($teamname)
    {
        $this->teamname     = $teamname;
    }
}

Match.php

<?php
 
class Match
{
    /** @var Team Home team */
    public $hteam;
 
    /**@var Team Away team */
    public $ateam;
 
    /**@var int Home team goals */
    public $hgoals;
 
    /** @var int Away team goals */
    public $agoals;
 
    public function __construct(Team $hteam, Team $ateam, $hgoals, $agoals)
    {
        $this->hteam = $hteam;
        $this->ateam = $ateam;
        $this->hgoals = $hgoals;
        $this->agoals = $agoals;
    }
}

League.php

<?php
 
class League
{
    protected $_matches = array();
    protected $_teams   = array();
 
    public function addMatch(Match $match)
    {
        $this->_matches[] = $match;
        return $this;
    }
 
    public function createTeam($teamname)
    {
        $team = new Team($teamname);
        $this->_teams[] = $team;
 
        return $team;
    }
 
    protected function _sortTeams(Team $a, Team $b)
    {
        // Absolute number of points
        if ($a->points != $b->points) {
            return ($a->points < $b->points);
        }
 
        // Direct comparison and away goals
 
        // Goals first team (x=a)
        $xgoals = 0;
 
        // Goals second team (y=b)
        $ygoals = 0;
 
        // Away goals team a
        $xagoals = 0;
 
        // Away goals team b
        $yagoals = 0;
 
        foreach ($this->_matches as $match) {
            if ($match->hteam === $a && $match->ateam === $b) {
                $xgoals += $match->hgoals;
                $ygoals += $match->agoals;
                $yagoals = $match->agoals;
            } else if ($match->hteam === $b && $match->ateam === $a) {
                $ygoals += $match->hgoals;
                $xgoals += $match->agoals;
                $xagoals = $match->agoals;
            }
        }
 
        /*
        if ($xgoals != $ygoals) {
            return ($xgoals < $ygoals);
        }
 
        if ($xagoals != $yagoals) {
            return ($xagoals < $yagoals);
        }
        */
 
        // Goal difference
        $adiff = ($a->hgoals + $a->agoals) - ($a->hgoalsAgainst + $a->agoalsAgainst);
        $bdiff = ($b->hgoals + $b->agoals) - ($b->hgoalsAgainst + $b->agoalsAgainst);
 
        if ($adiff != $bdiff) {
            return ($adiff < $bdiff);
        }
 
        // Absolute number of goals scored during season
        if (($a->hgoals + $a->agoals) != ($b->hgoals + $b->agoals)) {
            return (($a->hgoals + $a->agoals) < ($b->hgoals + $b->agoals));
        }
 
        // Team order undefined (possibly same rank)
        //throw new Exception('Couldn\'t determine team order.');
    }
 
    public function getTable()
    {
        foreach ($this->_matches as $match) {
            if ($match->hgoals > $match->agoals) {
                $match->hteam->points += 3;
 
                $match->hteam->wins++;
                $match->ateam->losses++;
            } else if ($match->hgoals == $match->agoals) {
                $match->hteam->points += 1;
                $match->ateam->points += 1;
 
                $match->hteam->draws++;
                $match->ateam->draws++;
            } else {
                $match->ateam->points += 3;
 
                $match->hteam->losses++;
                $match->ateam->wins++;
            }
 
            $match->hteam->hgoals += $match->hgoals;
            $match->hteam->hgoalsAgainst += $match->agoals;
 
            $match->ateam->agoals += $match->agoals;
            $match->ateam->agoalsAgainst += $match->hgoals;
 
            $match->hteam->gamesPlayed++;
            $match->ateam->gamesPlayed++;
        }
 
        $teamsSorted = $this->_teams;
 
        usort($teamsSorted, array($this, '_sortTeams'));
 
        return $teamsSorted;
    }
}

Dec 20 2010 • by Marc Ermshaus • type=post language=en php algorithmicadvent 0 comments

 

Algorithmic Advent: 19 – File extension filter iterator (PHP/SPL)

/**
 * Example:
 *
 * $path      = '/a/directory';
 * $whitelist = array('txt'); // List of file extensions to filter
 *
 * $iterator = new FileExtensionFilterIterator(
 *                 new RecursiveIteratorIterator(
 *                     new RecursiveDirectoryIterator($path)),
 *                 $whitelist);
 *
 * foreach ($iterator as $file) {
 *     echo $file, "<br />";
 * } * 
 */
class FileExtensionFilterIterator extends FilterIterator
{
    protected $whitelist;
 
    public function __construct(Iterator $iterator, array $whitelist)
    {
        parent::__construct($iterator);
        $this->whitelist = $whitelist;
    }
 
    public function accept()
    {
        $fileInfo = parent::current();
 
        // Allow only files
        if (!$fileInfo->isFile()) {
            return false;
        }
 
        // Only allow file extensinos from $whitelist
        $pi = pathinfo($fileInfo->getFilename());
        if (!in_array(strtolower($pi['extension']), $this->whitelist)) {
            return false;
        }
 
        return true;
    }
}

Dec 19 2010 • by Marc Ermshaus • type=post language=en php algorithmicadvent 0 comments

 

Algorithmic Advent: 18 – Jonsole

This is a very basic PHP/jQuery console „emulator“. I had to publish this in a hurry, so it is not as polished as I wanted it to be. I'm sorry for that.

<?php // Filename has to be "index.php"
 
// Server side
 
if (count($_GET) > 0) {
    $ret   = implode(' ', $_GET['args']);
    $error = false;
 
    switch ($_GET['args'][1]) {
        case 'date':
            $args = array_slice($_GET['args'], 2);
            $args = implode(' ', $args);
 
            $ret = date($args);
            break;
        case 'md5':
            $args = array_slice($_GET['args'], 2);
            $args = implode(' ', $args);
 
            $ret = md5($args);
            break;
        case 'strtoupper':
            $args = array_slice($_GET['args'], 2);
            $args = implode(' ', $args);
 
            $ret = strtoupper($args);
            break;
        default:
            $error = true;
            break;
    }
 
    echo json_encode(array('ret' => $ret, 'error' => $error));
 
    exit;
}
 
?><!DOCTYPE html>
 
<html>
 
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>Jonsole</title>
          <script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript">
 
function jonsole_handle_php(args)
{
    $.get('index.php', {'args': args, 'r': Math.floor(Math.random() * 100000)}, function (data) {
        if (data.error) {
            jonsole_println("<span class=\"error\">PHP: Error: \"" + data.ret + "\"</span>");
        } else {
            jonsole_println(data.ret);
        }
        $("#jonsole-wrapper").attr({ scrollTop: $("#jonsole-wrapper").attr("scrollHeight") });
    }, 'json');
}
 
function jonsole_println(line)
{
    $('#jonsole').html($('#jonsole').html() + line + "<br />");
}
 
function jonsole_set(line)
{
    $('#jonsole').html(line);
}
 
function jonsole_run(expr)
{
    var tmp     = expr.split(/ /),
        unknown = false;
 
    jonsole_println('<span class="system">$</span> ' + expr);
 
    if (tmp[0] == 'php') {
        jonsole_handle_php(tmp);
    } else if(tmp[0] == 'clear') {
        jonsole_set('');
    } else if(tmp[0] == 'help') {
        jonsole_println('<span class="system">This version of Jonsole supports the following commands:<br />\
- clear<br />\
- help<br />\
- version<br />\
- php<br />\
&nbsp;&nbsp;- md5 <em>string</em><br />\
&nbsp;&nbsp;- strtoupper <em>string</em><br />\
&nbsp;&nbsp;- date <em>format</em></span>');
    } else if(tmp[0] == 'version') {
        jonsole_println('<span class="system">Jonsole version: r1</span>');
    } else {
        unknown = true;
    }
 
    if (unknown) {
        jonsole_println("<span class=\"error\">Unknown command: \"" + expr + "\"</span>");
    }
 
    $("#jonsole-wrapper").attr({ scrollTop: $("#jonsole-wrapper").attr("scrollHeight") });
}
 
$('document').ready(function () {
    jonsole_println("<span class=\"system\">[Welcome to Jonsole] Type \"help\" for more info</span>");
 
    $('#jonsole-input').bind('keydown', function (key) {
 
        if (key.which == 13) {
            jonsole_run($(this).val());
            $(this).val('');
            return false;
        }
    }).focus();
});
 
</script>
 
<style type="text/css">
 
* {
    margin: 0;
    padding: 0;
}
 
#jonsole-wrapper {
    padding: 5px;
    background: #000;
    color: #fff;
    border: 5px solid #999;
    width: 600px;
    height: 400px;
    font-family: monospace;
    font-size: 11px;
    overflow: auto;
}
 
#jonsole {
 
}
 
#jonsole-wrapper .system {
    background: #666;
}
 
#jonsole-wrapper .error {
    background: #900;
}
 
#jonsole-input {
    background: #000;
    color: #fff;
    border: 1px solid #666;
    width: 550px;
    font-family: monospace;
    font-size: 11px;
    display: inline;
}
 
</style>
    </head>
 
    <body>
 
        <form action="" method="post">
            <div id="jonsole-wrapper">
                <div id="jonsole"></div>
                <p><span class="system">$</span> <input id="jonsole-input" type="text" autocomplete="off" /></p>
            </div>
        </form>
 
    </body>
 
</html>

Dec 18 2010 • by Marc Ermshaus • type=post language=en php algorithmicadvent 0 comments

 

Algorithmic Advent: 17 – Displaying the structure of an XML document

<?php
 
function nodeTypeToString($nodeType)
{
    $map = array(
         1 => 'XML_ELEMENT_NODE',
         2 => 'XML_ATTRIBUTE_NODE',
         3 => 'XML_TEXT_NODE',
         4 => 'XML_CDATA_SECTION_NODE',
         5 => 'XML_ENTITY_REFERENCE_NODE',
         6 => 'XML_ENTITY_NODE',
         7 => 'XML_PROCESSING_INSTRUCTION_NODE',
         8 => 'XML_COMMENT_NODE',                 //
         9 => 'XML_DOCUMENT_NODE',
        10 => 'XML_DOCUMENT_TYPE_NODE',           //
        11 => 'XML_DOCUMENT_FRAGMENT_NODE',
        12 => 'XML_NOTATION_NODE'
    );
 
    if (isset($map[$nodeType])) {
        return $map[$nodeType];
    }
 
    return 'UNKNOWN';
}
 
header('Content-Type: text/html; charset=UTF-8');
 
$doc = new DOMDocument();
$doc->preserveWhiteSpace = false;
 
$doc->loadXML('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
     <!DOCTYPE html [
 <!ELEMENT test (#PCDATA) >
 <!ENTITY % xx "&#37;zz;">
 <!ENTITY % zz "&#60;">
 ]>
    <html>
    <div>Some text<!--a comment-->
    <strong class="test">some <![CDATA[<tag>]]> more text</strong>
    </div></html>');
 
function rec(DOMNode $node, $indent = 0)
{
    if ($node->hasChildNodes()) {
        foreach ($node->childNodes as $child) {
            echo '<tr>';
            echo '<td>' . str_repeat('&nbsp;', $indent)
                    . $child->nodeName . '</td>';
            echo '<td>' . nodeTypeToString($child->nodeType) . '</td>';
 
            $nv = htmlspecialchars($child->nodeValue);
 
            $nv = str_replace(array("\n", "\r", "\t", ' '),
                              array('\n', '\r', '\t', '&nbsp;'), $nv);
 
            if ($nv === '') {
                $nv = '{empty}';
            } else {
                $nv = '"' . $nv . '"';
            }
 
            echo '<td>' . $nv . '</td>';
            echo '</tr>';
            rec($child, $indent + 4);
        }
    }
}
 
echo '<table border="1">';
echo '<tr><th>nodeName</th><th>nodeType</th><th>nodeValue</th></tr>';
rec($doc);
echo '</table>';

Dec 17 2010 • by Marc Ermshaus • type=post language=en php algorithmicadvent 0 comments

 

Algorithmic Advent: 16 – Expandable tree menu with PHP and jQuery

<?php
/**
 * This is an example on how to create an expandable tree menu structure with
 * PHP, CSS and jQuery.
 *
 * @version 2010-Dec-16
 *
 * @author Marc Ermshaus <http://www.ermshaus.org/>
 * @license GNU General Public License <http://www.gnu.org/licenses/gpl.html>
 */
 
define('X_BASEURL', '/nb/menutest');
define('X_CHARSET', 'UTF-8');
 
/**
 * Assembles an internal URL
 *
 * @param string $path
 * @param array $queryPart
 * @return string The assembled URL
 */
function url($path, $queryPart = array())
{
    $baseUrl = X_BASEURL;
 
    $url = $baseUrl . $path;
 
    if (count($queryPart) > 0) {
        $url .= '?' . http_build_query($queryPart);
    }
 
    return $url;
}
 
/**
 * Escapes a string for HTML display
 *
 * @param strng $s
 * @param int $quoteStyle
 * @param string $charset
 * @return string
 */
function escape($s, $quoteStyle = ENT_QUOTES, $charset = X_CHARSET)
{
    return htmlspecialchars($s, $quoteStyle, $charset);
}
 
/**
 * Arranges the input data into a tree structure
 *
 * @param array $data
 * @param int|null $parentId
 * @return array
 */
function toTree(array $data, $parentId = null)
{
    $rec = function (array $data, array &$root, $parent_id = null) use (&$rec)
    {
        $root['children'] = array();
 
        foreach ($data as $item) {
            if ($item['parent_id'] === $parent_id) {
 
                $newChild = array('data' => $item['data']);
 
                $root['children'][] = &$newChild;
 
                $rec($data, $newChild, $item['id']);
                unset($newChild);
            }
        }
    };
 
    $root = array('title' => 'root');
 
    $rec($data, $root, $parentId);
 
    return $root;
}
 
/**
 * Creates the HTML output for a navigation tree
 *
 * @param array $root Tree root (see toTree function)
 * @return string HTML code of navigation
 */
function menuHelper($root)
{
    $s = '';
 
    $s .= '<ul id="navigation">' . "\n";
 
    $recm = function (array $node, $depth = 0) use (&$recm)
    {
        $pad = '    ';
 
        foreach ($node['children'] as $child) {
            $spanClasses = 'title ';
 
            if (count($child['children']) > 0) {
                $spanClasses .= 'hasChildren ';
            }
 
            $spanClasses = trim($spanClasses);
 
            $s .= str_repeat($pad, $depth) . '<li>';
 
            $s .= '<span class="'.$spanClasses.'">';
 
            if ($child['data']['path'] !== null) {
                $s .= '<a href="'.url($child['data']['path']).'">'
                    . escape($child['data']['title']) . '</a>';
            } else {
                $s .= escape($child['data']['title']);
            }
 
            $s .= '</span>';
 
            if (count($child['children']) > 0) {
                $s .= "\n";
 
                $depth++;
 
                $s .= str_repeat($pad, $depth) . '<ul>' . "\n";
                $s .= $recm($child, $depth + 1);
                $s .= str_repeat($pad, $depth) . '</ul>' . "\n";
 
                $depth--;
 
                $s .= str_repeat($pad, $depth);
            }
 
            $s .= '</li>' . "\n";
        }
 
        return $s;
    };
 
    $s .= $recm($root, 1);
 
    $s .= '</ul>' . "\n";
 
    return $s;
}
 
$menuData = array(
    array('id'        => 1,
          'parent_id' => null,
          'data'      => array('title' => 'Item 1',
                               'path'  => '/item1')),
    array('id'        => 2,
          'parent_id' => null,
          'data'      => array('title' => 'Item 2',
                               'path'  => '/item2')),
    array('id'        => 3,
          'parent_id' => 1,
          'data'      => array('title' => 'Item 1.1',
                               'path'  => '/item1/item1')),
    array('id'        => 4,
          'parent_id' => 2,
          'data'      => array('title' => 'Item 2.1',
                               'path'  => '/item2/item1')),
    array('id'        => 5,
          'parent_id' => 2,
          'data'      => array('title' => 'Item 2.2',
                               'path'  => '/item2/item2')),
    array('id'        => 6,
          'parent_id' => 1,
          'data'      => array('title' => 'Item 1.2',
                               'path'  => '/item1/item2')),
    array('id'        => 7,
          'parent_id' => 4,
          'data'      => array('title' => 'Item 2.1.1',
                               'path'  => '/item2/item1/item1')),
    array('id'        => 8,
          'parent_id' => null,
          'data'      => array('title' => 'Item 3',
                               'path'  => '/item3'))
);
 
?><!DOCTYPE html>
 
<html>
 
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>Expandable tree menu</title>
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script>
 
        <style type="text/css">
        #navigation .title {
            cursor: pointer;
        }
 
        #navigation .hasChildren {
            background: #9ff;
        }
 
        #navigation .hasChildren:after {
            content: " (expand)";
        }
 
        #navigation .hidden {
            display: none;
        }
 
        #navigation .open-category {
 
        }
 
        /* "#navigation .open-category > .title" or
           "#navigation .title.open" won't work in IE6 */
        #navigation .open-title {
            color: #f00;
        }
        </style>
 
        <script type="text/javascript">
        $(document).ready(function () {
            $('#navigation ul').addClass('hidden');
            $('#navigation li').click(function (event) {
                // Entry has children?
                if ($(this).children('ul').length > 0) {
                    $(this).toggleClass('open-category');
                    $(this).children('.title').toggleClass('open-title');
                    $(this).children('ul').toggleClass('hidden');
                }
                event.stopPropagation();
            });
        });
        </script>
    </head>
 
    <body>
 
        <?php echo menuHelper(toTree($menuData)); ?>
 
    </body>
 
</html>

Dec 16 2010 • by Marc Ermshaus • type=post language=en php algorithmicadvent 3 comments