每月彙整: 2008 年 8 月

links for 2008-08-29

Zend Framework DB Load Balancing (Master/Slave Database)

網路上找來找去……
沒看到有人寫相關的解法….
加上不想用mysql-proxy來解這部份…

所以打算從code 的部份下手
看到最後只能動Zend Framework 的code 了(還沒想到可以不動ZF 的code 而做到DB LB的方法)

我是先從function save 下手去找..

Zend/Table/Row/Abstract.php:    public function save()

[sourcecode language=’php’]
public function save()
{
/**
* If the _cleanData array is empty,
* this is an INSERT of a new row.
* Otherwise it is an UPDATE.
*/
if (empty($this->_cleanData)) {
return $this->_doInsert();
} else {
return $this->_doUpdate();
}
}
[/sourcecode]

ZF會自已做update or insert 的區分

再看
[sourcecode language=’php’]
/**
* @return mixed The primary key value(s), as an associative array if the
* key is compound, or a scalar if the key is single-column.
*/
protected function _doInsert()
{
/**
* A read-only row cannot be saved.
*/
if ($this->_readOnly === true) {
require_once ‘Zend/Db/Table/Row/Exception.php’;
throw new Zend_Db_Table_Row_Exception(‘This row has been marked read-only’);
}

/**
* Run pre-INSERT logic
*/
$this->_insert();

/**
* Execute the INSERT (this may throw an exception)
*/
$data = array_intersect_key($this->_data, $this->_modifiedFields);
$primaryKey = $this->_getTable()->insert($data);

/**
* Normalize the result to an array indexed by primary key column(s).
* The table insert() method may return a scalar.
*/
if (is_array($primaryKey)) {
$newPrimaryKey = $primaryKey;
} else {
$newPrimaryKey = array(current((array) $this->_primary) => $primaryKey);
}

/**
* Save the new primary key value in _data. The primary key may have
* been generated by a sequence or auto-increment mechanism, and this
* merge should be done before the _postInsert() method is run, so the
* new values are available for logging, etc.
*/
$this->_data = array_merge($this->_data, $newPrimaryKey);

/**
* Run post-INSERT logic
*/
$this->_postInsert();

/**
* Update the _cleanData to reflect that the data has been inserted.
*/
$this->_refresh();

return $primaryKey;
}
[/sourcecode]

$primaryKey = $this->_getTable()->insert($data);

程式在
Zend/Adapter/Abstract.php
這邊有
function update()
function insert()

來看
[sourcecode language=’php’]
public function insert($table, array $bind)
{
// extract and quote col names from the array keys
$cols = array();
$vals = array();
foreach ($bind as $col => $val) {
$cols[] = $this->quoteIdentifier($col, true);
if ($val instanceof Zend_Db_Expr) {
$vals[] = $val->__toString();
unset($bind[$col]);
} else {
$vals[] = ‘?’;
}
}

// build the statement
$sql = “INSERT INTO ”
. $this->quoteIdentifier($table, true)
. ‘ (‘ . implode(‘, ‘, $cols) . ‘) ‘
. ‘VALUES (‘ . implode(‘, ‘, $vals) . ‘)’;

// execute the statement and return the number of affected rows
$stmt = $this->query($sql, array_values($bind));
$result = $stmt->rowCount();
return $result;
}
[/sourcecode]

不管insert or update
都是透過query 這個method 去執行sql

所以就在這邊下手吧

