月度归档:2012年08月

Zen-cart中的错误日志

在Zen-cart的cache目录里记录了PHP产生的错误日志,这些错误文件的产生是利用PHP引擎实现的而不是PHP程序。

实现这个逻辑的功能是includes/extra_configures/enable_error_logging.php文件:

  $pages_to_debug[] = '*';
  $pages_to_debug[] = '';
  $pages_to_debug[] = '';
  $pages_to_debug[] = '';

  $debug_logfile_path = DIR_FS_SQL_CACHE . '/myDEBUG-' . time() . '-' . mt_rand(1000,999999) . '.log';

  $errors_to_log = (version_compare(PHP_VERSION, 5.3, '>=') ? E_ALL & ~E_DEPRECATED & ~E_NOTICE : version_compare(PHP_VERSION, 5.4, '>=') ? E_ALL & ~E_DEPRECATED & ~E_NOTICE & ~E_STRICT : E_ALL & ~E_NOTICE);

  if (in_array('*', $pages_to_debug) || in_array($current_page_base, $pages_to_debug)) {
    @ini_set('log_errors', 1);          // store to file
    @ini_set('log_errors_max_len', 0);  // unlimited length of message output
    @ini_set('display_errors', 0);      // do not output errors to screen/browser/client
    @ini_set('error_log', $debug_logfile_path);  // the filename to log errors into
    @ini_set('error_reporting', $errors_to_log ); // log only errors according to defined rules
  }

实现过程是通过动态修改PHP配置实现的,PHP配置中,log_errors决定了是否记录PHP日志(默认是On),log_errors_max_len决定了日志的最大长度(默认是1024,0表示无限制),display_errors决定是否在浏览器中显示错误(0表示不显示),error_log记录了日志文件位置,error_reporting决定了日志的级别。

另外,includes/extra_configures/enable_error_logging.php文件被执行是在include/application_top.php文件的如下代码:

if ($za_dir = @dir(DIR_WS_INCLUDES . 'extra_configures')) {
  while ($zv_file = $za_dir->read()) {
    if (preg_match('/\.php$/', $zv_file) > 0) {
      /**
       * load any user/contribution specific configuration files.
       */
      include(DIR_WS_INCLUDES . 'extra_configures/' . $zv_file);
    }
  }
  $za_dir->close();
  unset($za_dir);
}

而include/application_top.php是入口文件index.php首先要加载的文件,所以如果错误产生在以上这段代码之前,那么Zen-cart的错误日志功能将不起作用,因为先发生的错误导致脚本停止了运行。如果默认PHP的display_error为Off,那么这类的错误将导致一个空白页出现,没有错误日志,也不显示错误提示。

如果在PHP的配置中,设置了error_log的文件名,那么此段代码之前的错误将记录到这个文件中,代码之后的产生在cache目录中。一般PHP的配置中,error_log是空的,如果PHP作为Apache的模块启动,那么此段代码之前的错误将由Apache负责记录到它的错误日志中,如果PHP是使用FastCGI方式启动的,那么此段代码之前的错误将丢弃,即找不到任何出错提示。

原创文件,转载务必保留出处。
永久链接:http://blog.ifeeline.com/105.html

PHP面向对象基础

1 关键的OOP概念
1.1 类
在OOP术语中,实体的性质和行为的具体定义成为类(class)。类用于表示要在应用程序中处理的实际事物。每个实体都包含一组性质和行为,在OOP中分别称为属性和方法。

class Employee{
private $name;
private $title;
protected $wage;

protected function clockIn(){
    echo “Member $this->name clocked in at  ”.date(“h:i:s”);
}
protected function clockOut(){
    echo “Member $this->name clocked out at ”.date(“h:i:s”)
}
}

1.2 对象
类提供了一个基础,可以在此基础上创建实体的特定实例,这个特定实例称为对象(object)。
$employee = new Employee();

