Archive for the 'PHP' Category

Lấy bảng dự báo thời tiết

Dễ lắm :D Ngày xửa ngày xưa thì dùng đoạn Javascript của bên Vnexpress.net. Nhưng giờ ít ai dùng cái đó nữa vì nó quá đơn giản. Thế nên sẽ lấy ở các nguồn khác thôi. Chung quy lại, là nên lấy của thằng Trung tâm khí tượng thủy văn Việt Nam (http://www.nchmf.gov.vn) là chuẩn nhất (chuyện, của Việt Nam mà :p)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* Get weather table
* @author Do Nam Khanh <donamkhanh@gmail.com>
* @return string
*/
function _weather() {
	$content = file_get_contents('http://www.nchmf.gov.vn/website/vi-VN/43/Default.aspx');
 
	$strBegin 	= '<!-- Begin Display content -->';
	$strEnd 	= '<TD class="thoitiet_rightbox_ver"></TD>';
 
	$pos1 = strpos($content, $strBegin);
 
	if(false === $pos1) {
		return '';
	}
	else {
		$pos2 = strpos($content, $strEnd);
		$content = substr($content, $pos1, ($pos2-$pos1));
		$content = str_replace('Cập nhật lúc', lang('updated_at'), $content);
 
		return strip_tags($content, '<table><tr><td><img>');
	}
}

Demo: http://halongtours.vn/demo

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Một PHP class tạo file ZIP

Tôi đang phải làm  chức năng nén/giải nén trong dự án hiện tại. Giải nén thì đã làm xong rồi, còn nén nữa là xong. Zip library của PHP hỗ trợ chức năng zip hơi bị kém, cái cơ bản nhất là cũng ngại phải viết => search & thấy class này khá nhẹ để nén 1 folder.

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
<?php
	require ("zipfile.inc.php");
 
	$zipfile = new zipfile();
 
	rec_listFiles('myFolder');
 
	header("Content-type: application/octet-stream");
	header("Content-disposition: attachment; filename=zipfile.zip");
	echo $zipfile->file();
 
	function rec_listFiles( $from = '.')
	{
		global $zipfile, $filedata;
 
		if(! is_dir($from))
			return false;
 
		if( $dh = opendir($from))
		{
			while( false !== ($file = readdir($dh)))
			{
				// Skip '.' and '..'
				if( $file == '.' || $file == '..')
					continue;
				$path = $from . '/' . $file;
				if( is_dir($path) ) {
					$zipfile->add_dir($path);
					rec_listFiles($path);
				}
				else {
					$filedata = implode("", file($path));
					$zipfile->add_file($filedata, $path);
				}
			}
			closedir($dh);
		}
	}
?>

Tạm đáp ứng yêu cầu :)

Source: http://www.devco.net/archives/2005/05/24/creating_zip_files_with_php.php

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

PHP post_max_size issue

Hôm nay tôi gặp phải 1 “tình huống” khá là củ chuối. Phần upload file (thật ra là upload file để update lại các store - khá nhiều, hiện tại là 1500 store) có validate file size, file type. Tôi test “khá kỹ ” ở local & mọi thứ đều ổn. Thông số local của tôi như sau:

  • Windows XP/Vertrigo
  • post_max_size = 100M
  • upload_max_size = 20M

Khi up 1 file có dung lượng quá 20MB (<100MB) thì hệ thống hiện thông báo rất đẹp với nội dung là bạn đã upload quá dung lượng quy định bla bla…:-@

Thế nhưng sau khi request done trên PMS (project management system), bên test report lại là khi upload 1 file lớn hơn 16MB thì chẳng hiện gì cả, không lỗi & giống như tự F5 lại trang.

Xem phpinfo của môi trường trên testing thì như sau:

  • post_max_size = 8M
  • upload_max_size = 16M

Vậy là dung lượng vượt quá post_max_size rồi, và PHP đã làm thế này đối với đoạn code của tôi:

If the size of post data is greater than post_max_size, the $_POST and $_FILES superglobals are empty

Thế cho nên đoạn code:

