Nisan 2010

You are currently browsing the monthly archive for Nisan 2010.

Ubuntu 10.04 dün yayınlandı ve Karmic’ten altı aydır az çekmemiş olan ben, henüz web sitesinde resmen duyurulmamışken repoda kararlı sürümü görür görmez yükseltme işlemine başladım.

“Vuzuh Vaşak” demek suretiyle herhalde 16.10 sürümü civarına yükseltme yapmış gibi görünmüş oldum.

  • Pencerelerde “kapat”, “küçült”, “panele indir” ikonları sola kaymış. Bana güzel göründü ancak hemen belirteyim: alıştığınız sağda görünen duruma dönmek mümkün ve şu belgede anlatılmış.
  • Gözle görülür şekilde daha hızlı boot ediyor.
  • Karmic’teki bugların hemen hepsi temizlenmiş.
  • Skype sorunsuz çalışıyor.
  • PHP 5.3.2 ile PHP 5.3.x’li günler başlamış.
  • Python 2.6.5
  • gcc 4.4
  • Multimedia işlerindeki sıkıntılar tarih olmuş.
  • Öntanımlı arama motoru Google yerine Yahoo! olmuş, iyi olmamış, ancak yeni kurulum değil yükseltme yapıyorsanız bunu görmüyorsunuz.
  • Firefox 3.6.3
  • Öntanımlı masaüstü arka planı morarmış.
  • Tüm donanımlar sorunsuz tanınıyor.
  • Çekirdek 2.6.32-21
  • Gwibber entegrasyonuyla sosyal medya desteği iyi olmuş.

Bir günlük deneyimin ardından ön sonuçlar:

  • Karmic kullanıyorsanız saniye kaybetmeden yükseltin.
  • Ubuntu Karmic’le kaybettiği prestiji tekrar kazanacak gibi görünüyor. Kullanışlı bir Gnu/Linux dağıtımı arıyorsanız Ubuntu 10.04 iyi bir seçim olacaktır.
Paylaş:
  • Print
  • FriendFeed
  • Twitter
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • DZone
  • LinkedIn
  • MySpace
  • Ping.fm
  • Reddit
  • StumbleUpon
  • Technorati

Etiketler: , ,

İki günlük bir aradan sonra serinin yeni bölümüyle(!) karşınızdayım.
Bu yazının ana konusu pcntl destekli, daemon olarak çalışan Azra Web Server. Değişiklik olsun diye önce benchmarklara bakalım. Aşağıdaki tabloda açıkça görülebildiği gibi Azra ağır yük altında bile Apache’den çok iyi, Nginx’ten biraz daha iyi.

X Ortalama (ms) real user sys
Apache 0.00938119351864 0m2.247s 0m0.068s 0m0.028s
Nginx 0,00461062550545 0m4.443s 0m0.152s 0m0.036s
Azra 0,00429985105991 0m1.992s 0m0.112s 0m0.024s

Benchmark yöntemi:
Apache, Nginx ve Azra kullanarak aşağıdaki kodu 400 kere çağırıyoruz.
test.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

$start = microtime(1);

for($n = 0; $n <= 10000; $n++)
{
    echo $n;
}
$end = microtime(1);
$elapsed = $end - $start;
echo "<br/>vakit(ms): $elapsed";
$fileHandle = fopen('serverBenchmark.txt' , 'a');
fputs($fileHandle , $elapsed . "\n");
fclose($fileHandle);

ve test için kullandığımız python kodu (benchmark.py):

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
import urllib2
from threading import Thread

class testSuite:
    def testConnection(self , url , thread = 0):
        page = urllib2.urlopen(url)
        data = page.read()
        page.close()
        print("Thread:%d" % thread)
           
    def simpleConnection(self , url , tourNum):
        for i in range(tourNum):
            self.testConnection(url)
       
    def threadedConnection(self , url , tourNum , threadNum):
        for i in range(tourNum):
            myThread = i % threadNum
            t = Thread(target=self.testConnection , args=(url , myThread))
            t.start()

myTestSuite = testSuite()

myTestSuite.threadedConnection('http://localhost:8114/test.php' , 400 , 10) #azra
#myTestSuite.threadedConnection('http://localhost/webserver/test.php' , 400 , 10) #apache
#myTestSuite.threadedConnection('http://localhost/test.php' , 400 , 10) #nginx

Gelişmeler:
Artık Azra, Ubuntu altında daemon olarak çalışabiliyor.
/etc/init.d/azra

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
#! /bin/sh
### BEGIN INIT INFO
# Provides:          azra
# Required-Start:    $all
# Required-Stop:     $all
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Start and stop azra web server
# Description:       Start and stop azra web server
### END INIT INFO

# Author: Can Ince <[EMAIL PROTECTED]>

# Do NOT "set -e"

PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="AZRA WEB SERVER"
NAME=Azra
DAEMON=/var/www/webserver/azra.php
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
PHP_CONFIG_FILE=/etc/php5/cgi/php.ini

# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0

# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME

# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh

# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions

# If the daemon is not enabled, give the user a warning and then exit,
# unless we are stopping the daemon
if [ "$START" != "yes" -a "$1" != "stop" ]; then
        log_warning_msg "To enable $NAME, edit /etc/default/$NAME and set START=yes"
        exit 0
fi

# Process configuration
export PHP_FCGI_CHILDREN PHP_FCGI_MAX_REQUESTS
DAEMON_ARGS="-q -b $FCGI_HOST:$FCGI_PORT -c $PHP_CONFIG_FILE"

do_start()
{
        # Return
        #   0 if daemon has been started
        #   1 if daemon was already running
        #   2 if daemon could not be started
        start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
                || return 1
        start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON \
                --background --make-pidfile --chuid $EXEC_AS_USER www-data $DAEMON -- \
                $DAEMON_ARGS \
                || return 2
}

do_stop()
{
        # Return
        #   0 if daemon has been stopped
        #   1 if daemon was already stopped
        #   2 if daemon could not be stopped
        #   other if a failure occurred
    killall azra.php
        start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE > /dev/null # --name $DAEMON
        RETVAL="$?"
        [ "$RETVAL" = 2 ] && return 2
        # Wait for children to finish too if this is a daemon that forks
        # and if the daemon is only ever run from this initscript.
        # If the above conditions are not satisfied then add some other code
        # that waits for the process to drop all resources that could be
        # needed by services started subsequently.  A last resort is to
        # sleep for some time.
        start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
        [ "$?" = 2 ] && return 2
        # Many daemons don't delete their pidfiles when they exit.
        rm -f $PIDFILE
        return "$RETVAL"
}
case "$1" in
  start)
        [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
        do_start
        case "$?" in
                0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
                2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
        esac
        ;;
  stop)
        [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
        do_stop
        case "$?" in
                0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
                2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
        esac
        ;;
  restart|force-reload)
        log_daemon_msg "Restarting $DESC" "$NAME"
        do_stop
        case "$?" in
          0|1)
                do_start
                case "$?" in
                        0) log_end_msg 0 ;;
                        1) log_end_msg 1 ;; # Old process is still running
                        *) log_end_msg 1 ;; # Failed to start
                esac
                ;;
          *)
                # Failed to stop
                log_end_msg 1
                ;;
        esac
        ;;
  *)
        echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
        exit 3
        ;;
esac

… ve /etc/defaults/Azra:

1
2
START=yes
VERBOSE=yes

Azra multi-process olarak çalışabiliyor.
Ana kod (azra.php)

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
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
#! /usr/bin/php-cgi
<?php
/**
 * Azra Web Server
 *
 * Web Server coded in pure PHP
 *
 * @package     Azra Web Server
 * @author      Can Ince
 * @copyright           Copyright (c) 2010, Logikit / Can Ince.
 * @version             0.0.2
 * @license     http://www.opensource.org/licenses/mit-license.php
 * @link        http://can.logikit.net
 */


set_time_limit(0);

interface server
{
    public function setWebRoot($path);
    public function setPort($port);
    public function serve();
}

final class webserver implements server
{
    public $socket;
    private $_port , $_webroot , $_queryString , $_webRootWithoutSlash, $_input , $_file , $_verbose , $_remoteAddr , $_remotePort;
    public function __construct()
    {
        $this->_verbose = 0;
        $_SERVER = array();
        $_COOKIE = array();
    }
   
    public function setPort($port)
    {
        $this->_port = $port;
    }
   
    public function verbose()
    {
        $this->_verbose = 1;
    }
   
    public function setWebRoot($path)
    {
        $this->_webroot = $path . '/';
        $this->_webRootWithoutSlash = $path;
    }
   
    private function _mimeHandler($input)
    {
            $fileinfo = pathinfo($input);
            switch ($fileinfo['extension'])
            {
                case 'png':
                    $mime = "text/png";  
                    break;
               
                case 'jpg':
                case 'jpeg':
                    $mime = "text/jpeg";  
                    break;
               
                case 'gif':
                    $mime = "text/gif";  
                    break;
               
                case 'css':
                    $mime = "text/css";  
                    break;
                   
                case 'swf':
                    $mime = "application/x-shockwave-flash";
                    break;
             
                default:
                    $mime = "text/html; charset=UTF-8";
            }
        return $mime;
    }
   
    private function _handleCode($contents)
    {
         //eval() edilecek kodun başında <?php ya da <? varsa çakar. halledelim.
        $contents = trim($contents);
        if(substr($contents , 0 , 5) == '<?php')
        {
            $contents = substr($contents , 5);
        }
        elseif(substr($contents , 0 , 2) == '<?')
        {
            $contents = substr($contents , 2);
        }
       
        return $contents;
    }
   
    private function _parseGet($str)
    {
        $_GET = array();
        $array = explode('&' , $str);
        foreach($array as $requestItem)
        {
            $itemArray = explode('=' , $requestItem);
            if(!empty($itemArray[0])) $_GET[$itemArray[0]] = $itemArray[1];
        }
        unset($_GET['hyperTextServer_php']); //bunu $_GET'e enjekte ediyor. neden, bilmiyorum...
    }
   
    private function _parsePost($str)
    {
        $_POST = array();
        $array = explode('&' , $str);
        foreach($array as $requestItem)
        {
            $itemArray = explode('=' , $requestItem);
            if(!empty($itemArray[0])) $_POST[$itemArray[0]] = $itemArray[1];
        }
    }
   
