月度归档:2015年01月

Zend Framework 1.x 自定义 MVC 流程

Zend Framework 1.x 提供了Zend_Application组件实现一个MVC流程,实际可以完全可以抛开而自定义一个MVC流程,比如我希望把模块名称分别在视图和控制器和模型文件夹中进行组织:
directory

把网站指定的public目录下并添加.htaccess文件:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]

自定义index.php文件(这里面的各个部分可以封装起来):

<?php
// 根目录
defined('APP_PATH')
|| define('APP_PATH', realpath(dirname(__FILE__) . '/..'));

// 搜索路径
set_include_path(
        implode(
                PATH_SEPARATOR,
                array(
                        realpath(APP_PATH . '/models'),
                        realpath(APP_PATH . '/plugins'),
                        realpath(APP_PATH . '/library'),
                        get_include_path(),
                )
        )
);

// 加载配置
require_once APP_PATH.'/configs/config.php';

// 自动类装载
require_once 'Zend/Loader/Autoloader.php';
$loader = Zend_Loader_Autoloader::getInstance();
$loader->setFallbackAutoloader(true);

// 前端控制器配置
$front = Zend_Controller_Front::getInstance();
$front->setParam("noViewRenderer", true);
$front->returnResponse(true);

// 多模块
$modules = array();
foreach($config['modules'] as $module=>$path){
        $modules[$module] = APP_PATH.$path;
}
if(empty($modules) || !isset($modules['default'])){
        $modules['default'] = APP_PATH.'/controllers/defalut';
}
$front->setControllerDirectory($modules);

// 注册插件
foreach($config['plugins'] as $plugin){
//      $front->registerPlugin(new $plugin);
}

// 设置路由
$router = $front->getRouter();

// 设置数据库
$params = $config['db'];
$db = Zend_Db::factory("Pdo_Mysql",$params);
Zend_Db_Table::setDefaultAdapter($db);
//$db->query("SET NAMES UTF8");

Zend_Registry::set("db", $db);

// 设置布局
$layout = new Zend_Layout();
$layout->setLayoutPath(APP_PATH."/views/layouts");
Zend_Registry::set("layout", $layout);

// 设置视图
$view = new Zend_View();
//$view->setBasePath(APP_PATH."/views");
$view->setScriptPath(APP_PATH."/views/scripts");
Zend_Registry::set("view",$view);

// 前端控制器分发
$response = $front->dispatch();

// 输出
$response->sendHeaders();
echo $response->getBody();

这里指定了控制器目录,关闭了默认视图,自定视图等,通过一个conf.php文件引入配置:

$config = array(
		"db" => array(
				"host"=>"localhost",
				"username"=>"root",
				"password"=>"root",
				"dbname"=>"test",
		),
		"modules" => array(
				"default"=>"/controllers/default",
				"admin"=>"/controllers/admin"
		),
		"plugins" => array("MyPlugin"),
);

可能会觉得为何不使用XML或者INI文件而产生疑惑,原因在于原生的PHP数组不需要进行任何转换,它是最高效的办法。

控制器 和 视图:

//controllers/default/IndexController.php
<?php
class IndexController extends Zend_Controller_Action{
        public function init(){
                $this->db = Zend_Registry::get("db");
                $this->view = Zend_Registry::get("view");
        }
        public function indexAction(){
        }
        public function testAction(){
                $emails = $this->db->fetchAll("SELECT * FROM emails ORDER BY rand() LIMIT 10");

                $this->view->emails = $emails;

                echo $this->view->render("default/index/index.phtml");
        }
}

//views/script/default/index/index.phtml
<?php
print_r($this->emails);

以上代码可以正确工作。

设置了多模块之后,控制器名称需要由 模块名+”_”+控制器名称+”Controller“组成。比如:

<?php
class Admin_IndexController extends Zend_Controller_Action{}

这里的首字母是大写的,实际上换成小写的也可以工作,它必须不区分大小写,因为它需要对应URI:admin/index,但是中间的下划线是必须的,同理,控制器中的Action方法应该也是不区分大小写的,实际上在确定Action方法是都被转换成小写字母,所以如果定义的方法有大写字母,那么在比较的时候讲无法匹配,那么就认为这个方法不存在,所以Action方法不应该包含大写字母。