[sourcecode language=’php’]
public function query($sql, $bind = array())
{
// connect to the database if needed
if ($sql instanceof Zend_Db_Select) {
if (preg_match(‘/UPDATE|INSERT|REPLACE/i’,$sql->__toString())) {
$this->closeConnection();
$this->_config[‘dbname’] = ‘DB_WRITE’;
}
} else {
if (preg_match(‘/UPDATE|INSERT|REPLACE/i’,$sql)) {
$this->closeConnection();
$this->_config[‘dbname’] = ‘DB_WRITE’;
}
}
$this->_connect();

// is the $sql a Zend_Db_Select object?
if ($sql instanceof Zend_Db_Select) {
$sql = $sql->assemble();
}

// make sure $bind to an array;
// don’t use (array) typecasting because
// because $bind may be a Zend_Db_Expr object
if (!is_array($bind)) {
$bind = array($bind);
}

// prepare and execute the statement with profiling
$stmt = $this->prepare($sql);
$stmt->execute($bind);
// return the results embedded in the prepared statement object
$stmt->setFetchMode($this->_fetchMode);

if ($sql instanceof Zend_Db_Select) {
if (preg_match(‘/UPDATE|INSERT|REPLACE/i’,$sql->__toString())) {
$this->closeConnection();
$this->_config[‘dbname’] = ‘DB_READ’;
$this->_connect();
}
} else {
if (preg_match(‘/UPDATE|INSERT|REPLACE/i’,$sql)) {
$this->closeConnection();
$this->_config[‘dbname’] = ‘DB_READ’;
$this->_connect();
}
}
return $stmt;
}
[/sourcecode]

就是很簡單的去parse $sql 是否有UPDATE,INSERT,REPLACE 之類的寫入語法
若有的話就改config

基本上就是這樣…

如果有人有更好的做法…
拜拖 指教一下小第………..
實在是不想弄到ZF 裡面的code 來達到DB LB的功能..

Zend Framework 1.6.0 RC 2初體驗

1. 下載 Zend Framework 1.6.0 RC 2
http://framework.zend.com/download

2. 建立專案目錄
(/home/kiang/public_html/zf/) ,將下載檔案解壓縮後的 library 目錄複製到專案目錄 /home/kiang/public_html/zf/library

3. 建立基本目錄結構
[cc lang=”ini” tab_size=”2″ lines=”40″]kiang@kiang-ubuntu:~/public_html/zf$ mkdir application public
kiang@kiang-ubuntu:~/public_html/zf$ cd application/
kiang@kiang-ubuntu:~/public_html/zf/application$ mkdir controllers models views configs
kiang@kiang-ubuntu:~/public_html/zf/application$ cd ../public/
kiang@kiang-ubuntu:~/public_html/zf/public$ mkdir css images js[/cc]

4. 建立 /home/kiang/public_html/zf/.htaccess ,將所有請求引導到 public 目錄
[cc lang=”apache” tab_size=”2″ lines=”40″]RewriteEngine on
RewriteBase /~kiang/zf/
RewriteRule ^$ public/ [L]
RewriteRule (.*) public/$1 [L][/cc]

5. 建立 /home/kiang/public_html/zf/public/.htaccess
[cc lang=”apache” tab_size=”2″ lines=”40″]# Rewrite rules for Zend Framework
RewriteEngine on
RewriteBase /~kiang/zf/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* index.php
# Security: Don’t allow browsing of directories
Options -Indexes[/cc]
*RewriteBase 是因為在使用者個人目錄下,會造成預設 Rewrite 出問題

5.5設定config.ini
[cc lang=”ini” tab_size=”2″ lines=”40″]; Production site configuration data
[production]
webhost = localhost
db.adapter = pdo_mysql
db.params.host = localhost
db.params.username = user
db.params.password = pass
db.params.dbname = dbname

; Staging site configuration data inherits from production and
; overrides values as necessary
[staging: production]
db.params.host = localhost_staging
db.params.dbname = dbname_staging

[memcached]
frontend.lifetime = 7200
frontend.automatic_serialization = true
backend.host = memcached.host
backend.port = 11211
backend.persistent = false[/cc]