1.3 属性
1) 声明属性
一般包含作用域描述符(public 或 private)
2) 调用属性
属性要使用->操作符引用
3) 属性作用域
PHP支持5种属性作用域:public private protected final 和 static。
public
共同属性可以由相应的对象直接操作和访问。OOP中并不鼓励使用公共属性,是因为直接访问使得类无法很容易地完成某种数据验证。

private
private属性只能在定义属性的类中被访问。指定为私有的属性不能有实例化的对象直接访问,也不能由其子类使用。

protected
在本类和子类中可以可以访问和操作保护属性,这是私用属性所没有的特性。

final
为属性设置final作用域时,将阻止在子类中覆盖这个属性。

4) 用__set()方法设置属性
设置方法(setter)负责隐藏属性的赋值实现,并在为类属性赋值之前验证类数据。形式为:
boolean __set([string property_name], [mixed value_to_assign])

5) 用__get()方法获取属性
获取方法(getter)负责封装获得类变量所需的代码。形式为:
Boolean __get([string property_name])

6) 创建定制方法和设置方法
对管理复杂面向对象应用程序而已,__set和__get方法是不够的,可以考虑为每个私有属性创建两个方法:
class Employee{
private $name;
public function getName(){
return $this->name;
}
public function setName(){
$this->name = $name;
}
}

1.4 常量
在类中使用const类定义常量。

1.5 方法
1) 声明方法
2) 调用方法
3) 方法作用域
PHP支持6种方法作用域:public private protected abstracet final 和 static
public
公共方法可以在任何位置任何时间访问。
private
标记为private的方法只能在类内部使用,不能由实例化的对象调用,也不能由类的子类使用。
protected
标记为protected的类方法只能在该类及其子类中使用。
abstract
抽象方法只在父类中声明,子类中实现。只有声明为abstract的类可以声明抽象方法。
final
标记为final的方法可以防止被子类覆盖。
4) 类型提示
类型提示只用于对象和数组,整数和浮点数或字符串等无法使用。

2 构造函数和析构函数
2.1 构造函数
PHP通过名称__construct来识别构造函数。
1) 调用父类构造函数
PHP不会自动调用父类的构造函数,必须使用parent关键字显式地调用。

2.2 析构函数
析构函数名必须是__destruct(),脚本执行结束时,PHP会撤销内存中的所有对象。如果创建了不那么容易丢失的数据,并应当在对象撤销时撤销这些数据,为此就需要创建一个定制的析构函数。

3 静态类成员
创建一系列只属于类的属性或方法。在类内部,静态方法只能访问静态属性,并且需要使用self关键字来引用,在类外部应该直接使用类名来引用静态成员。

4 instanceof关键字
PHP5的另一个新成员是instanceof关键字。使用这个关键字可以确定一个对象时类的实例、类的子类还是实现了某个特定接口,并进行相应的操作。

5 辅助函数
1) 创建类别名
class_alias()函数会创建一个类别名,允许用多个名来引用一个类。PHP5.3新增。
2) 确定类是否存在
class_exists()返回判断类是否存在。
3) 确定对象上下文
get_class()函数返回object所属的类名。
4) 了解类方法
get_class_methods()函数返回一个数组,其中包含class_name类中定义的所有方法名。
5) 了解类属性
get_class_vars()函数返回一个关联数组,其中包含class_name类中定义的所有属性名及其相应的值。
6) 了解声明类
函数get_declared_classes()返回一个数组,其中包含当前执行脚本中定义的所有类名。
7) 了解对象属性
get_object_vars()函数返回一个关联数组,其中包含object可用的已定义属性及其相应的值。没有值的属性在关联数组中将被赋值为NULL。
8) 确定对象的父类
get_parent_class()函数返回object所属类的父类名。如果object类是基类,那么就返回该类的名称。
9) 确定接口是否存在
interface_exists()函数确定一个接口是否存在。
10) 确定对象类型
is_a(object object, string class_name)函数判断object是否是class_name的对象。PHP5-5.3中删除了此函数(大概是引入了instanceof关键字的缘故)
11) 确定对象的子类类型
is_subclass_of(mixed object, string class_name),判断object是否是class_name子类的对象
12) 确定方法是否存在
method_exists(object object, string method_name),判断object中method_name是否存在。