1
2
3
4
$postMaxSize = int_get('post_max_size');
if($postMaxSize < $userFile['size']) {
//process error
}

chẳng hoạt động gì cả :((
Trên php.net có 1 trick để xử lý trường hợp này:

This can be tracked in various ways, e.g. by passing the $_GET variable to the script processing the data, i.e.

, and then checking if $_GET['processed'] is set.

Và đây là đoạn code của tôi:

1
2
3
if(isset($_GET['processed']) && !count($_POST)) {
  die('Error message here...');
}

Chạy ngon ;)

Rất cảm ơn anh NBThanh (Nguyễn Bá Thành) đã giúp đỡ em khắc phục vấn đề này :)

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Upgrade whois domain to v3.1

Lý do phải nâng cấp:
- Matbao.net đã thay đổi lại cấu trúc site của họ (thay đổi giao diện) nên phần parse data của mình sẽ không chính xác nữa.
- Dùng nsloop-up chỉ có thể check domain đó có hoạt động hay không, chứ không check được đã được mua hay chưa. có thể chủ sở hữu đã mua nhưng chưa active domain :)

Những thay đổi:
Không có nhiều thay đổi trong phiên bản nâng cấp này ngoài:
- Get xml content thay cho html content.
- Chuẩn hóa các function theo camelCase.
- Bỏ php short tag, chuyển về <?php echo …;?> nếu có.

Todo:
Mặc dù kết quả nhận được từ matbao.net là dạng XML nhưng vẫn gặp khó khăn trong khâu parse XML. Dùng các libs có sẵn trong PHP như simpleXML, XMLparse…đều bị lỗi do syntax của file XML này không chuẩn cho lắm. Tạm thời tôi dùng preg_match để parse XML. Khi nào có điều kiện sẽ nghiên cứu cách parse XML mà không biết trước cấu trúc :D Có bác nào tốt bụng thì phát triển thêm hộ tôi với :p