    private function _parseCookie($str)
    {
       
        $array = explode('; ' , $str);
        foreach($array as $requestItem)
        {
            $itemArray = explode('=' , $requestItem);
            if(!empty($itemArray[0])) $_COOKIE[$itemArray[0]] = $itemArray[1];
        }
    }
   
    private function _parseRequest($str)
    {
       
        $lines = explode("\n" , $str);
        $rawData = explode(' ' , $lines[0]);
        $_SERVER['REQUEST_METHOD'] = $rawData[0];
        if($_SERVER['REQUEST_METHOD'] == 'POST') $this->_parsePost(end($lines));
        $_SERVER['PHP_SELF'] = $this->_webRootWithoutSlash . $rawData[1];
        $_SERVER['HTTP_HOST'] = substr($lines[1] , 6);
        $_SERVER['HTTP_USER_AGENT'] = substr($lines[2] , 12);
        $_SERVER['HTTP_ACCEPT'] = substr($lines[3] , 8);
        $_SERVER['HTTP_ACCEPT_LANGUAGE'] = substr($lines[4] , 17);
        $_SERVER['HTTP_ACCEPT_ENCODING'] = substr($lines[5] , 17);
        $_SERVER['HTTP_ACCEPT_CHARSET'] = substr($lines[6] , 16);
        $_SERVER['HTTP_KEEP_ALIVE'] = substr($lines[7] , 12);
        $_SERVER['HTTP_CONNECTION'] = substr($lines[8] , 12);
        if(strstr($lines[9] , 'Referer'))
        {
            $_SERVER['HTTP_REFERER'] = substr($lines[9] , 9);
            $_SERVER['HTTP_COOKIE'] = substr($lines[10] , 8);
        }
        else $_SERVER['HTTP_COOKIE'] = substr($lines[9] , 8);
       
        if(!empty($_SERVER['HTTP_COOKIE'])) $this->_parseCookie($_SERVER['HTTP_COOKIE']);
       
        $_SERVER['SERVER_SIGNATURE'] = ' <address>Azra/0.0.1 Server at ' . $_SERVER['HTTP_HOST'] . ' Port ' . $this->_port . '</address>';
        $_SERVER['SERVER_SOFTWARE'] = 'Azra/0.0.1';
        $_SERVER['DOCUMENT_ROOT'] = $this->_webroot;
        $_SERVER['SCRIPT_FILENAME'] = $this->_webRootWithoutSlash . str_replace('./' , '/' , $this->_file);
        $_SERVER['SCRIPT_NAME'] = $this->_file;
        $_SERVER['REQUEST_TIME'] = time();
        $_SERVER['REMOTE_ADDR'] = $this->_remoteAddr;
        $_SERVER['REMOTE_PORT'] = $this->_remotePort;
        if(!empty($this->_queryString)) $_SERVER['QUERY_STRING'] = $this->_queryString;
    }
   
    private function _handle404()
    {
        return "<title>UHU 404</title><h2>UHU 404</h2>Iın-ıın-ıın-ıın-cav-cav-cav-cav-oooooo - oooooo - zört - zört - zört -zört (araba alarmı efekti) Çok afedersin müdürüm ama yok öyle bir dosya.";
    }
   
    public function openSocket()
    {
        $this->socket = @socket_create_listen($this->_port);
        if (!$this->socket)
        {
            echo "Soket olamiyor!\n";
            exit;
        }
        $this->_loadConfigHandler();
    }
   
    private function _loadCOnfigHandler()
    {
        $directoryConfigCandidate = $this->_webroot . '/.ConfigHandler.php';
       
        if ($directoryConfigCandidate && is_readable($directoryConfigCandidate))
        {
            require ($directoryConfigCandidate);
        }      
    }
   
    public function serve()
    {

        while (1) //daire-i ilanihaye!
        {
            $client = socket_accept($this->socket);
            socket_getpeername($client , $remoteAddr , $remotePort);
            $this->_remoteAddr = $remoteAddr;
            $this->_remotePort = $remotePort;
            $input = $this->_input = trim(socket_read ($client, 4096));
           
            if($this->_verbose == 1) echo $input;
           
            $input = explode(" " , $input);
            $input = $input[1];

            if(class_exists('ConfigHandler'))
            {
                $configHandler = new ConfigHandler($input , $this->_webroot);
 
                //konvansiyonumuza göre rewrite işi $configHandler'ın url özelliğinde bulunmalı
                if($configHandler->url != NULL) $input = realpath($configHandler->url);
            }
             
            $mime = $this->_mimeHandler($input);
               
            if ($input == "" || $input == '/')
            {
                $input = "/index.html";
            }
            $input = ".$input";
            $input = str_replace('//' , '/' , $input);
            $inputArray = explode('?' , $input);
            $this->_file = $inputArray[0];
           
            $input = $this->_webroot . $this->_file;
            if (file_exists($input) && is_readable($input))
            {
                if($this->_verbose == 1) echo "Dosya: $input\n";
                 
                $contents = file_get_contents($input);
                if(strstr($input , '.php'))
                {
                    if(!empty($inputArray[1])) $this->_queryString = $inputArray[1];
                   
                    $this->_parseGet($this->_queryString);
                    $this->_parseRequest($this->_input);
                   
                    $_REQUEST = array_merge($_GET , $_POST , $_COOKIE);
                   
                    $contents = $this->_handleCode($contents);
                   
                    //PHP çalıştıktan sonra sonucu echo etmesini değil, $contents stringine atmasını istiyoruz.
                    ob_start();
                    eval($contents);
                    //gidip $contents'in üstüne yazmak doğru alışkanlık değil ama yeni değişken register etmek istemiyorum
                    $contents = ob_get_contents();
                    ob_end_clean();
                }
                $output = "HTTP/1.0 200 OK\r\nServer: HyperTextServer\r\nConnection: close\r\nContent-Type: $mime\r\n\r\n$contents";
            }
            else
            {
                $contents = $this->_handle404();
                $output = "HTTP/1.0 404 OBJECT NOT FOUND\r\nServer: HyperTextServer\r\nConnection: close\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n$contents";
            }
            socket_write($client, $output);
            socket_close ($client);
        }
        socket_close ($this->socket);
    }
   
    public function start()
    {
        $pids = array();

        $this->openSocket();
        for($i = 0; $i < 10; $i++)
        {
            $pids[$i] = pcntl_fork();
           
                if(!$pids[$i])
                {
                    // child process
                    $this->serve();
                    exit();
                }
        }
       
        for($i = 0; $i < 10; $i++)
        {
            pcntl_waitpid($pids[$i], $status, WUNTRACED);
        }
    }
}

$server = new webserver();
$server->setWebRoot('/var/www/act');
$server->verbose(); //ayrıntıları stdout'a echo et.
$server->setPort(8115);
$server->start();

Ara-sonuçlar:
- Bir kaç sorun hala devam ediyor.
– Session’lar düzgün halledilemiyor.
– include’larda sorun yaşanıyor zira dirname(__FILE__) Azra’nın çalıştığı dizini döndürüyor.
- log işine çözüm bulmak lazım.
– header() kullanarak 302 yollanırken path düzgün alınamıyor.
Bu sorunlar da halledildikten sonra şaka olarak başlayan denememiz oldukça ilginç hale gelecek ve bu benchmarkları korumayı başarabilirsem 0.0.3 sürümüne Launchpad yolları görünüyor.

Paylaş:
  • Print
  • FriendFeed
  • Twitter
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • DZone
  • LinkedIn
  • MySpace
  • Ping.fm
  • Reddit
  • StumbleUpon
  • Technorati

Etiketler: , ,

Ubuntu’da PHP-PCNTL Desteği

Ubuntu üstünde PHP kullanıyoruz. Günün birince pcntl desteğine ihtiyacımız oluyor ve tüm PHP’yi yeniden derlemek istemiyoruz. Nasıl yapıyoruz? Şu şekilde:
- Önce bir dizin oluşturuyoruz ve o dizine geçiyoruz. Nerede oluşturduğumuz önemli değil

1
2
mkdir php
cd php

Sistemimizde PHP-dev paketi yoksa kuruyoruz:

1
sudo apt-get install php5-dev

- Daha sonra php source paketini indiriyoruz:

1
apt-get source php5

..ve pcntl dizinine geçiyoruz:

1
cd php5-[php sürümü]/ext/pcntl

Akabinde bildiğimiz PHP modül derleme işi:

1
2
3
phpize
./configure
make

Son olarak derlediğimiz paylaşılmış nesneyi ilgili yere kopyalıyoruz

1
2
cp modules/pcntl.so /usr/lib/php5/phpSürümTarihi/
echo "extension=pcntl.so" > /etc/php5/conf.d/pcntl.ini

Sunucuyu yeniden başlatıp phpinfo() çektiğimizde pcntl’i görüp mutlu oluyoruz.

Tahmin edebileceğiniz gibi, gelecek bölüm: “Bir Web Sunucusu Kodlama Denemesi – 4 (Pcntl destekli Azra)
:-)

Paylaş:
  • Print
  • FriendFeed
  • Twitter
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • DZone
  • LinkedIn
  • MySpace
  • Ping.fm
  • Reddit
  • StumbleUpon
  • Technorati

Etiketler: ,

Heyecan devam ediyor:-)

Bu bölümde:

- Bir önceki sürümde eksik kalan $_SERVER['REMOTE_ADDR'] ve $_SERVER['REMOTE_PORT'] halloldu.

- .ConfigHandler.php isimli dosyayı kullanarak Apache’de .htaccess ile çözülen işler saf PHP ile çözülür hale geldi. Bir rewrite örneğini yazının devamında bulabilirsiniz.

- Benchmark sevenler: Bu bölümde istemediğiniz kadar benchmark var;-) Ayrıca, benchmark işleri küçük bir Python kodu yardımıyla otomatik hale getirildi.

Bir önceki yazımda bir web sunucusunda bulunması gereken temel özelliklerin çoğuna sahip, PHP çalıştıran  bir web sunucusunu, yine PHP kullanarak hayata geçirebilmiş ve adını da Azra Erhat’ın anısına “Azra” koymuştum. Yukarıda gördüğünüz logo için ise, bir bakışta anlayabileceğiniz gibi hiç uğraşmadım, çünkü uğraşsam da daha iyisini yapamayacağımı biliyorum! Hayırsever bir arkadaş düzgün bir logo yapabilirse seve seve kullanırım. Şimdiki logo basitçe Göktürk alfabesi kullanarak “Azra” stringinden ibaret.