6 自动加载对象
将各个类放在单独的文件中是很常见的做法。PHP5中引入了自动加载对象的概念。自动加载运行定义特殊的__autoload函数,当引用未在脚本中定义的类时会自动调用这个函数。如下:

function __autoload($class){
	require_once(“classes/$class.class.php”);
}

定义了这个函数后,将不再需要哪些require_once语句,因为当第一次调用一个类时,就会调用__autoload并根据__autoload中定义的命令加载类。

7 PHP不支持的高级OOP特性
方法重载 操作符重载 多重继承

8 对象克隆
PHP5中所有对象在默认情况下都被视为引用。因为所有对象都被视为引用而不是值,所以复制对象更为困难。
可以在对象前面加clone关键字来克隆对象,如object = clone targetObject;

可以在对象中定义一个__clone()方法来调整对象的克隆行为。

问题:如果对象中含有对其它对象的引用,clone如何进行?

9 继承
在PHP中,类继承通过extends关键字实现。

如果父类有构造函数,而且子类没有构造函数,那么在子类实例化时确实会执行父类构造函数。如果子类也有构造函数,那么当子类实例化时,不论父类是否有构造函数,只会执行子类的构造函数,如果希望执行父类的构造的函数,可以在子类的构造函数中使用parent引用服务器的构造函数。

10 接口
接口定义了实现某种服务的一般规范,声明了必需的函数和常量,但不指定如何实现。
当类通过implements关键字实现了接口后,接口中的所有方法都必须实现,如实现类没有实现所有方法,则必需声明为抽象类。(通常,在接口名前加上字母I来标识接口名称)

类可以extends某个类和implements一个或多个接口。

11 抽象类
抽象类是不能被实例化的类,只能作为有其它类继承的基类。声明抽象类必须在定义前面加上关键字abstract。

关于抽象类和接口:
如要创建一个模型将由一些紧密相关的对象采用,就可以使用抽象类。如果要创建一个由一些不相关对象采用的功能,就使用接口。
如果必须从多个来源继承行为,就使用接口。
如果知道所有类都会共享一个公共的行为实现,就使用抽象类,并在其中实现该行为。

12 命名空间
从PHP5.3开始,引入了命名空间概念。可以为各个类指定一个命名空间。比如打开类定义文件,在开头写入namespace 名字;即可。

比如:
namespace Com\Vfeelit\Lib;
…..
在实际使用这个类的代码中,可以使用:
use Com\Vfeelit\Lib as VF;
然后通过new VF\Clean()来调用类。

永久链接: http://blog.ifeeline.com/103.html

PHP基础、函数与数组笔记

PHP基础
PHP支持的数据类型
布尔 整型 浮点型 字符串 数组 对象
注意:PHP将字符串看做数组,运行通过数组偏移记法访问特定的字符

类型相关函数
1 获取类型
string gettype(mixed var)返回值有8种可能的返回值:array boolean doubule integer object resource string 和unknown type
2 转换类型
boolean settype(mixed var, string type)将var指定变量转换为type指定的类型,type有7个可取值:array boolean float interget null object string

类型标示符函数
is_array() is_bool() is_float() is_integer() is_null() is_numeric() is_object() is_resource() is_scalar() 和 is_string()

总结:类型十分不严格,对于浮点数,出现了float和double,null是一种类型,是否跟unknown type等同不得而知,is_scalar()用来判断是否是常量,那么常量也是一种类型?

PHP的超级全局变量
PHP有9个预定义的变量数组,$_SERVER $_GET $_POST $_COOKIE $_FILES $_ENV $_SESSION 和 $_REQUEST
注意:$_REQUEST是$_GET $_POST $_COOKIE的集合
另外,$GLOBAL保持了所有的全局变量,比如在文件中定义的变量,它作为该数组的一个元素。