在自定义Zend_View时,有两个方法需要注意,视图有基准路径和脚本路径的概念,用setBasePath设置基准路径,所有脚本都放到这个目录(默认放入到scripts目录),可以用setScriptPath设置脚本放置的目录(或者不指定基路径,这里直接指定绝对路径),也可以调用addScriptPath添加多个脚本路径。

另外一种非常有用的方法是插件,当要把代码注入到某个MVC流程时,它就非常有用:

class MyPlugin extends Zend_Controller_Plugin_Abstract{
	public function preDispatch(Zend_Controller_Request_Abstract $request){
		echo "predispatch call().";
	}
}
// 为了使插件可用
$front->registerPlugin(new MyPlugin());

jQuery插件 – jQuery Validation Plugin

下载地址:http://jqueryvalidation.org

———————————————————————
有如下特点:
1.内置验证规则: 拥有必填、数字、Email、URL和信用卡号码等19类内置验证规则
2.自定义验证规则: 可以很方便地自定义验证规则
3.简单强大的验证信息提示: 默认了验证信息提示,并提供自定义覆盖默认的提示信息的功能
4.实时验证: 可通过keyup或blur事件触发验证,而不仅仅在表单提交的时候验证

使用方法:

// 应用库文件
<script src="jquery.js"></script>
<script src="jquery.validate.js"></script>
// 确定应用到哪个表单
$("#commentForm").validate();

Validation插件的验证信息默认语言是英文,如果要改成中文,只需要引入Validation提供的中文验证信息即可

<script src="scripts/jquery.validate.messages_cn.js" type="text/javascript"></script> 

———————————————————————

可以使用注释语法 和 自定义语法添加自定义规则。比如:

<input id="cemail" type="email" name="email" required>

$("#commentForm").validate();

其中required表示字段必填,type为email表示填入的内容必须是一个合法的电子邮件地址。也可以使用自定义规则:

<input id="cemail" type="email" name="email">

$("#signupForm").validate({
    rules: {
        email: {
	    required: true,
	    email: true
	}
    },
    messages: {
        email: "Please enter a valid email address"
    }
});

看起来,注释语法更加方便一些。这是基本用法,具体例子可以参考:

整个插件,实际就是在表单上调用validate方法,这样表单字段就会按照规则验证。跟jQuery风格一致,这个方法支持一个可选的配置对象。

1 debug (default: false) 是否开启debug模式,如果为true,表单不会提交并且相关信息会在终端显示
2 submitHandler (default: native form submit) 表单通过验证后的回调函数,比如要通过Ajax方式提交表单,这里提供接口。默认是表单默认行为。

$(".selector").validate({
	submitHandler: function(form) {
		$(form).ajaxSubmit();
	}
});

3 invalidHandler 表单没有通过验证时的回调函数,对错误进行统一处理

$(".selector").validate({
	invalidHandler: function(event, validator) {
	// 'this' refers to the form
	var errors = validator.numberOfInvalids();
	if (errors) {
		var message = errors == 1
			? 'You missed 1 field. It has been highlighted'
			: 'You missed ' + errors + ' fields. They have been highlighted';
			$("div.error span").html(message);
			$("div.error").show();
		} else {
			$("div.error").hide();
		}
	}
});

这里的validator参数是指应用到当前表单的验证器。
4 ignore (default: “:hidden”) 排除不需要验证的表单元素

$("#myform").validate({
	ignore: ".ignore"
});

默认隐藏表单元素被忽略,另外提交和重置按钮总是被忽略的。
5 rules (default: rules are read from markup (classes, attributes, data)) 默认规则从标签的类 属性 data中读取
规则是一组键值对,键是元素的名称,值是一个包含规则/参数对 或 是一个纯文本。 Can be combined with class/attribute/data rules. 每个规则可以有一个特定条件的依赖属性。

$(".selector").validate({
	rules: {
		// simple rule, converted to {required:true}
		name: "required",
		// compound rule
		email: {
			required: true,
			email: true
		}
	}
});

$(".selector").validate({
	rules: {
		contact: {
			required: true,
			email: {
				depends: function(element) {
					return $("#contactform_email").is(":checked");
				}
			}
		}
	}
});