Bu sürümde yeni ne var?

- Artık supergloballer ($_POST, $_GET, $_REQUEST, $_COOKIE, $_SERVER) tam olarak destekleniyor.

- .ConfigHandler.php diye bir dosya icat ettim. Azra bu dosya var mı diye bakıyor, varsa içeri alıyor ve ConfigHandler sınıfını çağırıyor.  Aşağıdaki dosyada da görebileceğiniz gibi, örneğin “url rewrite” işi çocuk oyuncağı.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class ConfigHandler
{
    public $url = NULL;
    public function __construct($input)
    {
        $this->azraRewrite($input);
    }
   
    public function azraRewrite($input)
    {
        $candidate = getcwd() . $input;
        if(!is_file($candidate) && !is_dir($candidate)) $this->url = '/router.php?' . $input;
        else $this->url = $input;
    }
}

- Web sunucumuzun yeni sürümü ise şu şekilde:

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
<?php
/**
 * Azra Web Server
 *
 * Web Server coded in pure PHP
 *
 * @package     Azra Web Server
 * @author      Can Ince
 * @copyright           Copyright (c) 2010, Logikit / Can Ince.
 * @version             0.0.2
 * @license     http://www.opensource.org/licenses/mit-license.php
 * @link        http://can.logikit.net
 */


set_time_limit(0);

interface server
{
    public function setWebRoot($path);
    public function setPort($port);
    public function serve();
}

final class webserver implements server
{
    private $_port , $_webroot , $_queryString , $_webRootWithoutSlash, $_input , $_file , $_verbose , $_remoteAddr , $_remotePort;
    public function __construct()
    {
        $this->_verbose = 0;
    }
   
    public function setPort($port)
    {
        $this->_port = $port;
    }
   
    public function verbose()
    {
        $this->_verbose = 1;
    }
   
    public function setWebRoot($path)
    {
        $this->_webroot = $path . '/';
        $this->_webRootWithoutSlash = $path;
    }
   
    private function _mimeHandler($input)
    {
            $fileinfo = pathinfo($input);
            switch ($fileinfo['extension'])
            {
                case 'png':
                    $mime = "text/png";  
                    break;
               
                case 'jpg':
                case 'jpeg':
                    $mime = "text/jpeg";  
                    break;
               
                case 'gif':
                    $mime = "text/gif";  
                    break;
               
                case 'css':
                    $mime = "text/css";  
                    break;
                   
                case 'swf':
                    $mime = "application/x-shockwave-flash";
                    break;
             
                default:
                    $mime = "text/html; charset=UTF-8";
            }
        return $mime;
    }
   
    private function _handleCode($contents)
    {
         //eval() edilecek kodun başında <?php ya da <? varsa çakar. halledelim.
        $contents = trim($contents);
        if(substr($contents , 0 , 5) == '<?php')
        {
            $contents = substr($contents , 5);
        }
        elseif(substr($contents , 0 , 2) == '<?')
        {
            $contents = substr($contents , 2);
        }
       
        return $contents;
    }
   
    private function _parseGet($str)
    {
        $_GET = array();
        $array = explode('&' , $str);
        foreach($array as $requestItem)
        {
            $itemArray = explode('=' , $requestItem);
            if(!empty($itemArray[0])) $_GET[$itemArray[0]] = $itemArray[1];
        }
        unset($_GET['hyperTextServer_php']); //bunu $_GET'e enjekte ediyor. neden, bilmiyorum...
    }
   
    private function _parsePost($str)
    {
        $_POST = array();
        $array = explode('&' , $str);
        foreach($array as $requestItem)
        {
            $itemArray = explode('=' , $requestItem);
            if(!empty($itemArray[0])) $_POST[$itemArray[0]] = $itemArray[1];
        }
    }
   
    private function _parseCookie($str)
    {
        $_COOKIE = array();
        $array = explode('; ' , $str);
        foreach($array as $requestItem)
        {
            $itemArray = explode('=' , $requestItem);
            if(!empty($itemArray[0])) $_COOKIE[$itemArray[0]] = $itemArray[1];
        }
    }
   
    private function _parseRequest($str)
    {
        $_SERVER = array();
        $lines = explode("\n" , $str);
        $rawData = explode(' ' , $lines[0]);
        $_SERVER['REQUEST_METHOD'] = $rawData[0];
        if($_SERVER['REQUEST_METHOD'] == 'POST') $this->_parsePost(end($lines));
        $_SERVER['PHP_SELF'] = $this->_webRootWithoutSlash . $rawData[1];
        $_SERVER['HTTP_HOST'] = substr($lines[1] , 6);
        $_SERVER['HTTP_USER_AGENT'] = substr($lines[2] , 12);
        $_SERVER['HTTP_ACCEPT'] = substr($lines[3] , 8);
        $_SERVER['HTTP_ACCEPT_LANGUAGE'] = substr($lines[4] , 17);
        $_SERVER['HTTP_ACCEPT_ENCODING'] = substr($lines[5] , 17);
        $_SERVER['HTTP_ACCEPT_CHARSET'] = substr($lines[6] , 16);
        $_SERVER['HTTP_KEEP_ALIVE'] = substr($lines[7] , 12);
        $_SERVER['HTTP_CONNECTION'] = substr($lines[8] , 12);
        if(strstr($lines[9] , 'Referer'))
        {
            $_SERVER['HTTP_REFERER'] = substr($lines[9] , 9);
            $_SERVER['HTTP_COOKIE'] = substr($lines[10] , 8);
        }
        else $_SERVER['HTTP_COOKIE'] = substr($lines[9] , 8);
       
        if(!empty($_SERVER['HTTP_COOKIE'])) $this->_parseCookie($_SERVER['HTTP_COOKIE']);
       
        $_SERVER['SERVER_SIGNATURE'] = ' <address>Azra/0.0.1 Server at ' . $_SERVER['HTTP_HOST'] . ' Port ' . $this->_port . '</address>';
        $_SERVER['SERVER_SOFTWARE'] = 'Azra/0.0.1';
        $_SERVER['DOCUMENT_ROOT'] = $this->_webroot;
        $_SERVER['SCRIPT_FILENAME'] = $this->_webRootWithoutSlash . str_replace('./' , '/' , $this->_file);
        $_SERVER['REQUEST_TIME'] = time();
        $_SERVER['REMOTE_ADDR'] = $this->_remoteAddr;
        $_SERVER['REMOTE_PORT'] = $this->_remotePort;
        if(!empty($this->_queryString)) $_SERVER['QUERY_STRING'] = $this->_queryString;
    }
   
    private function _handle404()
    {
        return "<title>UHU 404</title><h2>UHU 404</h2>Iın-ıın-ıın-ıın-cav-cav-cav-cav-oooooo - oooooo - zört - zört - zört -zört (araba alarmı efekti) Çok afedersin müdürüm ama yok öyle bir dosya.";
    }
   
    public function serve()
    {
        $socket = @socket_create_listen($this->_port);
        if (!$socket)
        {
            echo "Soket olamiyor!\n";
            exit;
        }
   
        while (1) //daire-i ilanihaye!
        {
            $client = socket_accept($socket);
            socket_getpeername($client , $remoteAddr , $remotePort);
            $this->_remoteAddr = $remoteAddr;
            $this->_remotePort = $remotePort;
            $input = $this->_input = trim(socket_read ($client, 4096));
           
            if($this->_verbose == 1) echo $input;
           

           

               
            $input = explode(" " , $input);
            $input = $input[1];
           
            $dirName = dirname(__FILE__);
     
            $directoryConfigCandidate = $dirName . '/.ConfigHandler.php';
           
            if ($directoryConfigCandidate && is_readable($directoryConfigCandidate))
            {
                require_once ($directoryConfigCandidate);
                if(class_exists('ConfigHandler'))
                {
                    $configHandler = new ConfigHandler($input);
                    //konvansiyonumuza göre rewrite işi $configHandler'ın url özelliğinde bulunmalı
                    if($configHandler->url != NULL) $input = $configHandler->url;
                }
            }
     
            $mime = $this->_mimeHandler($input);
               
            if ($input == "" || $input == '/')
            {
                $input = "/index.html";
            }
            $input = ".$input";
            $inputArray = explode('?' , $input);
            $this->_file = $inputArray[0];
           
            $input = $this->_webroot . $this->_file;
            if (file_exists($input) && is_readable($input))
            {
                if($this->_verbose == 1) echo "Dosya: $input\n";
                 
                $contents = file_get_contents($input);
                if(strstr($input , '.php'))
                {
                    $this->_queryString = $inputArray[1];
                   
                    $this->_parseGet($this->_queryString);
                    $this->_parseRequest($this->_input);
                   
                    $_REQUEST = array_merge($_GET , $_POST , $_COOKIE);
                   
                    $contents = $this->_handleCode($contents);
                   
                    //PHP çalıştıktan sonra sonucu echo etmesini değil, $contents stringine atmasını istiyoruz.
                    ob_start();
                    eval($contents);
                    //gidip $contents'in üstüne yazmak doğru alışkanlık değil ama yeni değişken register etmek istemiyorum
                    $contents = ob_get_contents();
                    ob_end_clean();
                }
                $output = "HTTP/1.0 200 OK\r\nServer: HyperTextServer\r\nConnection: close\r\nContent-Type: $mime\r\n\r\n$contents";
            }
            else
            {
                $contents = $this->_handle404();
                $output = "HTTP/1.0 404 OBJECT NOT FOUND\r\nServer: HyperTextServer\r\nConnection: close\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n$contents";
            }
            socket_write($client, $output);
            socket_close ($client);
        }
        socket_close ($socket);
    }
}
$server = new webserver();
$server->setWebRoot('/var/www/webserver');
//$server->verbose(); //ayrıntıları stdout'a echo et.
$server->setPort(8008);
$server->serve();

konsoldan

1
php-cgi azra.php

şeklinde çağırmak suretiyle çalıştırıyoruz.

- Benchmarklar:

Benchmark işleri için aşağıdaki test.php dosyasını kullandım. Göreceğiniz gibi bir for döngüsüne girip 10001′e kadar saymaktan ve çalışma süresini bir metin dosyasına yeni satır olarak yazmaktan başka bir iş yapmıyor:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