PHP函数
不要求非得在调用之前定义函数,因为PHP在执行前会把整个脚本读到引擎中。

函数的参数默认参数值必须位于参数列表末尾且为常数,如果有多个带默认值的参数,必须是连续的并且置于最后。

可以指定某个参数为可选参数,为此这些参数需要放在参数列表末尾,而且要指定其默认值为空。

PHP5引入了一个名为类型提示的特性,利用这个特性可以强制参数为某个类的对象或者数组,不过还不支持使用标量数据类型(如整数和字符串)的类型提示。[这种性质在强类型语言中天生就要求如此操作]

语言构造list()可以从数组中按照顺序取值填入list中给出的变量。[这种搞法虽然很好,但是相比比较特别]

数组
如果索引值是数值而且是递增的,可以再创建时省略索引值。

可以使用vprintf输出数值内容,比如vprintf(“Name: %s Email: %s Phone: %s”, $customer),这样它就按照顺序从$customer数组中取值,如果不是要输出而是要返回字符串,则可以使用vsprintf(),这个类似printf 和 sprintf。[这样的用法也是比较特别的]

可以使用print_r()来打印数组[在测试数组时,可以如此测试是否满足条件]

添加与删除数组元素

分别对应:

unshift		array_unshift(array array, mixed variable [, mixed variable…])	头插入
shift		array_shift(array array, mixed variable [, mixed variable…])	头删除
push		array_push(array array, mixed variable [, mixed variable…])	尾插入
pop		array_pop(array array, mixed variable [, mixed variable…])	尾删除


定位数组元素