6. 建立 /home/kiang/public_html/zf/public/index.php
[cc lang=”php” tab_size=”2″ lines=”40″]< ?php define('RootAppPath', '/home/kiang/public_html/zf/'); error_reporting(E_ALL|E_STRICT); ini_set('display_errors', 1); date_default_timezone_set('Asia/Taipei'); // directory setup and class loading set_include_path('.' . PATH_SEPARATOR . RootAppPath . 'library/' . PATH_SEPARATOR . RootAppPath . 'application/models' .PATH_SEPARATOR.'../application/configsy' . PATH_SEPARATOR . get_include_path()); include "Zend/Loader.php"; Zend_Loader::registerAutoload(); /* DB*/ // include db config.ini $db_config = new Zend_Config_Ini('config.ini','staging'); $registry = Zend_Registry::getInstance(); $registry->set(‘db_config’, $db_config);
// setup database
$db = Zend_Db::factory($db_config->db->adapter, $db_config->db->params->toArray());
// Profiler
//$db->getProfiler()->setEnabled(true);
$registry->set(‘db’, $db);
Zend_Db_Table::setDefaultAdapter($db);
// Set DB Charset UTF8
$db->query(“SET NAMES ‘utf8′”);

/* cache */
$cache_config = new Zend_Config_Ini(‘config.ini’,’memcached’);

$frontendOptions = array(
‘lifetime’ => $cache_config->frontend->lifetime, // cache lifetime of 2 hours
‘automatic_serialization’ => $cache_config->frontend->automatic_serialization
);

// Directory where to put the cache files
$server = array(
‘host’ => $cache_config->backend->host,
‘port’ => $cache_config->backend->port,
‘persistent’ => $cache_config->backend->persistent
);

$backendOptions = array(
‘servers’ => $server
);

// getting a Zend_Cache_Core object
$cache = Zend_Cache::factory(‘Core’, ‘Memcached’, $frontendOptions, $backendOptions);
$registry->set(‘cache’, $cache);

// setup controller
$frontController = Zend_Controller_Front::getInstance();
$frontController->throwExceptions(true);
$frontController->setControllerDirectory(RootAppPath . ‘application/controllers’);
$frontController->setBaseUrl(‘/~kiang/zf/’);
Zend_Layout::startMvc(array(‘layoutPath’=> RootAppPath . ‘application/views/layouts’));
// run!
$frontController->dispatch();[/cc]

7. 建立 /home/kiang/public_html/zf/application/controllers/IndexController.php
[cc lang=”php” tab_size=”2″ lines=”40″]
< ?php class IndexController extends Zend_Controller_Action { function indexAction() { //cache if (Zend_Registry::isRegistered('cache')) { $this->cache = Zend_Registry::get(‘cache’);
$this->cache->save(‘title_val’,’title_key’);
$cache = $this->cache->load(‘cache_key’);
}
}
}[/cc]

8. 建立 /home/kiang/public_html/zf/application/views/layouts/layout.phtml
[cc lang=”php” tab_size=”2″ lines=”40″]< !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

< ?php echo $this->escape($this->title); ?>

< ?php echo $this->layout()->content; >

[/cc]

9. 建立 /home/kiang/public_html/zf/application/views/scripts/index/index.phtml
[cc lang=”php” tab_size=”2″ lines=”40″]

hello world

[/cc]

10. 連到 http://localhost/~kiang/zf/ 看看是否正確 😉

參考文件:
Zend Framework 1.6.0 RC 2 初體驗

links for 2008-08-19

ZF controller init method

[cc lang=”php” tab_size=”2″ lines=”40″]public function __construct(Zend_Controller_Request_Abstract $request, Zend_Controller_Response_Abstract $response, array $invokeArgs = array())
{
$this->setRequest($request)
->setResponse($response)
->_setInvokeArgs($invokeArgs);
$this->_helper = new Zend_Controller_Action_HelperBroker($this);
$this->init();
}[/cc]

在zf 每個 controller 中
可以用function init 這個去代替__constructor (不知道這樣形容 恰不恰當)

Whenever Zend Framework instantiates a controller, the base Zend Framework class (in our case Zend_Controller_Action) has a lot of important code in the __construct() method.  It is possible to override the __construct() in your controller but it is not recommended. Instead, Zend Framework gives us the init() method. init() is the last thing called in __construct() and it is the appropriate place for us to put anything we want executed upon construction.