$start = microtime(1);

for($n = 0; $n <= 10000; $n++)
{
    //bir şey yapma. yalnızca say...
}
$end = microtime(1);
$elapsed = $end - $start;
echo "<br/>vakit(ms): $elapsed";
$fileHandle = fopen('serverBenchmark.txt' , 'a');
fputs($fileHandle , $elapsed . "\n");
fclose($fileHandle);

Testleri otomatik hale getirebilmek için de, en kolayıma Python geldiği için kısa ve basit bir kod yazdım:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import urllib2
from threading import Thread

class testSuite:
    def testConnection(self , url , thread = 0):
        page = urllib2.urlopen(url)
        data = page.read()
        page.close()
        print("Thread:%d" % thread)
           
    def simpleConnection(self , url , tourNum):
        for i in range(tourNum):
            self.testConnection(url)
       
    def threadedConnection(self , url , tourNum , threadNum):
        for i in range(tourNum):
            myThread = i % threadNum
            t = Thread(target=self.testConnection , args=(url , myThread))
            t.start()

myTestSuite = testSuite()
#myTestSuite.simpleConnection('http://localhost:8008/test.php' , 1000)
#myTestSuite.simpleConnection('http://localhost/webserver/test.php' , 1000)
myTestSuite.threadedConnection('http://localhost:8008/test.php' , 1000 , 10)

Bu kodu ‘benchmark.py’ adıyla kaydedip konsoldan

1
time python benchmark.py

ile çağırıyoruz ve böylece ne kadar sürede çalıştığını ayrıntılarıyla okuyabiliyoruz. Benim apache için webroot’um /var/www. Azra için webroot’um ise /var/www/webserver. localhost:8008′den Azra’ya, localhost:80 (varsayılan port) adresinden Apache’ye ulaşabiliyoruz.

Bunların yanında, bir tür “pseudo-multi-processing” elde edebilmek için aşağıdaki PHP kodunu kullandım:

1
2
3
4
5
6
7
8
9
10
<?php
set_time_limit(0);
include ("thread.php");

$t2 = new Thread("azra.php");
while ($t2->isActive())
{
    echo $t2->listen();
}
$t2->close();

Bu kodu da konsoldan

1
php-cgi server.php

ile çağırıyoruz. Göreceğiniz gibi server.php, yeni bir PID ile bildiğimiz, sevdiğimiz azra.php’yi çağırıyor.

Kullandığımız üçüncü parti PHP sınıfı (thread.php) ise aşağıda

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
<?php

/**
 * original version can be found at http://www.alternateinterior.com/2007/05/multi-threading-strategies-in-php.html
 * This version is slightly modified to run with PHP 5 - Can Ince
 */

class Thread
{
    var $pref ; // process reference
    var $pipes; // stdio
    var $buffer; // output buffer
    public function __construct($file)
    {
        $this->pref = 0;
        $this->buffer = "";
        $this->pipes = (array)NULL;
       
        $descriptor = array (0 => array ("pipe", "r"), 1 => array ("pipe", "w"), 2 => array ("pipe", "w"));
        $this->pref = proc_open ("php $file ", $descriptor, $this->pipes);
        stream_set_blocking ($this->pipes[1], 0);
    }

   
    public function isActive()
    {
        $this->buffer .= $this->listen();
        $f = stream_get_meta_data ($this->pipes[1]);
        return !$f["eof"];
    }
   
    public function close()
    {
        $r = proc_close ($this->pref);
        $this->pref = NULL;
        return $r;
    }
   
    public function tell($thought)
    {
        fwrite ($this->pipes[0], $thought);
    }
   
    public function listen()
    {
        $buffer = $this->buffer;
        $this->buffer = "";
        while ($r = fgets ($this->pipes[1], 1024))
        {
            $buffer .= $r;
        }
        return $buffer;
    }
   
    public function getError()
    {
        $buffer = "";
        while ($r = fgets ($this->pipes[2], 1024))
        {
            $buffer .= $r;
        }
        return $buffer;
    }
}

Benim lokal sistemimde benchmarklar aşağıdaki gibi çıktı:

—-Azra——–

0.00114736318588 – 10 farklı thread – 100 tur
real    0m0.334s
user    0m0.056s
sys    0m0.020s

0.0180547189713 – 10 farklı thread – 1000 tur
real    0m2.982s
user    0m0.192s
sys    0m0.056s

0.00165226125717 – 100 farklı thread – 1000 tur
real    0m5.251s
user    0m0.196s
sys    0m0.056s

0.00191986322403 – 10 farklı thread – 10000 tur
real    0m38.442s
user    0m1.640s
sys    0m0.608s

0.00110848665237 – tek thread – 100 tur
real    0m0.354s
user    0m0.092s
sys    0m0.068s

0.0111782860756 – tek thread – 1000 tur
real    0m3.421s
user    0m0.744s
sys    0m0.424s

0.00116171479225 – tek thread – 1000 tur (pseudo-multiprocess destekli)
real    0m4.293s
user    0m0.792s
sys    0m0.384s

0.00114714858532 – tek thread – 10000 tur
real    0m37.138s
user    0m8.209s
sys    0m4.280s

—-Apache——-

0.00138350725174 – 10 farklı thread – 100 tur
real    0m0.360s
user    0m0.068s
sys    0m0.028s

0.0136778688431 – 10 farklı thread – 1000 tur
real    0m3.524s
user    0m0.176s
sys    0m0.060s

0.00142541861534 – 100 farklı thread – 1000 tur
real    0m4.030s
user    0m0.144s
sys    0m0.040s

0.00147548000813 – 10 farklı thread – 10000 tur
real    0m46.358s
user    0m1.476s
sys    0m0.496s

0.00153880357742 – tek thread – 100 tur
real    0m0.499s
user    0m0.140s
sys    0m0.068s

0.0135918140411 – tek thread – 1000 tur
real    0m3.824s
user    0m0.972s
sys    0m0.504s

0.0135918140411 – tek thread – 10000 tur
real    1m7.090s
user    0m9.149s
sys    0m4.984s

Benchmarklara hızlıca göz attığımızda fark edebileceğimiz gibi, Azra multi-threaded isteklerde yavaş kalıyor. Bu durumu yeni Ubuntu çıktıktan sonra, PHP’yi pcntl ile derleyerek aşabilmeyi umuyorum. Diğer benchmarkların hepsinde Azra, Apache’ye fark atıyor.

Yeni bölümde yeni maceralarla görüşmek üzere:)

Paylaş:
  • Print
  • FriendFeed
  • Twitter
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • DZone
  • LinkedIn
  • MySpace
  • Ping.fm
  • Reddit
  • StumbleUpon
  • Technorati

Etiketler: , ,

Önceki yazımda basit bir web sunucusu kodlama denemesini anlatmıştım.

Bugün biraz daha vakit bulabildim ve hem yapay bir tür multi-processing ile denemeler yaptım, hem de superglobal değişkenleri işledim.

Başarısız taraftan başlayayım: Şu adreste bulunan PHP sınıfını -biraz tozunu alarak- kullandım zira yeni Ubuntu’ya bir hafta kala pcntl desteği ile yeniden PHP derlemekle uğraşmak istemedim. Ubuntu’yu yükselttikten sonra bu konuya döneceğim. Yukarıda bahsi geçen sınıf ile thread oluşturmak ise sistem kaynakları açısından korkunç pahalı sonuçlar ortaya çıkardı.

Başarılı tarafa gelince: Tüm supergloballeri ($_GET, $_POST, $_COOKIE, $_SERVER, $_REQUEST) $_SERVER['REMOTE_ADDR'] dışında başarıyla ayrıştırdım. Böylece ufak-tefek bir-iki eksik dışında gayet başarıyla çalışan, PHP üstünde yazılmış ve native olarak PHP destekleyen bir web sunucumuz oldu.

Elbet artık bu sunucuya bir de isim bulmak gerekti. Altan Tanrıverdi Hypatia‘yı önerdi. Bir bilim kadınının adını vermek fikri hoşuma gitmekle birlikte daha “bizden” bir ismi tercih ettim. Tanrıverdi’nin “bilimkadını adı” önerisi artı “bizden isim” arayışı, beni Azra Erhat‘ın anısına götürdü. Kodun kulağına ezanını okudum, vaftiz ettim ve adını böylece “Azra” koydum.

Kodun yeni hali şöyle:

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
<?php
/**
 * Azra Web Server
 *
 * Web Server coded in pure PHP
 *
 * @package     Azra Web Server
 * @author      Can Ince
 * @copyright           Copyright (c) 2010, Logikit / Can Ince.
 * @license     http://www.opensource.org/licenses/mit-license.php
 * @link        http://can.logikit.net
 */


interface server
{
    public function setWebRoot($path);
    public function setPort($port);
    public function serve();
}

final class webserver implements server
{
    private $_port , $_webroot , $_queryString , $_webRootWithoutSlash, $_input , $_file , $_verbose;
    public function __construct()
    {
        $this->_verbose = 0;
    }
   
    public function setPort($port)
    {
        $this->_port = $port;
    }
   
    public function verbose()
    {
        $this->_verbose = 1;
    }
   
    public function setWebRoot($path)
    {
        $this->_webroot = $path . '/';
        $this->_webRootWithoutSlash = $path;
    }
   
    private function _mimeHandler($input)
    {
            $fileinfo = pathinfo($input);
            switch ($fileinfo['extension'])
            {
                  case 'png':
                      $mime = "text/png";  
                      break;
                 
                  case 'jpg':
                  case 'jpeg':
                      $mime = "text/jpeg";  
                      break;
                 
                  case 'gif':
                      $mime = "text/gif";  
                      break;
                 
                  case 'css':
                      $mime = "text/css";  
                      break;
             
                  default:
                      $mime = "text/html; charset=UTF-8";
            }
        return $mime;
    }
   
    private function _handleCode($contents)
    {
         //eval() edilecek kodun başında <?php ya da <? varsa çakar. halledelim.
        $contents = trim($contents);
        if(substr($contents , 0 , 5) == '<?php')
        {
            $contents = substr($contents , 5);
        }
        elseif(substr($contents , 0 , 2) == '<?')
        {
            $contents = substr($contents , 2);
        }
       
        return $contents;
    }
   