in_array(mixed needle, array haystack [, boolean strict])
搜索一个特定值,第三个参数指定搜索时是否考虑类型。
array_key_exists(mixed key, array array)
搜索给定的索引值是否存在
array_search(mixed needle, array haystack [, Boolean strict])
搜索一个特定值,第三个参数指定搜索时是否考虑类型,它和in_array()区别是它返回第一个找到值对应的键,只有找不到时才返回FALSE
array_keys(array array [, mixed search_value [, boolean preserve_keys]]
返回一个数组,其中包含所搜索数组中找到的所有键。如果指定了search_value,则只返回与它匹配的键。
array_values(arrya array)
返回一个数组中的所有值,并自动为返回的数组提供数值索引。


遍历数组

key()		返回数组中当前指针所在位置的键。
current()	返回数组中当前指针所在位置的数组值
each()		返回数组的当前键值对数组(4个元素,非一般特殊),并将指针推进一个位置
next()		返回数组中当前指针所在位置下一个位置的数组值
prev()		返回数组中当前指针所在位置前一个位置的数组值
reset()		将数组指针设置回数组的开始位置并返回第一个数组单元值
end()		将指针移动到数组的最后一个位置并返回最后一个数组单元值
array_walk()	将数组中的各个元素传递到用户自定义函数。

确定数组的大小和唯一性
count()
返回数组中值的总数,第二个参数表示是否使用递归统计,sizeof()是它的别名。
array_count_values()
统计值出现的频率,返回的数组的索引是原来数组的值,对应是频道
array_unique()
删除数组中所有重复的值,返回一个由唯一值组成的数组。


数组排序

array_reverse()	把数组倒置
array_flip()	键值倒置

sort	asort	升序
rsort	arsort	降序
natsort	natcasesort	自然排序
ksort	krsort		键升序和键降序
a 表示associative,是关联排序之意,带a的表示保持关联
r 表示逆序
k 表示对键排序

usort() 是根据用户自定义规则排序

合并、拆分、接合和分解数组
array_merge()
将数组合并到一起,返回一个联合的数组。如果输入数组包含的某个键在结果数组中已经存在,那么该键值对将覆盖已经存在的元素。
array_merge_recursive()
将数组合并到一起,返回一个联合的数组。如果输入数组包含的某个键在结果数组中已经存在,那么它把这两个值合并成一个数组。
array_combine()
会生成一个新数组,这个数组由一组提交的键和对应的值组成。
array_slice()
返回数组一部分,从键offset开始,到offset+length位置结束。
array_splice()
会删除数组中从offset开始到offset+length结束的所有元素,并以数组的形式返回所删除的元素。
array_intersect()
返回交集
array_intersect_assoc()
返回交集,但是还考虑键值对应关系。
array_diff()
返回差集。
array_diff_assoc()
返回差集,但是还考虑键值对应关系


其它有用的数组函数

array_rand()		返回数组中一个或多个键。
shuffle()		随机对数组元素重新排序
array_sum()		将数组值汇总
array_chunk()		将数组分解为一个多维数组

永久链接: http://blog.ifeeline.com/98.html

PHP之文件包含语句

PHP提供了4种在应用程序中包含文件的语句

1 include()
include(/path/to/filename)
与print和echo语句一样,使用include()时可以忽略括号。

还可以根据条件来执行include()语句。如把include()语句放在if语句内,那么只有条件为真时才包含该文件,不过在条件语句中使用include时必须要放在花括号内。

当使用include包含一个文件时,此文件也是需要包含,比如名为hello.inc.php的文件内容为:
echo “hello world.”;
然后使用 include(hello.inc.php)这样是错误,hello.inc.php中必须如下:

<?php
echo “hello world.”;
?>

如果被包含的文件是纯的PHP文件,最后的?>可以省略,目前情况下,为了避免输出空格之类的内容,一般不写最后的界定符。

如果PHP配置指令allow_url_fopen是启用的,那么include还可以远程包含文件。

2 确保只包含一次
include_once() 和 include()相同,不过它会首先验证是否已经包含了该文件。

3 请求文件
require()基本和include()相同,主要有两点重要区别:
首先,无论require的位置如何,指定文件都将包含到出现require的脚本中。比如放入条件语句中,即使条件语句为假,也会包含指定的文件。
第二、require出错时脚本停止执行,而include则继续执行。比如引用了错误的目标路径,require将出错而停止执行,而include则不会。

4 确保只请求文件一次
require_once()和require()基本相同,只是require_once首先验证是否已经包含了该文件,如果包含了则忽略。

原创文章,转载务必保留出处。
永久链接: http://blog.ifeeline.com/94.html

Javascript 面向对象基础

对象是Javascript的基础。事实上,这门语言里所有的东西都是对象。从最基本的层次上说,对象是一系列属性的集合,和其他语言里的散列表结果类似。

两个创建简单对象并设置属性的例子:

//创建一个新的Object对象
var obj=new Object();
obj.val=5;
obj.click=function(){
      alert("hello");
};
//这是一段等价代码,用{…}简写方式,结合键值对来定义属性
var obj={
     val:5,
     click:function() {
            alert("hello");
     }
};

Javascript里任何函数都可以被实例化为一个对象。

function User(name){
     this.name=name;
}
var me=new User("My Name");
alert(me.name=="My Name");
alert(me.constructor == User);

User("Test");
//上下文对象没有设定,默认为全局的'window'对象
alert(window.name=="Test");

以下代码展示了constructor属性的使用,这一个属性在每个对象中都存在,并一直指向创建它的函数。这样就可以有效复制对象,用同一个基类创建对象并赋予不同的属性。

// User对象
function User(){}
// 创建一个User对象
var me = new User();
// 还是创建一个新的User对象(用前一个对象的constructor引用来创建)
var you = new me.constructor();
// 两个对象的constructor实质上一致
alert(me.constructor == you.constructor);

1、公共方法
要实现这种在对象的每个实例中都可以使用的公共方法,必须了解一个叫prototype的属性,这个属性包含了一个对象,该对象可以作为所有新副本的基引用(base reference)。本质上说,所有对象原型的属性都能在该对象的每个实例中找到。

因为对象的原型仍然是对象,和其它对象一样,可以给他们添加新的属性。给原型添加属性的结果是由该原型实例化的每个对象都会获得这些属性。

function User(name,age){
     this.name=name;
}

User.prototype.getName=funciton(){
      return this.name;
};

var user=new User(“Bob”);
alert(user.getName()==”Bob”);

2、私有方法
定义在构造函数内。私有方法和私有变量只允许其它的私有方法、私有变量和特权方法访问。这种方法可以定义一些只让对象内部访问,外部访问不到的代码。

3、特权方法
在构造函数中声明一个公共的方法,用一个公共属性引用这个方法,然后在这个方法内访问私有变量。如下:

function User(name, age){
    var year = (new Date()).getFullYear()-age;
    this.getYearBorn = function(){
        return year;
    };
}

可以看到,function(){ return year}使用了year这个私用变量,然后这个函数被this.getYearBorn引用,相当于getYearBorn是一个公共方法,单它能访问私有属性(可以访问私有变量这个就是它的特权)。

特权方法是动态生成的,因为它们是在运行时才添加到对象中的(在构造函数中),而不是在代码第一次编译时就已经生成的。

动态生成方法的例子:

function User(properties){
     for(var i in properties){
         (
            function(which){
               var p=i;
               //所谓的特权方法
               which[“get”+p]=function() {
                    return properties[p];
               };
               //所谓的特权方法
               which[‘set’+p]=function(val) {
                    properties[p]=val;
               };
            }
         )(this); //自执行函数,携带上下文信息
     }
}

这段代码根据传入的产生为User动态生成对应的Get和Set方法。

4、静态方法
类的方法:类名+”.”+字段,而对象的方法通过原型实现:this+”.”+字段 或 类名.prototype.字段。

Javascript语言特征

1、引用
引用指向的只能是具体的对象,而不是另一个引用(没有引用的引用的概念)。 在执行字符串链接操作时,结果总会是一个新的字符串对象,而非原字符串的修改版本(字符串链接时总是产生一个新字符串对象)。如下例子:

var item = "test";
var itemRef = item;     //现在itemRef 和 item指向同一个字符串对象
item += "ing";          //现在新的字符串对象产生了,item指向了新字符串对象
alert( item != itemRef );

注意这个情况只是在字符串链接操作时。

2、函数重载和类型检查
函数重载必须依赖两件事情:判断传入参数数量的能力和判断传入参数类型的能力。
1) 判断传入参数数量
JavaScript的每个函数都带有一个仅在这个函数范围内作用的变量(contextual variable)称为参数(argument),它是一个包含所有传给函数的参数的伪数组(pseudo-array), 所以它并非真正意义的数组(也就是说你不能修改它,也不能用push()来添加新元素),但可以访问其中的元素,也有.length属性[只读]。