Hướng dẫn nâng cấp:
- Download bản upgrade tại http://donamkhanh.com/download/whoisv3.1.php.txt
- Sau khi download, rename file whoisv3.1.php.txt thành index.php, rồi ghi đè file index.php trong thư mục whoisv3 (nếu chưa có phiên bản 3.0 thì có thể download tại http://donamkhanh.com/download/whoisv3.tar.bz2)

Yêu cầu:
- Chmod quyền hợp lý (cho đọc ghi đối với file checked_domain.txt)
- Thông số allow_url_fopen trên host phải thiết lập bằng On.

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Export to CSV file with Unicode

Ở bài trước tôi có đề cập đến việc export dữ liệu ra file CSV. Tuy nhiên, nếu export dữ liệu có chứa các ký tự Unicode thì sẽ không hiển thị được mặc dù khi mở bằng Notepad thì vẫn hiển thị đúng, tuy nhiên khi mở bằng Excel thì không hiển thị chính xác. Search trên mạng thấy giải pháp của anh Nguyễn Văn Hùng (Hưng?) đã giải quyết được (tôi mới test với German characters trong dự án Shop24 - rất okie).

Bài toán: export dữ liệu tiếng Việt UTF-8 thành file CSV có thể hiển thị đúng khi mở bằng Excel.
3 điểm dẫn đến thành công:
+ Dùng TAB (\t) thay cho COMMA (,) để phân tách các cột
+ Convert Encoding của dữ liệu cần output bằng UTF-16LE
+ Gắn chr(255)chr(254) vào đầu của kết quả cuối cùng trước khi output

PHP code đầy đủ (export order list):

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
/**
 * EXPORT ORDER LIST TO CSV FILE
 * @author khanhdn
 */
function exportCSV() {
	global $user;
	member_access();
 
	$memberInfo = member_info();
 
	$result = drupal_query("SELECT order_id
					,order_code
					,bill_firstname
					,bill_lastname
					,order_modify_date
					,gand_total
					,order_status
					 FROM {order}
					 WHERE shop_code = '".$memberInfo['shop_code']."'
					 ORDER BY order_creation_date DESC");
 
	$status_options = array(
                                '1'  => 'Neu'
				,'2' => 'In Bearbeitung'
				,'3' => 'Auf der Post'
				,'4' => 'Ausgeführt'
				,'5' => 'Zurück'
				);
 
	$csv = "Order code\tCustomer Name\tModify Date\tOrder Total\tOrder Status\r\n";
 
	if(count($result['data']))
	{
		foreach($result['data'] as $row)
		{
			$order_list = array(
							'order_code' 		=> "$row->order_code"
							,'customer_name' 	=> $row->bill_firstname. ' ' .$row->bill_lastname
							,'modify_date' 		=> mysqlTimestamp(strtotime($row->order_modify_date),'d.m.Y')
							,'order_total'		 => 'CHF '.$row->gand_total
							,'order_status'		=> $status_options[$row->order_status]
			);
 
			$csv .= join("\t", $order_list)."\r\n";
		}
 
	}
	$csv = chr(255).chr(254).mb_convert_encoding($csv, "UTF-16LE", "UTF-8");
 
	header("Content-type: application/x-msdownload");
	header("Content-disposition: csv; filename=" . date("Y-m-d") .
	"_order_list.csv; size=".strlen($csv));
	echo $csv;
	exit();
}

Source: Nguyễn Văn Hùng weblog

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Page expried problem

Hôm nay tôi dính phải 1 bug khá củ chuối: sau khi submit form, back button không hoạt động trên IE6 browser. Khi click vào nút back này sẽ xuất hiện 1 thông báo như sau:

Warning: page has expired The page you requested was created using information you submitted in a form. This page is no longer available. As a security precaution, Internet Explorer does not automatically resubmit your information for you.

To resubmit your information and view this Web page, click the Refresh button.

Sau một hồi tham khảo các cách giải quyết trên mạng thì sinh ra được cái hàm này:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* Fix back button on IE6 (stupid) browser
* @author khanhdn
*/
function fixBackButtonOnIE() {
//drupal_set_header("Expires: Sat, 27 Oct 1984 08:52:00 GMT GMT");    // Always expired (1)
//drupal_set_header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");    // always modified (2)
drupal_set_header("Cache-Control: no-store, no-cache, must-revalidate");    // HTTP/1.1 (3)
drupal_set_header("Cache-Control: public");    (4)
drupal_set_header("Pragma: no-cache");    // HTTP/1.0   (5)
 
ini_set('session.cache_limiter', 'private');    (6)
}

Tạm comment cái (1) (2), vẫn chạy okie. Có lẽ chỉ cần giữ lại (3) (4) (5) là đủ???

Dù sao cũng giải quyết được cái bug này >”<

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Using command - DirManager class

Mấy hôm trước lại được làm 1 task liên quan đến thao tác với thư mục bằng PHP :) Thật ra trước đó đã từng làm rồi nhưng giờ vẫn khoái…viết lại, bởi quan trọng nhất là cái class đó…không còn trong máy tính ở cty nữa :)) Ở nhà thì ko tiện remote connect vào (có ai mở hộ máy đâu mà vào :-p) nên quyết định viết lại & phải chạy được cả trên Unix & Windows.