$(".selector").validate({
	rules: {
		// at least 15€ when bonus material is included
		pay_what_you_want: {
			required: true,
			min: {
				// min needs a parameter passed to it
				param: 15,
				depends: function(element) {
					return $("#bonus-material").is(":checked");
				}
			}
		}
	}
});

6 messages (default: the default message for the method used)

$(".selector").validate({
	rules: {
		name: "required",
		email: {
			required: true,
			email: true
		}
	},
	messages: {
		name: "Please specify your name",
		email: {
			required: "We need your email address to contact you",
			email: "Your email address must be in the format of name@domain.com"
		}
	}
});

$(".selector").validate({
	rules: {
		name: {
			required: true,
			minlength: 2
		}
	},
	messages: {
		name: {
			required: "We need your email address to contact you",
			minlength: jQuery.format("At least {0} characters required!")
		}
	}
});

7 groups 对错误信息进行分组

$("#myform").validate({
	groups: {
		username: "fname lname"
	},
	errorPlacement: function(error, element) {
		if (element.attr("name") == "fname" || element.attr("name") == "lname" ) {
			error.insertAfter("#lastname");
		} else {
			error.insertAfter(element);
		}
	}
});

username由元素fname和lname是组成(组成元素中间用空格隔开)….
8 onsubmit (default: true)
在提交表单是验证表单。
9 onfocusout
10 onkeyup
11 onclick
针对单选和复选框
…..
详情内容参考(http://jqueryvalidation.org/validate)

当在一个表单上调用validate方法后,它返回一个应用到当前表单的validate,一些回调函数也需要携带这个validate的引用。

    Validator.form() – Validates the form.
    Validator.element() – Validates a single element.
    Validator.resetForm() – Resets the controlled form.
    Validator.showErrors() – Show the specified messages.
    Validator.numberOfInvalids() – Returns the number of invalid fields.

还提供了一些静态方法:

    jQuery.validator.addMethod() – Add a custom validation method.
    jQuery.validator.format() – Replaces {n} placeholders with arguments.
    jQuery.validator.setDefaults() – Modify default settings for validation.
    jQuery.validator.addClassRules() – Add a compound class method.

比如要添加一个验证方法,addMethod就用得上。

以下是内置的验证方法:

    required – Makes the element required.
    remote – Requests a resource to check the element for validity.
    minlength – Makes the element require a given minimum length.
    maxlength – Makes the element require a given maxmimum length.
    rangelength – Makes the element require a given value range.
    min – Makes the element require a given minimum.
    max – Makes the element require a given maximum.
    range – Makes the element require a given value range.
    email – Makes the element require a valid email
    url – Makes the element require a valid url
    date – Makes the element require a date.
    dateISO – Makes the element require an ISO date.
    number – Makes the element require a decimal number.
    digits – Makes the element require digits only.
    creditcard – Makes the element require a credit card number.
    equalTo – Requires the element to be the same as another one

官方提供的例子:更多例子

Zend Framework 1.X Zend_Json组件

PHP 5.2开始支持json_encode()和json_decode()这两个函数。实际上,这两个函数已经可以工作的非常好了,Zend Framework 1.X中Zend_Json组件实现的两个基本方法,默认是对它们的包装而已,不过提供了一个叫把xml装换成json的函数:

public function jsonAction(){
	$xml = "<?xml version='1.0'?>
<root>
  	<ele name='id_1' id='1'>1</ele>
	<ele name='id_2' id='2'>2</ele>
	<bok name='test' id='tttt'>
		<one ids='111' name='vfeelit'>111</one>
		<one ids='222' name='ifeeline'>222</one>
		<two name='id22'>2</two>
	</bok>
</root>";
        // XML  -> Json
	$js = Zend_Json::fromXml($xml,false);
        // Json -> PHP变量
	$js = Zend_Json::decode($js);
	print_r($js);
		
	echo $js['root']['ele'][0]['@attributes']['name'];		
}

////////////////////////////
// 输出
Array
(
    [root] => Array
        (
            [ele] => Array
                (
                    [0] => Array
                        (
                            [@attributes] => Array
                                (
                                    [name] => id_1
                                    [id] => 1
                                )

                            [@text] => 1
                        )

                    [1] => Array
                        (
                            [@attributes] => Array
                                (
                                    [name] => id_2
                                    [id] => 2
                                )

                            [@text] => 2
                        )

                )

            [bok] => Array
                (
                    [one] => Array
                        (
                            [0] => Array
                                (
                                    [@attributes] => Array
                                        (
                                            [ids] => 111
                                            [name] => vfeelit
                                        )

                                    [@text] => 111
                                )

                            [1] => Array
                                (
                                    [@attributes] => Array
                                        (
                                            [ids] => 222
                                            [name] => ifeeline
                                        )

                                    [@text] => 222
                                )

                        )

                    [two] => Array
                        (
                            [@attributes] => Array
                                (
                                    [name] => id22
                                )

                            [@text] => 2
                        )

                    [@attributes] => Array
                        (
                            [name] => test
                            [id] => tttt
                        )

                )

        )

)
id_1

Zend_Json::fromxml把XML转换为Json字符串,方法第一参数自然是XML字符串了,第二参数是控制是否读取XML元素的属性,默认为true表示忽略属性。如果要把XML转换成Json字符串,这的确是一个比较好封装,如果要把XML转换成PHP数组,再经过一次转换也是一个办法。

Zend_Json组件一般直接使用Zend_Json::decode()、Zend_Json::encode()和Zend_Json::fromxml()三个方法。

XML转换成PHP数组

XML文档或字符串,要转换成PHP的数组,PHP语言本身并没有提供支持。经搜索找到一个第三方函数,特收藏之。

原文链接:http://www.bin-co.com/php/scripts/xml2array/

<?php
/**
 * xml2array() will convert the given XML text to an array in the XML structure.
 * Link: http://www.bin-co.com/php/scripts/xml2array/
 * Arguments : $contents - The XML text
 *                $get_attributes - 1 or 0. If this is 1 the function will get the attributes as well as the tag values - this results in a different array structure in the return value.
 *                $priority - Can be 'tag' or 'attribute'. This will change the way the resulting array sturcture. For 'tag', the tags are given more importance.
 * Return: The parsed XML in an array form. Use print_r() to see the resulting array structure.
 * Examples: $array =  xml2array(file_get_contents('feed.xml'));
 *              $array =  xml2array(file_get_contents('feed.xml', 1, 'attribute'));
 */
function xml2array($contents, $get_attributes=1, $priority = 'tag') {
    if(!$contents) return array();

    if(!function_exists('xml_parser_create')) {
        //print "'xml_parser_create()' function not found!";
        return array();
    }

    //Get the XML parser of PHP - PHP must have this module for the parser to work
    $parser = xml_parser_create('');
    xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, "UTF-8"); # http://minutillo.com/steve/weblog/2004/6/17/php-xml-and-character-encodings-a-tale-of-sadness-rage-and-data-loss
    xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
    xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
    xml_parse_into_struct($parser, trim($contents), $xml_values);
    xml_parser_free($parser);

    if(!$xml_values) return;//Hmm...

    //Initializations
    $xml_array = array();
    $parents = array();
    $opened_tags = array();
    $arr = array();

    $current = &$xml_array; //Refference

    //Go through the tags.
    $repeated_tag_index = array();//Multiple tags with same name will be turned into an array
    foreach($xml_values as $data) {
        unset($attributes,$value);//Remove existing values, or there will be trouble

        //This command will extract these variables into the foreach scope
        // tag(string), type(string), level(int), attributes(array).
        extract($data);//We could use the array by itself, but this cooler.

        $result = array();
        $attributes_data = array();
        
        if(isset($value)) {
            if($priority == 'tag') $result = $value;
            else $result['value'] = $value; //Put the value in a assoc array if we are in the 'Attribute' mode
        }

        //Set the attributes too.
        if(isset($attributes) and $get_attributes) {
            foreach($attributes as $attr => $val) {
                if($priority == 'tag') $attributes_data[$attr] = $val;
                else $result['attr'][$attr] = $val; //Set all the attributes in a array called 'attr'
            }
        }

        //See tag status and do the needed.
        if($type == "open") {//The starting of the tag '<tag>'
            $parent[$level-1] = &$current;
            if(!is_array($current) or (!in_array($tag, array_keys($current)))) { //Insert New tag
                $current[$tag] = $result;
                if($attributes_data) $current[$tag. '_attr'] = $attributes_data;
                $repeated_tag_index[$tag.'_'.$level] = 1;

                $current = &$current[$tag];

            } else { //There was another element with the same tag name

                if(isset($current[$tag][0])) {//If there is a 0th element it is already an array
                    $current[$tag][$repeated_tag_index[$tag.'_'.$level]] = $result;
                    $repeated_tag_index[$tag.'_'.$level]++;
                } else {//This section will make the value an array if multiple tags with the same name appear together
                    $current[$tag] = array($current[$tag],$result);//This will combine the existing item and the new item together to make an array
                    $repeated_tag_index[$tag.'_'.$level] = 2;
                    
                    if(isset($current[$tag.'_attr'])) { //The attribute of the last(0th) tag must be moved as well
                        $current[$tag]['0_attr'] = $current[$tag.'_attr'];
                        unset($current[$tag.'_attr']);
                    }

                }
                $last_item_index = $repeated_tag_index[$tag.'_'.$level]-1;
                $current = &$current[$tag][$last_item_index];
            }

        } elseif($type == "complete") { //Tags that ends in 1 line '<tag />'
            //See if the key is already taken.
            if(!isset($current[$tag])) { //New Key
                $current[$tag] = $result;
                $repeated_tag_index[$tag.'_'.$level] = 1;
                if($priority == 'tag' and $attributes_data) $current[$tag. '_attr'] = $attributes_data;

            } else { //If taken, put all things inside a list(array)
                if(isset($current[$tag][0]) and is_array($current[$tag])) {//If it is already an array...

                    // ...push the new element into that array.
                    $current[$tag][$repeated_tag_index[$tag.'_'.$level]] = $result;
                    
                    if($priority == 'tag' and $get_attributes and $attributes_data) {
                        $current[$tag][$repeated_tag_index[$tag.'_'.$level] . '_attr'] = $attributes_data;
                    }
                    $repeated_tag_index[$tag.'_'.$level]++;

                } else { //If it is not an array...
                    $current[$tag] = array($current[$tag],$result); //...Make it an array using using the existing value and the new value
                    $repeated_tag_index[$tag.'_'.$level] = 1;
                    if($priority == 'tag' and $get_attributes) {
                        if(isset($current[$tag.'_attr'])) { //The attribute of the last(0th) tag must be moved as well
                            
                            $current[$tag]['0_attr'] = $current[$tag.'_attr'];
                            unset($current[$tag.'_attr']);
                        }
                        
                        if($attributes_data) {
                            $current[$tag][$repeated_tag_index[$tag.'_'.$level] . '_attr'] = $attributes_data;
                        }
                    }
                    $repeated_tag_index[$tag.'_'.$level]++; //0 and 1 index is already taken
                }
            }

        } elseif($type == 'close') { //End of tag '</tag>'
            $current = &$parent[$level-1];
        }
    }
    
    return($xml_array);
}  