function sendMessage( msg, obj ) {
    if( arguments.length == 2){
        obj.handleMsg( msg );
    }else{
        alert( msg);
    }
}
// 仅用一个参数调用
sendMessage("Hello World!");
// 使用两个参数调用
sendMessage("How are you?", {
    handleMsg: function( msg ){
        alert("This is a custom message:" + msg);
    }
});

还有一个用来判断传入参数类型的方法。如果没有提供参数,它的类型就为undefined,可以利用这特征来作判断。

function displayerror( msg ) {
    if( typeof msg == 'undefined' ) {
        msg = "An error occurred.";
    }
    alert( msg );
}

2) 判断传入参数类型
JavaScript的类型检查有两种特别有用的方法:
第一种方法是使用typeof操作符。这个工具提供了一个字符串名称,用于表达变量内容的类型。当变量不是object或array类型时,这是最完美的解决方法了。但是对于自定义的对象就不能用这个方法进行类型检查,因为它只返回object,很难跟其他的object区分开来。

第二种检查对象类型的方法,需要引用所有JavaScript对象都带有的一个属性,称为构造函数(construstor)。这个属性引用的是原本用来构造该对象的那个函数

变量类型检查对照表

变量			typeof变量		变量.构造函数
{an:"object"}		object			Object
["an","array"]		object			Array
function(){}		function		Function
"a string"		string			String
66			number			Number
true			boolean			Boolean
new User()		object			User