Class DirManager tạm thời có mấy method sau:

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
<?php
/**
     * File manager class
     * @author khanhdn
     */
 
    class DirManager {
        public $sourcePath      = '';
        public $destinationPath = '';
        private $operaSystem    = '';
        static $instance;
 
        public function getInstance() 
        { 
            if (self::$instance == null) { 
                self::$instance = new DirManager(); 
            } 
 
            return self::$instance; 
        } 
 
        public function __construct() {
 
        }
 
        /**
         * check opera system
         * @return bool true if Linux, false if Windows
         */
        private function isLinuxOS() {
           $this->operaSystem = $_SERVER['SERVER_SOFTWARE'];
 
           if(stripos($this->operaSystem, 'Win32')) {
                return false;
           }
 
            return true;
        }
 
        /**
         * Create directory
         * @param string folderName
         */
        public function createDir() {
            if(!is_dir($this->sourcePath)) {
                if(!mkdir($this->sourcePath, 0777)) {
                   die('Could not create sub-domain.');
                }
            }
            else {
                die($this->sourcePath.' is exist.');
            }
        }
 
        /**
         * copy using php function
         * @param string source
         * @param string dest
         */
        private function windowsCopyDir($source, $dest)
        {
            if(!is_dir($dest)) {
                mkdir($dest, 0777);
            }
            if($curdir = opendir($source)) {
                while($file = readdir($curdir)) {
                    if($file != '.' &amp;&amp; $file != '..') {
                        $srcfile = $source . '/' . $file;
                        $dstfile = $dest . '/' . $file;
 
                        if(is_file($srcfile)) {
                            copy($srcfile, $dstfile);
                        }
                        else if(is_dir($srcfile)) {
                            $this->windowsCopyDir($srcfile, $dstfile);
                        }
                    }
              }
              closedir($curdir);
            }
        }
 
        /**
         * Copy directory and all sub directory in it
         * @param bool useCommand
         */
        public function copyDir($useCommand=true) {
            if(is_dir($this->sourcePath)) {
                if($this->isLinuxOS()) {
                    if($useCommand) {
                        exec("cd {$this->sourcePath}; shopt -s dotglob; cp -pr . {$this->destinationPath}");
                    }
                    else {
                        $this->WindowsCopyDir($this->sourcePath, $this->destinationPath);
                    }
                 }
                 else {
                    if($useCommand) {
                        exec("xcopy {$this->sourcePath} {$this->destinationPath} /E");
                    }
                    else {
                        $this->WindowsCopyDir($this->sourcePath, $this->destinationPath);
                    }
                 }
            }
            else {
                die($this->sourcePath.' is not a directory.');
            }
        }
 
        /**
         * Remove directory and all sub directory in it
         * @param bool useCommand
         */
        public function removeDir($useCommand=false) {
            if(is_dir($this->sourcePath)) {
                if($this->isLinuxOS()) {
                    if($useCommand) {
                        exec("rm -rf {$this->sourcePath} {$this->destinationPath}");
                    }
                    else {
                        $this->windowsRemoveDir($this->sourcePath);
                    }
                }
                else {
                    if($useCommand) {
                        exec("DELLTREE {$this->sourcePath}");
                    }
                    else {
                        $this->windowsRemoveDir($this->sourcePath);
                    }
                }
            }
            else {
                die($this->sourcePath.' is not a directory.');
            }
        }
 
        /**
         * @param string source folder to delete
         * @param int level
         */
        private function windowsRemoveDir($source, $level=0) {
            // Trim the trailing slash
            $source = preg_replace("|^(.+?)/*$|", "\\1", $source);
 
            if(is_dir($source)) {
                if ( ! $current_dir = @opendir($source))
                    return;
 
                while(FALSE !== ($filename = @readdir($current_dir))) {
                    if ($filename != "." &amp;&amp; $filename != "..") {
                        if (is_dir($source.'/'.$filename)) {
                            $this->windowsRemoveDir($source.'/'.$filename, $level + 1);
                        }
                        else {
                            unlink($source.'/'.$filename);
                        }
                    }
                }
 
                @closedir($current_dir);
                @rmdir($source);
            }
            else {
                unlink($source);
            }
        }
    }
?>