    private function _parseGet($str)
    {
        $_GET = array();
        $array = explode('&' , $str);
        foreach($array as $requestItem)
        {
            $itemArray = explode('=' , $requestItem);
            if(!empty($itemArray[0])) $_GET[$itemArray[0]] = $itemArray[1];
        }
        unset($_GET['hyperTextServer_php']); //bunu $_GET'e enjekte ediyor. neden, bilmiyorum...
    }
   
    private function _parsePost($str)
    {
        $_POST = array();
        $array = explode('&' , $str);
        foreach($array as $requestItem)
        {
            $itemArray = explode('=' , $requestItem);
            if(!empty($itemArray[0])) $_POST[$itemArray[0]] = $itemArray[1];
        }
    }
   
    private function _parseCookie($str)
    {
        $_COOKIE = array();
        $array = explode('; ' , $str);
        foreach($array as $requestItem)
        {
            $itemArray = explode('=' , $requestItem);
            if(!empty($itemArray[0])) $_COOKIE[$itemArray[0]] = $itemArray[1];
        }
    }
   
    private function _parseRequest($str)
    {
        $_SERVER = array();
        $lines = explode("\n" , $str);
        $rawData = explode(' ' , $lines[0]);
        $_SERVER['REQUEST_METHOD'] = $rawData[0];
        if($_SERVER['REQUEST_METHOD'] == 'POST') $this->_parsePost(end($lines));
        $_SERVER['PHP_SELF'] = $this->_webRootWithoutSlash . $rawData[1];
        $_SERVER['HTTP_HOST'] = substr($lines[1] , 6);
        $_SERVER['HTTP_USER_AGENT'] = substr($lines[2] , 12);
        $_SERVER['HTTP_ACCEPT'] = substr($lines[3] , 8);
        $_SERVER['HTTP_ACCEPT_LANGUAGE'] = substr($lines[4] , 17);
        $_SERVER['HTTP_ACCEPT_ENCODING'] = substr($lines[5] , 17);
        $_SERVER['HTTP_ACCEPT_CHARSET'] = substr($lines[6] , 16);
        $_SERVER['HTTP_KEEP_ALIVE'] = substr($lines[7] , 12);
        $_SERVER['HTTP_CONNECTION'] = substr($lines[8] , 12);
        if(strstr($lines[9] , 'Referer'))
        {
            $_SERVER['HTTP_REFERER'] = substr($lines[9] , 9);
            $_SERVER['HTTP_COOKIE'] = substr($lines[10] , 8);
        }
        else $_SERVER['HTTP_COOKIE'] = substr($lines[9] , 8);
       
        if(!empty($_SERVER['HTTP_COOKIE'])) $this->_parseCookie($_SERVER['HTTP_COOKIE']);
       
        $_SERVER['SERVER_SIGNATURE'] = ' <address>Azra/0.0.1 Server at ' . $_SERVER['HTTP_HOST'] . ' Port ' . $this->_port . '</address>';
        $_SERVER['SERVER_SOFTWARE'] = 'Azra/0.0.1';
        $_SERVER['DOCUMENT_ROOT'] = $this->_webroot;
        $_SERVER['SCRIPT_FILENAME'] = $this->_webRootWithoutSlash . str_replace('./' , '/' , $this->_file);
        $_SERVER['REQUEST_TIME'] = time();
        if(!empty($this->_queryString)) $_SERVER['QUERY_STRING'] = $this->_queryString;
    }
   
    public function serve()
    {
        $socket = @socket_create_listen($this->_port);
        if (!$socket)
        {
          echo "Soket olamiyor!\n";
          exit;
        }

        while (1) //daire-i ilanihaye!
        {
          $client = socket_accept($socket);

          $input = $this->_input = trim(socket_read ($client, 4096));

          if($this->_verbose == 1) echo $input;
         
          $input = explode(" " , $input);
          $input = $input[1];

          $mime = $this->_mimeHandler($input);
         
          if ($input == "" || $input == '/')
          {
            $input = "/index.html";
          }
          $input = ".$input";
          $inputArray = explode('?' , $input);
          $this->_file = $inputArray[0];
          $input = $this->_webroot . $this->_file;


          if (file_exists($input) && is_readable($input))
          {
            if($this->_verbose == 1) echo "Dosya: $input\n";
           
            $contents = file_get_contents($input);
            if(strstr($input , '.php'))
            {
                $this->_queryString = $inputArray[1];
               
                $this->_parseGet($this->_queryString);
                $this->_parseRequest($this->_input);
               
                $_REQUEST = array_merge($_GET , $_POST , $_COOKIE);
               
                $contents = $this->_handleCode($contents);
               
                //PHP çalıştıktan sonra sonucu echo etmesini değil, $contents stringine atmasını istiyoruz.
                ob_start();
                eval($contents);
                //gidip $contents'in üstüne yazmak doğru alışkanlık değil ama yeni değişken register etmek istemiyorum
                $contents = ob_get_contents();
                ob_end_clean();
            }
            $output = "HTTP/1.0 200 OK\r\nServer: HyperTextServer\r\nConnection: close\r\nContent-Type: $mime\r\n\r\n$contents";
          }
          else
          {
            $contents = "<title>UHU 404</title><h2>UHU 404</h2>Iın-ıın-ıın-ıın-cav-cav-cav-cav-oooooo - oooooo - zört - zört - zört -zört (araba alarmı efekti) Çok afedersin müdürüm ama yok öyle bir dosya.";
            $output = "HTTP/1.0 404 OBJECT NOT FOUND\r\nServer: HyperTextServer\r\nConnection: close\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n$contents";
          }
          socket_write($client, $output);
          socket_close ($client);
        }
        socket_close ($socket);
    }
}
$server = new webserver();
$server->setWebRoot('/var/www/webserver');
//$server->verbose(); //ayrıntıları stdout'a echo et.
$server->setPort(8008);
$server->serve();

…ve benchmarklar:

Önceki yazıdaki benchmarkla aynı şartlarda ve aynı php dosyasını kullanarak 100 çalıştırma ortalaması

apache: 0.012167468071

azra: 0.00674695968628

Temiz şekilde iki kat hızlı.

socket_getpeername() de düzgün çalışabilir ve peer adresini alabilirsem $_SERVER['REMOTE_ADDR'] de dolacak ve hiç sorun kalmayacak.

Paylaş:
  • Print
  • FriendFeed
  • Twitter
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • DZone
  • LinkedIn
  • MySpace
  • Ping.fm
  • Reddit
  • StumbleUpon
  • Technorati

Etiketler: ,

Önyargılar Üzerine

Bu yazı bir süredir kafamın içinde bir yerlerde yazılmayı bekliyordu; bugünü bekliyormuş.

Öncelikle yazılarını -genel olarak yaklaşım açısını- fevkalade bulduğum, beğendiğim Altan Tanrıverdi’den bir alıntı yapmak isterim:
‘Doğrusu da budur. Diğer taraftan “senin yerinde olsaydım” ibaresi ile başlayan cevaplar da yanlış sayılmaz ki birazdan ben de öyle yapacağım.’
Yazının tamamı da oldukça ilginç ve şuradan okunabilir.

Tanrıverdi yazısında, “deneyimli bir programcıdan çaylağa nasihatler” kabilinden  -doğru yorumlayabildiğim ölçüde- diğer unsurların yanında, satır aralarında önyargılardan kaçınmak gerektiğinden de dem vuruyor.

Ben de fikirlerimi bu çerçeveye oturtmak bu buradan yola çıkarak düşüncelerimi geliştirmek isterim:

“Çok bilen çok önyargı sahibi olandır.” genel olarak doğru bir önerme olarak kabul görebilir, ta ki “düzgün fikir sahibi olmak” ile “önyargı sahibi olmak” ayrıştırılabilsin.

Son günlerde heyecanlı gençleri sıkça görüyorum: “Hoca, iyi diyorsun da, falancayı filanca şekilde yapsak daha iyi sonuç alamaz mıydık?”

…ve sıkça yanıtlıyorum: “Ben benchmark severim. Görelim!”

Doğal olarak işbu genç arkadaşlarımızın bir çoğu “Görürüz!” diyor da arkasını -adı üstünde “gençlik” hezeyanının sonucu- bir türlü getiremiyor.

Oysa ben ne söylüyorum? “Disiplinli düşünebilme yetisi en büyük yetenektir.”

“Disiplinli düşünme” ya da “düşünceyi disiplin altına alma” derken bir yöntemler bütününden söz ediyorum; önyargılardan değil.

Devlet dairesinde memur olacak kişiler yazının bundan sonrasını okumayabilirler, ancak gelecek için kendinden bir şeyler bekleyenler okumaya devam etmeli.

- Eğitim sistemimiz ne yazık ki analist yetiştirmek üstüne değil, “vasat adam” yetiştirmek üstüne kurulu. Bu sistemden “ukala papağanlar yetiştirmek” durumundan ötesini -ne yazıktır- elde edemiyoruz. Çocuk pırıl pırıl, harika bir sistem çözümleyicisi olacak, biz ona “hayır!” diyoruz, “önce üniversite bilmemne hadisesini kazan!” ve beynini böylece -o vakte kadar elimizden kurtarabilmişse bile- düdüklüyoruz, dimağının ırzına geçiyoruz. Ondan sonra da oradan mezun olunca adama (bakın artık “adam” oldu) “haydi, sistemlerimiz, kıçımız başımız sana emanet!” diyoruz. (Tabii işin böyle olduğunu bildiğimiz için demiyoruz ama sistem buna yönelik)

- İşbu (artık) “adam” mektebi bitirdikten sonra elimize düşüyor ve dünyanın gerçekte kaç bucak olduğunu biraz olsun görmeye başlıyor: Zaten düdüklenmiş beyniyle (özellikle yazılım sektöründen söz ediyorsak) okulda hoca olmaktan gayrısını başaramamış hocaların elinde, beyni çift kat düdüklenmiş adamdan söz ediyoruz. Söz ediyoruz ve bu adamların sektörde “insan kaynağı” diye ortalıkta dolaştığını da böylece söylüyoruz.