可以使用{}构建一个对象,使用[]构建一个数组,construstor保存的就是这个对象的构造函数名,构造函数 作为 对象类型 引用(按照严格面向对象的说法就是构造函数指明了构造这个对象的类,它引用到了类,所以它是一个对象),注意typeof始终返回一个字符串作为类型标识。
3、作用域
在Javascript里,作用域是由函数划分的,而不是由块(block)划分的。基于浏览器的Javascript的一个有趣特征是,所有属于全局作用域的变量其实都是window对象的属性。如果变量没有显式定义,它就是全局定义的,虽然它可能只在这个函数作用域的范围内使用。

4、闭包
闭包(closure)意味着内层的函数可以引用存在于包围它的函数内的变量,即使外层函数的执行已经终止。

闭包能实现的额外作用:
1) 在一些函数式程序设计语言里,有一种称为Curry化的技术。本质上,Curry化是一种通过把多个参数填充到函数体中,实现将函数转换为一个新的经过简化的(使之接受参数更少)函数的技术。

     function addGenerator(num)
     {
          //返回一个函数
          return function(toAdd)
          {
               return num+toAdd;
          };
     }
     var addFive=addGenerator(5);
     aler(addFive(4)==9);

2) 通过自执行的匿名函数你可以把所有原本属于全局的变量都隐藏起来。

    
     (
          function()
          {
               //这个变量如果没有用自执行的匿名函数包围起来那么它就是全局的                 
               var msg="Thanks for visiting!";
               window.onunload=funtion()
               {
                    alert(msg);
               };
          }  
     )();

这是一种有效的避免变量冲突的方法。

3) 使用匿名函数来激发出创建多个使用闭包的函数所需的作用域

    var obj=document.getElementById("main");
    var items=["click","keypress"];
    for(var i=0;i &lt; args.length; i++ ) {
        (function(){
            var itemm = item[i];
            obj["on" + item ] = function() {
                alert( "Thanks for your " + item );
            }
        })();
    }

关于闭包的工作原理,可以参考:http://jibbering.com/faq/notes/closures/

5、上下文对象
在Javascript中,代码总是有一个上下文对象(代码处在该对象内)。上下文对象是通过this变量体现的,这个变量永远指向当前代码所处的对象中。全局对象其实是window对象的属性。这意味着即使是在全局上下文中,this变量也能指向一个对象。Javascript提供了call和apply两个方法,可以使用这两个方法改变上下文对象。

function changeColor( color ) {
    this.style.color = color;
}
// 在window对象中待用函数会失败,因为它没有style属性
changeColor("while");
//找出ID为main的节点
var main = document.getElementById("main");
//使用call方法将它的颜色设置为黑色。call方法将上下文对象设置为第一个参数,并将其它产生作为原函数的参数
changeColor.call(main, "black");
//设置body元素颜色的函数
function setBodyColor() {
    // apply方法将上下文对象设置为第一个参数指定的body元素,第二个元素是传给函数的所有参数的数组
    changeColor.apply(document.body, arguments);
}
// 将body的背景色设置为黑色
setBodyColor("black");

这些精彩内容总结来自Pro Javascript Techniques一书,此书作者为jQuery之父John Resig。

原创文章,转载务必保留出处。
永久链接: http://blog.ifeeline.com/86.html

Magento多域多店的数据库实现

Magento安装之后就有一个叫Main Website的网站,并且其默认的商店和商店视图也创建好了。

管理的位置在System/Manage Stores
Magento网站管理
可以看到,这里有三个基本操作,不过需要首先明白,一个所谓的Website可以有多个Store, 而一个Store可以对应多个Store View,所以如果全新开始设置的时候先是设置一个Website,然后创建一个或多个Store放入Website中,然后创建Store View放入对应的Store中。

