Archive for the ‘php’ Category

xdebug configuration

Monday, October 27th, 2008

I’m going to post my xdebug config file because when I’m installing new developer machine (not so often … may be several times in year ) I usually have to search for some basic configuration and copy/paste/change it. It took me some time so I prefer to have it in my blog.

#change this
zend_extension=/usr/lib/php5/20060613+lfs/xdebug.so
 
[debug]
; Remote settings
xdebug.remote_autostart=off
xdebug.remote_enable=on
xdebug.remote_handler=dbgp
xdebug.remote_mode=req
xdebug.remote_host=localhost
xdebug.remote_port=9000
 
; General
xdebug.auto_trace=on
xdebug.collect_includes=on
xdebug.collect_params=off
xdebug.collect_return=off
xdebug.default_enable=on
xdebug.extended_info=1
xdebug.manual_url=http://www.php.net
xdebug.show_local_vars=0
xdebug.show_mem_delta=0
xdebug.max_nesting_level=100
;xdebug.idekey=
 
; Trace options
xdebug.trace_format=1
xdebug.trace_output_dir=/tmp
xdebug.trace_options=1
xdebug.trace_output_name=crc32
 
; Profiling
xdebug.profiler_append=1
xdebug.profiler_enable=1
xdebug.profiler_enable_trigger=1
xdebug.profiler_output_dir=/tmp
xdebug.profiler_output_name=trace.%c

Share/Save/Bookmark

php 5.3: notes about closures and lambda functions

Friday, July 18th, 2008

Few days ago a friend of mine told me that php will support closures and lambda functions.
You can find example code and explanation about lambda functions and closures in php’s wiki

I just want to drop a few notes.
1. there are (still) no lambda classes, but I hope in next releases they will be implemented.
2. lambda function definitions must end with “;”. I think it’s important because I made a parse error first time :)
3. “By default, all imported variables are copied as values into the closure. This makes it impossible for a closure to modify the variable in the parent scope. By prepending an & in front of the variable name in the use declaration, the variable is imported as a reference instead. In that case, changes to the variable inside the closure will affect the outside scope.”
Well this is actually not correct. Objects are passed by reference. If you need to use objects in closures you still have to use clone.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
 
class foo
{
        public $bar = 5;
}
 
 
$foo = new foo;
$lambda = function() use ($foo) {
        echo "Imported variable:".$foo->bar."\n";
        $foo->bar++;
};
$lambda();
echo "Original variable: ".$foo->bar."\n";
?>

result is:

Imported variable:5
Original variable: 6

4. closures combined with Reflection allow calling private method of a given object.

1
2
3
4
5
6
7
8
9
10
11
<?php
 
class Foo {
  private static function bar() { echo "I'm private method\n"; }
}
 
$class = new ReflectionClass('Foo');
$method = $class->getMethod('bar');
$closure = $method->getClosure();
$closure();
?>

Output is:

I’m private method

Share/Save/Bookmark

extending Zend_Db_Table to create NestedSet models

Sunday, July 13th, 2008

Last days I’ve been trying to customize Zend’s Framework. My goal is to create simpler models. Currently I’m making some db models that handle stuff around wide used designs. Such design is the Nested Set model. It is an adjacent list realized in SQL as a tree. You can find a good introduction to nested sets at mysql developer zone.

Now let’s create a basic table.

1
2
3
4
5
6
CREATE TABLE nested_table (
    `nested_table_id` int UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
    `nested_table_left` int UNSIGNED NOT NULL,
    `nested_table_right` int UNSIGNED NOT NULL,
    `nested_table_name` varchar(60) NOT NULL
);

And now let’s create a model that will work with this table.

1
2
3
4
5
6
7
8
class NestedTable extends Custom_Db_Nestedset {
    //basic mapping
    protected $_name     = "nested_table"; //table name
    protected $_primary  = "nested_table_id"; //primary key
    protected $_left     = "nested_table_left"; //left column
    protected $_right    = "nested_table_right"; //right column
    protected $_toString = "nested_table_name"; //used when retrieving tree. if not set primary key will be used
}