- “Kendini geliştirmek istiyor musun? Unut!” diyoruz, “okulda ne öğrendiysen unut, çünkü hepsi ya önyargılı, ya yanlış, ya eksik ya da düpedüz palavra! Yeni doğmuşsun gibi yap. Aç bak, dokümantasyon orada. En başından oku, hepsini oku. Sonra gel bize anlat ‘ruby fastcgi ile şöyle çalışırmış’, ‘python aslında ne kadar şahaneymiş, php bilmemneymiş’. ama önce oku! Edindiğin bilgiyi sınıflandırmak için, sana uygun gelebilecek bir yöntem geliştir. Bunları yapmıyorsan, ancak devlet dairesinde memur olursun. Çocukların da devlet dairesinde memur olur.”

Bunları söylerken devlet memuru olma durumunu aşağılıyor muyuz? Hayır. Haddimize düşmez. Öte taraftan, bir yazılımcının devlet memuru ya da okulda hoca olmak için hakikaten çok rasyonel gerekçeleri olmalı. Sizin kişisel olarak verebileceklerinizin çok çok sınırlı olduğu bir ekosistemde isterseniz allame-i cihan olun, hiç ama hiç bir şeyi değiştiremezsiniz. Oysa, bir yazılımcı “bir şeyleri daha doğru yapmak” için vardır; öyle yazılımcıdır.

Önceki gün yazdığı kod hakkında (istediği kadar iyi çalışıyor olsun) şüpheye düşmeyen adama dahi artık yazılımcı değil “kod ağası” denir.

Öğrendiklerinizin, uzun süredir eski öğrendiklerinizin yerini almaması durumunu gözlemliyorsanız (yaşınız kaç olursa olsun) kod yazmaktan elinizi-ayağınızı-eteğinizi çekmenin vakti gelmiş demektir.

Yok, aksi durumda diretiyor ve üstelik ukalalık yapmaya kalkıyorsanız, yeri burası değildir!

Paylaş:
  • Print
  • FriendFeed
  • Twitter
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • DZone
  • LinkedIn
  • MySpace
  • Ping.fm
  • Reddit
  • StumbleUpon
  • Technorati

Etiketler: ,

Bir Web Sunucusu Kodlama Denemesi

Her şey Altan Tanrıverdi‘nin Ubuntu üzerine nginx kurulumu yazısını görmemle başladı. Hayır, Nginx’i daha önce görmemiş değildim, ancak Tanrıverdi’yi Apache’den başka bir web sunucusu kurmaya yönelten şey Apache’nin sistem kaynakları açısından “pahalı” sayılabilecek bir sunucu olmasıydı.