创建一个新Website:
Magento创建新网站
有两个字段必填,这些信息会写入core_website表中。当创建了Store后回来编辑Website,如下:
Magento编辑网站
多出了Default Store和Set as Default这两个选择,说明Website有一个默认Store的概念。这个Set as Default就是设置当前这个Website的Store作为默认首先进入的网站,由于Magento支持多网站,所以也有一个默认网站的问题。

创建新的Store:
Magento创建新商店
这个时候需要选择这个商店属于哪个Website,然后是这个商店的名字,接下来就是这个商店对应的根目录(可能需要创建针对此商店的新的根目录),这些信息会写入core_store表中。

不过需要注意的时候,为这个Store建立的第一个Store View后,这个Store Virew就是这个Store的默认商店视图。可以返回来编辑这个Store发现:
Magento编辑商店
多了Default Store View,事实上,当创建Store时无法知道默认的Store View,当创建了第一个Store View后,这个Default Store View就是第一个Store View,如果创建了多个Store View,那么这里就可以选择哪个作为默认的Store View了。

新建Store View:
Magento创建新的商店视图
需要选择它归属于哪个Store,Status标示它是否启用。这些信息写入core_store表中。

对应数据库结构关系:
Magento多域多店数据库关系

core_store引用了core_store_group 和 core_website,其实它只要引用core_store_group就可以了,不过逻辑上core_store也属于core_website,所以它也引用core_website,同时从core_store定位它的core_website时,可以不需要经过core_store_group表,貌似提升效率。

core_store_group引用了core_website。

core_website和core_store_group和core_store分别对应Website和Store和Store View的概念。

core_website和core_store是系统最核心的表,它实现了多网站概念,其它的很多表都要引用它们。

三个表的详情:
Magento core_website表结构
注意default_group_id 和 is_default这两个字段。

Magento core_store_group表结构
注意root_category_id和default_store_id。

Magento core_store表结构

原创文章,转载务必保留出处。
永久链接: http://blog.ifeeline.com/72.html

Magento 多域设置

Magento支持多域多店设置,使用一个后台管理多个域方便了管理,它的原理不复杂,根据过来的HOST决定使用哪个域。

不过需要首先理解Website、Store、Store View的概念,如果能从数据库设计上理解的话,这个多域设置是最清楚的。
首先到System/Manage Stores创建一个Website:

注意这里为这个网站设置的代码为mgt,然后为这个网站设置一个Store。


注意这里需要选择Root Category,这意味着需要首先创建针对这个域的根目录。接下来为这个Store创建一个Store View。


注意这里也有一个针对Store View的code。

完成之后去到System/configuration,首先选择左上角的Current Configuration Scrop下拉选择刚刚创建的Website,然后选择左边Configuration下面的GENERAL的Web,修改如下两项:

Magento多域设置

Magento多域设置


注意把全部的右边对应的勾去掉,然后保存,这样在Magento后台设置的内容就完成了。

接下来去到Apache的虚拟机配置文件中:

Magento多域Apache虚拟机设置

Magento多域Apache虚拟机设置


注意这里使用了SetEnv指令配置环境变量,一个是MAGE_RUN_CODE,一个是MAGE_RUN_TYPE,当MAGE_RUN_TYPE值是website时,MAGE_RUN_CODE对应的是创建Website时输入的code,当MAGE_RUN_TYPE值是store时,MAGE_RUN_CODE对应的是创建Store view时输入的code。

一般以上方式的设置比较实用,不过如果你使用的是cpanel之类的虚拟主机,就是无法修改配置文件,那只能依靠.htaccess来设置了。

SetEnvIf Host .*magentoo.net MAGE_RUN_CODE=mgt
SetEnvIf Host .*magentoo.net MAGE_RUN_TYPE=website   # 'website' or 'store'

完成之后修改一下本地的HOST文件,让magentoo.net指向本地地址127.0.0.1(网站运行在本地的时候)。

访问http://magento.net成功,多域设置完成。

原创文章,转载务必保留出处。
永久链接: http://blog.ifeeline.com/63.html