Writing not english characters in swf applications under linux is a well known problem. These days I had some spare time to inspect it and try to fix it. So far I’ve a solution. I don’t know if this one is the best one or if there is a better way but I’m not an expert in flash/flex development. The idea is to replace characters that came from flash player by their original characters using their code. eg. “193” with “а” (а is in Cyrillic) and so on. I know this will fix the problem for specified language (Bulgarian in this case), but will not fix it in general, so I decided to make something like a character map and load replacements dynamically. I use JSON for characters map. Here is an example file:

{"193":"а",
"194":"б",
"215":"в",
"199":"г",
"196":"д",
"197":"е",
"214":"ж",
"218":"з",
"201":"и",
"202":"й",
"203":"к",
"204":"л",
"205":"м",
"206":"н",
"207":"о",
"208":"п",
"210":"р",
"211":"с",
"212":"т",
"213":"у",
"198":"ф",
"200":"х",
"195":"ц",
"222":"ч",
"219":"ш",
"221":"щ",
"216":"ь",
"223":"ъ",
"192":"ю",
"209":"я"
}

and the code that reads this file and replaces characters:

package net.iordanov {
    public class Cyrillic {
        import com.adobe.serialization.json.JSON;
        import mx.rpc.http.HTTPService;
        import mx.rpc.events.ResultEvent;
        import mx.rpc.events.FaultEvent;
        import mx.controls.Alert;
 
        private static var data:Object; 
 
        public static function loadCharMap(file:String):void
        {
            var service:HTTPService = new HTTPService();
            service.addEventListener("result", processJson);
            service.addEventListener("fault", failure);
            service.url = file;
            service.send();
        }
 
        public static function processJson(event:ResultEvent):void
        {
            data = JSON.decode(event.result.toString());
        }
 
        public static function failure(event:FaultEvent):void
        {
            Alert.show(event.toString());
        }
        public static function correctLinux(str:String):String
        {
            var char:String;
            var charCode:Number;
            var ret:String = "";
 
            for (var i:int = 0; i<str.length; i++) {
                char = str.charAt(i);
                charCode = str.charCodeAt(i);
                if (data[charCode]) {
                    ret += data[charCode];
                } else {
                    ret +=char;
                }
            }
            return ret;
        }
    }
}

An example mxml file witch tests the code above:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" layout="absolute" creationComplete="loadLang()">
 
    <mx:Script>
        <![CDATA[
        import net.iordanov.Cyrillic;
        import mx.rpc.events.ResultEvent;
        import mx.events.StyleEvent;
 
        private function fixLinux(event:Event):void {
            text1.text = Cyrillic.correctLinux(text1.text);
        }
 
        private function loadLang():void
        {
            Cyrillic.loadCharMap("http://dev.iordanov.net/flex/lang/bg.js");
        }
        ]]>
    </mx:Script>
    <mx:TextInput id="text1" width="100" change="fixLinux(event)"/>
</mx:Application>

and simple html to load this swf application.

<object width="550" height="400">
  <param name="movie" value="somefilename.swf" />
  <param name="wmode" value="transparent" /> 
  <embed src="cyrillic.swf" width="550" height="400" wmode="transparent"></embed>
</object>

Note that wmode must be set to transparent. In other case (I don’t know why, as I say I’m not an expert in flash/flex development) user must hold Ctrl key pressed while typing.

Share/Save/Bookmark

Posted in flex | No Comments »

xdebug configuration

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

Posted in php | No Comments »

Extjs rating extension

September 6th, 2008

Lately i was playing with extjs and i created an extension for rating. The extension itself has 2 components. The first one is the visual one. It’s a kind of slider with some modifications. It’s usage is the same as a slider’s. The second component is a form field that holds the value of the first one in a hidden input flield. The input element takes an extra parameter called starConfig and this parameter is passed to the constructor of the first component.

The number of stars is calculated dynamically and it’s equal to maxValue/increment
for example

1
2
3
4
5
{
    minValue: 0,
    maxValue: 100
    increment: 10	
}

This will give us 10 stars and each star increases the value of the hidden field by 10. By default increment = 1 and maxValue = 5

you can see demo here and download the extension here

Share/Save/Bookmark

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

Posted in php | 4 Comments »

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

Posted in database, php | 17 Comments »

php magic __set and __get

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

Posted in php | 1 Comment »

SplFastArray

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

Posted in php | 2 Comments »