And that’s it. Now we have class that work as a nested set. Here is example controller.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class NestedController extends Zend_Controller_Action {
 
    public function testAction()
    {
        $table = new NestedTable;
        $root  = $table->createRoot(); //creating root node.
        $node1 = $table->insertAsFirstChildOf($root); //insert as first child of root category.
        $node2 = $table->insertAsNextSiblingOf($node1); //insert node next to given node. In our case next to node1.
        $node3 = $table->insertAsPrevSiblingOf($node2); //insert node before given node.
        $node4 = $table->insertAsLastChildOf($root); //insert node as last child of root category.
 
        $node2_1 = $table->insertAsFirstChildOf($node2); //go one level deeper.
        $table->deleteNode($node2);// delete node2 and his childs.
 
        $table->getTree(); //retrieve full tree (subtrees are still not supported)
    }
}

and here is the nested set class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
<?php
 
/**
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license
 *
 * @category  Custom
 * @package  Custom_Db
 * @subpackage Nestedset
 * @copyright Copyright (c) 2008 Ivan Iordanov <ivan@iordanov.net>
 * @version  $Id: Nestedset.php 2008-06-06
 * @see    http://dev.mysql.com/tech-resources/articles/hierarchical-data.html
 */
 
 
/**
 * @see Zend_Db_Table
 */
require_once 'Zend/Db/Table.php';
 
/**
 * Class for SQL Nested set interface.
 *
 * @category  Custom
 * @package  Custom_Db
 * @subpackage Nestedset
 * @copyright Copyright (c) 2008 Ivan Iordanov (http://dev.iordanov.net)
 */
 
class Custom_Db_Nestedset extends Zend_Db_Table
{
 
    /**
    * left column in nested table
    *
    * @var String
    */
    protected $_left;
 
    /**
    * right column in nested table
    *
    * @var String
    */
    protected $_right;
 
    /**
    * Column to be retrieved with getTree method
    * If not set primary key will be used.
    *
    * @var String
    */
    protected $_toString;
 
    /**
    * Additional data to be inserted.
    *
    * @var Array
    */
    private  $_insertData = array();
 
    /**
    * constructor
    */
    public function __construct()
    {
        parent::__construct();
        if (!$this->_toString)
        {
            $this->_toString = $this->_primary[1];
        }
    }
 
    /**
    * Additional data to be inserted
    *
    * @param array
    * @access public
    */
    public function setInsertData(array $data)
    {
        $this->_insertData = $data;
    }
 