测试:

$xml = '<?xml version="1.0" encoding="utf-8"?>
<root>
  	<ele id="1">1</ele>
	<ele id="2">2</ele>
	<bok>
		<one ids="111" name="vfeelit">111</one>
		<one ids="222" name="ifeeline">222</one>
		<two>2</two>
	</bok>
</root>';

$arr = xml2array($xml,1,'attribute');
print_r($arr);

输出:

Array
(
    [root] => Array
        (
            [ele] => Array
                (
                    [0] => Array
                        (
                            [value] => 1
                            [attr] => Array
                                (
                                    [id] => 1
                                )

                        )

                    [1] => Array
                        (
                            [value] => 2
                            [attr] => Array
                                (
                                    [id] => 2
                                )

                        )

                )

            [bok] => Array
                (
                    [one] => Array
                        (
                            [0] => Array
                                (
                                    [value] => 111
                                    [attr] => Array
                                        (
                                            [ids] => 111
                                            [name] => vfeelit
                                        )

                                )

                            [1] => Array
                                (
                                    [value] => 222
                                    [attr] => Array
                                        (
                                            [ids] => 222
                                            [name] => ifeeline
                                        )

                                )

                        )

                    [two] => Array
                        (
                            [value] => 2
                        )

                )

        )

)

同名元素会自动变成一个数组,每个元素对应一个关联数组,下标为value对应值,下标attr对应元素的属性。