Đoạn code trên chắc chắn với những lập trình viên có kinh nghiệm hay nói cách khác là trình độ, họ sẽ khẽ mĩm cười & chẳng nói gì đâu. Còn nhiều vấn đề. Tôi cũng biết điều đó :( Viết OOP không phải cứ quẳng 1 đống hàm vào trong class thì nó là OOP :cry: Nhưng, tạm thời đến lúc này trình độ của tôi chỉ có vậy. Sẽ còn phải cố nhiều đây :clown:

Một điểm thú vị khi viết method copyDir đó là nếu set $useCommand = true, tức là sử dụng command line để copy files & folders (nhanh hơn là dùng PHP để đọc & copy), nếu chạy trên Windows, okie, chạy tốt (trên máy của tôi :p), còn trên Linux thì nó lại copy nguyên cái thư mục nguồn vào bên trong thư mục đích. Cái tôi cần ở đây là chỉ copy các file & folder ở bên trong thư mục nguồn vào thư mục đích.

Và tôi sai ở đây:

1
exec("cp -pr {$this->sourcePath} {$this->destinationPath}");

Đúng ra phải là:

1
exec("cd {$this->sourcePath}; shopt -s dotglob; cp -pr . {$this->destinationPath}");

Tức là:
- Set thư mục nguồn làm thư mục gốc (nhảy vào bên trong thư mục nguồn).
- Hiện tất cả file ẩn.
- Copy tất cả file & folder ở thư mục gốc vào thư mục đích

=> Chạy rất nuột :p

1
2
3
4
$dirObj = DirManager::getInstance();
$dirObj->sourcePath      = 'foo';
$dirObj->destinationPath = 'bar';
$dirObj->copyDir();

Toàn bộ đám bậu sậu trong foo đã nằm trọn trong vòng tay của bar :p

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Export to CSV file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$row[] = 'Column 1';
$row[] = 'Column 2';
$row[] = 'Column 3';
$data .= implode(',', $row)."n"; // Join all values without any trailing commas and add a new line 
 
$row = ''; // We must clear the previous values
$row[] = 'Value of column 1';
$row[] = 'Value of column 2';
$row[] = 'Value of column 3';
$data .= implode(',', $row)."n"; 
 
// Output the headers to download the file
header("Content-type: application/x-msdownload");
header("Content-Disposition: attachment; filename=file.csv");
header("Pragma: no-cache");
header("Expires: 0");
echo $data;

Đại khái cú pháp để có thể export ra file csv là:
- Dòng đầu tiên là tên các cột, đặt trong dấu “…” và phân cách nhau bởi dấu phẩy.
- Các dòng còn lại là các giá trị của cột tương ứng, khi gặp ký tự kết thúc hàng thì sẽ tự động (?) tạo hàng mới.
- Cuối cùng gọi header tương ứng.

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Working with checkbox

Tôi thường hay dùng checkbox trong trường hợp list các record ra & cho phép chọn record tương ứng để xóa. Ngày trước, khi muốn lấy giá trị của các checkbox được chọn, tôi làm như sau:

1
2
3
4
5
6
7
8
9
if(is_array($_POST['checkbox_name'])) {
    $list_selected_id = '';
    foreach($_POST['checkbox_name'] as $key =&gt; $val) {
        $list_selected_id .= ','.$val;
    }
 
    $list_selected_id = substr($list_selected_id,1);
    //output will be: 1,2,3,...,n
}

Còn ngày nay, chỉ với 1 dòng đơn giản như sau:

1
$list_selected_id = implode(',', $_POST['checkbox_name']);

Hix, quá ngắn gọn, quá sáng sủa :(

Và thêm 1 điều nữa, tôi thấy Drupal (và các framework khác) thường dùng cách implode mảng để build các câu SQL. Quả thực khi áp dụng đúng lúc đúng chỗ thì phát huy tác dụng khỏi phải bàn cãi :D

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Refer URL

Một kinh nghiệm khá hay khi khần view có liên quan đến permission (login required).

Bình thường tôi code phần này khá ẩu, nếu user truy cập vào 1 page đòi hỏi xác thực (authorized) thì tôi chỉ làm đơn giản là redirect nó sang login form. Nếu nhập đúng user & pass thì lại redirect nó về trang home. Vấn đề là ở đây. Khách hàng yêu cầu là khi login xong, phải trở lại đúng trang trước đó.

Dùng session để giải quyết. Okie, nhưng một số chỗ session chết sặc tiết. Vậy tại sao không đẩy nó lên URL nhỉ? Và nữa, viết thêm 1 cái hàm chuyên kiểm tra đã login hay chưa như sau:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function check_login($uid)
 
{
    global $user,$base_url;
    if(!$user-&gt;uid) {
        $preURL = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
        header('Location: '.$base_url.'/member/login?target='.$preURL);
    }
}
 
function loginProcess() {
   //if login successful
      header('Location: '.$_GET[