    /**
    * Retrieve whole tree
    *
    * @access public
    * @return array
    */
    public function getTree()
    {
        //todo: add custom node
        $ret = $this->_db->query("
            SELECT COUNT(parent.{$this->_primary[1]}) - 1 as depth, node.{$this->_toString}
            FROM {$this->_name} AS node, {$this->_name} AS parent
            WHERE node.{$this->_left}
            BETWEEN parent.{$this->_left}
            AND parent.{$this->_right}
            GROUP BY node.{$this->_primary[1]}
            ORDER BY node.{$this->_left}
        ");
        return $ret->fetchAll();
 
    }
 
    /**
    * Insert node as first child
    *
    * @param int
    * @access public
    * @return int
    */
    public function insertAsFirstChildOf($id)
    {
        $row = $this->retrieveData($id);
 
        $right = (int) $row->{$this->_right};
        $left  = (int) $row->{$this->_left};
 
        $this->_db->query("UPDATE {$this->_name} SET {$this->_right} = {$this->_right} + 2 WHERE {$this->_right} > {$left}");
        $this->_db->query("UPDATE {$this->_name} SET {$this->_left} = {$this->_left} + 2 WHERE {$this->_left} > {$left}");
 
        $data = array(
            $this->_left => $left + 1,
            $this->_right => $left + 2
        );
        $this->_insertData = array_merge($this->_insertData, $data);
 
        return $this->insert($this->_insertData);
    }
 
    /**
    * Insert node as last child
    *
    * @param int
    * @access public
    * @return int
    */
    public function insertAsLastChildOf($id)
    {
        $row = $this->retrieveData($id);
 
        $right = (int) $row->{$this->_right};
        $left  = (int) $row->{$this->_left};
 
        $this->_db->query("UPDATE {$this->_name} SET {$this->_right} = {$this->_right} + 2 WHERE {$this->_right} >= {$right}");
        $this->_db->query("UPDATE {$this->_name} SET {$this->_left} = {$this->_left} + 2 WHERE {$this->_left} > {$right}");
 
 
        $data = array(
            $this->_left => $right,
            $this->_right => $right + 1,
        );
        $this->_insertData = array_merge($this->_insertData, $data);
 
        return $this->insert($this->_insertData);
 
    }
 
    /**
    * Insert node as next sibling of given node
    *
    * @param int
    * @access public
    * @return int
    * @throws Exception
    */
    public function insertAsNextSiblingOf($id)
    {
        $row = $this->retrieveData($id);
        $right = (int) $row->{$this->_right};
        $left  = (int) $row->{$this->_left};
 
        if ($left === 1) {
            throw new Exception("Root node can't have siblings");
        }
 
        $this->_db->query("UPDATE {$this->_name} SET {$this->_right} = {$this->_right} + 2 WHERE {$this->_right} > {$right}");
        $this->_db->query("UPDATE {$this->_name} SET {$this->_left} = {$this->_left} + 2 WHERE {$this->_left} > {$right}");
 
 
        $data = array(
            $this->_left => $right+1,
            $this->_right => $right + 2,
        );
 
        $this->_insertData = array_merge($this->_insertData, $data);
        return $this->insert($this->_insertData);
 
    }
 
    /**
    * Insert node as prev sibling of given node
    *
    * @param int
    * @access public
    * @return int
    * @throws Exception
    */
    public function insertAsPrevSiblingOf($id)
    {
        $row = $this->retrieveData($id);
        $right = (int) $row->{$this->_right};
        $left  = (int) $row->{$this->_left};
 
        if ($left === 1) {
            throw new Exception("Root node can't have siblings");
        }
 
 
        $this->_db->query("UPDATE {$this->_name} SET {$this->_right} = {$this->_right} + 2 WHERE {$this->_right} > {$left}");
        $this->_db->query("UPDATE {$this->_name} SET {$this->_left} = {$this->_left} + 2 WHERE {$this->_left} >= {$left}");
 
 
        $data = array(
            $this->_left => $left,
            $this->_right => $left + 1,
        );
 
        $this->_insertData = array_merge($this->_insertData, $data);
        return $this->insert($this->_insertData);
    }
 
    /**
    * Delete node with it's child(s) and return affected rows
    *
    * @param int
    * @access public
    * @return int
    */
 
    public function deleteNode($id)
    {
        $row = $this->retrieveData($id);
        $right = (int) $row->{$this->_right};
        $left  = (int) $row->{$this->_left};
        $width = $right - $left + 1;
        $res = $this->_db->query("DELETE FROM {$this->_name} WHERE {$this->_left} BETWEEN {$left} AND {$right}");
 
        $this->_db->query("UPDATE {$this->_name} SET {$this->_right} = {$this->_right} - {$width} WHERE {$this->_right} > {$right}");
        $this->_db->query("UPDATE {$this->_name} SET {$this->_left} = {$this->_left} - {$width} WHERE {$this->_left} > {$right}");
 
        return $res->rowCount();
    }
 
    /**
    * Insert root node
    *
    * @access public
    * @return int
    */
    public function createRoot()
    {
 
        $data = array(
            $this->_left => 1,
            $this->_right => 2
        );
        $this->_insertData = array_merge($this->_insertData, $data);
        return $this->insert($this->_insertData);
    }
 
    /**
    * Insert node
    *
    * @param int
    * @access private
    * @return Zend_Db_Row
    */
    private function retrieveData($id)
    {
        $select = $this->select()->where($this->_primary[1] .' = ?', $id);
        return $this->fetchRow($select);
    }
}

Share/Save/Bookmark

php magic __set and __get

Sunday, June 15th, 2008

Last days I have a problem with php magic functions and specially with overloading. Here is an example code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Container
{
private $_data;
 
public function __set($key, $value)
{
$this->_data[$key] = $value;
}
 
public function __get($key)
{
return $this->_data[$key];
}
 
public function __isset($key)
{
return isset($this->_data[$key]);
}
 
public function __unset($key)
{
if ($this->__isset($this->_data[$key]))
unset($this->_data[$key]);
}
}

when you try to use this code with arrays for example:

1
2
3
4
$c = new Container;
$c->arr = array();
$c->arr['test'] = 'test';
var_dump($c->arr['test']);

it rise a notice: Indirect modification of overloaded property Container::$arr has no effect.
and $c1->arr[’test’] is null instead of test.

a work around is first to get this array into a temporary variable, make changes you need and then set it again.

1
2
3
4
5
6
$c = new Container;
$c->arr = array();
$tmpArr = $c->arr;
$tmpArr['test'] = 'test';
$c->arr = $tmpArr;
var_dump($c->arr['test']);

this one works as expected. Actually php interpreter seems to do same thing. Trying to get value if exists and change it. Problem is that when it takes this value there is no place to store it and rise this error. Trying to modify property in not existing array. Solution is to make this array available. Returning values by reference seems to work.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Container
{
private $_data;
 
public function __set($key, $value)
{
$this->_data[$key] = $value;
}
 
public function &amp;__get($key)
{
return $this->_data[$key];
}
 
public function __isset($key)
{
return isset($this->_data[$key]);
}
 
public function __unset($key)
{
if ($this->__isset($this->_data[$key]))
unset($this->_data[$key]);
}
}

Now this code works as expected:

1
2
3
4
$c = new Container;
$c->arr = array();
$c->arr['test'] = 'test';
var_dump($c->arr['test']);

Share/Save/Bookmark

SplFastArray

Tuesday, June 10th, 2008

Few days ago I’ve downloaded last version of php5.3 from snaps.php.net and I’ve made some tests with SplFastArray. Results are very strange for me:

here is result of last test I’ve made few minutes ago.

$ php fastarray.php
——————————————————————-
marker time index ex time perct
——————————————————————-
Start 1213124261.36967200 - 0.00%
——————————————————————-
simple array foreach 1213124261.57657300 0.20690083503723 13.10%
——————————————————————-
fast array foreach 1213124261.89191900 0.31534600257874 19.96%
——————————————————————-
simple array for 1213124262.36776400 0.47584509849548 30.12%
——————————————————————-
fast array for 1213124262.94954500 0.58178091049194 36.82%
——————————————————————-
Stop 1213124262.94959900 5.4121017456055E-5 0.00%
——————————————————————-
total - 1.5799269676208100.00%
——————————————————————-

and here is the source code I use for test:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php
$iterations = 1000000;
$a = new SplFastArray($iterations);
$b = array();
for ($i=0;$i<$iterations;$i++) {
$a[$i] = $i;
$b[$i] = $i;
}
require_once 'Benchmark/Timer.php';
$timer = new Benchmark_Timer();
$timer->start();
 
 
foreach($b as $k=>$v) {
}
$timer->setMarker('simple array foreach');
 
foreach($a as $k=>$v) {
}
$timer->setMarker('fast array foreach');
 
 
for ($i=0;$i<count($b);$i++) {
}
$timer->setMarker('simple array for');
 
 
for ($i=0;$i<$a->getSize();$i++) {
}
 
$timer->setMarker('fast array for');
 
$timer->stop();
echo $timer->display();
 
?>

Share/Save/Bookmark