Apache’de bir web sunucusundan beklediğimiz her şey ve hatta fazlası var. Apache modül listesine (http://httpd.apache.org/modules/ ve http://modules.apache.org/) daha önce göz atmadıysanız bir bakın; belki de adını bile duymadığınız, hiç işiniz düşmemiş olduğu için kullanmadığınız bir çok modül olduğunu göreceksiniz. Oysa çoğu LAMP/LAPP programcısı, günlük işlerinde php ve rewrite modulleri dışındakileri kullanmaya pek ihtiyaç duymaz.

Elbette asla yılların eskitemediği dost Apache’ye kafa tutmak için değildi, ancak kafama şu soruyu takmadan edemedim:

“Acaba yalnız PHP yorumlayan ve başka hiç bir iş yapmayan bir web sunucusunu yine PHP ile kodlasak nasıl olurdu?”

Tabii bu soru büyük olasılıkla benden başka bir sürü kişinin de aklından geçmişti veAmerika’yı yeniden keşfetmemek için önce Hz. Google’a sordum.

Google bana “Nano Web Server var.” dedi. Bir de baktım ki, gerçekten bu arkadaş Apache’yi neredeyse bire-bir PHP’ye port etmiş. Modüllerine kadar yazmış. Ubuntu için deb paketi bile var ve daemon olarak kuruluyor. Öte taraftan bu çalışma hem benim ölçeğimi aşıyor hem de başlangıçtaki mantığımla ters düşüyor: PHP’den başka şey çalıştırmak istesem zaten “runtime’da yorumlanan” değil “derlenmiş” kod kullanmayı tercih ederim. Benim fikrimin özü, zaten native olarak PHP yorumlayabilen bir kod yapısı kullanarak bir web sunucusu yapmak.

Bunun dışında bir de phpStack‘i buldum. phpStack benim amaçladığım şeye oldukça yakındı ancak bana göre işleri olması gerekenden daha karmaşık hale getiriyordu.

Böylece kendi denememi yapmaya karar verdim. Önce php5-cgi paketini kurdum. PHP Manual’dan jenerik bir soket kodu aldım. Kodu düzenledim, mime türlerine göre gönderilecek header’ları ayarladım, PHP yorumlayıcısı desteği verdim ve sonuç:

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
<?php

interface server
{
    public function setWebRoot($path);
    public function setPort($port);
    public function serve();
}

final class webserver implements server
{
    private $_port , $_webroot;
    public function __construct()
    {

       //belki ileride lazım olur. şimdilik dummy
    }
   
    public function setPort($port)
    {
        $this->_port = $port;
    }
   
    public function setWebRoot($path)
    {
        $this->_webroot = $path;
    }
   
    private function _mimeHandler($input)
    {
            $fileinfo = pathinfo($input);
            switch ($fileinfo['extension'])
            {
                  case 'png':
                      $mime = "text/png";  
                      break;
                 
                  case 'jpg':
                  case 'jpeg':
                      $mime = "text/jpeg";  
                      break;
                 
                  case 'gif':
                      $mime = "text/gif";  
                      break;
                 
                  case 'css':
                      $mime = "text/css";  
                      break;
             
                  default:
                      $mime = "text/html; charset=UTF-8";
            }
        return $mime;
    }
   
    private function _handleCode($contents)
    {
         //eval() edilecek kodun başında <?php ya da <? varsa çakar. halledelim.
        $contents = trim($contents);
        if(substr($contents , 0 , 5) == '<?php')
        {
            $contents = substr($contents , 5);
        }
        elseif(substr($contents , 0 , 2) == '<?')
        {
            $contents = substr($contents , 2);
        }
       
        return $contents;
    }
   
    public function serve()
    {
        $socket = @socket_create_listen($this->_port);
        if (!$socket)
        {
          echo "Soket olamiyor!\n";
          exit;
        }
        while (1) //daire-i ilanihaye!
        {
          $client = socket_accept($socket);
          $input = trim(socket_read ($client, 4096));
          echo $input; //tarayıcı ne diyor? bu veriyi ileride $_SERVER array'ini doldurmak için kullanmak lazım.
          $input = explode(" " , $input);
          $input = $input[1];

          $mime = $this->_mimeHandler($input);
         
          if ($input == "" || $input == '/')
          {
            $input = "/index.html";
          }
          $input = ".$input";
          $inputArray = explode('?' , $input);
          $input = $this->_webroot . $inputArray[0];

          if (file_exists($input) && is_readable($input))
          {
            echo "Dosya: $input\n";
           
            $contents = file_get_contents($input);
            if(strstr($input , '.php'))
            {
             
                $contents = $this->_handleCode($contents);
               
                //PHP çalıştıktan sonra sonucu echo etmesini değil, $contents stringine atmasını istiyoruz.
                ob_start();
                eval($contents);
                //gidip $contents'in üstüne yazmak doğru alışkanlık değil ama yeni değişken register etmek istemiyorum
                $contents = ob_get_contents();
                ob_end_clean();
            }
            $output = "HTTP/1.0 200 OK\r\nServer: HyperTextServer\r\nConnection: close\r\nContent-Type: $mime\r\n\r\n$contents";
          }
          else
          {
            $contents = "<title>UHU 404</title><h2>UHU 404</h2>Iın-ıın-ıın-ıın-cav-cav-cav-cav-oooooo - oooooo - zört - zört - zört -zört (araba alarmı efekti) Çok afedersin müdürüm ama yok öyle bir dosya.";
            $output = "HTTP/1.0 404 OBJECT NOT FOUND\r\nServer: HyperTextServer\r\nConnection: close\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n$contents";
          }
          socket_write($client, $output);
          socket_close ($client);
        }
        socket_close ($socket);
    }
}
$server = new webserver();
$server->setWebRoot('/var/www/');
$server->setPort(8008);
$server->serve();

Bu kodu (örneğin /var/www altına) hyperTextServer.php adıyla kaydediyoruz.
Daha sonra varsayılan olarak görüntülenmesi için örnek bir /var/www/index.html dosyası oluşturuyoruz.
Son olarak

1
php-cgi /var/www/hyperTextServer.php

ile sunucuyu çalıştırdıktan sonra tarayıcıdan

http://localhost:8008

adresini çağırdığımızda oluşturduğumuz index.html dosyasının servis edildiğini görüyoruz.
Artık bir adım daha ileri giderek bir PHP dosyası çağırmayı deneyebiliriz:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$start = microtime(1);

for($n = 0; $n <= 10000; $n++)
{
    echo $n . ' ';
}
$end = microtime(1);
$elapsed = $end - $start;
echo "<br/>vakit(ms): $elapsed";
$fileHandle = fopen('serverBenchmark.txt' , 'a');
fputs($fileHandle , $elapsed . "\n");
fclose($fileHandle);

Bu kodu da /var/www/test.php adıyla kaydedip tarayıcıdan
http://localhost:8008/test.php adresini çağırdığımızda PHP kodumuzun da gayet başarılı şekilde yorumlandığını görüyoruz.
Gördüğünüz gibi test.php dosyası, serverBencmark.txt dosyasına, çalışmasının ne kadar zaman aldığını yeni bir satır olarak yazıyor. Böylece sevdiğimiz “benchmarkçılık” oyununu oynayabileceğiz.
test.php kodunu arka arkaya yüz kere önce kendi yazdığımız sunucu ile 8008′den, daha sonra da yine yüz kere apache üstünden (default 80.port) çalıştırıp basit bir aritmetik ortalama alacağız.
Benim lokal sistemimdeki sonuçlar söyle:
Apache ortalama: 0,0174917006493 ms
Bizim sunucu ortalama: 0,0098775601387 ms

Görüyoruz ki, beklediğimizin aksine PHP üstünde çalışan bizim sunucu, derlenmiş kod olarak çalışan Apache’den iki kat kadar hızlı çalışıyor. Bu durumun açıklamasının aslında çok da zor olmadığını düşünüyorum: Apache bir sürü kontrol yapıyor, aynı anda birden fazla bağlantı kabul edebiliyor, bir sürü port dinliyor, vs. Öte taraftan bizim sunucumuz yalnız PHP kodunu çalıştırmaya odaklanmış. $_SERVER’a, $_REQUEST’e bir değer atamak için tarayıcı verisini işlemiyor ve tek, basit bir süreç olarak çalışıyor.
Aynı işleri bizim sunucumuza yaptırdığımızda, büyük olasılıkla aradaki hız farkı kapanacak. Yine de şu andaki duruma bakarak iki kat yavaşlayacağını öngörmüyorum sonuç olarak başlangıçta umduğumun çok daha fazlasını alabildiğimi gözlemliyorum.

Bundan sonra yapılacaklar:
1- $_SERVER, $_REQUEST, $_POST, $_GET, $_FILES ve $_COOKIE arraylerinin doldurulması.
2- pcntl eklentisi ile süreçlerin “çatallanması”

Yukarıdaki iki maddelik yapılacaklar listesi de temizlendiğinde elimizde basbayağı düzgün çalışan bir web sunucumuz olacak.

Paylaş:
  • Print
  • FriendFeed
  • Twitter
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • DZone
  • LinkedIn
  • MySpace
  • Ping.fm
  • Reddit
  • StumbleUpon
  • Technorati

Etiketler: , , , ,

Biraz Daha Sağduyu Üstüne

Düşünmeye “düşünmeye nereden başlamalıyım?” sorusuyla başlamış ve bir ara-sonuç olarak “düşünmeye düşünmek için bir yöntem bulmakla başlamalıyım”a varmıştım ve düşünmeye başlama yöntemi olarak “sağduyu” ve “dil”i bulmuştum.

Pekiyi, sağduyu genelgeçer bir yöntem olarak yeterli midir, ya da daha başka şekilde soracak olursam “Sağduyu evrensel midir?”
Bu soruyu yanıtlayabilmek için herhalde sağduyunun ne olduğuna ve nasıl geliştiğine bakmak gerekir.
Öyledir ya: Hepimiz çok farklı kişisel geçmişlerden gelsek ve farklı yaş ya da cinseyette olsak dahi, “olgunluk” denilen önyargılar toplamına ulaştığımız vakitten itibaren, özellikle herkesin söz söyleyebileceği konularda (“şu anda yağmur yağıyor.” önermesinde olduğu gibi) aynı yargılara varırız. Kimimiz “Hayır, aslında biz yağmur yağıyor sanıyoruz” derken diğerimiz “Hayır hayır, yağmur yağdığını sanıyor, biz de ona uyuyoruz” ve bir diğerimiz de “Basbayağı yağmur yağıyor işte!” yargılarına varabiliriz. Ancak daha dikkatli baktığımız zaman, görürüz ki, bu üç argüman da “yağmur”u içeriyordur; en azından “içinde yağmur olan bir şeyler olduğu” konusunda uzlaşabiliriz. Üstelik büyük olasılıkla ezici bir çoğunluğumuz üçüncü argümandan taraf olur.

“Yani ’sağduyu evrenseldir!’ mi demek istiyorum?”
Doğallıkla her zaman değil ve yukarıda belittiğim gibi, sağduyu ancak ortak önyargılarımız kadar evrensel olabilir. Yaşamında hiç yağmur görmemiş, kuzey kutbundan gelen bir kişinin, “gökten düşen suya” dair çok farklı çıkarımları olabilir.

“Biraz can sıkıcı bir duruma sokmadım mı kendimi? Başlangıçta sağduyuya ‘doğru’ ve ‘kesin’ olabileceği için sığınmıştım ancak şimdi baktığımda durum hiç de öyle görünmüyor öyle değil mi?”
Aslında değil. Çünkü, buraya kadar olan çıkarımlarımı da farkında olmadan sağduyumu kullanarak yapmıştım. Bu satırları okuyan birileri varsa, onlar da sağduyularını kullanarak buradaki düşünceler hakkında kendileri adına bir kanıya varıyorlar. Demek ki, sağduyu “kesin” ve “doğru” olduğunu bize henüz kanıtlayamamış olsa bile, bir başlangıç noktası olarak iş görür gibi duruyor.

“İyi anladım; Tutturmuşum bir “sağduyudur” gidiyorum ama tüm çıkarımlarımı salt sayduyuya dayanarak yapabilir miyim?”
Herhalde yapamam. Newton fiziği ya da Euclides geometrisi uzun yıllar -hatta yüzyıllar- iş gördüler. Üstelik bir kez kabul edilip “kanıtlanmış” gibi görüldükleri bu uzun zaman boyunca insanlar bir sürü işlerini bu prensipler üstüne kurdular ve gayet de memnundular. Ancak öyle bir vakit geldi ki, insanlar işlerinde bu modellerin yetersiz olduğunu fark ettiler ve başka fizik ve geometri modelleri ortaya atıldı ve özellikle fizikte, hala atılıyor da. Bırak benim gibi kulaktan dolmacı ukala adamı, çoğu teorik fizikçi bile, bazı anlar geliyor ki, ancak varsayımlarla konuşmak durumunda kalıyor; Adı üstünde “teorik”.
Dahası, “avucumda bir tane fasulye varken, aynı avucuma bir tane daha fasulye koyduğumda, artık elimde iki fasulye olmuş olacağı” fikri bile bir varsayımdan ibaret; yalnızca bugün için işimize geldiğinden “öyleymiş gibi yapıyoruz.”
O zaman ben de pekala şimdilik işimi gören sağduyu ile başlayıp, onunla işimi görebildiğim yere kadar gidip, daha sonra başka bir yöntem, ya da “başka bir yöntem daha” arayabilirim.

Adam Smith’in Mezarı. Wikipedia arşivinden

“Tamam. Örnekleri dallandırıp-budaklandırırsam, basitlik istediğim yerde karmaşaya yol açmış olacağım. Şimdilik ’sağduyu’ ile idare etmek ‘tamam’ gibi görünüyor ve artık buradan gitmem gereken yeri düşünmeye başlayabilirim. Bir de: Bu yazının hiç bir yerinde dedikodu yapmamışım. Hasta mıyım acaba?”
Herhalde kimsenin adı geçmediği içindir. Yine de tutmayayım kendimi: “Sağduyunun büyük savunucularından” ve üçkağıdın, düzenbazlık ve sahterkarlığın nedenlerini düzgün şekilde açıklamaya girişebilmiş sayılı insanlardan Adam Smith’in (Ne demezsin! Daha şimdiden dedikodunun kokusunu almaya başladım!)aslında gizliden-gizliye Jean-Jacques Rousseau hayranı olduğunu söyleyenler var! Başkaları da Smith’in o “görünmez el”e kafayı takmasına, aslında o elin bir türlü paltosunun cebinden çıkmamış olmasının neden olduğunu söyler!

Paylaş:
  • Print
  • FriendFeed
  • Twitter
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • DZone
  • LinkedIn
  • MySpace
  • Ping.fm
  • Reddit
  • StumbleUpon
  • Technorati

Don Quijote. Wikipedia arşivinden

Geliştirdiğim “büyük-iri” sayılabilecek türden bir projeyi müşterime göstermek istiyorum. Müşterim de ciddi bir insan; yaptığınız iş kadar, nasıl yaptığınızla da ilgileniyor. Bunun için Active Collab kullanıyoruz ve memnunuz. Kodlamayı da rahatça takip edebilmesi için ona da VPS üstünde bir hesap açtım; her şey iyi.

Kodun bitirdiğim bir kısmını VPS’e yükledim ve test edeceğim. Fakat o da ne! Lokal sistemde şahane çalışan kod, VPS üstünde patlamasın mı!

Lokal sistem ubuntu 9.10/Suhosin-Patch’li PHP 5.2.10-2ubuntu6.4. Sunucu ise ubuntu 9.04/PHP 5.2.6-3ubuntu4.5

Yani ortada çok tuhaf konfigürasyonlar yok. Ubuntu’nun iki ardıl sürümü. Yazdığım kod da öyle “patlayabilir” cinsinden değil, sıradan bir kod.

Yalınlaştırmak gerekirse:

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
<?php

class B
{
    public function __construct()
    {
    }
}

class A extends B
{
    public function __construct()
    {
        parent::__construct();
        //biraz kod
    }
   
    public function index()
    {
        //biraz kod
        $this->_methodPrivate();
        //biraz daha kod
    }
   
    private function _methodPrivate()
    {
        //burada da biraz iş...
    }
}

Türünden bir kod. Bakıyorum bakıyorum “hata nerede?” diye, bulamıyorum.
Başkasına sorarak iş yapmayı sevmememe rağmen, “herhalde benim gözüm görmüyor” diyerek friendfeed PHP odasına dahi soruyorum. I-ıh. Çözebilen yok çünkü ortada çözülecek bir şey yok.

Genel olarak bu tür işlerin FTP encoding dertlerinden olduğunu önceki deneyimlerimden (“acılarımdan” diye de okuyabilirsiniz) biliyorum ve kullandığım gFTP’yi bırakıp konsoldan sftp çekiyorum: Sonuç aynı. SSH ile VPS’e geçip nano’ya yapıştırıyorum: “Bana mısın!” demiyor!
Hissel olarak şunu deniyorum:
_methodPrivate()’ı methodprivate() şeklinde denediğimde her ne hikmetse kod çalışıyor! Ancak öyle yarım-yamalak, ne yaptığımı bilmez halde iş yapamam ki!

“Hmm” diyorum, “Bu eski PHP sürümü. Bug vardır bunda!” (Bakınız kaşıntı başlıyor.)
“Sahi, bu Ubuntu 9.04′e nasıl PHP 5.3 kurarım?” (Madem 9.10′da çalışıyor, sürüm yükseltsene be adam! PHP de düzelmiş olur ha? Yook kaşınıyorum ya, 9.04′e kuracağım!)
Vakıa, “Ubuntu 9.04 PHP5.3″ diye arıyorum ve şu yazıyı buluyorum. Bir bakıyorum; abim derlemiş, çalıştırmış phpinfo() ekran görüntüsü bile koymuş.

Bir programcının, Ubuntu kurduktan sonra kuracağı ilk paketlerden biri “build-essential”dır, tamam anladık ama, insan olmanın belki de en kötü taraflarından biri de kişinin mutluluklar kadar acıları da çabuk unutması. Hafızam beni yanıltmıyorsa, en son geçen yıl yine bu aylarda kaynak koddan PHP derlemiştim; demek ki akıllanmamışım!

Abimin yazısından yola çıkarak PHP5.3.0 indirdikten sonra ve kendi işlerime uygun olarak şöyle bir konfigürasyon öngörüyorum:

1
sudo ./configure --with-apxs2=/usr/bin/apxs2 --with-mysql --with-pdo-mysql --with-zlib --enable-calendar --with-curl --enable-dba --with-enchant --with-gd --enable-gd-native-ttf --with-gmp --with-mcrypt --with-readline --enable-soap --enable-zip --with-pgsql --with-pdo-pgsql

Tabii Allah’ın emri: “o kütüphane eksik, şu yok, bu yok” uyarıları içinde bir sürem “sudo apt-get install libfalanca-dev” yazmakla geçiyor…

…ve derlemeye başlıyorum.
Amca önce enchant bindingini derlerken çakıyor. “Abim yazısında enchant’ı öve öve bitirememiş ama benim ne işim olacak?” diyorum ve konfigürasyondan önce –with-enchant’ı çıkarıyorum ve biraz daha ilerliyoruz.
Bilahare, Phar derlenirken su koyuveriyor. Gidiyorum, dokümantasyona bir bakıyorum ki adamlar 5.3′ten itibaren PHP-core’a dahil etmişler.

“Behey adam!”, diyorum, “madem core’a dahil edecektin, gcc4.3 ile çakmayanından yapsaydın ya!”… Zaten “warning”ler gırla gidiyor derleme boyunca; üstünde bile durmaya değmez…

Derken, PHP bug raporlarının içinde yama ararken kendimi buluyorum, ancak yama bulamıyorum!
Tek önerdikleri gecelik snapshottan kurulum.
Onu da yapıyorum. İndirdiğim ve üstünde uğraştığım PHP dizinini uçurarak php5.3-201004092030 diye bir sürüm buluyorum ve baştan başlıyorum. Tabii Phar yine derlenmiyor!

“Çok da sevmişim Phar’ını!” diyerek konfigürasyona nihai şeklini veriyorum:

1
sudo ./configure --with-apxs2=/usr/bin/apxs2 --with-mysql --with-pdo-mysql --with-zlib --enable-calendar --with-curl --enable-dba --with-gd --enable-gd-native-ttf --with-gmp --with-mcrypt --with-readline --enable-soap --enable-zip --with-pgsql --with-pdo-pgsql --disable--phar

Ve nihayet bu sefer derlenmeyi başarıyor PHP, ama dert burada bitmiyor.
“make install” kısmına geldiğimizde “İlla /etc/apache2/httpd.conf dosyanda ‘LoadModule’ bulacağım!” diye tutturuyor.
“Ya sabır!” çekerek açıyorum dosyayı, elle “#LoadModule tiriviri” yazıyorum ve bunu yutturmayı beceriyorum.
Son olarak apache’yi yeniden başlatıyorum phpinfo() çekince karşımda “PHP5.3.3-dev” yazısını görüyorum.

Artık çakan koduma bakıp çalışmasını keyifle izleyebilirim. Hala açık sayfaya refresh çekiyorum…ve…
Kod yine de çalışmıyor, aynı yerde çakıyor!
“Kesin encoding bu! Kesin encoding bu! Encoding ulan!” diyerek bildiğim son numaraları da deniyorum: lokal makineden bir dizini sshfs ile vps’in ilgili dizinine mount ediyorum, lokaldeki dizinden bağlanmış uzak sistem dizinine kopyala+yapıştır deniyorum… Tabii ki yine olmuyor!
…derken, bir anda ilham geliyor ve methodPrivate() içindeki tarihleri Türkçe bastırmak için kullandığım

1
setlocale(LC_ALL , 'tr_TR.UTF-8');

satırını comment ediyorum, deniyorum, a-aa çalışıyor!
O ana kadar hiç aklıma gelmemiş zira işletim sistemini kurar kurmaz apt-get install language-pack-tr-all gibisinden bir paketi de kurmuşum.
Yukarıdaki satırı

1
setlocale(LC_TIME , 'tr_TR.UTF-8');

yapıyorum (biraz altta tekrar en_GB lokaline çevirmem gerekiyor yoksa PgSQL sorgularım patlıyor) ve iş bitiyor!
Kodda bir kelimelik değişiklik yapabilmiş olmak için sisteme gecelik snapshot bir sürüm kurduğumla kalıyorum… Kendi kendime sinirli sinirli gülüyorum ve bu yazıyı yazmaya başlıyorum!

Paylaş:
  • Print
  • FriendFeed
  • Twitter
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • DZone
  • LinkedIn
  • MySpace
  • Ping.fm
  • Reddit
  • StumbleUpon
  • Technorati

Etiketler: , , ,

Her seçim bir kaybediştir.” Jean-Paul Sartre

Tedrisat itibarıyla dilbilimci/çevirmen sayılabilirim. (“Aşkın Metafiziği” kitabının başlangıcı gibi oldu ancak ben Danzig’li değilim) Doğal dillerle beraber, yapay dillerle tanışmamın da, yaşım/teknoloji karşılaştırmasını yaptığımız vakitte “erken” bir yaşta, televizyonun renkli yayına geçtiği tarihlerde, dokuz ya da on yaşımda (ve kaza eseri) olduğunu söyleyebilirim.

“Yapay dil” derken hem Esperanto, hem de Basic ve Assembly’den söz ediyorum. Aynı zamanda Internet’in kavram olarak bile yaşamımızda bulunmadığı, tüm bilebildiklerimizin, aslında bizden daha fazlasını bilmeyen “Sözde Ermeni Kıyımı” yollu “bilgisayar mühendislerinin” çevirdiği boktan kitaplardan ve bir takım ingilizce metinlerden edinilmeye çalışıldığı zamanlardan da söz ediyorum.

Sinclair ZX Spectrum ve daha sonra anamın zavallı memur maaşına yapılan taksitlerle edinilen Amstrad CPC 6128′den ve Locomotif Basic’ten, Pascal’dan, yetmedikleri yerde Assembly’den de söz ediyorum, zira o zamanlar o makinelerde oyun oynamak kod yazmaktan daha zor, kod yazmak ise deveye hendek atlatmak demekti!

Zaman içinde hasb-el-kader (daha doğrusu “merak” ve “yaşamın akışı”) bir kaç doğal ve yapay dil daha öğrenmek olanağı buldum, fırsat buldukça yenilerini inceleyebilmek ve vaktim oldukça öğrenmek için hala can atıyorum ve bu hevesim, görülebileceği gibi çocukluktan kalma. Diğer yandan, üst cümlede altı çizilecek ayrıntı benim için “vakit”.

Çocukluk hevesim programcılığın, akademik kariyerimin önüne geçip ekmek teknem haline gelmesi, 1999 yılının başlarına ve o vakit başka bir Türk programcıya gönderdiğim, yazdığı kodla ilgili bir soru içeren bir e-postaya gelen yanıta denk gelir: “Hocam, sen hakikaten dilbilimci misin? Benle kafa buluyorsun gibime geliyor. Söylediğin şeyleri ben programcı olarak yazdığım kodda düşünmemiştim.”

Elbet o vakitler çok cahildim ve nasıl bir batağa saplandığımı(!) bilmiyordum ancak kariyer değişikliği için beni tetikleyen bu küçücük “gaz” oldu. O vakitten bu yana durmak dinlenmek bilmeden öğreniyorum, kod yazıyorum, hala yazılım tasarım mantığı üstüne öğrenmeye ve öğretmeye devam ediyorum ve bugün geriye baktığımda utanmadan söyleyebilirim ki, akademik ortamlarda “şu ya da bu programlama dili”, “şu ya da bu kod tasarım örüntüsü” ile beynim düdüklenmediği için çok şanslıyım ve bu sayede aynı fakültelerde derslerde örnek verilen kodlar yazabiliyorum.

Bu uzun sayılabilecek girişi, nasıl düşündüğümü ve fikirlerimin altında yatan geçmiş alışkanlıkları ve önyargıları tanımlayabilmek için yazmak zorundaydım. Şimdiye dönelim.

Güncel durum şudur: İş çok. Buna rağmen gördüğünüz gibi blog yazmaya vakit ayırabiliyorum ve bunu temelde iki programlama diline borçluyum: Python ve PHP. Bana PHP hızlı, Python düzgün ve tertemiz kod yazdırıyor. Son on yıldır PHP ile uğraşmıyor olsam (sorunların nasıl çabucak çözülebileceğine dair deneyimi göz önüne alın) ve Python kütüphaneleri anında işimi PHP ile görebildiğim kadar zengin olsa, bugün tereddüt etmeden her şeyimi Python’a taşırdım. İşin doğrusu şu ki, bugün de çoğu hassas işlerimi Pyton’a yaptırıp, PHP’den çağırıyorum ve son üç yıldır günden güne Python’u daha çok benimsediğimi hissediyorum. Bunu ben koskoca bir PHP framework’ü yazacak derecede PHP’yi seven adam olarak söylüyorum.

Pekiyi, bu kadar vakti nereden bulabiliyorum? Basit: JAVA kodlamıyorum. JAVA biliyor muyum? Herhalde sizden iyi biliyorum(!) ve uzun vakti “Düzgün bir programlama dili nasıl olmalı?” sorusuyla geçmiş kişi olarak JAVA’yı hakikaten çok beğeniyorum ancak söz bana ait değil, anonim: “JAVA ile üç birim vakit harcayan, PHP ile bir birim vakit harcar.” Üstelik JAVA’nın PHP ve Python’a her benchmarkta üstün çıktığı da kılavuz sitemez bir köy.

Müşteri ya da patrona “Hacı bu işi JAVA/Spring ile kodlayalım, süper olur ama iki kat fazla para ve vakit isterim!” diye anlatamazsınız. Adam size fazladan $20.000 vereceğine, iş için kullandığı sunucuyu $500 fazla harcayarak daha iyi hale getirir, olur biter!

Neymiş? Hızlı kod için PHP, düzgün kod için Python, vaktiniz varsa JAVA!

Paylaş:
  • Print
  • FriendFeed
  • Twitter
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • DZone
  • LinkedIn
  • MySpace
  • Ping.fm
  • Reddit
  • StumbleUpon
  • Technorati

Etiketler: , ,