JavaScript判断按钮被点击的方法

废话不多说了,直接给大家贴代码了。

<HTML> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<HEAD> 
<TITLE></TITLE> 
<script> 
document.onclick=function(e){ 
var evt=e||window.event; 
var tar=evt.target||evt.srcElement; 
if( (tar.tagName.toLowerCase()=="input"&&tar.type=="button") || tar.tagName.toLowerCase()=="button"){ 
alert("你点击的是一个按钮") 
} 
} 
</script> 
</HEAD> 
<BODY> 
<input id="button1" type="button" value="1111"/> 
<input id="button2" type="button" value="2222"/> 
<input id="button3" type="button" value="3333"/> 
<input id="button4" type="button" value="4444"/> 
</BODY> 
</HTML> 
google 浏览器版:
<HTML> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<HEAD> 
<TITLE></TITLE> 
<script> 
document.onclick=function()
{ var obj = event.srcElement; 
if(obj.type == "button"){ 
alert(obj.id); } 
} 
</script> 
</HEAD> 
<BODY> 
<input id="button1" type="button" value="1111"/> 
<input id="button2" type="button" value="2222"/> 
<input id="button3" type="button" value="3333"/> 
<input id="button4" type="button" value="4444"/> 
</BODY> 
</HTML> 

SyntaxHighlighter.highlight();

jQuery同步提交示例代码

本文实例讲述了jQuery同步提交的方法。分享给大家供大家参考,具体如下:

使用jQuery 框架,同步提交演示

在一些严格检测或者递归、循环调用的情况下,如果不能同步返回数据则可能会出现错误结果,所以需要使用同步提交技术,jQuery默认是异步操作,需要显式设置异步属性async为false,便可以实现同步。

自定 data 提交

function GroupCheck(url, operate, check, group, joker) 
{ 
 var result = -1; 
 $.ajax( 
 { 
  type : "POST", 
  async : false, 
  url : url, 
  data : 
  { 
   operate : operate, 
   id_atGroup : group, 
   id_atJoker : joker 
  } 
 }).done(function(msg) 
 { 
  if (msg != null && msg != "") 
  { 
   if (msg == 0) 
   { 
    alert("操作成功2"); 
   } 
   else 
   { 
    alert("操作失败2"); 
   } 
  } 
  else 
  { 
   alert("服务器异常2"); 
   // check.checked = true; 
  } 
 }).fail(function() 
 { 
  // alert("error"); 
 }).always(function() 
 { 
  // alert("complete"); 
 }); 
 return result; 
}

取得 form 作为提交的数据

var $form = $('#theForm1'); 
url = $form.attr('action'); 
$.ajax( 
{ 
 type : "POST", 
 async : false, 
 url : url, 
 data : $form.serialize() 
}).done(function(msg) 
{ 
 // 完成代码 
});

SyntaxHighlighter.highlight();

由浅入深讲解Javascript继承机制与simple-inheritance源码分析

老生常谈的问题,大部分人也不一定可以系统的理解。Javascript语言对继承实现的并不好,需要工程师自己去实现一套完整的继承机制。下面我们由浅入深的系统掌握使用javascript继承的技巧。

1. 直接使用原型链

这是最简粗暴的一种方式,基本没法用于具体的项目中。一个简单的demo如下:

function SuperType(){
  this.property = true;
}
SuperType.prototype.getSuperValue = function(){
  return this.property;
}
function SubType(){
  this.subproperty = false;
}
//继承
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function(){
  return this.subproperty;
}
var instance = new SubType();

这种方式的问题是原型中的属性会被所用实例共享,通过一个实例改变一个继承过来的属性时,会影响到其他实例。,这显然不是一种常规意义上的继承。

2.使用构造函数

构造函数本质上也只是一个函数而已,可以在任何作用域中调用,在子构造函数中调用父构造函数,就可以实现简单的继承。

function SuperType(){
  this.colors = {"red","blue","green"}
}
function SubType(){
  SuperType.call(this);  
}
var instance = new SubType();

这种实现避免了多个实例共享属性的问题,但是又出现了新的问题,比如没法共享函数,而且 instance instanceof SuperType 为false。

3. 组合使用原型和构造函数

function SuperType(name){
  this.name = name;
  this.colors = {"red","blue","green"}
}
SuperType.prototype.sayName = function(){
  //code
}
function SubType(name,age){
  SuperType.call(this,name); 
  this.age = age;
}
SubType.prototype = new SuperType();
var instance = new SubType();

组合使用原型和构造函数是javascript中最常用的继承模式。使用这种方式,每个实例都有自己的属性,同时可以共享原型中的方法。但是这种方式的缺点是:无论什么情况,都会调用两次超类构造函数。一次是在创建子类原型时,另一次是在子类构造函数内部。这种问题该怎么解决呢?

4. 寄生组合式继承

SubType的原型并不一定非要是SuperType的实例,只需是一个构造函数的原型是SuperType的原型的普通对象就可以了。Douglas Crockford的方法如下:

function obejct(o){
  function F(){};
  F.prototype = o;
  return new F();
}

其实这也就是ES5中Object.create的实现。那么我们可以修改本文中的第3种方案:

function inheritPrototype(subType,superType){
  var prototype = object(superType.prototype);
  prototype.constructor = subType;
  subType.prototype = prototype;
}
function SuperType(name){
  this.name = name;
  this.colors = {"red","blue","green"}
}
SuperType.prototype.sayName = function(){
  //code
}
function SubType(name,age){
  SuperType.call(this,name); 
  this.age = age;
}
inheritPrototype(SubType,SuperType);
var instance = new SubTYpe();

其实寄生组合式继承已经是一种非常好的继承实现机制了,足以应付日常使用。如果我们提出更高的要求:比如如何在子类中调用父类的方法呢?

5.simple-inheritance库的实现

看这么难懂的代码,起初我是拒绝的,但是深入之后才发现大牛就是大牛,精妙思想无处不在。我对每一行代码都有详细的注释。如果你想了解细节,请务必详细研究,读懂每一行。我觉得这个实现最精妙的地方就是按需重写父类方法,在实例对象中可以通过_super调用父类的同名方法,类似于java的实现。

(function(){
  //initializing用于控制类的初始化,非常巧妙,请留意下文中使用技巧
  //fnTest返回一个正则比表达式,用于检测函数中是否含有_super,这样就可以按需重写,提高效率。当然浏览器如果不支持的话就返回一个通用正则表达式
  var initializing = false,fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
  //所有类的基类Class,这里的this一般是window对象
  this.Class = function(){};
  //对基类添加extend方法,用于从基类继承
  Class.extend = function(prop){
    //保存当前类的原型
    var _super = this.prototype;
    //创建当前类的对象,用于赋值给子类的prototype,这里非常巧妙的使用父类实例作为子类的原型,而且避免了父类的初始化(通过闭包作用域的initializing控制)
    initializing = true;
    var prototype = new this();   
    initializing = false;
    //将参数prop中赋值到prototype中,这里的prop中一般是包括init函数和其他函数的对象
    for(var name in prop){
      //对应重名函数,需要特殊处理,处理后可以在子函数中使用this._super()调用父类同名构造函数, 这里的fnTest很巧妙:只有子类中含有_super字样时才处理从写以提高效率
      prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name])?
       (function(name,fn){
        return function(){
          //_super在这里是我们的关键字,需要暂时存储一下
          var tmp = this._super; 
          //这里就可以通过this._super调用父类的构造函数了       
          this._super = _super[name];
          //调用子类函数 
          fn.apply(this,arguments);
          //复原_super,如果tmp为空就不需要复原了
          tmp && (this._super = tmp);
        }
       })(name,prop[name]) : prop[name];
    }
    //当new一个对象时,实际上是调用该类原型上的init方法,注意通过new调用时传递的参数必须和init函数的参数一一对应
    function Class(){
      if(!initializing && this.init){
        this.init.apply(this,arguments);  
      }
    }    
    //给子类设置原型
    Class.prototype = prototype;
    //给子类设置构造函数
    Class.prototype.constructor = Class;
    //设置子类的extend方法,使得子类也可以通过extend方法被继承
    Class.extend = arguments.callee;
    return Class;
  }
})();
var Human = Class.extend({
 init: function(age,name){
  this.age = age;
  this.name = name;
 },
 say: function(){
  console.log("I am a human");
 }
});
var Man = Human.extend({
  init: function(age,name,height){
    this._super(age,name);
    this.height = height;
  }, 
  say: function(){
    this._super();
    console.log("I am a man"); 
  }
});
var man = new Man(21,'bob','191');
man.say();

SyntaxHighlighter.highlight();

详解JavaScript基于面向对象之继承

一、面相对象继承机制
      这个实例使用UML很好的解释了继承机制。
      说明继承机制最简单的方式是,利用一个经典的例子就是几何形状。实际上,几何形状只有两种,即椭圆形(是圆形的)和多边形(具有一定数量的边)。圆是椭圆的一种,它只有一个焦点。三角形、矩形和五边形都是多边形的一种,具有不同数量的边。正方形是矩形的一种,所有的边等长。这就构成了一种完美的继承关系,很好的解释了面向对象的继承机制。
       在这个例子中,形状是椭圆形和多边形的基类(通常我们也可以叫它父类,所有类都由它继承而来)。椭圆具有一个属(foci),说明椭圆具有的焦点的个数。圆形继承了椭圆形,因此圆形是椭圆形的子类,椭圆形是圆形的超类。同样,三角形、矩形和五边形都是多边形的子类,多边形是它们的超类。最后,正方形继承了矩形。
      最好用图来解释这种继承关系,这是 UML(统一建模语言)的用武之地。UML的主要用途之一是,可视化地表示像继承这样的复杂对象关系。下面的图示是解释形状和它的子类之间关系的UML图示:

      在UML中,每个方框表示一个类,由类名说明。三角形 、矩形和五边形顶部的线段汇集在一起,指向形状,说明这些类都由形状继承而来。同样,从正方形指向矩形的箭头说明了它们之间的继承关系。
二、ECMAScript继承机制的实现
      要用ECMAScript实现继承机制,您可以从要继承的基类入手。所有开发者定义的类都可作为基类。出于安全原因,本地类和宿主类不能作为基类,这样可以防止公用访问编译过的浏览器级的代码,因为这些代码可以被用于恶意攻击。
       选定基类后,就可以创建它的子类了。是否使用基类完全由你决定。有时,你可能想创建一个不能直接使用的基类,它只是用于给子类提供通用的函数。在这种情况下,基类被看作抽象类。尽管ECMAScript并没有像其他语言那样严格地定义抽象类,但有时它的确会创建一些不允许使用的类。通常,我们称这种类为抽象类。
      创建的子类将继承超类的所有属性和方法,包括构造函数及方法的实现。记住,所有属性和方法都是公用的,因此子类可直接访问这些方法。子类还可添加超类中没有的新属性和方法,也可以覆盖超类的属性和方法。由于JS并不是正统的面向对象语言,一些名词也需要做出改变。
三、ECMAScript继承的方式
      ECMAScript语言中将被继承的类(基类)称为超类型,子类(或派生类)称为子类型。和其他功能一样,ECMAScript实现继承的方式不止一种。这是因为JavaScript中的继承机制并不是明确规定的,而是通过模仿实现的。这意味着所有的继承细节并非完全由解释程序处理。作为开发者,你有权决定最适用的继承方式。下面为您介绍几种具体的继承方式。
(1)原型链方式
      继承这种形式在ECMAScript中原本是用于原型链的。上一篇博文已经介绍了创建对象的原型方式。原型链扩展了这种方式,以一种有趣的方式实现继承机制。prototype 对象是个模板,要实例化的对象都以这个模板为基础。总而言之,prototype 对象的任何属性和方法都被传递给那个类的所有实例。原型链利用这种功能来实现继承机制。我们来看一个例子:

function A() {//超类型A中必须没有参数 
  this.color = "red"; 
  this.showColor = function () { 
    return this.color; 
  }; 
}; 
function B() {//子类型B 
  this.name = "John"; 
  this.showName = function () { 
    return this.name; 
  }; 
}; 
B.prototype = new A();//子类型B继承了超类型A,通过原型,形成链条 
var a = new A(); 
var b = new B(); 
document.write(a.showColor());//输出:blue 
document.write(b.showColor());//输出:red 
document.write(b.showName());//输出:John 

      在原型链中,instanceof运算符的运行方式也很独特。对B的所有实例,instanceof为A和B都返回true。ECMAScript的弱类型世界中,这是极其有用的工具,不过使用对象冒充时不能使用它。例如:

var b = new B(); 
document.write(b instanceof A);//输出:true 
document.write(b instanceof B);//输出:true 

       使用原型链方式实现了继承,但是这种方式无法共享和子类型给超类型传递参数。我们可以借用构造函数方式(也就是对像冒充)的方式来解决这两个问题。
(2)对象冒充方式
      对象冒充方式的其原理如下:构造函数使用this关键字给所有属性和方法赋值(即采用对象声明的构造函数方式)。因为构造函数只是一个函数,所以可使A构造函数成为B的方法,然后调用它。B就会收到A的构造函数中定义的属性和方法。例如,用下面的方式改写上面的例子创建对象A和B:
call()方法

function A(Color) {//创建超类型A 
  this.color = Color; 
  this.showColor = function () { 
     return this.color; 
  }; 
}; 
function B(Color,Name) {//创建子类型B 
  A.call(this, Color);//对象冒充,给超类型传参 
  this.name = Name;//新添加的属性 
  this.showName =  
}; 
var a = new A("blue"); 
var b = new B("red", "John"); 
document.write(a.showColor());//输出:blue 
document.write(b.showColor());//输出:red 
document.write(b.showName());//输出:John 

apply()方法
和上面call()方法唯一的区别就是在子类型B中的代码:
A.call(this,arguments);//对象冒充,给超类型传参 
      当然,只有超类型中的参数顺序与子类型中的参数顺序完全一致时才可以传递参数对象。如果不是,就必须创建一个单独的数组,按照正确的顺序放置参数。
      使用对象冒充方式虽然解决了共享和传参的问题,但是没有原型,复用就更不可能了,所以我们组合上述的两种方式,即原型链方式和对象冒充的方式实现JS的继承。
(3)混合方式
      这种继承方式使用构造函数定义类,并非使用任何原型。对象冒充的主要问题是必须使用构造函数方式,这不是最好的选择。不过如果使用原型链,就无法使用带参数的构造函数了。开发者如何选择呢?答案很简单,两者都用。由于这种混合方式使用了原型链,所以instanceof运算符仍能正确运行。
       在上一篇文章,创建对象的最好方式是用构造函数定义属性,用原型定义方法。这种方式同样适用于继承机制,用对象冒充继承构造函数的属性,用原型链继承prototype对象的方法。用这两种方式重写前面的例子,代码如下:

function A(Color) { 
  this.color = Color; 
}; 
A.prototype.showColor = function () { 
  return this.color; 
}; 
function B(Color, Name) { 
  A.call(this, Color);//对象冒充 
  this.name = Name; 
}; 
B.prototype = new A();//使用原型链继承 
B.prototype.showName = function () { 
  return this.name; 
}; 
var a = new A("blue"); 
var b = new B("red", "John"); 
document.write(a.showColor());//输出:blue 
document.write(b.showColor());//输出:red 
document.write(b.showName());//输出:John 

SyntaxHighlighter.highlight();

jQuery Validate初步体验(一)

jQuery 是一个快速、简单的JavaScript library, 它简化了HTML 文件的traversing,事件处理、动画、Ajax 互动,从而方便了网页制作的快速发展。 jQuery 是为改变你编写JavaScript 的方式而设计的。

jQuery Validate 插件为表单提供了强大的验证功能,让客户端表单验证变得更简单。

但是在学习的过程中,我也遇到了疑惑,网上的很多例子貌似都是依赖jquery.metadata.js这个库,然后在标签里写成class=”required remote” 这样的形式,class本身是呈现样式的,现在被附上各种校验的规则,看上去有些乱。那如果不依赖jquery.metadata.js,又该怎么写。

1、只引入jquery.js(具体版本自己选择)和jquery.validate.js

<!DOCTYPE html>
<html>
<head>
<script" width=100% src="js/jquery.js"></script>
<script" width=100% src="js/jquery.validate.js"></script>
<script>
 $().ready(function() {
 $("#registerForm").validate();
 });
</script>
</head>
<body>
 <form id="registerForm" method="get" action="">
 <fieldset>
  <p>
  <label for="cusername">用户名</label> 
  <input id="cusername" name="username" type="text" required="true" rangelength="[2,10]">
  </p>
  <p>
  <label for="cpassword">密码</label>
  <input id="cpassword" name="password" type="password" required="true" minlength="6">
  </p>
  <p>
  <label for="cconfirmpassword">确认密码</label> 
  <input id="cconfirmpassword" name="confirmpassword" type="password" required="true" equalTo="#cpassword">
  </p>
  <p>
  <label for="cemail">邮箱</label> 
  <input id="cemail" name="email" required="true" email="true"> </input>
  </p>
  <p>
  <input type="submit" value="提交">
  </p>
 </fieldset>
 </form>
</body>
</html> 

事实证明,只引入上面的两个JS文件也能完成简单的表单验证。

2、不过由于默认的提示信息是英文的,为了能有一个友好的提示,所以,接下来要做的就是让提示信息显示成中文了。

方法一、通过javascript自定义提示信息。

<!DOCTYPE html>
<html>
<head>
<script" width=100% src="js/jquery.js"></script>
<script" width=100% src="js/jquery.validate.js"></script>
<script>
 $().ready(function() {
 $("#registerForm").validate({
  rules : {
  username : {
   required : true,
   rangelength:[2,10]
  },
  password : {
   required : true,
   minlength:6
  },
  confirmpassword : {
   required : true,
   equalTo:"#cpassword"
  },
  email : {
   required : true,
   email : true
  }
  },
  messages : {
  username : {
   required : '请输入姓名',
   rangelength:'长度在 {0} 到 {1} 之间'
  },
  password : {
   required : '请输入密码',
   minlength:'密码不能少于 {0}位'
  },
  confirmpassword : {
   required : '请再次输入密码',
   equalTo:'两次输入的密码不一致'
  },
  email : {
   required :'请输入邮箱',
   email : '请输入有效的电子邮件地址'
  }
  }
 });
 });
</script>
</head>
<body>
 <form id="registerForm" method="get" action="">
 <fieldset>
  <p>
  <label for="cusername">用户名</label> 
  <input id="cusername" name="username" type="text"/>
  </p>
  <p>
  <label for="cpassword">密码</label> 
  <input id="cpassword" name="password" type="password"/>
  </p>
  <p>
  <label for="cconfirmpassword">确认密码</label> 
  <input id="cconfirmpassword" name="confirmpassword" type="password"/>
  </p>
  <p>
  <label for="cemail">邮箱</label>
  <input id="cemail" name="email" type="email"/>
  </p>
  <p>
  <input type="submit" value="提交">
  </p>
 </fieldset>
 </form>
</body>
</html> 

首先这里有一个方法调用: $(“#registerForm”).validate([options]) ,这是用来验证选择的表单,方法的参数是可选项,可以输入0个或者多个键值对(key/value),这个方法是为了处理例如:submit , focus ,  keyup , blur, click 触发验证的,对象是整个表单的元素,或者是单个元素,使用 rules 和 messages 定义验证的元素,使用errorClass, errorElement, wrapper, errorLabelContainer, errorContainer, showErrors, success, errorPlacement, highlight, unhighlight, ignoreTitle去控制非法元素的错误信息显示。其中rules里也可以输入0个或者多个键值对,他的key对应的是元素的name属性值,例如username,confirmpassword等等。而他的value里则是一些验证规则。messages同rules一样可以输入0个或者多个键值对,他的key也是对应的元素的name属性值,而他的value里则是验证错误的提示信息。简而言之,rules{}中定义验证规则的方法。 messages{}中定义错误输出。

上面有一点需要注意的就是  equalTo:”#cpassword”,这个键值对里的value是元素的ID值(如果注意到#号就应该能察觉到)。

通过上面的写法,你就可以自定义提示信息了。或许你会有疑问了,难道我每次验证表单的时候都要重新自定义提示信息吗?当然不是了,你还可以Ctrl C+Ctrl V。这当然是玩笑话。。。不过,接下来的方法二会解决你的疑问。

方法二、自定义一份提示信息,然后保存成JS文件。把他作为模板,然后在需要的页面直接引入就行。我是从网上下载了一份。

(function( factory ) {
 if ( typeof define === "function" && define.amd ) {
 define( ["jquery", "../jquery.validate"], factory );
 } else {
 factory( jQuery );
 }
}(function( $ ) {
/*
 * Translated default messages for the jQuery validation plugin.
 * Locale: ZH (Chinese, 中文 (Zhōngwén), 汉语, 漢語)
 */
$.extend($.validator.messages, {
 required: "这是必填字段",
 remote: "请修正此字段",
 email: "请输入有效的电子邮件地址",
 url: "请输入有效的网址",
 date: "请输入有效的日期",
 dateISO: "请输入有效的日期 (YYYY-MM-DD)",
 number: "请输入有效的数字",
 digits: "只能输入数字",
 creditcard: "请输入有效的信用卡号码",
 equalTo: "你的输入不相同",
 extension: "请输入有效的后缀",
 maxlength: $.validator.format("最多可以输入 {0} 个字符"),
 minlength: $.validator.format("最少要输入 {0} 个字符"),
 rangelength: $.validator.format("请输入长度在 {0} 到 {1} 之间的字符串"),
 range: $.validator.format("请输入范围在 {0} 到 {1} 之间的数值"),
 max: $.validator.format("请输入不大于 {0} 的数值"),
 min: $.validator.format("请输入不小于 {0} 的数值")
});
})); 

页面的代码和JV1.HTML几乎是一模一样,只是多引入了一份JS文件。

<!DOCTYPE html>
<html>
<head>
<script" width=100% src="js/jquery.js"></script>
<script" width=100% src="js/jquery.validate.js"></script>
<script" width=100% src="js/messages_zh.js"></script> 
<script>
 $().ready(function() {
 $("#registerForm").validate();
 });
</script>
</head>
<body>
 <form id="registerForm" method="get" action="">
 <fieldset>
  <p>
  <label for="cusername">用户名</label> 
  <input id="cusername" name="username" type="text" required="true" rangelength="[2,10]">
  </p>
  <p>
  <label for="cpassword">密码</label>
  <input id="cpassword" name="password" type="password" required="true" minlength="6">
  </p>
  <p>
  <label for="cconfirmpassword">确认密码</label> 
  <input id="cconfirmpassword" name="confirmpassword" type="password" required="true" equalTo="#cpassword">
  </p>
  <p>
  <label for="cemail">邮箱</label> 
  <input id="cemail" name="email" required="true" email="true"> </input>
  </p>
  <p>
  <input type="submit" value="提交">
  </p>
 </fieldset>
 </form>
</body>
</html> 

方法一和方法二并不互斥,两种方法是可以结合使用的。你可以先用方法二保存一份比较通用的模板,然后再用方法一去按具体情况来自定义提示。

以上就是我今天下午学习的收获了。据说在新版本中,又有了新的写法,既不需要依赖上面提到的jquery.metadata.js库,也不需要通过javascript自定义提示信息,而是在标签里以 data-rule-验证规则、data-msg-提示信息 这样的格式来重新定义。跃跃欲试……

下面是官网提供的默认校验规则。

(1)required:true 必输字段
(2)remote:”check.php” 使用ajax方法调用check.php验证输入值
(3)email:true 必须输入正确格式的电子邮件
(4)url:true 必须输入正确格式的网址
(5)date:true 必须输入正确格式的日期
(6)dateISO:true 必须输入正确格式的日期(ISO),例如:2009-06-23,1998/01/22 只验证格式,不验证有效性
(7)number:true 必须输入合法的数字(负数,小数)
(8)digits:true 必须输入整数
(9)creditcard: 必须输入合法的信用卡号
(10)equalTo:”#field” 输入值必须和#field相同
(11)accept: 输入拥有合法后缀名的字符串(上传文件的后缀)
(12)maxlength:5 输入长度最多是5的字符串(汉字算一个字符)
(13)minlength:10 输入长度最小是10的字符串(汉字算一个字符)
(14)rangelength:[5,10] 输入长度必须介于 5 和 10 之间的字符串”)(汉字算一个字符)
(15)range:[5,10] 输入值必须介于 5 和 10 之间
(16)max:5 输入值不能大于5
(17)min:10 输入值不能小于10

SyntaxHighlighter.highlight();

轻松使用jQuery双向select控件Bootstrap Dual Listbox

本文主要为大家介绍了双向select控件Bootstrap Dual Listbox的使用方法,Bootstrap Dual列表是一个为响应Twitter优化的列表框插件,它可以用在所有的现代浏览器和触摸设备上,分享给大家,具体如下:

效果图:

一、使用

1、引用css和js文件

 <link href="scripts/bootstrap-3.3.5-dist/css/bootstrap.min.css" rel="stylesheet" />
  <!--<link href="//cdnjs.cloudflare.com/ajax/libs/prettify/r298/prettify.min.css" rel="stylesheet">-->
  <link href="scripts/duallistbox/bootstrap-duallistbox.min.css" rel="stylesheet" />
  <script" width=100% src="scripts/jquery/jquery-2.1.4.min.js"></script>
  <script" width=100% src="scripts/bootstrap-3.3.5-dist/js/bootstrap.min.js"></script>
  <!--<script" width=100% src="//cdnjs.cloudflare.com/ajax/libs/prettify/r298/run_prettify.min.js"></script>-->
  <script" width=100% src="scripts/duallistbox/jquery.bootstrap-duallistbox.min.js"></script>

2、初始化class属性为demo1的select元素

<script type="text/javascript">
    $(function () {
      var demo2 = $('.demo1').bootstrapDualListbox({
        nonSelectedListLabel: 'Non-selected',
        selectedListLabel: 'Selected',
        preserveSelectionOnMove: 'moved',
        moveOnSelect: false,
        nonSelectedFilter: 'ion ([7-9]|[1][0-2])'
      });

      $("#showValue").click(function () {
        alert($('[name="duallistbox_demo1"]').val());
      });
    });
  </script>

3、html代码

<div class="col-md-7">
    <select multiple="multiple" size="10" name="duallistbox_demo1" class="demo1">
      <option value="1">Option 1</option>
      <option value="2">Option 2</option>
      <option value="3" selected="selected">Option 3</option>
      <option value="4">Option 4</option>
      <option value="5">Option 5</option>
      <option value="6" selected="selected">Option 6</option>
      <option value="7">Option 7</option>
      <option value="8">Option 8</option>
      <option value="9">Option 9</option>
      <option value="10">Option 10</option>
    </select>
    <br />
    <input id="showValue" type="button" value="show selected data" />
  </div>

这样就完成了插件的调用

二、扩展

一个通用的、初始化数据的js函数:

/*初始化duallistbox*/
    //queryParam1:参数
    //selectClass:select元素class属性
    //selectedDataStr:选中数据,多个以,隔开
    function initListBox(queryParam1,selectClass, selectedDataStr) {
      var paramData = {
        'testParam1': queryParam1
      }
      $.ajax({
        url: 'DataHandler.ashx',
        type: 'get',
        data: paramData,
        async: true,
        success: function (returnData) {
          var objs = $.parseJSON(returnData);
          $(objs).each(function () {
            var o = document.createElement("option");
            o.value = this['id'];
            o.text = this['name'];
            if ("undefined" != typeof (selectedDataStr) && selectedDataStr != "") {
              var selectedDataArray = selectedDataStr.split(',');
              $.each(selectedDataArray, function (i, val) {
                if (o.value == val) {
                  o.selected = 'selected';
                  return false;
                }
              });
            }
            $("." + selectClass + "")[0].options.add(o);
          });
          //渲染dualListbox
          $('.' + selectClass + '').bootstrapDualListbox({
            nonSelectedListLabel: 'Non-selected',
            selectedListLabel: 'Selected',
            preserveSelectionOnMove: 'moved',
            moveOnSelect: false//,
            //nonSelectedFilter: 'ion ([7-9]|[1][0-2])'
          });
        },
        error: function (e) {
          alert(e.msg);
        }
      });
    }

html代码:

<div class="col-md-7">
    <select multiple="multiple" size="10" name="duallistbox_demo2" class="demo2">
    </select>
    <br />
    <input id="showValue" type="button" value="show selected data" />
  </div>

调用:

$(function () {
      //初始化
      initListBox('hangwei.cnblogs.com', 'demo2');

      $("#showValue").click(function () {
        alert($('[name="duallistbox_demo2"]').val());
      });
    });

DataHandler.ashx代码:

<%@ WebHandler Language="C#" Class="DataHandler" %>

using System;
using System.Web;
using System.Collections.Generic;
using Newtonsoft.Json;

public class DataHandler : IHttpHandler {
  
  public void ProcessRequest (HttpContext context) {
    var china = new { id = "China", name = "中国" };
    var usa = new { id = "USA", name = "美国" };
    var rsa = new { id = "Russia", name = "俄罗斯" };
    var en = new { id = "English", name = "英国" };
    var fra = new { id = "France", name = "法国" };
    List<object> list = new List<object>();
    list.Add(china);
    list.Add(usa);
    list.Add(rsa);
    list.Add(en);
    list.Add(fra);
    string returnJson = JsonConvert.SerializeObject(list);
    context.Response.ContentType = "text/plain";
    context.Response.Write(returnJson);    
  }
 
  public bool IsReusable {
    get {
      return false;
    }
  }

}

效果:

SyntaxHighlighter.highlight();

分享Javascript实用方法二

JavaScript一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在HTML(标准通用标记语言下的一个应用)网页上使用,用来给HTML网页增加动态功能。

承接上一篇,

Object

keys

object的keys方法能够获取一个给定对象的所有键(key/属性名)并以数组的形式返回。这个方法可以用于键的筛选、匹配等。

var basket = {
strawberry: 12,
banana: 20,
apple: 30,
juice: 20
};
console.log(Object.keys(basket)); 
//[ 'strawberry', 'banana', 'apple', 'juice' ]

create

create方法用于创建一个新的对象,可选参数(proto, [ propertiesObject ]),第一个为原型,比如Array.prototype之类的,第二个为需要给新建对象的一些新属性之类的,这个参数对象的属性名将是新建对象的属性,值则是属性描述符(value、writable、configurable等)。

var o = Object.create({}, {p: {value: 42}});
var O = Object.create({}, {p: {value: 66, writable: true, enumerable: true}});
console.log(o.p); //42
console.log(O.p); //66
o.p = 20;
O.p = 80;
console.log(o.p); //42
console.log(O.p); //80

属性描述符中writable默认为false,因此o.p即便在后来重新赋值也是不能改变其值的,而O.p则能够在后来改变值,此外,create方法proto必须传入相应参数,否则会报错TypeError,当然以上代码在严格模式下也会报错,因为o.p被重写- –

assign

assign方法,es6的新特性,支持传参(target, …sources),用于将任意多个源对象的键值对添加的目标对象,类似于lodash的assign和underscore的extendOwn方法。

var boy = {handsome: true, rich: true}, girl = {cute: true, hair: 'long'};
var couples = Object.assign({}, boy, girl);
console.log(couples); //{ handsome: true, rich: true, cute: true, hair: 'long' }

assign方法常用于框架层面的数据处理,比如你定义了一个client用于发送HTTP请求,使用的时候获取接受到的参数之外自己可能得加上什么默认的属性。

Number

isNaN

Number的isNaN方法用来判断传入值是否是NaN的值,与全局的isNaN方法不同的是它不会强制将传入参数转化为数字类型,只有在参数是真正的数字类型,且值为 NaN 的时候才会返回 true。不过就自己而言全局的isNaN用的多一点,就用来判断字符串是不是只包含数字,

console.log(isNaN('123f')); //true
console.log(isNaN('123')); //true

此外,isFinite(value)方法用于判断传入参数是否是有穷数,isInteger(value)方法用于判断传入参数是否是整数。

toFixed

toFixed方法用来将数字转化为特定的字符串,支持传入参数(digits),0 < digits <= 20,在转换的时候会自动进行四舍五入以及0补充。

var cool = 666.666;
console.log(cool.toFixed(1)); //666.7
console.log(cool.toFixed(6)); //666.666000

这段时间发生了很多事情,从待了116天的杭州来到北京,开始一段新的工作与生活。不舍、惆怅、激动、兴奋等情绪交织缠绵…七匹狼,认识了其余六狼,很珍惜这段大家一起努力一起嗨皮的日子,尤记得夜爬宝石山,俯瞰西湖,English poor,哈哈哈…

ps:javascript split() 定义和用法

split() 方法用于把一个字符串分割成字符串数组。

语法

stringObject.split(separator,howmany)
参数 描述
separator 必需。字符串或正则表达式,从该参数指定的地方分割 stringObject。
howmany 可选。该参数可指定返回的数组的最大长度。如果设置了该参数,返回的子串不会多于这个参数指定的数组。如果没有设置该参数,整个字符串都会被分割,不考虑它的长度。

SyntaxHighlighter.highlight();

JavaScript 七大技巧(二)

上篇文章给大家介绍了JavaScript 七大技巧(二),写JavaScript代码已经很久了,都记不起是什么年代开始的了。对于JavaScript这种语言近几年所取得的成就,我感到非常的兴奋;我很幸运也是这些成就的获益者。我写了不少的文章,章节,还有一本专门讨论它的书,然而,我现在依然能发现一些关于这种语言的新知识。下面的描述的就是过去让我不由得发出“啊!”的感叹的编程技巧,这些技巧你应该现在就试试,而不是等着未来的某个时候偶然的发现它们。

var band = {
   "name":"The Red Hot Chili Peppers",
   "members":[
   {
   "name":"Anthony Kiedis",
   "role":"lead vocals"
   },
   {
   "name":"Michael 'Flea' Balzary",
   "role":"bass guitar, trumpet, backing vocals"
   },
   {
   "name":"Chad Smith",
   "role":"drums,percussion"
   },
   {
   "name":"John Frusciante",
   "role":"Lead Guitar"
   }
   ],
   "year":""
   }

你可以在JavaScript里直接使用JSON,可以把它封装在函数里,甚至作为一个API的返回值形式。  我们把这称作 JSON-P ,很多的API都使用这种形式。  你可以调用一个数据提供源,在script代码里直接返回 JSON-P 数据:  

01  
12  

这是调用 Delicious 网站提供的 Web service 功能,获得JSON格式的最近的无序书签列表。  

基本上,JSON是最轻便的描述复杂数据结构的方法,而且它能在浏览器里运行。  

你甚至可以在PHP里用 json_decode() 函数来运行它。  

JavaScript的自带函数(Math, Array 和 String)  

让我感到惊奇的一个事情是,当我研究了JavaScript里的math和String函数后,发现它们能极大的简化我的编程劳动。  

使用它们,你可以省去复杂的循环处理和条件判断。  

例如,当我需要实现一个功能,找出数字数组里最大的一个数时,我过去是这样写出这个循环的,就像下面:

 var numbers =
  [,,,,];
   var max = ;
   for(var i=;i
   if(numbers[i]
  > max){
   max = numbers[i];
   }
   }
   alert(max);

  我们不用循环也能实现: 

 var numbers =

  [,,,,];
   numbers.sort(function(a,b){return b -
  a});
   alert(numbers[]);

  需要注意的是,你不能对一个数字字符数组进行 sort() ,因为这种情况下它只会按照字母顺序进行排序。  
如果你想知道更多的用法,可以阅读 这篇不错的关于 sort() 的文章。  

再有一个有意思的函数就是 Math.max()。  

这个函数返回参数里的数字里最大的一个数字:

Math.max(12,123,3,2,433,4); // returns 433 

因为这个函数能够校验数字,并返回其中最大的一个,所以你可以用它来测试浏览器对某个特性的支持情况:

 var scrollTop=
  Math.max(
   doc.documentElement.scrollTop,
   doc.body.scrollTop
   );

  这个是用来解决IE问题的。你可以获得当前页面的 scrollTop 值,但是根据页面上 DOCTYPE 的不同,上面这两个属性中只有一个会存放这个值,而另外一个属性会是 undefined,所以你可以通过使用 Math.max() 得到这个数。  

阅读这篇文章你会得到更多的关于使用数学函数来简化JavaScript的知识。  

另外有一对非常有用的操作字符串的函数是 split() 和 join()。我想最有代表性的例子应该是,写一个功能,用来给页面元素附加CSS样式。  

是这样的,当你给页面元素附加一个CSS class时,要么它是这个元素的第一个CSS class,或者是它已经有了一些class  , 需要在已有的class后加上一个空格,然后追加上这个class。而当你要去掉这个class时,你也需要去掉这个class前面的空格(这个在过去非常重要,因为有些老的浏览器不认识后面跟着空格的class)。 

 于是,原始的写法会是这样:

 function addclass(elm,newclass){
   var c =
  elm.className;
   elm.className = (c === '') ? newclass : c+' '+newclass;
   }  你可以使用 split() 和 join() 函数自动完成这个任务: function addclass(elm,newclass){
   var classes =
  elm.className.split(' ');
   classes.push(newclass);
   elm.className = classes.join(' ');
   }  

这会确保所有的class都被空格分隔,而且你要追加的class正好放在最后。

是个短视的行为。工具包可以帮你快速的开发,但如果你不深入理解JavaScript,你也会做错事。

用 JSON 形式存储数据

  在我发现JSON之前,我使用各种疯狂的方法把数据存贮在JavaScript固有的数据类型里面,例如:数组,字符串,中间夹杂着容易进行拆分的标志符号以及其它的令人讨厌的东西。

  Douglas Crockford 发明了JSON 之后,一切全变了。

  使用JSON,你可以使用JavaScript自有功能把数据存贮成复杂的格式,而且不需要再做其它的额外转换,直接可以访问使用。

SyntaxHighlighter.highlight();

javascript日期验证之输入日期大于等于当前日期

<script>
  $(function () {
    var d = new Date();

    var strDate = getDateStr(d);
    $("#beginTime").val(strDate);
    $("#endTime").val(strDate);
    //$("#beginTime").val("2015-10-10");

    $("#beginTime").change(function () {
    var d2 = new Date($("#beginTime").val());
    if (d2 < d) {
      alert("填写的动工日期必须大于当前日期.");
      $("#beginTime").val(strDate);
    }
    });
    $("#endTime").change(function () {
    var d2 = new Date($("#beginTime").val());
    var d3 = new Date($("#endTime").val());
    if (d3< d2) {
      alert("填写的完工日期不能小于动工日期.");
      $("#endTime").val(getDateStr(d2));
    }
    });
  });

  function getDateStr(date) {
    var month = date.getMonth() + 1;
    var strDate = date.getFullYear() + '-' + month + '-' + date.getDate();
    return strDate;
  }
</script>

SyntaxHighlighter.highlight();

jsonp跨域请求数据实现手机号码查询实例分析

本文实例讲述了jsonp跨域请求数据实现手机号码查询的方法。分享给大家供大家参考,具体如下:

前言

网上有很多开放的api,我们在本地通过ajax获取数据时,总会碰到一个问题,那就是跨域!如果不借助php等,仅仅通过js怎么解决跨域的问题呢?或许jsonp是个不错的选择。

知识准备

之前一篇《说说JSON和JSONP 也许你会豁然开朗》对jsonp已经介绍的很详细了,这里就不多介绍了,直接上实例吧

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<meta name="author" content="@my_coder">
 <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<title> 手机号查询 </title>
<style type="text/css">
html{color:#000;background:#fff;}
body,ul,li,input,h1,button,p{padding:0;margin:0;}
li{list-style:none;}
html{background:#F6F8FC;overflow:hidden;}
.outer{margin:0 auto;width:280px;position:relative;}
h1{font-size:20px;text-align:center;border-bottom:1px dotted #A3C4DB;padding:10px 0;}
p{font-size:14px;padding:14px 0 10px;}
input[type="text"]{width:200px;height:30px;font-size:18px;}
.button{display:inline-block;width:60px;font-size:16px;text-align:center;line-height:34px;background:linear-gradient(#fff,#ccc);border:1px solid #004;border-radius:3px;cursor:pointer;}
ul{padding-top:26px;}
li {font-size:18px;line-height:30px;}
.error{position:absolute;left:4px;top:80px;color:red;font-size:14px;display:none;}
</style>
</head>
<body>
  <h1>手机号码归属地查询</h1>
  <div class="outer">
    <p>请输入手机号码</p>
    <input type="text" >
    <span class="button">查询</span>
    <span class="error">号码有误 或 无数据</span>
    <ul>
      <li class="num">手机号码: <span></span></li>      
      <li class="province">归属省份: <span></span></li>
      <li class="operators">运 营 商: <span></span></li>
    </ul>
  </div>
<script type="text/javascript"" width=100% src="jquery-1.8.0.min.js" ></script>
<script>
  var tel;
  var ajax=function(){
    //淘宝接口  
    $.ajax({
       type: "get",
       url: 'http://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel='+tel,
       dataType: "jsonp",
       jsonp: "callback",
       success: function(data){
        console.log(data);
        $('.error').css('display','none');
        var province = data.province,
          operators = data.catName,
          num = data.telString;
        $('.num span').html(num);
        $('.province span').html(province);
        $('.operators span').html(operators);
       },
       error:function (){  
        $('li span').html('');
        $('.error').css('display','block');    
       }
     });
  }
  var reg = /^(13|15|18)[0-9]{9}$/;
  //点击查询
  $('.button').click(function(){
    tel=$('input[type=text]').val();
    if(tel){
      if(reg.test(tel)){
        ajax();
      }else{
        $('li span').html('');
        $('.error').css('display','block');  
      }
    }
  });
  //键盘事件
  $(window).keydown(function(event){
    tel=$('input[type=text]').val();
    if(event.keyCode==13) {
      if(tel){
        if(reg.test(tel)){
          ajax();
        }else{
          $('li span').html('');
          $('.error').css('display','block');  
        }
      }
    }
  });
</script>
</body>
</html>

SyntaxHighlighter.highlight();

详解JavaScript正则表达式之RegExp对象

一、RegExp对象概述
       RegExp对象表示正则表达式,RegExp是正则表达式的缩写,它是对字符串执行模式匹配的强大工具。RegExp对象用于规定在文本中检索的内容。当您检索某个文本时,可以使用一种模式来描述要检索的内容。RegExp就是这种模式。简单的模式可以是一个单独的字符;更复杂的模式包括了更多的字符,并可用于解析、格式检查、替换等。
正则表达式可以规定字符串中的检索位置,以及要检索的字符类型等。
二、创建RexExp对象
创建正则表达式和创建字符串类似,创建正则表达式有两种方式:
       (1)使用字面量创建RegExp对象的语法:
       /pattern/attributes;
       (2)使用new关键词创建RegExp对象的语法:
       new RegExp(pattern, attributes);
参数释义:
       1参数pattern是一个字符串,指定了正则表达式的模式或其他正则表达式。
       2参数attributes是一个可选的模式字符串,包含属性 “g”、”i” 和 “m”,分别用于指定全局匹配、不区分大小写的匹配和多行匹配。
       RegExp对象用于存储检索模式。通过new关键词来创建RegExp对象。以下代创建了名为pattern的 RegExp对象,其模式是 “e”,当使用该RegExp对象在一个字符串中检索时,将寻找的是字符 “e”。

<span style="font-size:18px;">var pattern=new RegExp("e"); 
var pattern=new RegExp("e",gi);//设置全局搜素不区分大小写</span> 

上述的也可以改成字面量的方式来创建,这种方式也是我们经常使用的方法:

<span style="font-size:18px;">var pattern=/e/; 
var pattern=/e/gi;</span> 

三、RegExp对象详细解析
(1)RegExp对象属性


这些基本我们在上述的例子都已经见过,但我们还是举几个简单的例子来看一下:

<span style="font-size:18px;">var pattern=/e/gim; 
document.write(pattern.global+" ");//输出:true。说明设置了全局模式 
document.write(pattern.ignoreCase+" ");//输出:true 
document.write(pattern.multiline+" ");//输出:true 
document.write(pattern.source+" ");//输出:e</span> 

(2)RegExp对象方法

 

RegExp对象有3个方法:test()、exec()以及compile()。
1)test()方法检索字符串中的指定值,返回值是true或false。

<span style="font-size:18px;">var pattern=/e/; 
var str="The best things in life are free"; 
document.write(pattern.test(str));//输出:true</span> 

2)exec()方法检索字符串中的指定值,返回值是被找到的值;如果没有发现匹配,则返回null。

实例:

<span style="font-size:18px;">var pattern=/e/; 
var str="The best things in life are free"; 
document.write(pattern.exec(str));//输出:e</span> 

实例:
      向RegExp对象添加第二个参数,以设定检索。如果需要找到所有某个字符的所有存在,则可以使用 “g” 参数。
      在使用 “g” 参数时,exec() 的工作原理如下:

  •       1找到第一个 “e”,并存储其位置。
  •       2如果再次运行exec(),则从存储的位置开始检索,并找到下一个 “e”,并存储其位置。
<span style="font-size:18px;">var pattern=/e/g; 
var str="The best things in life are free"; 
do 
{ 
 var result=pattern.exec(str); 
 document.write(result+" "); 
} 
while(result!=null)</span> 

输出的结果为:e e e e e e null
3)compile()方法用于改变正则表达式,compile()既可以改变检索模式,也可以添加或删除第二个参数。

<span style="font-size:18px;">var pattern=/e/; 
var str="The best things in life are free"; 
document.write(pattern.test(str));//输出:true 
pattern.compile("d"); 
document.write(pattern.test(str));//输出:false</span> 

(3)支持正则表达式的String对象的方法

由于正则表达式和String对象有着一定的联系,因此String对象的一些方法可用于正则表达式:

<span style="font-size:18px;">var pattern=/e/g;//开启全局模式 
var str="The best things in life are free"; 
document.write(str.match(pattren)+"<br/>");//以数组的形式输出:e,e,e,e,e,e 
document.write(str.search(pattren)+"<br/>");//输出:2(返回第一个匹配到的位置) 
document.write(str.replace(pattren,"a")+"<br/>");//输出:Tha bast things in lifa ara fraa 
var pattern1=/\s/g;//\s表示空格字符 
document.write(str.split(pattren1));//输出:The,best,things,in,life,are,free</span> 

(4)元字符是拥有特殊含义的字符:

由于这些使用广泛,我们只是举几个例子:

<span style="font-size:18px;">var pattern=/b.ue/;//点符号表示匹配除了换行符以外的任意字符。 
var str="blue"; 
document.write(pattern.test(str));//输出:true</span> 

(5)方括号用于查找某个范围的字符:

<span style="font-size:18px;">var pattern=/[a-z]oogle/;//[a-z]表示26个小写字母,任意一个都可以匹配 
var str="woogle"; 
document.write(pattren.test(str));//输出:true</span> 

(6)量词

<span style="font-size:18px;">var pattern=/go+gle/;//o*表示匹配至少一个0 
var str="google"; 
document.write(pattren.test(str));//输出:true</span> 

四、常用的正则表达式
主要的是看变量patttern模式字符串表示的正则表达式。其余的是一些JS的基本的东西,可以忽略。
(1)检查邮政编码

<span style="font-size:18px;">var pattern=/^[0-9]{6}$/;//必须是6位,并且都是是数字 
var str=prompt("请输入邮政编码:"); 
if(pattern.test(str)) 
{ 
 alert("您输入的是正确的邮政标号!"); 
} 
else 
{ 
 alert("您输入的是错误的邮政标号!"); 
}</span> 

输入一些数据运行的结果为:
输入:056500

 输入:123

(2)简单电子邮件地址验证

<span style="font-size:18px;">var pattern=/^([\w\.\-]+)@([\w\-]+)\.([a-zA-Z]{2,4})$/; 
var str=prompt("请输入邮箱名称:"); 
if(pattern.test(str)) 
{ 
 alert("您输入的是正确的邮箱名称!"); 
} 
else 
{ 
 alert("您输入的是错误的邮箱名称!"); 
}</span> 

(3)检查上传文件压缩包

<span style="font-size:18px;">var pattern=/[\w]+\.zip|rar|gz/;//\w表示所有数字和字母以及下划线 
var str=prompt("请输入压缩包的名称:"); 
if(pattern.test(str)) 
{ 
 alert("您输入的是正确的压缩包名称!"); 
} 
else 
{ 
 alert("您输入的是错误的压缩包名称!"); 
}</span> 
<span style="font-size:18px;">var pattern=/^[1][0-9]{10}$/; 
var str=prompt("请输入手机号码:"); 
if(pattern.test(str)) 
{ 
 alert("您输入的是正确的手机号码!"); 
} 
else 
{ 
 alert("您输入的是错误的手机号码!"); 
}</span> 

SyntaxHighlighter.highlight();

js自定义回调函数

背景分析

首先看一段js的代码,主要实现添加的时候首先通过异步请求判断是否存在,如果不存在的话,在进行添加操作:

function add(url,data) {
  var isExited = isExited(data); 
  if(!isExited){
    addRequest(url, data); 
  }
}

当我添加一个数据的时候,我首先通过判断是否在数据库中存在(当然,如果前后台彻底分离的话,不应该前端进行业务逻辑的判断,前端只应该,用来展示数据),首先,isExited()的请求是ajax请求实现,这个是异步的,很显然,界面很有可能在没有返回结果的时候,就执行下边的函数(通常情况下是的),就使得isExited的值是undefined,这显然不是想要的,如果要实现类似的功能可以使用用回调函数实现,下边介绍一个案例。

过程如下

前台jsp界面如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>JS回调函数案例</title>
  <!-- Bootstrap -->
  <link href="<c:url value='/lib/bootstrap/css/bootstrap.min.css'/>" rel="stylesheet">

  <script type="text/javascript">

    /**
     * 删除的请求
     */
    function supplierDelete(element) {
      var id = element.parentNode.parentNode.cells[0].innerHTML;
      modalDeleteRequest('${pageContext.request.contextPath}/oms/supplier/remove/', id);
    }
  </script>
</head>
<body>
<!-- 顶部导航 -->
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation" id="menu-nav">

</div>

<div class="container partner-table-container textFont">
  <table class="table table-striped detailTableSet">
    <caption><h2>JS回调函数案例</h2></caption>
    <br>
    <tr class="table-hover form-horizontal">
      <td class="info">123</td>
      <td class="info">123</td>
      <td class="info">123</td>
      <td class="info">123</td>
      <td class="info">123</td>
    </tr>
      <tr>
        <td>123</td>
        <td>123</td>
        <td>123</td>
        <td>123</td>
        <td>123</td>
        <td>
          <a onclick="supplierUpdate(this)">修改</a> 
          <a onclick="supplierDelete(this)">删除</a>
        </td>
      </tr>
  </table>
</div>

<!--显示成功失败的modal-->
<%@include file="/modal-custom.jsp" %>

<script" width=100% src="<c:url value='/lib/jquery-1.8.3.min.js'/>"></script>
<script" width=100% src="<c:url value='/lib/bootstrap/js/bootstrap.min.js'/>"></script>
<script type="text/javascript"" width=100% src="<c:url value='/js/modal-operate.js'/>"></script>
</body>
</html>

主要的js代码如下:

<script type="text/javascript">

    /**
     * 删除的请求
     */
    function supplierDelete(element) {
      var id = element.parentNode.parentNode.cells[0].innerHTML;
      modalDeleteRequest('${pageContext.request.contextPath}/oms/supplier/remove/', id);
    }
  </script>

这里就是当点击按钮的时候进行删除,但是我想弹出一个确认删除对话框,如果弹出之后选择的是确认之后,才调用具体的删除方法,还有这里边引用了一个modal框(bootstrap框架),主要是用于展示弹出框信息,代码如下:

<%@ page language="java" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!-- 模态框(Modal) -->
<div class="modal fade" id="modal-result" tabindex="-1" role="dialog"
   aria-labelledby="myModalLabel" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close"
            data-dismiss="modal" aria-hidden="true">
          ×
        </button>
        <h4 class="modal-title" id="myModalLabel">
          信息
        </h4>
      </div>
      <div class="modal-body" id="modal-add-result-text">
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-default"
            data-dismiss="modal">关闭
        </button>
      </div>
    </div>
    <!-- /.modal-content -->
  </div>
  <!-- /.modal -->
</div>

下边是今天的主角:

/**
 * 删除请求的操作
 * @param url 删除请求的url
 * @param id 删除的id
 */
function modalDeleteRequest(url, id) {
  confirmIsDelete(url, id, deleteRequest);
}
/**
 * 在删除警告框确认之后调用的回调函数
 * @param url
 * @param id
 */
function deleteRequest(url, id) {
  $.get(url + id, function (result) {
    $("#modal-add-result-text").text(result.msg);
    $("#modal-result").modal('show');
  }, "json");
}


/**
 * 弹出对话框确认是否删除
 * @param url 删除请求的url
 * @param id 删除请求的id
 * @param callback 回调函数,在最后的时候需要进行回调的函数
 */
function confirmIsDelete(url, id, callback) {
  var confirmDeleteDialog = $('<div class="modal fade"><div class="modal-dialog">'
    + '<div class="modal-content"><div class="modal-header"><button type="button" class="close" '
    + 'data-dismiss="modal" aria-hidden="true">×</button>'
    + '<h4 class="modal-title">确认删除</h4></div><div class="modal-body">'
    + '<div class="alert alert-warning">确认要删除吗?删除之后无法恢复哦!</div></div><div class="modal-footer">'
    + '<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>'
    + '<button type="button" class="btn btn-success" id="deleteOK">删除</button></div></div>'
    + '</div></div>');

  confirmDeleteDialog.modal({
    keyboard: false
  }).on({
    'hidden.bs.modal': function () {
      $(this).remove();
    }
  });

  var deleteConfirm = confirmDeleteDialog.find('#deleteOK');
  deleteConfirm.on('click', function () {
    confirmDeleteDialog.modal('hide'); //隐藏dialog
    //需要回调的函数
    callback(deleteRequest(url, id));
  });
}

这里写图片描述
这里写图片描述

/**
 * 回调函数测试方法
 * 
 * @param callback
 * 回调的方法
 */
function testCallback(callback) {
  alert('come in!');
  callback();
}

/**
 * 被回调的函数
 */
function a() {
  alert('a');
}

/**
 * 开始测试方法
 */
function start() {
  testCallback(a);
}

SyntaxHighlighter.highlight();

jQuery Validate初步体验(二)

在上篇文章给大家介绍了jQuery Validate初步体验(一) ,本文继续给大家分享jquery validate相关知识,对本文感兴趣的朋友快来学习吧。

刚刚试了所谓的新版的用法。千万别问我是多新,因为我也不知道。。。

<!DOCTYPE html>
<html>
<head>
<script" width=100% src="js/jquery.js"></script>
<script" width=100% src="js/jquery.validate.js"></script>
<script>
 $().ready(function() {
  $("#registerForm").validate();
 });
</script>
</head>
<body>
 <form id="registerForm" method="get" action="">
  <fieldset>
   <p>
    <label for="cusername">用户名</label> 
    <input id="cusername" name="username" type="text" data-rule-required="true" data-rule-rangelength="[2,10]" data-msg-required="用户名不能为空" data-msg-rangelength="用户名长度必须是{0}到{1}个字符">
   </p>
   <p>
    <label for="cpassword">密码</label> 
    <input id="cpassword" name="password" type="password" data-rule-required="true" data-rule-minlength="6" data-msg-required="密码不能为空" data-msg-minlength="密码必须不少于{0}位">
   </p>
   <p>
    <label for="cconfirmpassword">确认密码</label> 
    <input id="cconfirmpassword" name="confirmpassword" type="password" data-rule-required="true" data-rule-equalTo="#cpassword" data-msg-required="请再次输入密码" data-msg-equalTo="两次输入的密码不一致">
   </p>
   <p>
    <label for="cemail">邮箱</label> 
    <input id="cemail" name="email" data-rule-required="true" data-rule-email="true" data-msg-required="邮箱不能为空" data-msg-email="邮箱的格式不正确"/>
   </p>
   <p>
    <input type="submit" value="提交">
   </p>
  </fieldset>
 </form>
</body>
</html> 

 个人感觉这种用法比通过javascript自定义验证规则,要方便和简单多了。昨天在刚开始自定义的时候,总是无法正确的提示,当时完全不知道为什么。直到检查了多遍才发现,原来是因为我的rules里的键值对之间漏了一个逗号。所以,如果默认的校验规则已经满足你的验证需求,而你只是想改变一下提示语。那我个人建议你用这种新版的用法,当然也可以用 系列(一) 里的方法

二。具体看情况自己选择。

还有我上面的提示都是默认的黑色,身为提示,那样也太没存在感了。

为了增加存在感,你只需要在<head></head>之间插入下面的代码就可以了。

<style>
 #registerForm label.error{
  margin-left: 10px;
  color:red;
 }
</style> 

请注意上面的#号后面跟着是表单的ID,你需要改成你自己相应的表单ID。我昨天尝试的时候,找到的资料里写的是#frm。当时我还以为#frm又是我没接触过的新用法呢,我还一直疑惑,为什么我的提示不显示成红色。基础差哎。。。

如果默认的验证规则已经不能满足你的需求,那么接下来,你就得自定义验证规则了。

比如,这里有一个需求,要你检查用户输入的邮编是否合法,这时你就得自定义验证规则了,使用的方法是jQuery.validator.addMethod()。

<!DOCTYPE html>
<html>
<head>
<script" width=100% src="js/jquery.js"></script>
<script" width=100% src="js/jquery.validate.js"></script>
<script>
 $().ready(function() {
  jQuery.validator.addMethod("isZipCode", function(value, element) {
   var tel = /^[0-9]{6}$/;
   return this.optional(element) || (tel.test(value));
  }, "请正确填写您的邮政编码");
  $("#registerForm").validate({
   rules : {
    zipCode : {
     required : true,//对于required的提示语句没有进行任何的处理,他会使用默认的英文的提示。
     isZipCode : true//isZipCode为自定义的验证规则
    }
   },
   messages : {//当你不写提示语句,他会使用上面方法返回的提示。
    zipCode : {
     isZipCode : '请输入正确的邮编'
    }
   }
  });
 });
</script>
<style>
#registerForm label.error {
 margin-left: 10px;
 color: red;
}
</style>
</head>
<body>
 <form id="registerForm" method="get" action="">
  <fieldset>
   <p>
    <label for="czipCode">中国邮编</label> 
    <input id="czipCode" name="zipCode" />
   </p>
   <p>
    <input type="submit" value="提交">
   </p>
  </fieldset>
 </form>
</body>
</html> 

当然,你也可以把这个验证规则提取出来存到一个JS文件里,然后在要用的地方引入JS文件。

jQuery.validator.addMethod("isZipCode", function(value, element) {
 var tel = /^[0-9]{6}$/;
 return this.optional(element) || (tel.test(value));
}, "请正确填写您的邮政编码"); 
<!DOCTYPE html>
<html>
<head>
<script" width=100% src="js/jquery.js"></script>
<script" width=100% src="js/jquery.validate.js"></script>
<script" width=100% src="js/validate.expand.js"></script>
<script>
 $().ready(function() {
  $("#registerForm").validate({
   rules : {
    zipCode : {
     required : true,//对于required的提示语句没有进行任何的处理,他会使用默认的英文的提示。
     isZipCode : true//isZipCode为自定义的验证规则
    }
   },
   messages : {//当你不写提示语句,他会使用方法返回的提示。
    zipCode : {
     isZipCode : '请输入正确的邮编'
    }
   }
  });
 });
</script>
<style>
#registerForm label.error {
 margin-left: 10px;
 color: red;
}
</style>
</head>
<body>
 <form id="registerForm" method="get" action="">
  <fieldset>
   <p>
    <label for="czipCode">中国邮编</label> 
    <input id="czipCode" name="zipCode" />
   </p>
   <p>
    <input type="submit" value="提交">
   </p>
  </fieldset>
 </form>
</body>
</html> 

 接下来,就要好好说说jQuery.validator.addMethod() 这个方法,这个方法一共有三个参数。

第一个参数 :“isZipCode” 是定义方法名,必须保证方法名唯一。

第二个参数:是一个回调(callback)函数。

  这个回调函数有三个参数:

    第一个:value ,是当前被验证的元素的值。

    第二个:element,是当前被验证的元素。

    第三个:param,是传入的参数,例如: minlength : 6  里的参数为6;  rangelength:[2,10]  里的参数为2和10。当你没有传入参数,这个可以省略不写。

    在这个回调函数里有一个this.optional(element)函数要特别注意。当表单的输入值为空时,即element的值为空,this.optional(element)的返回值为true,类似于判空操作,也就是说该表单输入项不是必填项,当不填时通过了验证。如果element的值不为空,this.optional(element)的返回值就是false,这时就要根据  ||  后面的验证来判断最终的返回为true 或false。所以当某个输入项不是必填项但如果填写了又需要按照一定的规则来验证的时候,一定要记得带上this.optional(element)。

第三个参数:验证的提示信息。

  这个参数可以直接是一句提示信息,例如,”这是必填字段”;也可以通过创建函数jQuery.validator.format(value)来显示,例如,$.validator.format(“请输入长度在 {0} 到 {1} 之间的字符串”),其中 {0} {1} 代表该方法的参数,也就是回调(callback)函数里的第三个参数param。

官网提供的additional-methods.js里包含一些常用的验证方式,比如lettersonly,ziprange,nowhitespace等,但我粗略的看了一下,发现大部分只是仅供参考,实用性不强,不能直接使用。

// 邮政编码验证 
jQuery.validator.addMethod("isZipCode", function(value, element) {
var tel = /^[0-9]{6}$/;
return this.optional(element) || (tel.test(value));
}, "请正确填写您的邮政编码");
// 手机号码验证 
jQuery.validator.addMethod("mobile", function(value, element) { 
var length = value.length;
var mobile = /^(13[0-9]|14[0-9]|15[0-9]|18[0-9])\d{8}$/;
return this.optional(element) || (length == 11 && mobile.test(value)); 
}, "手机号码格式错误"); 
// QQ号码验证 
jQuery.validator.addMethod("qq", function(value, element) { 
var tel = /^[1-9]\d{4,10}$/; 
return this.optional(element) || (tel.test(value)); 
}, "qq号码格式错误"); 
// IP地址验证 
jQuery.validator.addMethod("ip", function(value, element) { 
var ip = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; 
return this.optional(element) || (ip.test(value) && (RegExp.$1 < 256 && RegExp.$2 < 256 && RegExp.$3 < 256 && RegExp.$4 < 256)); 
}, "Ip地址格式错误"); 
// 字母和数字的验证 
jQuery.validator.addMethod("chrnum", function(value, element) { 
var chrnum = /^([a-zA-Z0-9]+)$/; 
return this.optional(element) || (chrnum.test(value)); 
}, "只能输入数字和字母(字符A-Z, a-z, 0-9)"); 
// 中文的验证 
jQuery.validator.addMethod("chinese", function(value, element) { 
var chinese = /^[\u4e00-\u9fa5]+$/; 
return this.optional(element) || (chinese.test(value)); 
}, "只能输入中文"); 

SyntaxHighlighter.highlight();

JavaScript 七大技巧(一)

JavaScript是一门非常流行的编程语言,许多开发者都会把JavaScript选为入门语言,本文向大家分享JavaScript七大实用技巧、最佳实践等非常实用的内容。在过去,如果你想创建一个对象,你需要这样:

 var car = new Object();
   car.colour = 'red';
   car.wheels = ;
   car.hubcaps = 'spinning';
   car.age = ;  下面的写法能够达到同样的效果: var car = {
   colour:'red',
   wheels:,
   hubcaps:'spinning',
   age:
   }

简单多了,你不需要反复使用这个对象的名称。

这样 car 就定义好了,也许你会遇到 invalidUserInSession 的问题,这只有你在使用IE时会碰到,只要记住一点,不要右大括

号前面写分号,你就不会有麻烦。  

另外一个十分方便的简写是针对数组的。  

传统的定义数组的方法是这样:

 var moviesThatNeedBetterWriters
  = new Array(
   'Transformers','Transformers','Avatar','Indiana
  Jones '
   );  简写版的是这样: var moviesThatNeedBetterWriters
  = [
   'Transformers','Transformers','Avatar','Indiana
  Jones '
   ];

  对于数组,这里有个问题,其实没有什么图组功能。但你会经常发现有人这样定义上面的 car ,就像这样

 var car = new Array();
   car['colour'] = 'red';
   car['wheels'] = ;
   car['hubcaps'] = 'spinning';
   car['age'] = ;

数组不是万能的;这样写不对,会让人困惑。图组实际上是对象的功能,人们混淆了这两个概念。  
另外一个非常酷的简写方法是使用与三元条件符号。  

你不必写成下面的样子…

 var direction;
   if(x < ){
   direction = ;
   } else {
   direction = -;
   }
...

  你可以使用三元条件符号简化它:  

var direction

  = x < 200 ? 1 : -1; 

SyntaxHighlighter.highlight();

javascript性能优化之事件委托实例详解

本文实例分析了javascript性能优化之事件委托。分享给大家供大家参考,具体如下:

为下面每个LI绑定一个click事件

<ul id="myLinks">
  <li id="goSomewhere" >Go somewhere</li>
  <li id="doSomething" >Do something</li>
  <li id="sayHi" >Say hi</li>
</ul>

一、传统写法

var item1=document.getElementById("goSomewhere");
var item2=document.getElementById("doSomething");
var item3=document.getElementById("sayHi");
item1.onclick = function(){
  console.log('goSomewhere');
}
item2.onclick = function(){
  console.log('doSomething');
}
item3.onclick = function(){
  alert("hello");
}

在javascript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,事件越多,性能越差。

导致原因是多方面:

1、每个函数都是对象,都会占用内存;内存中的对象越多,性能就越差。
2、必须事先指定所有事件处理程序而导致的DOM访问次数,会延迟整个页面的交互就绪时间。

二、事件委托

对“事件处理程序过多”问题的解决方案就是事件委托。

事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。例如:click事件会一直冒泡到document层次。也就是说,我们可以为整个页面指定一个onclick事件处理程序,而不必给每个可单击的元素分别添加事件处理程序。

事件委托方法:

var list=document.getElementById("myLinks");
list.onclick = function(e){
  var target = e.target;  
  switch(target.id){
   case "goSomewhere":
    console.log('goSomewhere');
    break; 
   case "doSomething":
    console.log('doSomething');
    break; 
   case "sayHi":
    alert("hello");
    break; 
  }
}

三、使用事件委托的优点:

1)document对象很快就可以访问,而且可以在页面生命周期的任何时间点上为它添加事件处理程序(无需等待DOMContentLoaded或load事件)。换句话说,只要可单击的元素呈现在页面上,就可以立即具备适当的功能。

2)在页面中设置事件处理程序所需的时间更少。只添加一个事件处理程序所需的Dom引用更少,所花的时间也更少。

SyntaxHighlighter.highlight();

JavaScript 模块的循环加载实现方法

“循环加载”(circular dependency)指的是,a脚本的执行依赖b脚本,而b脚本的执行又依赖a脚本。

// a.js
var b = require('b');

// b.js
var a = require('a');

通常,”循环加载”表示存在强耦合,如果处理不好,还可能导致递归加载,使得程序无法执行,因此应该避免出现。

但是实际上,这是很难避免的,尤其是依赖关系复杂的大项目,很容易出现a依赖b,b依赖c,c又依赖a这样的情况。这意味着,模块加载机制必须考虑”循环加载”的情况。

本文介绍JavaScript语言如何处理”循环加载”。目前,最常见的两种模块格式CommonJS和ES6,处理方法是不一样的,返回的结果也不一样。

一、CommonJS模块的加载原理

介绍ES6如何处理”循环加载”之前,先介绍目前最流行的CommonJS模块格式的加载原理。

CommonJS的一个模块,就是一个脚本文件。require命令第一次加载该脚本,就会执行整个脚本,然后在内存生成一个对象。

{
 id: '...',
 exports: { ... },
 loaded: true,
 ...
}

上面代码中,该对象的id属性是模块名,exports属性是模块输出的各个接口,loaded属性是一个布尔值,表示该模块的脚本是否执行完毕。其他还有很多属性,这里都省略了。(详细介绍情参见《require() 源码解读》。)

以后需要用到这个模块的时候,就会到exports属性上面取值。即使再次执行require命令,也不会再次执行该模块,而是到缓存之中取值。

二、CommonJS模块的循环加载

CommonJS模块的重要特性是加载时执行,即脚本代码在require的时候,就会全部执行。CommonJS的做法是,一旦出现某个模块被”循环加载”,就只输出已经执行的部分,还未执行的部分不会输出。

让我们来看,官方文档里面的例子。脚本文件a.js代码如下。

exports.done = false;
var b = require('./b.js');
console.log('在 a.js 之中,b.done = %j', b.done);
exports.done = true;
console.log('a.js 执行完毕');

上面代码之中,a.js脚本先输出一个done变量,然后加载另一个脚本文件b.js。注意,此时a.js代码就停在这里,等待b.js执行完毕,再往下执行。

再看b.js的代码。

exports.done = false;
var a = require('./a.js');
console.log('在 b.js 之中,a.done = %j', a.done);
exports.done = true;
console.log('b.js 执行完毕');

上面代码之中,b.js执行到第二行,就会去加载a.js,这时,就发生了”循环加载”。系统会去a.js模块对应对象的exports属性取值,可是因为a.js还没有执行完,从exports属性只能取回已经执行的部分,而不是最后的值。

a.js已经执行的部分,只有一行。

exports.done = false;

因此,对于b.js来说,它从a.js只输入一个变量done,值为false。

然后,b.js接着往下执行,等到全部执行完毕,再把执行权交还给a.js。于是,a.js接着往下执行,直到执行完毕。我们写一个脚本main.js,验证这个过程。

var a = require('./a.js');
var b = require('./b.js');
console.log('在 main.js 之中, a.done=%j, b.done=%j', a.done, b.done);

执行main.js,运行结果如下。

$ node main.js

在 b.js 之中,a.done = false
b.js 执行完毕
在 a.js 之中,b.done = true
a.js 执行完毕
在 main.js 之中, a.done=true, b.done=true

上面的代码证明了两件事。一是,在b.js之中,a.js没有执行完毕,只执行了第一行。二是,main.js执行到第二行时,不会再次执行b.js,而是输出缓存的b.js的执行结果,即它的第四行。

exports.done = true;

三、ES6模块的循环加载

ES6模块的运行机制与CommonJS不一样,它遇到模块加载命令import时,不会去执行模块,而是只生成一个引用。等到真的需要用到时,再到模块里面去取值。

因此,ES6模块是动态引用,不存在缓存值的问题,而且模块里面的变量,绑定其所在的模块。请看下面的例子。

// m1.js
export var foo = 'bar';
setTimeout(() => foo = 'baz', 500);

// m2.js
import {foo} from './m1.js';
console.log(foo);
setTimeout(() => console.log(foo), 500);

上面代码中,m1.js的变量foo,在刚加载时等于bar,过了500毫秒,又变为等于baz。

让我们看看,m2.js能否正确读取这个变化。

$ babel-node m2.js

bar
baz

面代码表明,ES6模块不会缓存运行结果,而是动态地去被加载的模块取值,以及变量总是绑定其所在的模块。

这导致ES6处理”循环加载”与CommonJS有本质的不同。ES6根本不会关心是否发生了”循环加载”,只是生成一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。

请看下面的例子(摘自 Dr. Axel Rauschmayer 的《Exploring ES6》)。

// a.js
import {bar} from './b.js';
export function foo() {
 bar(); 
 console.log('执行完毕');
}
foo();

// b.js
import {foo} from './a.js';
export function bar() { 
 if (Math.random() > 0.5) {
 foo();
 }
}

按照CommonJS规范,上面的代码是没法执行的。a先加载b,然后b又加载a,这时a还没有任何执行结果,所以输出结果为null,即对于b.js来说,变量foo的值等于null,后面的foo()就会报错。

但是,ES6可以执行上面的代码。

$ babel-node a.js

执行完毕

a.js之所以能够执行,原因就在于ES6加载的变量,都是动态引用其所在的模块。只要引用是存在的,代码就能执行。

我们再来看ES6模块加载器SystemJS给出的一个例子。

// even.js
import { odd } from './odd'
export var counter = 0;
export function even(n) {
 counter++;
 return n == 0 || odd(n - 1);
}

// odd.js
import { even } from './even';
export function odd(n) {
 return n != 0 && even(n - 1);
}

上面代码中,even.js里面的函数foo有一个参数n,只要不等于0,就会减去1,传入加载的odd()。odd.js也会做类似操作。

运行上面这段代码,结果如下。

$ babel-node
> import * as m from './even.js';
> m.even(10);
true
> m.counter
6
> m.even(20)
true
> m.counter
17

上面代码中,参数n从10变为0的过程中,foo()一共会执行6次,所以变量counter等于6。第二次调用even()时,参数n从20变为0,foo()一共会执行11次,加上前面的6次,所以变量counter等于17。

这个例子要是改写成CommonJS,就根本无法执行,会报错。

// even.js
var odd = require('./odd');
var counter = 0;
exports.counter = counter;
exports.even = function(n) {
 counter++;
 return n == 0 || odd(n - 1);
}

// odd.js
var even = require('./even').even;
module.exports = function(n) {
 return n != 0 && even(n - 1);
}
$ node
> var m = require('./even');
> m.even(10)
TypeError: even is not a function

SyntaxHighlighter.highlight();

解决Redefining already defined constructor for class…的问题

本地PHP环境PHP5.4,安装ecshop2.7.3后,很多地方会报如下的错

Redefining already defined constructor for class XXX

检查代码可以发现,对应的位置是某个类的构造函数,具体写法如下:

/**
	     * 构造函数
	     *
	     * @access  public
	     * @param
	     *
	     * @return void
	     */
	    function alipay()
	    {
	    }
	 
	    function __construct()
	    {
	        $this->alipay();
	    }

其中使用和类名相同点函数名作为构造函数是php4时代的写法,php5时代的构造函数是 __construct(),ecshop为了兼容老版本的php,所以采用了上面的写法。

但是从php5.4开始,对于这样的两种写法同时出现的情况,要求必须__construct()在前,同名函数在后,所以只需要对调两个函数的位置即可。

/**
	     * 构造函数
	     *
	     * @access  public
	     * @param
	     *
	     * @return void
	     */
	     function __construct()
	    {
	        $this->alipay();
	    }
	    function alipay()
	    {
	    }
	 	

SyntaxHighlighter.highlight();

php模拟登陆并提交数据

给入账号密码

<?php
if (!array_key_exists('account', $_GET)) die('Account Request');
if (!array_key_exists('password', $_GET)) die('Password Request');
if (!array_key_exists('words', $_GET)) die('Words Request');
$split = '-';
$account  = $_GET['account'];
$password = $_GET['password'];
$words = $_GET['words'];
$ch = curl_init();
$cookie = dirname(__FILE__).'/'.uniqid().'.txt';
curl_setopt_array($ch, array(
    CURLOPT_URL=>'http://www.shanbay.com/accounts/login/',
    CURLOPT_HEADER=>0,
    CURLOPT_RETURNTRANSFER=>1,
    CURLOPT_COOKIEJAR => $cookie
));
$data = curl_exec($ch);
if (curl_errno($ch)){
    curl_close($ch);
    echo curl_error($ch);
    die();
}
//  解析token
preg_match('/<input type=\'hidden\' name=\'(.*)\' value=\'(.*)\' \/>/', $data, $tokenArray);
if (is_array($tokenArray) && count($tokenArray)>1){
    $loginKey = $tokenArray[1];
    $loginToken = $tokenArray[2];
    //  执行登陆
    curl_setopt_array($ch, array(
        CURLOPT_URL=>'http://www.shanbay.com/accounts/login/',
        CURLOPT_HEADER=>0,
        CURLOPT_RETURNTRANSFER=>1,
        CURLOPT_COOKIEJAR => $cookie,
        CURLOPT_COOKIEFILE => $cookie,
        CURLOPT_POST => 1,
        CURLOPT_POSTFIELDS=>$loginKey.'='.$loginToken.'&username='.$account.'&password='.$password.'&token=&token='
    ));
    $data = curl_exec($ch);
    if (curl_errno($ch)){
        curl_close($ch);
        echo curl_error($ch);
        die();
    }
    //  检查头部
    $header = curl_getinfo($ch);
    if ($header['http_code'] == 302){
        //  登陆成功 添加单词
        $wordURLFormat = 'http://www.shanbay.com/bdc/vocabulary/add/batch/?words=%s&_=%s';
        $wordURL = sprintf($wordURLFormat,str_replace($split, '', $words), time()*1000+mt_rand(0, 1000));
        $headerArray = array(
            'X-Requested-With: XMLHttpRequest'
        );
        $verbose = fopen(dirname(__FILE__).'/error.txt', 'w+');
        
        curl_setopt_array($ch, array(
            CURLOPT_URL=>$wordURL,
            CURLOPT_HEADER=>0,
            CURLOPT_RETURNTRANSFER=>1,
            CURLOPT_COOKIEJAR => $cookie,
            CURLOPT_COOKIEFILE => $cookie,
            CURLOPT_HTTPHEADER=>$headerArray,
            CURLOPT_REFERER=>'http://www.shanbay.com/bdc/vocabulary/add/batch/',
            CURLOPT_VERBOSE=>1,
            CURLOPT_STDERR=>$verbose,
            CURLOPT_CUSTOMREQUEST=>"GET"
        ));
        $data = curl_exec($ch);
        if (curl_errno($ch)){
            curl_close($ch);
            echo curl_error($ch);
            die();
        }
        curl_close($ch);
        $dataJSON = json_decode($data, true);
        if (count($dataJSON['notfound_words'])){
            $errorString = array();
            foreach ($dataJSON['notfound_words'] as $v) $errorString[] = $v;
            echo "Not found for those words:".implode(' ', $errorString);
            die();
        }
        unlink($cookie);
        echo 'success';
    }
    die();
}
echo 'Token parse error';

SyntaxHighlighter.highlight();

PHP常用的小程序代码段

本文实例讲述了PHP常用的小程序代码段。分享给大家供大家参考,具体如下:

1.计算两个时间的相差几天

$startdate=strtotime("2009-12-09");$enddate=strtotime("2009-12-05");

上面的php时间日期函数strtotime已经把字符串日期变成了时间戳,这样只要让两数值相减,然后把秒变成天就可以了,比较的简单,如下:

$days=round(($enddate-$startdate)/3600/24) ;echo $days; //days为得到的天数;

2.分页

/*** author jackluo* $url 地址,$count 总数,$page 当前面,$Pagesize 分页大小*/ function page_paper($url,$count,$page,$pagesize){  $allpage = ceil($count/$pagesize);  if($allpage<=3){   for($i=1;$i<=$allpage;$i++){    if($i==$page){     echo '<a href="'.$url.'&page='.$page.'" class="page_ovr">'.$i.'</a>';    }else{     echo '<a href="'.$url.'&page='.$i.'" >'.$i.'</a>';    }   }  }else{   $currentpage =  $allpage-$page;   if($page<=3){    for($i=1;$i<=$page;$i++){     if($i == $page){      echo '<a href="'.$url.'&page='.$i.'" class="page_ovr">'.$i.'</a>';     }else{      echo '<a href="'.$url.'&page='.$i.'" >'.$i.'</a>';     }    }    //后三条    if($currentpage<=3){     for($i=($page+1);$i<=$allpage;$i++){      echo '<a href="'.$url.'&page='.$i.'" >'.$i.'</a>';     }    }else{     for($i=($page+1);$i<=($page+3);$i++){      echo '<a href="'.$url.'&page='.$i.'" >'.$i.'</a>';     }    }   }else{    //前三条    for($i=($page-3);$i<=$page;$i++){     if($i == $page){      echo '<a href="'.$url.'&page='.$i.'" class="page_ovr">'.$i.'</a>';     }else{      echo '<a href="'.$url.'&page='.$i.'" >'.$i.'</a>';     }    }    if($currentpage<=3){     for($i=($page+1);$i<=$allpage;$i++){      echo '<a href="'.$url.'&page='.$i.'" >'.$i.'</a>';     }    }else{     //后三条     for($i=($page+1);$i<=($page+3);$i++){       echo '<a href="'.$url.'&page='.$i.'" >'.$i.'</a>';     }    }   }  }}
//获得手机归属地function phonenumberinfo($phone){  $list = array();  $soap =  new SoapClient('http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx?wsdl');  $result =(array) $soap->getMobileCodeInfo(array(    'mobileCode'=>$phone  ));  list($moblie,$location,$lbs) = explode(' ', $result['getMobileCodeInfoResult']);  if($lbs){   $type =  array('移动','电信','联通');   foreach($type as $key=>$value){    $ps = strpos($lbs, $value);    if($ps){     $procver = substr($lbs, 0,$ps);     $list['province'] = $procver;     $list['operator'] = $value;     $list['city'] = $location;     $list['type'] = $key;     break;    }   }   return $list;  }}

SyntaxHighlighter.highlight();

PHP使用PHPexcel导入导出数据的方法

本文实例讲述了PHP使用PHPexcel导入导出数据的方法。分享给大家供大家参考,具体如下:

导入数据:

<?phperror_reporting(E_ALL); //开启错误set_time_limit(0); //脚本不超时date_default_timezone_set('Europe/London'); //设置时间/** Include path **/set_include_path(get_include_path() . PATH_SEPARATOR . 'http://www.jb51.net/../Classes/');//设置环境变量/** PHPExcel_IOFactory */include 'PHPExcel/IOFactory.php';//$inputFileType = 'Excel5'; //这个是读 xls的 $inputFileType = 'Excel2007';//这个是计xlsx的//$inputFileName = './sampleData/example2.xls';$inputFileName = './sampleData/book.xlsx';  echo 'Loading file ',pathinfo($inputFileName,PATHINFO_BASENAME),' using IOFactory with a defined reader type of ',$inputFileType,'<br />';  $objReader = PHPExcel_IOFactory::createReader($inputFileType);  $objPHPExcel = $objReader->load($inputFileName);  /*  $sheet = $objPHPExcel->getSheet(0);  $highestRow = $sheet->getHighestRow(); //取得总行数  $highestColumn = $sheet->getHighestColumn(); //取得总列  */   $objWorksheet = $objPHPExcel->getActiveSheet();//取得总行数  $highestRow = $objWorksheet->getHighestRow();//取得总列数  echo 'highestRow='.$highestRow;  echo "<br>";  $highestColumn = $objWorksheet->getHighestColumn();  $highestColumnIndex = PHPExcel_Cell::columnIndexFromString($highestColumn);//总列数  echo 'highestColumnIndex='.$highestColumnIndex;  echo "<br />";  $headtitle=array();  for ($row = 1;$row <= $highestRow;$row++)  {   $strs=array();   //注意highestColumnIndex的列数索引从0开始   for ($col = 0;$col < $highestColumnIndex;$col++)   {    $strs[$col] =$objWorksheet->getCellByColumnAndRow($col, $row)->getValue();   }    $info = array(     'word1'=>"$strs[0]",     'word2'=>"$strs[1]",     'word3'=>"$strs[2]",     'word4'=>"$strs[3]",    );    //在这儿,你可以连接,你的数据库,写入数据库了    print_r($info);    echo '<br />';  }?>

导出数据:

(如果有特殊的字符串 = 麻烦  str_replace(array(‘=’),”,$val[‘roleName’]);)

private function _export_data($data = array()){ error_reporting(E_ALL); //开启错误 set_time_limit(0); //脚本不超时 date_default_timezone_set('Europe/London'); //设置时间 /** Include path **/ set_include_path(FCPATH.APPPATH.'/libraries/Classes/');//设置环境变量 // Create new PHPExcel object Include 'PHPExcel.php'; $objPHPExcel = new PHPExcel(); // Set document properties $objPHPExcel->getProperties()->setCreator("Maarten Balliauw")    ->setLastModifiedBy("Maarten Balliauw")    ->setTitle("Office 2007 XLSX Test Document")    ->setSubject("Office 2007 XLSX Test Document")    ->setDescription("Test document for Office 2007 XLSX, generated using PHP classes.")    ->setKeywords("office 2007 openxml php")    ->setCategory("Test result file"); // Add some data $letter = array('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');     if($data){  $i = 1;  foreach ($data as $key => $value) {  $newobj = $objPHPExcel->setActiveSheetIndex(0);  $j = 0;   foreach ($value as $k => $val) {   $index = $letter[$j]."$i";   $objPHPExcel->setActiveSheetIndex(0)->setCellValue($index, $val);   $j++;  }   $i++;  } }    $date = date('Y-m-d',time());   // Rename worksheet $objPHPExcel->getActiveSheet()->setTitle($date); $objPHPExcel->setActiveSheetIndex(0); // Redirect output to a client's web browser (Excel2007) header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); header('Content-Disposition: attachment;filename="'.$date.'.xlsx"'); header('Cache-Control: max-age=0'); $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007'); $objWriter->save('php://output'); exit;}
public function export_data($data = array()){ # code... include_once(APP_PATH.'Tools/PHPExcel/Classes/PHPExcel/Writer/IWriter.php') ; include_once(APP_PATH.'Tools/PHPExcel/Classes/PHPExcel/Writer/Excel5.php') ; include_once(APP_PATH.'Tools/PHPExcel/Classes/PHPExcel.php') ; include_once(APP_PATH.'Tools/PHPExcel/Classes/PHPExcel/IOFactory.php') ; $obj_phpexcel = new PHPExcel(); $obj_phpexcel->getActiveSheet()->setCellValue('a1','Key'); $obj_phpexcel->getActiveSheet()->setCellValue('b1','Value');  if($data){  $i =2;  foreach ($data as $key => $value) {  # code...  $obj_phpexcel->getActiveSheet()->setCellValue('a'.$i,$value);  $i++;  } }  $obj_Writer = PHPExcel_IOFactory::createWriter($obj_phpexcel,'Excel5'); $filename = "outexcel.xls"; header("Content-Type: application/force-download");  header("Content-Type: application/octet-stream");  header("Content-Type: application/download");  header('Content-Disposition:inline;filename="'.$filename.'"');  header("Content-Transfer-Encoding: binary");  header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");  header("Cache-Control: must-revalidate, post-check=0, pre-check=0");  header("Pragma: no-cache");  $obj_Writer->save('php://output'); }

SyntaxHighlighter.highlight();

教大家制作简单的php日历

最近的一个项目中,需要将数据用日历方式显示,网上有很多的JS插件,后面为了自己能有更大的控制权,决定自己制作一个日历显示。如下图所示:

一、计算数据
1、new一个Calendar类

2、初始化两个下拉框中的数据,年份与月份

3、初始化要搜索的年份和月份

4、计算得出日历中每一天的数据信息,包括css、天数

<?php require_once 'calendar.php'; $util = new Calendar(); $years = array(2012, 2013, 2014, 2015, 2016);//年份选择自定义 $months = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);//月份数组 //获取post的年份数据 if(empty($_POST['ddlYear'])) {  $year = date('Y'); }else {  $year = $_POST['ddlYear']; } //获取post的月份数据 if(empty($_POST['ddlMonth'])) {  $month = date('n'); }else {  $month = $_POST['ddlMonth']; } $calendar = $util->threshold($year, $month);//获取各个边界值 $caculate = $util->caculate($calendar);//计算日历的天数与样式 $draws = $util->draw($caculate);//画表格,设置table中的tr与td?>

二、html展示
1、休息天的背景色是不同的,不是当前搜索年月的天数字体颜色也是不同的

2、div中做初始化年份与月份的下拉框的操作,并选中当前要搜索的年月

3、数据已计算好,哪个td属于哪个tr也已做好,直接将table打印出来即可
  

<div style="padding:20px">  <select name="ddlYear">  <?php foreach($years as $data) {?>   <option value="<?php echo $data?>" <?php if($year == $data) echo 'selected="selected"'?>><?php echo $data?></option>  <?php }?>  </select>  <select name="ddlMonth">  <?php foreach($months as $data) {?>   <option value="<?php echo $data?>" <?php if($month == $data) echo 'selected="selected"'?>><?php echo $data?></option>  <?php }?>  </select>  <input type="submit" value="修改"/> </div> <table width="100%" cellspacing="0" class="table_calendar">  <thead class="f14">    <tr>     <td width="16%">日</td>     <td width="14%">一</td>     <td width="14%">二</td>     <td width="14%">三</td>     <td width="14%">四</td>     <td width="14%">五</td>     <td width="14%">六</td>    </tr>  </thead>  <tbody class="f14">   <?php foreach($draws as $draw) {?>    <tr>    <?php foreach($draw as $date) {?>     <td class="<?php echo $date['tdclass']?>">      <p class="<?php echo $date['pclass']?>"><?php echo $date['day']?></p>     </td>    <?php }?>     </tr>   <?php }?>  </tbody> </table>

三、Calendar类
1、threshold方法,生成日历的各个边界值

  1)计算这个月总天数

  2)计算这个月第一天与最后一天,各是星期几

  3)计算日历中的第一个日期与最后一个日期
  

/**  * @deprecated 生成日历的各个边界值  * @param string $year  * @param string $month  * @return array  */ function threshold($year, $month) {  $firstDay = mktime(0, 0, 0, $month, 1, $year);  $lastDay = strtotime('+1 month -1 day', $firstDay);  //取得天数   $days = date("t", $firstDay);  //取得第一天是星期几  $firstDayOfWeek = date("N", $firstDay);  //获得最后一天是星期几  $lastDayOfWeek = date('N', $lastDay);    //上一个月最后一天  $lastMonthDate = strtotime('-1 day', $firstDay);  $lastMonthOfLastDay = date('d', $lastMonthDate);  //下一个月第一天  $nextMonthDate = strtotime('+1 day', $lastDay);  $nextMonthOfFirstDay = strtotime('+1 day', $lastDay);    //日历的第一个日期  if($firstDayOfWeek == 7)   $firstDate = $firstDay;  else    $firstDate = strtotime('-'. $firstDayOfWeek .' day', $firstDay);  //日历的最后一个日期  if($lastDayOfWeek == 6)   $lastDate = $lastDay;  elseif($lastDayOfWeek == 7)    $lastDate = strtotime('+6 day', $lastDay);  else   $lastDate = strtotime('+'.(6-$lastDayOfWeek).' day', $lastDay);    return array(    'days' => $days,     'firstDayOfWeek' => $firstDayOfWeek,     'lastDayOfWeek' => $lastDayOfWeek,    'lastMonthOfLastDay' => $lastMonthOfLastDay,    'firstDate' => $firstDate,    'lastDate' => $lastDate,    'year' => $year,    'month' => $month  ); }

2、caculate方法,计算日历的天数与样式

  1)将上个月的天数计算出来,本月第一天的星期不是星期天的话,就需要根据上个月的最后一天计算

  2)将本月的天数遍历出来,如果是休息天就加上特殊的css样式

  3)将下个月的天数计算出来,分三种情况,星期日、星期六和工作日
  

/**  * @author Pwstrick   * @param array $calendar 通过threshold方法计算后的数据  * @deprecated 计算日历的天数与样式  */ function caculate($calendar) {  $days = $calendar['days'];  $firstDayOfWeek = $calendar['firstDayOfWeek'];//本月第一天的星期  $lastDayOfWeek = $calendar['lastDayOfWeek'];//本月最后一天的星期  $lastMonthOfLastDay = $calendar['lastMonthOfLastDay'];//上个月的最后一天  $year = $calendar['year'];  $month = $calendar['month'];    $dates = array();  if($firstDayOfWeek != 7) {   $lastDays = array();   $current = $lastMonthOfLastDay;//上个月的最后一天   for ($i = 0; $i < $firstDayOfWeek; $i++) {    array_push($lastDays, $current);//添加上一个月的日期天数    $current--;   }   $lastDays = array_reverse($lastDays);//反序   foreach ($lastDays as $index => $day) {    array_push($dates, array('day' => $day, 'tdclass' => ($index ==0 ?'rest':''), 'pclass' => 'outter'));   }  }    //本月日历信息  for ($i = 1; $i <= $days; $i++) {   $isRest = $this->_checkIsRest($year, $month, $i);   //判断是否是休息天   array_push($dates, array('day' => $i, 'tdclass' => ($isRest ?'rest':''), 'pclass' => ''));  }    //下月日历信息  if($lastDayOfWeek == 7) {//最后一天是星期日   $length = 6;  }  elseif($lastDayOfWeek == 6) {//最后一天是星期六   $length = 0;  }else {   $length = 6 - $lastDayOfWeek;  }  for ($i = 1; $i <= $length; $i++) {   array_push($dates, array('day' => $i, 'tdclass' => ($i==$length ?'rest':''), 'pclass' => 'outter'));  }    return $dates; }

3、draw方法,画表格,设置table中的tr与td

  1)数据将要用table标签来显示,所以这里要将各个tr下面的td排列好

  2)$index % 7 == 0 计算表格每行的第一列

  3)$index % 7 == 6 || $index == ($length-1) 计算每行的最后一列,或$caculate的最后一个数据

  4)将中间行添加到$tr中,就是每一行的array

 /**  * @author Pwstrick  * @param array $caculate 通过caculate方法计算后的数据  * @deprecated 画表格,设置table中的tr与td  */ function draw($caculate) {  $tr = array();  $length = count($caculate);  $result = array();  foreach ($caculate as $index => $date) {   if($index % 7 == 0) {//第一列    $tr = array($date);   }elseif($index % 7 == 6 || $index == ($length-1)) {    array_push($tr, $date);    array_push($result, $tr);//添加到返回的数据中    $tr = array();//清空数组列表   }else {    array_push($tr, $date);   }  }  return $result; }

SyntaxHighlighter.highlight();

PHP安装threads多线程扩展基础教程

一、下载pthreads扩展

下载地址:

二、判断PHP是ts还是nts版

通过phpinfo(); 查看其中的 Thread Safety 项,这个项目就是查看是否是线程安全,如果是:enabled,一般来说应该是ts版,否则是nts版。

三、根据PHP ts/nts版选择对应pthreads的版本

本人php版本是5.4.17的所以下载php_pthreads-0.1.0-5.4-ts-vc9-x86.zip文件包,其中0.1.0表示为当前pthreads版本号,5.4为php版本号,ts就是之前判断php对应的ts、nts版,vs9代表是Visual Studio 2008 compiler编译器编译的,最后的x86代表的是32位的版本。

四、下载pthreads扩展

下载地址:

五、安装pthreads扩展

复制php_pthreads.dll 到目录 bin/php/ext/ 下面。
复制pthreadVC2.dll 到目录 bin/php/ 下面。
复制pthreadVC2.dll 到目录 C:/windows/system32 下面。
打开php配置文件php.ini。在后面加上extension=php_pthreads.dll
提示!Windows系统需要将 pthreadVC2.dll 所在路径加入到 PATH 环境变量中。我的电脑—>鼠标右键—>属性—>高级—>环境变量—>系统变量—>找到名称为Path的—>编辑—>在变量值最后面加上pthreadVC2.dll的完整路径(本人的为C:/WINDOWS/system32/pthreadVC2.dll)。

六、添加thread类

<?phpclass Thread{  var $hooks = array();  var $args = array();  function thread()  {  }  function addthread($func)  {    $args = array_slice(func_get_args(), 1);    $this->hooks[] = $func;    $this->args[] = $args;    return true;  }  function runthread()  {    if(isset($_GET['flag']))    {      $flag = intval($_GET['flag']);    }    if($flag || $flag === 0)    {      call_user_func_array($this->hooks[$flag], $this->args[$flag]);    }    else    {      for($i = 0, $size = count($this->hooks); $i < $size; $i++)      {        $fp=fsockopen($_SERVER['HTTP_HOST'],$_SERVER['SERVER_PORT']);        if($fp)        {          $out = "GET {$_SERVER['PHP_SELF']}?flag=$i HTTP/1.1rn";          $out .= "Host: {$_SERVER['HTTP_HOST']}rn";          $out .= "Connection: Closernrn";          fputs($fp,$out);          fclose($fp);        }      }    }  }}
include('thread.php');class AsyncOperation extends Thread {  public function __construct($arg){    $this->arg = $arg;  }  public function run(){    if($this->arg){      printf("Hello %s/n", $this->arg);    }  }}$thread = new AsyncOperation("World");if($thread->start())  $thread->join();

SyntaxHighlighter.highlight();

PHP YII框架开发小技巧之模型(models)中rules自定义验证规则

YII的models中的rules部分是一些表单的验证规则,对于表单验证十分有用,在相应的视图(views)里面添加了表单,在表单被提交之前程序都会自动先来这里面的规则里验证,只有通过对其有效的限制规则后才能被提交,可以很有效地保证表单安全和信息的有效性。还是给大家具体说明一下:

以下是视图(views)部分的简单代码:

<?php $form=$this->beginWidget('CActiveForm', array(   'id'=>'tag-form',   'enableAjaxValidation'=>false, )); ?>   <div class="row">     <?php echo $form->labelEx($model,'tagname'); ?>     <?php echo $form->textField($model,'tagname',array('size'=>20,'maxlength'=>32)); ?>   </div>   <div class="row">     <?php echo $form->labelEx($model,'tagtype'); ?>     <?php echo $form->radioButtonList($model,'tagtype'array(1=>"普通TAG",2=>"系统默认TAG"),array('separator'=>'','labelOptions'=>array('class'=>'tagtypelabel'))); ?>   </div>   <?php echo $form->errorSummary($model); ?>   <div class="row buttons">     <?php echo CHtml::submitButton($model->isNewRecord ? '添加' : '修改'); ?>   </div> <?php $this->endWidget(); ?> 

模型(models)中rules部分的简单代码:

public function rules() {   return array(     array('tagname,tagtype', 'required'),     array('tagtype', 'numerical', 'integerOnly'=>true),     array('tagname', 'length', 'max'=>32),     array('tagname', 'match', 'pattern'=>'/^[/x{4e00}-/x{9fa5}A-Za-z0-9]+$/u',         'message'=>'标签不合法,必须为汉字、字母或者数字!'),     array('tagname', 'checktagname', 'on'=>'create,update'),//插入TAG时检查是否已经存在该tag     array('tagid, tagname, tagtype', 'safe', 'on'=>'search'),   ); } 

系统默认有这些验证规则:

boolean : CBooleanValidator 的别名, 确保属性的值是CBooleanValidator::trueValue 或 CBooleanValidator::falseValue . 
captcha : CCaptchaValidator 的别名,确保了特性的值等于 CAPTCHA 显示出来的验证码. 
compare : CCompareValidator 的别名, 确保了特性的值等于另一个特性或常量. 
email : CEmailValidator 的别名,确保了特性的值是一个有效的电邮地址. 
default : CDefaultValueValidator 的别名, 为特性指派了一个默认值. 
exist : CExistValidator 的别名, 确保属性值存在于指定的数据表字段中. 
file : CFileValidator 的别名, 确保了特性包含了一个上传文件的名称. 
filter : CFilterValidator 的别名, 使用一个filter转换属性. 
in : CRangeValidator 的别名, 确保了特性出现在一个预订的值列表里. 
length : CStringValidator 的别名, 确保了特性的长度在指定的范围内. 
match : CRegularExpressionValidator 的别名, 确保了特性匹配一个正则表达式. 
numerical : CNumberValidator 的别名, 确保了特性是一个有效的数字. 
required : CRequiredValidator 的别名, 确保了特性不为空. 
type : CTypeValidator 的别名, 确保了特性为指定的数据类型. 
unique : CUniqueValidator 的别名, 确保了特性在数据表字段中是唯一的. 
url : CUrlValidator 的别名, 确保了特性是一个有效的路径.

基本上还是比较全面的,一般的都够用了,但是还是有时候有的验证需要自定义。就以上面的代码为例,我们在添加TAG时需要检查系统之前是否已经存在这个TAG,如果存在则不让用户添加。这个就需要在添加之前去查询数据库,看该TAG是否已经存在,这里我们就需要自定一个验证规则了。

关键有一下两个步骤:

1、在rules中 添加代码:array(‘tagname’, ‘checktagname’, ‘on’=>’create,update’),//插入TAG时检查是否已经存在该tag

注:我在其中用了 ‘on’=>’create,update’,所以这个验证规则之对create,update场景生效

2、在该模型(models)中添加验证函数:

public function checktagname($attribute,$params){   $oldtag = Tag::model()->findByAttributes(array('tagname'=>$this->tagname));   if($oldtag->tagid > 0){     $this->addError($attribute, '该TAG已经存在!');   } } 

其中需要说明的是:

(1)该验证函数的参数必须是($attribute,$params),不能缺少其中任何一个;

(2)$this->addError($attribute, ‘该TAG已经存在!’);这个是你想要在视图中输出的错误提示信息。

就是这么简单,有了这个方法,表单验证的各种想要的规则就都可以自定义了。

下面给大家介绍Yii自定义验证规则

最简单的定义验证规则的方法是在使用它的模型(model)内部定义。

比方说,你要检查用户的密码是否足够安全.

通常情况下你会使用 CRegularExpression 方法验证,但为了本指南,我们假设不存在此验证方法.

首先在模型(model)中添加两个常量

const WEAK = 0;
const STRONG = 1;然后在模型(model)的 rules 方法中设置:

/** * @return array validation rules for model attributes. */public function rules(){  return array(    array('password', 'passwordStrength', 'strength'=>self::STRONG),  );}

确保你写的规则不是一个已经存在的规则,否则将会报错.

现在要做的是在模型(model)中创建一个名称为上面填写的规则的方法(即 passwordStrength)。

/** * check if the user password is strong enough * check the password against the pattern requested * by the strength parameter * This is the 'passwordStrength' validator as declared in rules(). */public function passwordStrength($attribute,$params){  if ($params['strength'] === self::WEAK)    $pattern = '/^(?=.*[a-zA-Z0-9]).{5,}$/';   elseif ($params['strength'] === self::STRONG)    $pattern = '/^(?=.*/d(?=.*/d))(?=.*[a-zA-Z](?=.*[a-zA-Z])).{5,}$/';       if(!preg_match($pattern, $this->$attribute))   $this->addError($attribute, 'your password is not strong enough!');}

刚才创建的方法需要两个参数:* $attribute 需要验证的属性* $params 在规则中自定义的参数

在模型的 rules 方法中我们验证的是 password 属性,所以在验证规则中需要验证的属性值应该是 password.

在 rules 方法中我们还设置了自定义的参数 strength,它的值将会放到 $params 数组中.

你会发现在方法中我们使用了 CModel::addError().

添加错误接受两个参数:第一个参数是在表单中显示错误的属性名,第二个参数时显示的错误信息 。

完整的方法:继承 CValidator 类

如果你想把规则使用在多个模型(model)中,最好的方法时继承 CValidator 类。

继承这个类你可以使用像 CActiveForm::$enableClientValidation (Yii 1.1.7 版本后可用) 类似的其他功能。

创建类文件

首先要做的是创建类文件.最好的方法时类的文件名和类名相同,可以使用 yii 的延迟加载(lazy loading)功能。

让我们在应用(application)的扩展(extensiions)目录(在 protected 文件夹下)下新建一个文件夹.

将目录命名为: MyValidators

然后创建文件: passwordStrength.php

在文件中创建我们的验证方法

class passwordStrength extends CValidator{  public $strength;  private $weak_pattern = '/^(?=.*[a-zA-Z0-9]).{5,}$/';  private $strong_pattern = '/^(?=.*/d(?=.*/d))(?=.*[a-zA-Z](?=.*[a-zA-Z])).{5,}$/';...}

在类中创建属性,此属性为在验证规则中使用的参数.

CValidator 会自动根据参数来填充这些属性.

我们也创建了两个其他的属性,它们为 preg_match 函数使用的正则表达式.

/** * Validates the attribute of the object. * If there is any error, the error message is added to the object. * @param CModel $object the object being validated * @param string $attribute the attribute being validated */protected function validateAttribute($object,$attribute){  // check the strength parameter used in the validation rule of our model  if ($this->strength == 'weak')   $pattern = $this->weak_pattern;  elseif ($this->strength == 'strong')   $pattern = $this->strong_pattern;  // extract the attribute value from it's model object  $value=$object->$attribute;  if(!preg_match($pattern, $value))  {    $this->addError($object,$attribute,'your password is too weak!');  }}

SyntaxHighlighter.highlight();

PHP的运行机制与原理(底层)

说到php的运行机制还要先给大家介绍php的模块,PHP总共有三个模块:内核、Zend引擎、以及扩展层;PHP内核用来处理请求、文件流、错误处理等相关操作;Zend引擎(ZE)用以将源文件转换成机器语言,然后在虚拟机上运行它;扩展层是一组函数、类库和流,PHP使用它们来执行一些特定的操作。比如,我们需要mysql扩展来连接MySQL数据库;当ZE执行程序时可能会需要连接若干扩展,这时ZE将控制权交给扩展,等处理完特定任务后再返还;

最后,ZE将程序运行结果返回给PHP内核,它再将结果传送给SAPI层,最终输出到浏览器上。

PHP说简单,但是要精通也不是一件简单的事。我们除了会使用之外,还得知道它底层的工作原理。

PHP是一种适用于web开发的动态语言。具体点说,就是一个用C语言实现包含大量组件的软件框架。更狭义点看,可以把它认为是一个强大的UI框架。

了解PHP底层实现的目的是什么?动态语言要像用好首先得了解它,内存管理、框架模型值得我们借鉴,通过扩展开发实现更多更强大的功能,优化我们程序的性能。

1. PHP的设计理念及特点

多进程模型:由于PHP是多进程模型,不同请求间互不干涉,这样保证了一个请求挂掉不会对全盘服务造成影响,当然,随着时代发展,PHP也早已支持多线程模型。

弱类型语言:和C/C++、Java、C#等语言不同,PHP是一门弱类型语言。一个变量的类型并不是一开始就确定不变,运行中才会确定并可能发生隐式或显式的类型转换,这种机制的灵活性在web开发中非常方便、高效,具体会在后面PHP变量中详述。

引擎(Zend)+组件(ext)的模式降低内部耦合。

中间层(sapi)隔绝web server和PHP。

语法简单灵活,没有太多规范。缺点导致风格混杂,但再差的程序员也不会写出太离谱危害全局的程序。

2. PHP的四层体系

PHP的核心架构如下图:

从图上可以看出,PHP从下到上是一个4层体系:

Zend引擎:Zend整体用纯C实现,是PHP的内核部分,它将PHP代码翻译(词法、语法解析等一系列编译过程)为可执行opcode的处理并实现相应的处理方法、实现了基本的数据结构(如hashtable、oo)、内存分配及管理、提供了相应的api方法供外部调用,是一切的核心,所有的外围功能均围绕Zend实现。

Extensions:围绕着Zend引擎,extensions通过组件式的方式提供各种基础服务,我们常见的各种内置函数(如array系列)、标准库等都是通过extension来实现,用户也可以根据需要实现自己的extension以达到功能扩展、性能优化等目的(如贴吧正在使用的PHP中间层、富文本解析就是extension的典型应用)。

Sapi:Sapi全称是Server Application Programming Interface,也就是服务端应用编程接口,Sapi通过一系列钩子函数,使得PHP可以和外围交互数据,这是PHP非常优雅和成功的一个设计,通过sapi成功的将PHP本身和上层应用解耦隔离,PHP可以不再考虑如何针对不同应用进行兼容,而应用本身也可以针对自己的特点实现不同的处理方式。

上层应用:这就是我们平时编写的PHP程序,通过不同的sapi方式得到各种各样的应用模式,如通过webserver实现web应用、在命令行下以脚本方式运行等等。

如果PHP是一辆车,那么车的框架就是PHP本身,Zend是车的引擎(发动机),Ext下面的各种组件就是车的轮子,Sapi可以看做是公路,车可以跑在不同类型的公路上,而一次PHP程序的执行就是汽车跑在公路上。因此,我们需要:性能优异的引擎+合适的车轮+正确的跑道。

3. Sapi

如前所述,Sapi通过通过一系列的接口,使得外部应用可以和PHP交换数据并可以根据不同应用特点实现特定的处理方法,我们常见的一些sapi有:

apache2handler:这是以apache作为webserver,采用mod_PHP模式运行时候的处理方式,也是现在应用最广泛的一种。

cgi:这是webserver和PHP直接的另一种交互方式,也就是大名鼎鼎的fastcgi协议,在最近今年fastcgi+PHP得到越来越多的应用,也是异步webserver所唯一支持的方式。

cli:命令行调用的应用模式

4. PHP的执行流程&opcode

我们先来看看PHP代码的执行所经过的流程。

从图上可以看到,PHP实现了一个典型的动态语言执行过程:拿到一段代码后,经过词法解析、语法解析等阶段后,源程序会被翻译成一个个指令(opcodes),然后ZEND虚拟机顺次执行这些指令完成操作。PHP本身是用C实现的,因此最终调用的也都是C的函数,实际上,我们可以把PHP看做是一个C开发的软件。

PHP的执行的核心是翻译出来的一条一条指令,也即opcode。

Opcode是PHP程序执行的最基本单位。一个opcode由两个参数(op1,op2)、返回值和处理函数组成。PHP程序最终被翻译为一组opcode处理函数的顺序执行。

常见的几个处理函数:

ZEND_ASSIGN_SPEC_CV_CV_HANDLER : 变量分配 ($a=$b)

ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER:函数调用

ZEND_CONCAT_SPEC_CV_CV_HANDLER:字符串拼接 $a.$b

ZEND_ADD_SPEC_CV_CONST_HANDLER: 加法运算 $a+2

ZEND_IS_EQUAL_SPEC_CV_CONST:判断相等 $a==1

ZEND_IS_IDENTICAL_SPEC_CV_CONST:判断相等 $a===1

5. HashTable ― 核心数据结构

HashTable是zend的核心数据结构,在PHP里面几乎并用来实现所有常见功能,我们知道的PHP数组即是其典型应用,此外,在zend内部,如函数符号表、全局变量等也都是基于hash table来实现。

PHP的hash table具有如下特点:

支持典型的key->value查询

可以当做数组使用

添加、删除节点是O(1)复杂度

key支持混合类型:同时存在关联数组合索引数组

Value支持混合类型:array (“string”,2332)

支持线性遍历:如foreach

Zend hash table实现了典型的hash表散列结构,同时通过附加一个双向链表,提供了正向、反向遍历数组的功能。其结构如下图:

可以看到,在hash table中既有key->value形式的散列结构,也有双向链表模式,使得它能够非常方便的支持快速查找和线性遍历。

散列结构:Zend的散列结构是典型的hash表模型,通过链表的方式来解决冲突。需要注意的是zend的hash table是一个自增长的数据结构,当hash表数目满了之后,其本身会动态以2倍的方式扩容并重新元素位置。初始大小均为8。另外,在进行key->value快速查找时候,zend本身还做了一些优化,通过空间换时间的方式加快速度。比如在每个元素中都会用一个变量nKeyLength标识key的长度以作快速判定。

双向链表:Zend hash table通过一个链表结构,实现了元素的线性遍历。理论上,做遍历使用单向链表就够了,之所以使用双向链表,主要目的是为了快速删除,避免遍历。Zend hash table是一种复合型的结构,作为数组使用时,即支持常见的关联数组也能够作为顺序索引数字来使用,甚至允许2者的混合。

PHP关联数组:关联数组是典型的hash_table应用。一次查询过程经过如下几步(从代码可以看出,这是一个常见的hash查询过程并增加一些快速判定加速查找。):

getKeyHashValue h;index = n & nTableMask;Bucket *p = arBucket[index];while (p) {  if ((p->h == h) & (p->nKeyLength == nKeyLength)) {    RETURN p->data;   }  p=p->next;}

PHP索引数组:索引数组就是我们常见的数组,通过下标访问。例如 $arr[0],Zend HashTable内部进行了归一化处理,对于index类型key同样分配了hash值和nKeyLength(为0)。内部成员变量nNextFreeElement就是当前分配到的最大id,每次push后自动加一。正是这种归一化处理,PHP才能够实现关联和非关联的混合。由于push操作的特殊性,索引key在PHP数组中先后顺序并不是通过下标大小来决定,而是由push的先后决定。例如 $arr[1] = 2; $arr[2] = 3;对于double类型的key,Zend HashTable会将他当做索引key处理

6. PHP变量

PHP是一门弱类型语言,本身不严格区分变量的类型。PHP在变量申明的时候不需要指定类型。PHP在程序运行期间可能进行变量类型的隐示转换。和其他强类型语言一样,程序中也可以进行显示的类型转换。PHP变量可以分为简单类型(int、string、bool)、集合类型(array resource object)和常量(const)。以上所有的变量在底层都是同一种结构 zval。

Zval是zend中另一个非常重要的数据结构,用来标识并实现PHP变量,其数据结构如下:

Zval主要由三部分组成:

type:指定了变量所述的类型(整数、字符串、数组等)

refcount&is_ref:用来实现引用计数(后面具体介绍)

value:核心部分,存储了变量的实际数据

Zvalue是用来保存一个变量的实际数据。因为要存储多种类型,所以zvalue是一个union,也由此实现了弱类型。

PHP变量类型和其实际存储对应关系如下:

IS_LONG   -> lvalue
IS_DOUBLE -> dvalue
IS_ARRAY  -> ht
IS_STRING -> str
IS_RESOURCE -> lvalue

引用计数在内存回收、字符串操作等地方使用非常广泛。PHP中的变量就是引用计数的典型应用。Zval的引用计数通过成员变量is_ref和ref_count实现,通过引用计数,多个变量可以共享同一份数据。避免频繁拷贝带来的大量消耗。

在进行赋值操作时,zend将变量指向相同的zval同时ref_count++,在unset操作时,对应的ref_count-1。只有ref_count减为0时才会真正执行销毁操作。如果是引用赋值,则zend会修改is_ref为1。

PHP变量通过引用计数实现变量共享数据,那如果改变其中一个变量值呢?当试图写入一个变量时,Zend若发现该变量指向的zval被多个变量共享,则为其复制一份ref_count为1的zval,并递减原zval的refcount,这个过程称为“zval分离”。可见,只有在有写操作发生时zend才进行拷贝操作,因此也叫copy-on-write(写时拷贝)

对于引用型变量,其要求和非引用型相反,引用赋值的变量间必须是捆绑的,修改一个变量就修改了所有捆绑变量。

整数、浮点数是PHP中的基础类型之一,也是一个简单型变量。对于整数和浮点数,在zvalue中直接存储对应的值。其类型分别是long和double。

从zvalue结构中可以看出,对于整数类型,和c等强类型语言不同,PHP是不区分int、unsigned int、long、long long等类型的,对它来说,整数只有一种类型也就是long。由此,可以看出,在PHP里面,整数的取值范围是由编译器位数来决定而不是固定不变的。

对于浮点数,类似整数,它也不区分float和double而是统一只有double一种类型。

在PHP中,如果整数范围越界了怎么办?这种情况下会自动转换为double类型,这个一定要小心,很多trick都是由此产生。

和整数一样,字符变量也是PHP中的基础类型和简单型变量。通过zvalue结构可以看出,在PHP中,字符串是由由指向实际数据的指针和长度结构体组成,这点和c++中的string比较类似。由于通过一个实际变量表示长度,和c不同,它的字符串可以是2进制数据(包含),同时在PHP中,求字符串长度strlen是O(1)操作。

在新增、修改、追加字符串操作时,PHP都会重新分配内存生成新的字符串。最后,出于安全考虑,PHP在生成一个字符串时末尾仍然会添加

常见的字符串拼接方式及速度比较:

假设有如下4个变量:$strA=‘123′; $strB = ‘456′; $intA=123; intB=456;

现在对如下的几种字符串拼接方式做一个比较和说明:

$res = $strA.$strB和$res = “$strA$strB”

这种情况下,zend会重新malloc一块内存并进行相应处理,其速度一般

$strA = $strA.$strB

这种是速度最快的,zend会在当前strA基础上直接relloc,避免重复拷贝

$res = $intA.$intB

这种速度较慢,因为需要做隐式的格式转换,实际编写程序中也应该注意尽量避免

$strA = sprintf (“%s%s”,$strA.$strB);

这会是最慢的一种方式,因为sprintf在PHP中并不是一个语言结构,本身对于格式识别和处理就需要耗费比较多时间,另外本身机制也是malloc。不过sprintf的方式最具可读性,实际中可以根据具体情况灵活选择。

PHP的数组通过Zend HashTable来天然实现。

foreach操作如何实现?对一个数组的foreach就是通过遍历hashtable中的双向链表完成。对于索引数组,通过foreach遍历效率比for高很多,省去了key->value的查找。count操作直接调用HashTable->NumOfElements,O(1)操作。对于’123’这样的字符串,zend会转换为其整数形式。$arr[‘123′]和$arr[123]是等价的

资源类型变量是PHP中最复杂的一种变量,也是一种复合型结构。

PHP的zval可以表示广泛的数据类型,但是对于自定义的数据类型却很难充分描述。由于没有有效的方式描绘这些复合结构,因此也没有办法对它们使用传统的操作符。要解决这个问题,只需要通过一个本质上任意的标识符(label)引用指针,这种方式被称为资源。

在zval中,对于resource,lval作为指针来使用,直接指向资源所在的地址。Resource可以是任意的复合结构,我们熟悉的mysqli、fsock、memcached等都是资源。

如何使用资源:

注册:对于一个自定义的数据类型,要想将它作为资源。首先需要进行注册,zend会为它分配全局唯一标示。

获取一个资源变量:对于资源,zend维护了一个id->实际数据的hash_tale。对于一个resource,在zval中只记录了它的id。fetch的时候通过id在hash_table中找到具体的值返回。

资源销毁:资源的数据类型是多种多样的。Zend本身没有办法销毁它。因此需要用户在注册资源的时候提供销毁函数。当unset资源时,zend调用相应的函数完成析构。同时从全局资源表中删除它。

资源可以长期驻留,不只是在所有引用它的变量超出作用域之后,甚至是在一个请求结束了并且新的请求产生之后。这些资源称为持久资源,因为它们贯通SAPI的整个生命周期持续存在,除非特意销毁。很多情况下,持久化资源可以在一定程度上提高性能。比如我们常见的mysql_pconnect ,持久化资源通过pemalloc分配内存,这样在请求结束的时候不会释放。

对zend来说,对两者本身并不区分。

PHP中的局部变量和全局变量是如何实现的?对于一个请求,任意时刻PHP都可以看到两个符号表(symbol_table和active_symbol_table),其中前者用来维护全局变量。后者是一个指针,指向当前活动的变量符号表,当程序进入到某个函数中时,zend就会为它分配一个符号表x同时将active_symbol_table指向a。通过这样的方式实现全局、局部变量的区分。

SyntaxHighlighter.highlight();

PHP新建类问题分析及解决思路

下面先给大家分析php新建类的问题

index.php文件

function __autoload($_className) {require $_className.'.class.php';}//新建类??if (isset($_GET['index'])) {$m=new Main($_GET['index']);}else{$m=new Main();}include $m->ui();

main.class.php文件

class Main{private $index;//构造方法,初始化数据public function __construct($index=''){$this->index=$index;}//ui函数include相应的包含文件public function ui(){if(empty($this->index)||!file_exists($this->index.'.inc')){ $this->index='start';}return $this->index.'.inc';} }

红字的部分有啥意义了:类中构造函数传参值已设默认是空(public function __construct($index=”)),为啥不能直接写$m=new Main($_GET[‘index’]);。如果不想在index做红字的if判断,类里需要怎么写了。谢谢,不是太理解

——解决思路———————-

if (isset($_GET['index'])) { $m=new Main($_GET['index']); //如果 $_GET['index'] 存在则将 $_GET['index'] 作为参数}else{ $m=new Main(); //否则使用默认参数}

直接使用 $_GET[‘index’] 将可能引发 NOTICE 级别错误

不加区别的使用传入数据,可能引发安全问题

——解决思路———————-

稍微改了一下你看咋样。

<?phpclass Main{ private $index; //构造方法,初始化数据 public function __construct($index='') { $this->index=$index?$index:''; } //ui函数include相应的包含文件 public function ui() { if(empty($this->index)

——解决思路———————-

!file_exists($this->index.'.inc')) {  $this->index='start'; } return $this->index.'.inc'; } }

ps:php怎么创建文件?

php项目开发过程中,常常需要自动创建一些文件,如生成静态html,生成php缓存文件,生成txt文件等等。下面就分享一下如何利用php程序创建文件,并向文件中写入内容。

一个项目中,可能不止一次需要生成文件,因此我们可以定义一个函数,当需要创建文件时再来调用这个函数,即可。

步骤一、定义函数writefile,用于以写的方式打开一个文件,文件不存在时自动创建,并向文件写入内容,代码如下。

<?phpfunction writefile($fname,$str){ $fp=fopen($fname,"w"); fputs($fp,$str); fclose($fp);}?>
<?php$filename='test.txt';$str='abc';writefile($filename,$str);?>

SyntaxHighlighter.highlight();

php+ajax无刷新上传图片实例代码

本文分享了php结合ajax实现无刷新上传图片的实例代码,分享给大家,希望大家可以和小编一起学习学习,共同进步。

1.引入文件

<!--图片上传begin--><script type="text/javascript"" width=100% src="/js/jquery.form.js"></script><script type="text/javascript"" width=100% src="/js/uploadImg.js"></script><link href="/css/uploadImg.css" rel="stylesheet" type="text/css" /><!--图片上传end-->

2.html部分

<div class="upimg">       <input name="icon" type="text" class="imgsrc" value="<!--{$contents.icon}-->" />       <div class="showimg">        <!--{if $contents.icon}-->        <img" width=100% src="<!--{$contents.icon}-->" height="120px">        <!--{/if}-->       </div>                 <div class="btn" style="height:20px;">          <span>添加图片</span>          <input class="fileupload" type="file" name="pic[]">       </div>       </div> 

3.给fileupload加上表单

/*图片上传*/  $(".fileupload").wrap("<form action='/bookstore/book/uploadpic' method='post' enctype='multipart/form-data'></form>"); //函数处理

4.ajax文件上传

jQuery(function ($) {   $(".fileupload").change(function(){ //选择文件     if ('' === $(this).val()) return;    var upimg = $(this).parent().parent().parent();    var showimg = upimg.find('.showimg');    var btn = upimg.find('.btn span');    var imgsrc = upimg.find('.imgsrc');    $(this).parent().ajaxSubmit({       //dataType: 'json', //数据格式为json       beforeSend: function() { //开始上传         showimg.empty(); //清空显示的图片         imgsrc.val("");        btn.html("上传中..."); //上传按钮显示上传中       },       uploadProgress: function(event, position, total, percentComplete) {       },       success: function(data) { //成功         //获得后台返回的json数据,显示文件名,大小,以及删除按钮         var img = data;        //显示上传后的图片         imgsrc.val("");        imgsrc.val(img);        showimg.html("<img width='120' height='120'" width=100% src='"+img+"'>");         btn.html("上传成功"); //上传按钮还原       },       error:function(xhr){ //上传失败         btn.html("上传失败");       }     });   }); }); 

5.后台进行处理

public function uploadpicAction(){ //图片上传和显示    $data = "";    $src = $this->uploadFiles2($imgpath = "https://www.zhuxianfei.com/upload/book" ,$filesname = "pic");          isset($src[0]['src']) && $src[0]['src'] ? $data = $this->concaturl($src[0]['src']) : null;    echo $data;   }

6.将返回的数据交给前端,进行一些处理。

进而提交到后台数据库。

SyntaxHighlighter.highlight();

跟我学习javascript的最新标准ES6

虽然ES6都还没真正发布,但已经有用ES6重写的程序了,各种关于ES789的提议已经开始了,这你敢信。潮流不是我等大众所能追赶的。

潮流虽然太快,但我们不停下学习的步伐,就不会被潮流丢下的,下面来领略下ES6中新特性,一堵新生代JS的风采。

箭头操作符

如果你会C#或者Java,你肯定知道lambda表达式,ES6中新增的箭头操作符=>便有异曲同工之妙。它简化了函数的书写。操作符左边为输入的参数,而右边则是进行的操作以及返回的值Inputs=>outputs。

我们知道在JS中回调是经常的事,而一般回调又以匿名函数的形式出现,每次都需要写一个function,甚是繁琐。当引入箭头操作符后可以方便地写回调了。请看下面的例子。

var array = [1, 2, 3];//传统写法array.forEach(function(v, i, a) { console.log(v);});//ES6array.forEach(v = > console.log(v));

大家可以打开文章开头提到的traceur在线代码转译页面输入代码来查看效果。

类的支持

ES6中添加了对类的支持,引入了class关键字(其实class在JavaScript中一直是保留字,目的就是考虑到可能在以后的新版本中会用到,现在终于派上用场了)。JS本身就是面向对象的,ES6中提供的类实际上只是JS原型模式的包装。现在提供原生的class支持后,对象的创建,继承更加直观了,并且父类方法的调用,实例化,静态方法和构造函数等概念都更加形象化。

下面代码展示了类在ES6中的使用。再次嗦一句,你可以将代码贴到traceur自己查看运行结果。

//类的定义class Animal { //ES6中新型构造器 constructor(name) { this.name = name; } //实例方法 sayName() { console.log('My name is '+this.name); }}//类的继承class Programmer extends Animal { constructor(name) { //直接调用父类构造器进行初始化 super(name); } program() { console.log("I'm coding..."); }}//测试我们的类var animal=new Animal('dummy'),wayou=new Programmer('wayou');animal.sayName();//输出 ‘My name is dummy'wayou.sayName();//输出 ‘My name is wayou'wayou.program();//输出 ‘I'm coding...' 

增强的对象字面量

对象字面量被增强了,写法更加简洁与灵活,同时在定义对象的时候能够做的事情更多了。具体表现在:

  • 可以在对象字面量里面定义原型
  • 定义方法可以不用function关键字
  • 直接调用父类方法

这样一来,对象字面量与前面提到的类概念更加吻合,在编写面向对象的JavaScript时更加轻松方便了。

//通过对象字面量创建对象var human = { breathe() { console.log('breathing...'); }};var worker = { __proto__: human, //设置此对象的原型为human,相当于继承human company: 'freelancer', work() { console.log('working...'); }};human.breathe();//输出 ‘breathing...'//调用继承来的breathe方法worker.breathe();//输出 ‘breathing...'

字符串模板

字符串模板相对简单易懂些。ES6中允许使用反引号 ` 来创建字符串,此种方法创建的字符串里面可以包含由美元符号加花括号包裹的变量${vraible}。如果你使用过像C#等后端强类型语言的话,对此功能应该不会陌生。

//产生一个随机数var num=Math.random();//将这个数字输出到consoleconsole.log(`your num is ${num}`);

解构

自动解析数组或对象中的值。比如若一个函数要返回多个值,常规的做法是返回一个对象,将每个值做为这个对象的属性返回。但在ES6中,利用解构这一特性,可以直接返回一个数组,然后数组中的值会自动被解析到对应接收该值的变量中。

var [x,y]=getVal(),//函数返回值的解构 [name,,age]=['wayou','male','secrect'];//数组解构function getVal() { return [ 1, 2 ];}console.log('x:'+x+', y:'+y);//输出:x:1, y:2 console.log('name:'+name+', age:'+age);//输出: name:wayou, age:secrect 

参数默认值,不定参数,拓展参数

1、默认参数值

现在可以在定义函数的时候指定参数的默认值了,而不用像以前那样通过逻辑或操作符来达到目的了。

function sayHello(name){ //传统的指定默认参数的方式 var name=name||'dude'; console.log('Hello '+name);}//运用ES6的默认参数function sayHello2(name='dude'){ console.log(`Hello ${name}`);}sayHello();//输出:Hello dudesayHello('Wayou');//输出:Hello WayousayHello2();//输出:Hello dudesayHello2('Wayou');//输出:Hello Wayou

2、不定参数

不定参数是在函数中使用命名参数同时接收不定数量的未命名参数。这只是一种语法糖,在以前的JavaScript代码中我们可以通过arguments变量来达到这一目的。不定参数的格式是三个句点后跟代表所有不定参数的变量名。比如下面这个例子中,…x代表了所有传入add函数的参数。

//将所有参数相加的函数function add(...x){ return x.reduce((m,n)=>m+n);}//传递任意个数的参数console.log(add(1,2,3));//输出:6console.log(add(1,2,3,4,5));//输出:15 

3、拓展参数

拓展参数则是另一种形式的语法糖,它允许传递数组或者类数组直接做为函数的参数而不用通过apply。

var people=['Wayou','John','Sherlock'];//sayHello函数本来接收三个单独的参数人妖,人二和人三function sayHello(people1,people2,people3){ console.log(`Hello ${people1},${people2},${people3}`);}//但是我们将一个数组以拓展参数的形式传递,它能很好地映射到每个单独的参数sayHello(...people);//输出:Hello Wayou,John,Sherlock //而在以前,如果需要传递数组当参数,我们需要使用函数的apply方法sayHello.apply(null,people);//输出:Hello Wayou,John,Sherlock 

let与const 关键字

可以把let看成var,只是它定义的变量被限定在了特定范围内才能使用,而离开这个范围则无效。const则很直观,用来定义常量,即无法被更改值的变量。

for (let i=0;i<2;i++)console.log(i);//输出: 0,1console.log(i);//输出:undefined,严格模式下会报错

for of 值遍历

我们都知道for in 循环用于遍历数组,类数组或对象,ES6中新引入的for of循环功能相似,不同的是每次循环它提供的不是序号而是值。

var someArray = [ "a", "b", "c" ]; for (v of someArray) { console.log(v);//输出 a,b,c}

注意,此功能google traceur并未实现,所以无法模拟调试,下面有些功能也是如此

iterator, generator

这一部分的内容有点生涩,详情可以参见这里。以下是些基本概念。

  • iterator:它是这么一个对象,拥有一个next方法,这个方法返回一个对象{done,value},这个对象包含两个属性,一个布尔类型的done和包含任意值的value
  • iterable: 这是这么一个对象,拥有一个obj[@@iterator]方法,这个方法返回一个iterator
  • generator: 它是一种特殊的iterator。反的next方法可以接收一个参数并且返回值取决与它的构造函数(generator function)。generator同时拥有一个throw方法
  • generator 函数: 即generator的构造函数。此函数内可以使用yield关键字。在yield出现的地方可以通过generator的next或throw方法向外界传递值。generator 函数是通过function*来声明的
  • yield 关键字:它可以暂停函数的执行,随后可以再进进入函数继续执行

模块

在ES6标准中,JavaScript原生支持module了。这种将JS代码分割成不同功能的小块进行模块化的概念是在一些三方规范中流行起来的,比如CommonJS和AMD模式。

将不同功能的代码分别写在不同文件中,各模块只需导出公共接口部分,然后通过模块的导入的方式可以在其他地方使用。下面的例子来自tutsplus:

// point.jsmodule "point" { export class Point { constructor (x, y) {  public x = x;  public y = y; } }} // myapp.js//声明引用的模块module point from "/point.js";//这里可以看出,尽管声明了引用的模块,还是可以通过指定需要的部分进行导入import Point from "point"; var origin = new Point(0, 0);console.log(origin);

Map,Set 和 WeakMap,WeakSet

这些是新加的集合类型,提供了更加方便的获取属性值的方法,不用像以前一样用hasOwnProperty来检查某个属性是属于原型链上的呢还是当前对象的。同时,在进行属性值添加与获取时有专门的get,set 方法。

下方代码来自es6feature

// Setsvar s = new Set();s.add("hello").add("goodbye").add("hello");s.size === 2;s.has("hello") === true;// Mapsvar m = new Map();m.set("hello", 42);m.set(s, 34);m.get(s) == 34;

有时候我们会把对象作为一个对象的键用来存放属性值,普通集合类型比如简单对象会阻止垃圾回收器对这些作为属性键存在的对象的回收,有造成内存泄漏的危险。而WeakMap,WeakSet则更加安全些,这些作为属性键的对象如果没有别的变量在引用它们,则会被回收释放掉,具体还看下面的例子。

正文代码来自es6feature

// Weak Mapsvar wm = new WeakMap();wm.set(s, { extra: 42 });wm.size === undefined// Weak Setsvar ws = new WeakSet();ws.add({ data: 42 });//因为添加到ws的这个临时对象没有其他变量引用它,所以ws不会保存它的值,也就是说这次添加其实没有意思

Proxies

Proxy可以监听对象身上发生了什么事情,并在这些事情发生后执行一些相应的操作。一下子让我们对一个对象有了很强的追踪能力,同时在数据绑定方面也很有用处。

以下例子借用自这里。

//定义被侦听的目标对象var engineer = { name: 'Joe Sixpack', salary: 50 };//定义处理程序var interceptor = { set: function (receiver, property, value) { console.log(property, 'is changed to', value); receiver[property] = value; }};//创建代理以进行侦听engineer = Proxy(engineer, interceptor);//做一些改动来触发代理engineer.salary = 60;//控制台输出:salary is changed to 60

上面代码我已加了注释,这里进一步解释。对于处理程序,是在被侦听的对象身上发生了相应事件之后,处理程序里面的方法就会被调用,上面例子中我们设置了set的处理函数,表明,如果我们侦听的对象的属性被更改,也就是被set了,那这个处理程序就会被调用,同时通过参数能够得知是哪个属性被更改,更改为了什么值。

Symbols

我们知道对象其实是键值对的集合,而键通常来说是字符串。而现在除了字符串外,我们还可以用symbol这种值来做为对象的键。Symbol是一种基本类型,像数字,字符串还有布尔一样,它不是一个对象。Symbol 通过调用symbol函数产生,它接收一个可选的名字参数,该函数返回的symbol是唯一的。之后就可以用这个返回值做为对象的键了。Symbol还可以用来创建私有属性,外部无法直接访问由symbol做为键的属性值。

以下例子来自es6features

(function() { // 创建symbol var key = Symbol("key"); function MyClass(privateData) { this[key] = privateData; } MyClass.prototype = { doStuff: function() { ... this[key] ... } };})();var c = new MyClass("hello")c["key"] === undefined//无法访问该属性,因为是私有的

Math,Number,String,Object 的新API

对Math,Number,String还有Object等添加了许多新的API。下面代码同样来自es6features,对这些新API进行了简单展示。

Number.EPSILONNumber.isInteger(Infinity) // falseNumber.isNaN("NaN") // falseMath.acosh(3) // 1.762747174039086Math.hypot(3, 4) // 5Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2"abcde".contains("cd") // true"abc".repeat(3) // "abcabcabc"Array.from(document.querySelectorAll('*')) // Returns a real ArrayArray.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior[0, 0, 0].fill(7, 1) // [0,7,7][1,2,3].findIndex(x => x == 2) // 1["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"]["a", "b", "c"].keys() // iterator 0, 1, 2["a", "b", "c"].values() // iterator "a", "b", "c"Object.assign(Point, { origin: new Point(0,0) })

Promises

//创建promisevar promise = new Promise(function(resolve, reject) { // 进行一些异步或耗时操作 if ( /*如果成功 */ ) { resolve("Stuff worked!"); } else { reject(Error("It broke")); }});//绑定处理程序promise.then(function(result) { //promise成功的话会执行这里 console.log(result); // "Stuff worked!"}, function(err) { //promise失败会执行这里 console.log(err); // Error: "It broke"});

SyntaxHighlighter.highlight();

详解JavaScript语言的基本语法要求

JavaScript语言的基本语法要求有哪些呐?下面将为大家一一解答:

 一、区分大小写
       JavaScript语言区分字符大小写,两个字符串相同大小写不同,被认为是不同的字符串。JavaScript语言的关键字也区分大小写,按语法要求应小写。
二、书写格式
        JavaScript语言忽略语句间空白,即语句间的空格,空行,缩进等。为了提高程序的可读性,应当使用这些格式,使程序更加清晰,可读性更高。
 三、注释语句
        为了提高程序的可维护性和可读性,应当有一定的注释语句,它是给读程序的人员看的,有单行注释和多行注释,单行注释以双斜线开始,多行以/*,*/为开始和结束的标识。

<script type="text/javascript">//JavaScript代码放置的位置(单行注释)  function theAlert(textToAlert) {  alert(textToAlert);  }  /*  定义一个名字叫theAlert的函数,此函数带一个参数textToAlert。  函数体内调用JavaScript语言的内部函数alert()输出参数。(多行注释或块注释)  */  theAlert("Hello World");//调用定义的函数输出参数  </script> 

四、分号的使用
       JavaScript语言中语句以分号结束。有些代码,比如循环结构或者选择结构的条件语句后面不需要用分号,否则会改变原结构的执行路径。
       例如:if(a==1):加上分号后,不论a的值是否为1都将执行条件语句后面的内容,条件测试失败。
五、JavaScript放置的位置
       JavaScript代码可以放在HTML页面中的<head></head>标签内,也可以放在<body></body>标签内,在<script>
标签开始部分,需要用户声明是JavaScript脚本类型。如果JavaScript代码没有直接放在HTML页面,而在另一个文件
中,比如MyPage.js,可以使用<script>的src属性链接它:    

<script type=”text/Javascript”src=” MyPage.js”>

六、JavaScript中的保留字
       JavaScript保留了一部分单词用于专门用途,称为保留字,不能用于常量,变量,标识符等的命名。还有一些特殊的单词,为避免歧义也不能用于命名。
       那么现在就来写一个简单的JavaScript开发的例子:通过运行程序,输出一条“Hello World”语句。具体的操作步骤:
       (1)启动VS 2010,建立一个网站,命名为Ch-2.aspx,默认主页为Default.aspx。在右侧“解决方案资源管理器”的窗口中找到网站名称,用鼠标右键单击,在弹出的快捷菜单中选择“添加新项”。

       (2)在打开的“添加新项”对话框中列出了已安装的模板选项,选择“HTML页”选项,在下面的名称对话框中命名为“MyPage.htm”,然后单击“添加”按钮。

       (3)在MyPage.htm页面,把光标定位在<title>和</title>之间,把标题改为My First Page。然后在标签<title></title>后面添加要执行的代码。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  <html xmlns="http://www.w3.org/1999/xhtml"> <head>  <title>My First Page</title>  <script type="text/javascript">//JavaScript代码放置的位置(单行注释)  function theAlert(textToAlert) {  alert(textToAlert);  }  /*  定义一个名字叫theAlert的函数,此函数带一个参数textToAlert。  函数体内调用JavaScript语言的内部函数alert()输出参数。(多行注释或块注释)  */  theAlert("Hello World"); //调用定义的函数输出参数  </script> </head> <body>  </body> </html> 

       (4)然后选择“文件”菜单中的“全部保存”,保存编写的代码。

       (5)要运行程序,选择“调试”菜单中的“启动调试”,或者按键盘上的功能键F5,或者单击工具栏中的“启动调试”按钮。运行结果显示一个提示框。

       上述代码在浏览器HTML解析的工作过程:
       首先,打开script标签,声明是JavaScript,即下面的一行代码:

       <scripttype=”text/Javascript” >

       然后,声明一个函数theAlter(),使用参数textToAlter,它调用内部函数alter()完成显示功能。即下面一段代码:

SyntaxHighlighter.highlight();

跟我学习javascript创建对象(类)的8种方法

8中javascript创建对象(类)的方法,依依介绍给大家,希望大家喜欢。

1. 使用Object构造函数来创建一个对象

下面代码创建了一个person对象,并用两种方式打印出了Name的属性值。 

 var person = new Object();  person.name="kevin";  person.age=31;  alert(person.name);  alert(person["name"])

上述写法的另外一种表现形式是使用对象字面量创建一个对象,不要奇怪person[“5”],这里是合法的;另外使用这种加括号的方式字段之间是可以有空格的如person[“my age”].

var person =   {    name:"Kevin",    age:31,    5:"Test"  };  alert(person.name);  alert(person["5"]);

虽然Object 构造函数或对象字面量都可以用来创建单个对象,但这些方式有个明显的缺点:使用同一个接口创建很多对象,会产生大量的重复代码。为解决这个问题,人们开始使用工厂模式的一种变体。

2、工厂模式

工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程,考虑到在ECMAScript 中无法创建类,开发人员就发明了一种函数,用函数来封装以特定接口创建对象的细节,如下面的例子所示。

function createPerson(name, age, job){  var o = new Object();  o.name = name;  o.age = age;  o.job = job;  o.sayName = function(){    alert(this.name);  };  return o;}var person1 = createPerson("Nicholas", 29, "Software Engineer");var person2 = createPerson("Greg", 27, "Doctor");

工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。随着JavaScript
的发展,又一个新模式出现了。

3. 构造函数模式

像Object 和Array 这样构造函数,在运行时会自动出现在执行环境中。此外,也可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法。例如,可以使用构造函数模式将前面的例子重写如下。 

function Person(name, age, job){  this.name = name;  this.age = age;  this.job = job;  this.sayName = function(){  alert(this.name);};}var person1 = new Person("Nicholas", 29, "Software Engineer");var person2 = new Person("Greg", 27, "Doctor");

在这个例子中,Person()函数取代了createPerson()函数。我们注意到,Person()中的代码除了与createPerson()中相同的部分外,还存在以下不同之处:

  • 没有显式地创建对象;
  • 直接将属性和方法赋给了this 对象;
  • 没有return 语句。

要创建Person 的新实例,必须使用new 操作符。以这种方式调用构造函数实际上会经历以下4个步骤:

(1) 创建一个新对象;
(2) 将构造函数的作用域赋给新对象(因此this 就指向了这个新对象);
(3) 执行构造函数中的代码(为这个新对象添加属性);
(4) 返回新对象。
在前面例子的最后,person1 和person2 分别保存着Person 的一个不同的实例。这两个对象都有一个constructor(构造函数)属性,该属性指向Person,如下所示。

alert(person1.constructor == Person); //truealert(person2.constructor == Person); //true

对象的constructor 属性最初是用来标识对象类型的。但是,提到检测对象类型,还是instanceof操作符要更可靠一些。我们在这个例子中创建的所有对象既是Object 的实例,同时也是Person的实例,这一点通过instanceof 操作符可以得到验证。

alert(person1 instanceof Object); //truealert(person1 instanceof Person); //truealert(person2 instanceof Object); //truealert(person2 instanceof Person); //true

创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型;而这正是构造函数模式胜过工厂模式的地方。在这个例子中,person1 和person2 之所以同时是Object 的实例,是因为所有对象均继承自Object。

构造函数的问题

构造函数模式虽然好用,但也并非没有缺点。使用构造函数的主要问题,就是每个方法都要在每个实例上重新创建一遍。

ECMAScript 中的函数是对象,因此每定义一个函数,也就是实例化了一个对象。从逻辑角度讲,此时的构造函数也可以这样定义。

function Person(name, age, job){  this.name = name;  this.age = age;  this.job = job;  this.sayName = new Function("alert(this.name)"); // 与声明函数在逻辑上是等价的}

从这个角度上来看构造函数,更容易明白每个Person 实例都包含一个不同的Function 实例(以显示name 属性)的本质。说明白些,以这种方式创建函数,会导致不同的作用域链和标识符解析,但创建Function 新实例的机制仍然是相同的。因此,不同实例上的同名函数是不相等的,以下代码可以证明这一点。

alert(person1.sayName == person2.sayName); //false

然而,创建两个完成同样任务的Function 实例的确没有必要;况且有this 对象在,根本不用在执行代码前就把函数绑定到特定对象上面。因此,大可像下面这样,通过把函数定义转移到构造函数外部来解决这个问题。

function Person(name, age, job){  this.name = name;  this.age = age;  this.job = job;  this.sayName = sayName;}function sayName(){  alert(this.name);}var person1 = new Person("Nicholas", 29, "Software Engineer");var person2 = new Person("Greg", 27, "Doctor");

如果对象需要定义很多方法,那么就要定义很多个全局函数,于是我们这个自定义的引用类型就丝毫没有封装性可言了。好在,这些问题可以通过使用原型模式来解决。

4、原型模式

function Person(){}Person.prototype.name = "Nicholas";Person.prototype.age = 29;Person.prototype.job = "Software Engineer";Person.prototype.sayName = function(){  alert(this.name);};var person1 = new Person();person1.sayName(); //"Nicholas"var person2 = new Person();person2.sayName(); //"Nicholas"alert(person1.sayName == person2.sayName); //true

要理解原型对象,可见我的另一篇博客:JavaScript prototype详解

前面例子中每添加一个属性和方法就要敲一遍Person.prototype。为减少不必要的输入,也为了从视觉上更好地封装原型的功能,更常见的做法是用一个包含所有属性和方法的对象字面量来重写整个原型对象,如下面的例子所示。

function Person(){}Person.prototype = {  name : "Nicholas",  age : 29,  job: "Software Engineer",  sayName : function () {    alert(this.name);  }};

在上面的代码中,我们将Person.prototype 设置为等于一个以对象字面量形式创建的新对象。最终结果相同,但有一个例外:constructor 属性不再指向Person 了。前面曾经介绍过,每创建一个函数,就会同时创建它的prototype 对象,这个对象也会自动获得constructor 属性。而我们在这里使用的语法,本质上完全重写了默认的prototype 对象,因此constructor 属性也就变成了新对象的constructor 属性(指向Object 构造函数),不再指向Person 函数。此时,尽管instanceof操作符还能返回正确的结果,但通过constructor 已经无法确定对象的类型了,如下所示。

var friend = new Person();alert(friend instanceof Object); //truealert(friend instanceof Person); //truealert(friend.constructor == Person); //falsealert(friend.constructor == Object); //true

在此,用instanceof 操作符测试Object 和Person 仍然返回true,但constructor 属性则等于Object 而不等于Person 了。如果constructor 的值真的很重要,可以像下面这样特意将它设置回适当的值。

function Person(){}  Person.prototype = {  constructor : Person,  name : "Nicholas",  age : 29,  job: "Software Engineer",  sayName : function () {    alert(this.name);  }};

需要注意一点就是:实例中的指针仅指向原型,而不指向构造函数。

原型对象的问题:原型模式也不是没有缺点。首先,它省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况下都将取得相同的属性值。虽然这会在某种程度上带来一些不方便,但还不是原型的最大问题。原型模式的最大问题是由其共享的本性所导致的。

function Person(){}Person.prototype = {  constructor: Person,  name : "Nicholas",  age : 29,  job : "Software Engineer",  friends : ["Shelby", "Court"],  sayName : function () {    alert(this.name);  }};var person1 = new Person();var person2 = new Person();person1.friends.push("Van");alert(person1.friends); //"Shelby,Court,Van"alert(person2.friends); //"Shelby,Court,Van"alert(person1.friends === person2.friends); //true

5、组合使用构造函数模式和原型模式(最常用)

创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。结果,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。另外,这种混成模式还支持向构造函数传递参数;可谓是集两种模式之长。

function Person(name, age, job){  this.name = name;  this.age = age;  this.job = job;  this.friends = ["Shelby", "Court"];}Person.prototype = {  constructor : Person,  sayName : function(){    alert(this.name);  }}var person1 = new Person("Nicholas", 29, "Software Engineer");var person2 = new Person("Greg", 27, "Doctor");person1.friends.push("Van");alert(person1.friends); //"Shelby,Count,Van"alert(person2.friends); //"Shelby,Count"alert(person1.friends === person2.friends); //falsealert(person1.sayName === person2.sayName); //true

6、动态原型模式

有其他OO 语言经验的开发人员在看到独立的构造函数和原型时,很可能会感到非常困惑。动态原型模式正是致力于解决这个问题的一个方案,它把所有信息都封装在了构造函数中,而通过在构造函数中初始化原型(仅在必要的情况下),又保持了同时使用构造函数和原型的优点。换句话说,可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。来看一个例子。

function Person(name, age, job){  //属性  this.name = name;  this.age = age;  this.job = job;  //方法  ---------------------------------------------  if (typeof this.sayName != "function"){    Person.prototype.sayName = function(){      alert(this.name);    };  }  --------------------------------------------  }var friend = new Person("Nicholas", 29, "Software Engineer");friend.sayName();

7、寄生构造函数模式

通常,在前述的几种模式都不适用的情况下,可以使用寄生(parasitic)构造函数模式。这种模式的基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象;但从表面上看,这个函数又很像是典型的构造函数。下面是一个例子。

function Person(name, age, job){  var o = new Object();  o.name = name;  o.age = age;  o.job = job;  o.sayName = function(){    alert(this.name);  };  return o;}var friend = new Person("Nicholas", 29, "Software Engineer");friend.sayName(); //"Nicholas"

在这个例子中,Person 函数创建了一个新对象,并以相应的属性和方法初始化该对象,然后又返回了这个对象。除了使用new 操作符并把使用的包装函数叫做构造函数之外,这个模式跟工厂模式其实是一模一样的。构造函数在不返回值的情况下,默认会返回新对象实例。

8、稳妥构造函数模式

function Person(name, age, job){  //创建要返回的对象  var o = new Object();  //可以在这里定义私有变量和函数  //添加方法  o.sayName = function(){    alert(name);  };//返回对象return o;}

SyntaxHighlighter.highlight();

JavaScript制作淘宝星级评分效果的思路

小编也是刚开始学JavaScript,觉得淘宝评星效果很棒,于是产生了自己写一个的想法,先给大家分享一下实现效果:

现附上自己写的源代码

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <script language="JavaScript" type="text/javascript">  function star(n)  {   var array=new Array();   array[0]=document.getElementById("oneStar");   array[1]=document.getElementById("twoStar");   array[2]=document.getElementById("threeStar");   array[3]=document.getElementById("fourStar");   array[4]=document.getElementById("fiveStar");   for(var i=0;i<=n;i++)   {    array[i].innerText="";   }   for( var j=4;j>n;j--)   {    array[j].innerText="";   }   document.getElementById("evaluate").innerText="您的评价是"+(n+1)+"星";  } </script> <title>评星</title></head><body><strong>请您对我们作出评价:</strong><span id="star"> <span style="cursor: pointer " onclick="star(0)"id="oneStar" ></span> <span style="cursor: pointer " onclick="star(1)" id="twoStar" ></span> <span style="cursor: pointer " onclick="star(2)" id="threeStar" ></span> <span style="cursor: pointer " onclick="star(3)" id="fourStar" ></span> <span style="cursor: pointer " onclick="star(4)" id="fiveStar" ></span></span><span id="evaluate"></span></body></html>

一开始的时候用了两个for循环就是这样的:

 for(var i=0;i<=n;i++)   {    document.getElementById("fiveStar").innerText="";   }   for( var j=4;j>n;j--)   {    document.getElementById("fiveStar").innerText="";   }
<!DOCTYPE html><head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> <title>淘宝评分效果</title> <style type="text/css">  ul, li {margin: 0; padding: 0; border: 0;}  .shop-rating {   height: 25px;   overflow: hidden;   zoom: 1;   padding: 2px 0;   position: relative;   z-index: 999;   font:12px Arial;   color:#000;   line-height:1.2em  }  .shop-rating span {   height: 23px;   display: block;   line-height: 23px;   float: left;  }  .shop-rating span.title {   width: 125px;   text-align: right;   margin-right: 5px;  }  .shop-rating ul {   float: left;  }  .shop-rating .result {   margin-left: 20px;   padding-top: 2px;  }  .shop-rating .result span {   color: #ff6d02;  }  .rating-level,  .rating-level a {   background: url(http://files.jb51.net/demoimg/201007/o_star.png) no-repeat scroll 1000px 1000px;  }  .rating-level {   background-position: 0px 0px;   width: 120px;   height: 23px;   position: relative;   z-index: 1000;  }  .shop-rating .result em {   color: #f60;   font-family: arial;   font-weight: bold;  }  .rating-level li {   display: inline;  }  .rating-level a {   line-height: 23px;   height: 23px;   position: absolute;   top: 0px;   left: 0px;   text-indent: -999em;   *zoom: 1;   outline: none;  }  .rating-level a.one-star {   width: 20%;   z-index: 6;  }  .rating-level a.two-stars {   width: 40%;   z-index: 5;  }  .rating-level a.three-stars {   width: 60%;   z-index: 4;  }  .rating-level a.four-stars {   width: 80%;   z-index: 3;  }  .rating-level a.five-stars {   width: 100%;   z-index: 2;  }  .rating-level .current-rating, .rating-level a:hover {background-position:0 -28px}  .rating-level a.one-star:hover,.rating-level a.two-stars:hover,.rating-level a.one-star.current-rating,.rating-level a.two-stars.current-rating{background-position:0 -116px;}  .rating-level .three-stars .current-rating,.rating-level .four-stars .current-rating,.rating-level .five-stars .current-rating{background-position:0 -28px;} </style></head><body><div class="shop-rating"> <span class="title">你对我人品的评价:</span> <ul class="rating-level" id="stars2">  <li><a href="javascript:void(0);" class="one-star" star:value="20">20</a></li>  <li><a href="javascript:void(0);" class="two-stars" star:value="40">40</a></li>  <li><a href="javascript:void(0);" class="three-stars" star:value="60">60</a></li>  <li><a href="javascript:void(0);" class="four-stars" star:value="80">80</a></li>  <li><a href="javascript:void(0);" class="five-stars" star:value="100">100</a></li> </ul> <span id="stars2-tips" class="result"></span> <input type="hidden" id="stars2-input" name="b" value="" size="2" /></div><script> var TB = function() {  var T$ = function(id) { return document.getElementById(id) }  var T$$ = function(r, t) { return (r || document).getElementsByTagName(t) }  var Stars = function(cid, rid, hid, config) {   var lis = T$$(T$(cid), 'li'), curA;   for (var i = 0, len = lis.length; i < len; i++) {    lis[i]._val = i;    lis[i].onclick = function() {     T$(rid).innerHTML = '<em>' + (T$(hid).value = T$$(this, 'a')[0].getAttribute('star:value')) + '分</em> - ' + config.info[this._val];     curA = T$$(T$(cid), 'a')[T$(hid).value / config.step - 1];    };    lis[i].onmouseout = function() {     curA && (curA.className += config.curcss);    }    lis[i].onmouseover = function() {     curA && (curA.className = curA.className.replace(config.curcss, ''));    }   }  };  return {Stars: Stars} }().Stars('stars2', 'stars2-tips', 'stars2-input', {  'info' : ['人品极差', '人品不咋地', '人品一般吧', '人品不错', '人品极好啊'],  'curcss': ' current-rating',  'step': 20 });</script></body></html>

SyntaxHighlighter.highlight();

jquery动态增加删减表格行特效

基于jQuery表格增加删除代码是一款动态增加删减表格行特效代码。分享给大家供大家参考。具体如下:
运行效果截图如下:

具体代码如下:

html代码:

<div style="width:720px;margin:20px auto;">   <table id="tab11" style="display: none">     <tbody>       <tr>         <td height="30" align="center">           <input type="text" name="NO" size="2" value="1" />         </td>         <td align="center">           <input type="text" name="start_end_time" />         </td>         <td align="center">           <input type="text" name="unit_department" />         </td>         <td align="center">           <input type="text" name="post" />         </td>         <td>           <input type="button" id="Button1" onclick="deltr(this)" value="删行" />         </td>       </tr>     </tbody>   </table>   <input type="button" id="btn_addtr" value="增行" />   <table id="dynamicTable" width="700" border="0" cellspacing="0" cellpadding="0">     <thead>       <tr>         <td height="30" align="center" bgcolor="#CCCCCC">ID</td>         <td align="center" bgcolor="#CCCCCC">起止时间</td>         <td align="center" bgcolor="#CCCCCC">单位/部门</td>         <td align="center" bgcolor="#CCCCCC">职位</td>         <td></td>       </tr>     </thead>     <tbody>       <tr>         <td height="30" align="center">           <input type="text" name="NO" size="2" value="1" />         </td>         <td align="center">           <input type="text" name="start_end_time" />         </td>         <td align="center">           <input type="text" name="unit_department" />         </td>         <td align="center">           <input type="text" name="post" />         </td>         <td>           <input type="button" id="Button2" onclick="deltr(this)" value="删行" />         </td>       </tr>     </tbody>   </table> </div>
$(function () {      var show_count = 20;  //要显示的条数      var count = 1;  //递增的开始值,这里是你的ID      $("#btn_addtr").click(function () {         var length = $("#dynamicTable tbody tr").length;        //alert(length);        if (length < show_count)  //点击时候,如果当前的数字小于递增结束的条件        {          $("#tab11 tbody tr").clone().appendTo("#dynamicTable tbody");  //在表格后面添加一行          changeIndex();//更新行号        }      });      });    function changeIndex() {      var i = 1;      $("#dynamicTable tbody tr").each(function () { //循环tab tbody下的tr        $(this).find("input[name='NO']").val(i++);//更新行号      });    }     function deltr(opp) {      var length = $("#dynamicTable tbody tr").length;      //alert(length);      if (length <= 1) {        alert("至少保留一行");      } else {        $(opp).parent().parent().remove();//移除当前行        changeIndex();      }    }

SyntaxHighlighter.highlight();

JavaScript获取浏览器信息的方法

Window有navigator对象让我们得知浏览器的全部信息.我们可以利用一系列的API函数得知浏览器的信息.

JavaScript代码如下:

function message(){ txt = "<p>浏览器代码名: " + navigator.appCodeName + "</p>";txt+= "<p>浏览器名称: " + navigator.appName + "</p>"; txt+= "<p>浏览器平台和版本: " + navigator.appVersion + "</p>";txt+= "<p>是否开启cookie: " + navigator.cookieEnabled + "</p>";txt+= "<p>操作系统平台: " + navigator.platform + "</p>"; txt+= "<p>User-agent头部值: " + navigator.userAgent + "</p>"; document.getElementById("example").innerHTML=txt; if ((navigator.appName=="Netscape" || navigator.appName=="Microsoft Internet Explorer") && (parseFloat(navigator.appVersion)>=4)){ alert("您的浏览器够先进了!"); }else { alert("是时候升级您的浏览器了!");} }

我们可以通过这个函数通知用户浏览器是否应该去更新浏览器,同样也可以帮用户得知浏览器的相关信息

各大浏览器的基本信息

楼主测试了几乎当前主流的浏览器,当然不管多少浏览器都是Trident,Blink,Gecko,Webkit这几种的浏览器内核,解析上不会有太大的出入,现附上相关截图如下:

这是Edge的

IE11,楼主没有用IE6,不过应该不会有太大的出入

Safari的,楼主是Window系统Safari版本比较低

搜狗浏览器,曾经楼主也迷恋过它一段时间呢!

QQ浏览器(微信版),比较给力,现在楼主除了Chrome用的最多的浏览器,双核Trident和Blink,Chrome内核下飞快

360安全浏览器的兼容模式,用的是IE的Trident内核

Firefox,不多说了,Netscape正版,开发者必备的浏览器

Chrome.现在楼主用的最多的浏览器,Google实在是好,力挺

360安全浏览器极速模式,Chrome内核,速度很快

代码结果分析

PS:javascript取得浏览器地址及参数方法

用javascript获取 url网址信息

  执行 用javascript获取url网址信息 代码效果如下

  location.host=www。dw。cn  location.hostname=www。dw。cn  location.href=http://www。dw。cn/news/2010-1/201011820131610618.shtml  location.pathname=/news/2010-1/201011820131610618.shtml  location.protocol=http:

  详细介绍 window.location方法获取URL

  统一资源定位符 (Uniform Resource Locator, URL) 完整的URL由这几个部分构成:

  scheme://host:port/path?query#fragment

  scheme: 通信协议

  常用的http,ftp,maito等

  host:主机

  服务器(计算机)域名系统 (DNS) 主机名或 IP 地址。

  port:端口号

  整数,可选,省略时使用方案的默认端口,如http的默认端口为80。

  path:路径

  由零或多个”/”符号隔开的字符串,一般用来表示主机上的一个目录或文件地址。

  query:查询

  可选,用于给动态网页(如使用CGI、ISAPI、PHP/JSP/ASP /ASP.NET等技术制作的网页)传递参数,可有多个参数,用”&”符号隔开,每个参数的名和值用”=”符号隔开。

fragment: 信息片断

  字符串,用于指定网络资源中的片断。例如一个网页中有多个名词解释,可使用fragment直接定位到某一名词解释。(也称为锚点.) 

示例:

  1, window.location.href 

整个URl字符串(在浏览器中就是完整的地址栏)

  返回值:。dw。cn:80/index.asp?id=209#asp

  2,window.location.protocol

  URL 的协议部分

  返回值:http:

 3,window.location.host

 URL 的主机部分,

 返回值:www。dw。cn

 4,window.location.port

 URL 的端口部分。如果采用默认的80端口(update:即使添加了:80),那么返回值并不是默认的80而是空字符。

 本例返回值:空

 5,window.location.pathname

 URL 的路径部分(就是文件地址)

 返回值:/index.asp

 6,window.location.search

 查询(参数)部分。除了给动态语言赋值以外,我们同样可以给静态页面,并使用javascript来获得相信应的参数值

 返回值:?id=209

 7,window.location.hash

SyntaxHighlighter.highlight();

跟我学习javascript的异步脚本加载

先来看这行代码:

<script src = "allMyClientSideCode.js"></script>

这有点儿……不怎么样。“这该放在哪儿?”开发人员会奇怪,“靠上点,放到<head>标签里?还是靠下点,放到<body>标签里?”这两种做法都会让富脚本站点的下场很凄惨。<head>标签里的大脚本会滞压所有页面渲染工作,使得用户在脚本加载完毕之前一直处于“白屏死机”状态。而<body>标签末尾的大脚本只会让用户看到毫无生命力的静态页面,原本应该进行客户端渲染的地方却散布着不起作用 的控件和空空如也的方框。

完美解决这个问题需要对脚本分而治之:那些负责让页面更好看、更好用的脚本应该立即加载,而那些可以待会儿再加载的脚本稍后再加载。但是怎样才能既滞压这些脚本,又能保证它们在被调用时的可用性呢?

一、<script>标签的再认识

现代浏览器中的<script>标签分成了两种新类型:经典型和非阻塞型。接下来讨论如何运用这两种标签来尽快加载页面。

1、阻塞型脚本何去何从?

标准版本的<script>标签常常被称作阻塞型标签。这个词必须放在上下文中进行理解:现代浏览器看到阻塞型<script>标签时,会跳过阻塞点继续读取文档及下载其他资源(脚本和样式表)。但直到脚本下载完毕并运行之后,浏览器才会评估阻塞点之后的那些资源。因此,如果网页文档的<head>标签里有5 个阻塞型<script>标签,则在所有这5 个脚本均下载完毕并运行之前,用户除了页面标题之外看不到任何东西。不仅如此,即便这些脚本运行了,它们也只能看到阻塞点之前的那部分文档。如果想看到<body>标签中正等待加载的那些好东西,就必须给像document.onreadystatechange 这样的事件绑定一个事件处理器。

基于上述原因,现在越来越流行把脚本放在页面<body>标签的尾部。这样,一方面用户可以更快地看到页面,另一方面脚本也可以主动亲密接触DOM 而无需等待事件来触发自己。对大多数脚本而言,这次“搬家”是个巨大的进步。

但并非所有脚本都一样。在向下搬动脚本之前,请先问自己2 个问题。

  • 该脚本是否有可能被<body>标签里的内联JavaScript 直接调用?答案可能一目了然,但仍值得核查一遍。
  • 该脚本是否会影响已渲染页面的外观?Typekit 宿主字体就是一个例子。如果把Typekit 脚本放在文档末尾,那么页面文本就会渲染两次,即读取文档时即刻渲染,脚本运行时再次渲染。

上述问题只要有一个答案是肯定的,那么该脚本就应该放在<head>标签中,否则就可以放在<body>标签中,文档形如:

<html><head>  <!--metadata and stylesheets go here -->  <script" width=100% src="headScripts.js"></scripts></head><body>  <!-- content goes here -->  <script" width=100% src="bodyScripts.js"></script></body></html>

这确实大大缩短了加载时间,但要注意一点,这可能让用户有机会在加载bodyScripts.js 之前与页面交互。

2、 脚本的提前加载与延迟运行

上面建议将大多数脚本放在<body>中,因为这样既能让用户更快地看到网页,又能避免操控DOM之前绑定“就绪”事件的开销。但这种方式也有一个缺点,即浏览器在加载完整个文档之前无法加载这些脚本,这对那些通过慢速连接传送的大型文档来说会是一大瓶颈。

理想情况下,脚本的加载应该与文档的加载同时进行,并且不影响DOM 的渲染。这样,一旦文档就绪就可以运行脚本,因为已经按照<script>标签的次序加载了相应脚本。

如果大家已经读到这里了,那么一定会迫不及待地想写一个自定义Ajax 脚本加载器以满足这样的需求!不过,大多数浏览器都支持一个更为简单的解决方案。

<script defer src = "deferredScript.js">

添加defer(延迟)属性相当于对浏览器说:“请马上开始加载这个脚本吧,但是,请等到文档就绪且所有此前具有defer 属性的脚本都结束运行之后再运行它。”在文档<head>标签里放入延迟脚本,既能带来脚本置于<body>标签时的全部好处,又能让大文档的加载速度大幅提升!

不足之处就是,并非所有浏览器都支持defer属性。这意味着,如果想确保自己的延迟脚本能在文档加载后运行,就必须将所有延迟脚本的代码都封装在诸如jQuery 之$(document).ready 之类的结构中。

上一节的页面例子改进如下:

<html><head>  <!-- metadata and stylesheets go here -->  <script" width=100% src="headScripts.js"></scripts>  <script defer" width=100% src="deferredScripts.js"></script></head><body>  <!-- content goes here --></body></html>

请记住deferredScripts 的封装很重要,这样即使浏览器不支持defer,deferredScripts 也会在文档就绪事件之后才运行。如果页面主体内容远远超过几千字节,那么付出这点代价是完全值得的。

3、 脚本的并行加载

如果你是斤斤计较到毫秒级页面加载时间的完美主义者,那么defer也许就像是淡而无味的薄盐酱油。你可不想一直等到此前所有的defer 脚本都运行结束,当然也肯定不想等到文档就绪之后才运行这些脚本,你就是想尽快加载并且尽快运行这些脚本。这也正是现代浏览器提供了async(异步)属性的原因。

<script async src = "speedyGonzales.js"><script async src = "roadRunner.js">

如果说defer 让我们想到一种静静等待文档加载的有序排队场景,那么async 就会让我们想到混乱的无政府状态。前面给出的那两个脚本会以任意次序运行,而且只要JavaScript 引擎可用就会立即运行,而不论文档就绪与否。
对大多数脚本来说,async 是一块难以下咽的鸡肋。async 不像defer那样得到广泛的支持。同时,由于异步脚本会在任意时刻运行,它实在太容易引起海森堡蚁虫之灾了(脚本刚好结束加载时就会蚁虫四起)。
当我们加载一些第三方脚本,而且也不在乎它们谁先运行谁后运行。因此,对这些第三方脚本使用async 属性,相当于一分钱没花就提升了它们的运行速度。
上一个页面示例再添加两个独立的第三方小部件,得到的结果如下:

<html><head>  <!-- metadata and stylesheets go here -->  <script" width=100% src="headScripts.js"></scripts>  <script" width=100% src="deferredScripts.js" defer></script></head><body>  <!-- content goes here -->  <script async defer" width=100% src="feedbackWidget.js"></script>  <script async defer" width=100% src="chatWidget.js"></script></body></html>

这个页面结构清晰展示了脚本的优先次序。对于绝大多数浏览器,DOM的渲染只会延迟至headScripts.js 结束运行时。进行DOM渲染的同时会在后台加载deferredScripts.js。接着,在DOM 渲染结束时将运行deferredScripts.js 和那两个小部件脚本。这两个小部件脚本在那些支持async 的浏览器中会做无序运行。如果不确定这是否妥当,请勿使用async!
二、可编程的脚本加载
虽然<script>标签简单得令人心动,但有些情况确实需要更精致的脚本加载方式。我们可能只想给那些满足一定条件的用户加载某个脚本,譬如白金会员或达到一定级别的玩家,也可能只想当用户单击激活时才加载某个特性,譬如聊天小部件。
1、直接加载脚本
我们可以用类似下面这样的代码来插入<script>标签。

var head = document.getElementsByTagName('head')[0];var script = document.createElement('script');script.src = '/js/feature.js';head.appendChild(script);

稍等,我们如何才能知道脚本何时加载结束呢?我们可以给脚本本身添加一些代码以触发事件,但如果要为每个待加载脚本都添加这样的代码,那也太闹心了。或者是另外一种情况,即我们不可能给第三方服务器上的脚本添加这样的代码。HTML5 规范定义了一个可以绑定回调的onload 属性。

script.onload = function() {// 现在可以调用脚本里定义的函数了};

不过, IE8 及更老的版本并不支持onload , 它们支持的是onreadystatechange。某些浏览器在插入<script>标签时还会出现一些“灵异事件”。而且,这里甚至还没谈到错误处理呢!为了避免
所有这些令人头疼的问题,在此强烈建议使用脚本加载库。

三、yepnope的条件加载
yepnope是一个简单的、轻量级的脚本加载库(压缩后的精简版只有1.7KB),其设计目标就是真诚服务于最常见的动态脚本加载需求。
yepnope 最简单的用法是,加载脚本并对脚本完成运行这一事件返回一个回调。

yepnope({  load: 'oompaLoompas.js',  callback: function() {  console.log('oompa-Loompas ready!');  }});

还是无动于衷?下面我们要用yepnope 来并行加载多个脚本并按给定次序运行它们。举个例子,假设我们想加载Backbone.js,而这个脚本又依赖于Underscore.js。为此,我们只需用数组形式提供这两个脚本的位置作为加载参数。

yepnope({  load: ['underscore.js', 'backbone.js'],  complete: function() {  // 这里是Backbone 的业务逻辑  }});

请注意,这里使用了complete(完成)而不是callback(回调)。

其差别在于,脚本加载列表中的每个资源均会运行callback,而只有当所有脚本都加载完成后才会运行complete。yepnope 的标志性特征是条件加载。给定test 参数,yepnope 会根据该参数值是否为真而加载不同的资源。举个例子,可以以一定的准确度判断用户是否在用触摸屏设备,从而据此相应地加载不同的样式表及脚本。

yepnope({  test: Modernizr.touch,  yep: ['touchStyles.css', 'touchApplication.js'],  nope: ['mouseStyles.css', 'mouseApplication.js'],  complete: function() {  // 不管是哪一种情况,应用程序均已就绪!  }});

我们只用寥寥几行代码就搭好了舞台,可以基于用户的接入设备而给他们完全不同的使用体验。当然,不是所有的条件加载都需要备齐yep(是)和nope(否)这两种测试结果。yepnope 最常见的用法之一就是加载垫片脚本以弥补老式浏览器缺失的功能。

yepnope({  test: window.json,nope: ['json2.js'],  complete: function() {  // 现在可以放心地用JSON 了  }});

页面使用了yepnope 之后应该变成下面这种漂亮的标记结构:

<html><head>  <!-- metadata and stylesheets go here -->  <script" width=100% src="headScripts.js"></scripts>  <script" width=100% src="deferredScripts.js" defer></script></head><body>  <!-- content goes here --></body></html>

很眼熟?这个结构和讨论defer 属性那一节给出的结构一样,唯一的区别是这里的某个脚本文件已经拼接了yepnope.js(很可能就在deferredScripts.js 的顶部),这样就可以独立地加载那些根据条件再加载的脚本(因为浏览器需要垫片脚本)和那些想要动态加载的脚本(以便回应用户的动作)。结果将是一个更小巧的deferredScripts.js。
四、Require.js/AMD 模块化加载
开发人员想通过脚本加载器让混乱不堪的富脚本应用变得更规整有序一些,而Require.js 就是这样一种选择。Require.js 这个强大的工具包能够自动和AMD技术一起捋顺哪怕最复杂的脚本依赖图。
现在先来看一个用到Require.js 同名函数的简单脚本加载示例。

require(['moment'], function(moment) {  console.log(moment().format('dddd')); // 星期几});

require 函数接受一个由模块名称构成的数组,然后并行地加载所有这些脚本模块。与yepnope 不同,Require.js 不会保证按顺序运行目标脚本,只是保证它们的运行次序能满足各自的依赖性要求,但前提是
这些脚本的定义遵守了AMD(Asynchronous Module Definition,异步模块定义)规范。
案例一: 加载 JavaScript 文件

 <script" width=100% src="./js/require.js"></script> <script>   require(["./js/a.js", "./js/b.js"], function() {        myFunctionA();        myFunctionB();     }); </script>

如案例一 所示,有两个 JavaScript 文件 a.js 和 b.js,里面各自定义了 myFunctionA 和 myFunctionB 两个方法,通过下面这个方式可以用 RequireJS 来加载这两个文件,在 function 部分的代码可以引用这两个文件里的方法。

require 方法里的这个字符串数组参数可以允许不同的值,当字符串是以”.js”结尾,或者以”/”开头,或者就是一个 URL 时,RequireJS 会认为用户是在直接加载一个 JavaScript 文件,否则,当字符串是类似”my/module”的时候,它会认为这是一个模块,并且会以用户配置的 baseUrl 和 paths 来加载相应的模块所在的 JavaScript 文件。配置的部分会在稍后详细介绍。

这里要指出的是,RequireJS 默认情况下并没有保证 myFunctionA 和 myFunctionB 一定是在页面加载完成以后执行的,在有需要保证页面加载以后执行脚本时,RequireJS 提供了一个独立的 domReady 模块,需要去 RequireJS 官方网站下载这个模块,它并没有包含在 RequireJS 中。有了 domReady 模块,案例一 的代码稍做修改加上对 domReady 的依赖就可以了。
案例二: 页面加载后执行 JavaScript

 <script" width=100% src="./js/require.js"></script> <script>   require(["domReady!", "./js/a.js", "./js/b.js"], function() {        myFunctionA();        myFunctionB();     }); </script>

执行案例二的代码后,通过 Firebug 可以看到 RequireJS 会在当前的页面上插入为 a.js 和 b.js 分别声明了一个 < script> 标签,用于异步方式下载 JavaScript 文件。async 属性目前绝大部分浏览器已经支持,它表明了这个 < script> 标签中的 js 文件不会阻塞其他页面内容的下载。
案例三:RequireJS 插入的 < script>

<script type="text/javascript" charset="utf-8" async="" data-requirecontext="_"  data-requiremodule="js/a.js"" width=100% src="js/a.js"></script>

AMD推行一个由Require.js 负责提供的名叫define 的全局函数,该函数有3 个参数:

  • 模块名称,
  • 模块依赖性列表,
  • 在那些依赖性模块加载结束时触发的回调。

使用 RequireJS 来定义 JavaScript 模块
这里的 JavaScript 模块与传统的 JavaScript 代码不一样的地方在于它无须访问全局的变量。模块化的设计使得 JavaScript 代码在需要访问”全局变量”的时候,都可以通过依赖关系,把这些”全局变量”作为参数传递到模块的实现体里,在实现中就避免了访问或者声明全局的变量或者函数,有效的避免大量而且复杂的命名空间管理。
如同 CommonJS 的 AMD 规范所述,定义 JavaScript 模块是通过 define 方法来实现的。
下面我们先来看一个简单的例子,这个例子通过定义一个 student 模块和一个 class 模块,在主程序中实现创建 student 对象并将 student 对象放到 class 中去。
案例四: student 模块,student.js

define(function(){    return {     createStudent: function(name, gender){        return {          name: name,          gender: gender        };     }    };  });

案例五:class 模块,class.js

 define(function() {  var allStudents = [];     return {       classID: "001",       department: "computer",       addToClass: function(student) {       allStudents.push(student);       },       getClassSize: function() {       return allStudents.length;       }     };    }  );

案例六: 主程序

 require(["js/student", "js/class"], function(student, clz) {    clz.addToClass(student.createStudent("Jack", "male"));    clz.addToClass(student.createStudent("Rose", "female"));    console.log(clz.getClassSize()); // 输出 2  });

student 模块和 class 模块都是独立的模块,下面我们再定义一个新的模块,这个模块依赖 student 和 class 模块,这样主程序部分的逻辑也可以包装进去了。
案例七:依赖 student 和 class 模块的 manager 模块,manager.js

define(["js/student", "js/class"], function(student, clz){   return {     addNewStudent: function(name, gender){     clz.addToClass(student.createStudent(name, gender));     },     getMyClassSize: function(){     return clz.getClassSize();     }    }; });

案例八:新的主程序

require(["js/manager"], function(manager) {    manager.addNewStudent("Jack", "male");    manager.addNewStudent("Rose", "female");    console.log(manager.getMyClassSize());// 输出 2  });

通过上面的代码示例,我们已经清楚的了解了如何写一个模块,这个模块如何被使用,模块间的依赖关系如何定义。

SyntaxHighlighter.highlight();

JavaScript性能优化之小知识总结

随着网络的发展,网速和机器速度的提高,越来越多的网站用到了丰富客户端技术。而现在Ajax则是最为流行的一种方式。JavaScript是一种解释型语言,所以能无法达到和C/Java之类的水平,限制了它能在客户端所做的事情,为了能改进他的性能,我想基于我以前给JavaScript做过的很多测试来谈谈自己的经验,希望能帮助大家改进自己的JavaScript脚本性能。

前言

一直在学习javascript,也有看过《犀利开发Jquery内核详解与实践》,对这本书的评价只有两个字犀利,可能是对javascript理解的还不够透彻异或是自己太笨,更多的是自己不擅于思考懒得思考以至于里面说的一些精髓都没有太深入的理解。

鉴于想让自己有一个提升,进不了一个更加广阔的天地,总得找一个属于自己的居所好好生存,所以平时会有意无意的去积累一些使用jQuerry的常用知识,特别是对于性能要求这一块,总是会想是不是有更好的方式来实现。

下面是我总结的一些小技巧,仅供参考。(我先会说一个总标题,然后用一小段话来说明这个意思 再最后用一个demo来简单言明)

避免全局查找

在一个函数中会用到全局对象存储为局部变量来减少全局查找,因为访问局部变量的速度要比访问全局变量的速度更快些

    function search() {      //当我要使用当前页面地址和主机域名      alert(window.location.href + window.location.host);    }    //最好的方式是如下这样 先用一个简单变量保存起来    function search() {      var location = window.location;      alert(location.href + location.host);    }

定时器

如果针对的是不断运行的代码,不应该使用setTimeout,而应该是用setInterval,因为setTimeout每一次都会初始化一个定时器,而setInterval只会在开始的时候初始化一个定时器

var timeoutTimes = 0;    function timeout() {      timeoutTimes++;      if (timeoutTimes < 10) {        setTimeout(timeout, 10);      }    }    timeout();    //可以替换为:    var intervalTimes = 0;    function interval() {      intervalTimes++;      if (intervalTimes >= 10) {        clearInterval(interv);      }    }    var interv = setInterval(interval, 10);

 字符串连接

如果要连接多个字符串,应该少使用+=,如

s+=a;s+=b;s+=c;

应该写成s+=a + b + c;

而如果是收集字符串,比如多次对同一个字符串进行+=操作的话,最好使用一个缓存,使用JavaScript数组来收集,最后使用join方法连接起来

  var buf = [];    for (var i = 0; i < 100; i++) {      buf.push(i.toString());    }    var all = buf.join("");

避免with语句

和函数类似 ,with语句会创建自己的作用域,因此会增加其中执行的代码的作用域链的长度,由于额外的作用域链的查找,在with语句中执行的代码肯定会比外面执行的代码要慢,在能不使用with语句的时候尽量不要使用with语句。

 with (a.b.c.d) {      property1 = 1;      property2 = 2;    }    //可以替换为:    var obj = a.b.c.d;    obj.property1 = 1;    obj.property2 = 2;

数字转换成字符串

般最好用”” + 1来将数字转换成字符串,虽然看起来比较丑一点,但事实上这个效率是最高的,性能上来说:

(“” +) > String() > .toString() > new String()

浮点数转换成整型

很多人喜欢使用parseInt(),其实parseInt()是用于将字符串转换成数字,而不是浮点数和整型之间的转换,我们应该使用Math.floor()或者Math.round()

各种类型转换

var myVar = "3.14159",    str = "" + myVar, // to string     i_int = ~ ~myVar, // to integer     f_float = 1 * myVar, // to float     b_bool = !!myVar, /* to boolean - any string with length                 and any number except 0 are true */    array = [myVar]; // to array

如果定义了toString()方法来进行类型转换的话,推荐显式调用toString(),因为内部的操作在尝试所有可能性之后,会尝试对象的toString()方法尝试能否转化为String,所以直接调用这个方法效率会更高

多个类型声明

在JavaScript中所有变量都可以使用单个var语句来声明,这样就是组合在一起的语句,以减少整个脚本的执行时间,就如上面代码一样,上面代码格式也挺规范,让人一看就明了。

插入迭代器

如var name=values[i]; i++;前面两条语句可以写成var name=values[i++]

使用直接量

var aTest = new Array(); //替换为    var aTest = [];    var aTest = new Object; //替换为    var aTest = {};    var reg = new RegExp(); //替换为    var reg = /../;    //如果要创建具有一些特性的一般对象,也可以使用字面量,如下:    var oFruit = new O;    oFruit.color = "red";    oFruit.name = "apple";    //前面的代码可用对象字面量来改写成这样:    var oFruit = { color: "red", name: "apple" };

 使用DocumentFragment优化多次append

一旦需要更新DOM,请考虑使用文档碎片来构建DOM结构,然后再将其添加到现存的文档中。

for (var i = 0; i < 1000; i++) {      var el = document.createElement('p');      el.innerHTML = i;      document.body.appendChild(el);    }    //可以替换为:    var frag = document.createDocumentFragment();    for (var i = 0; i < 1000; i++) {      var el = document.createElement('p');      el.innerHTML = i;      frag.appendChild(el);    }    document.body.appendChild(frag);

使用一次innerHTML赋值代替构建dom元素

对于大的DOM更改,使用innerHTML要比使用标准的DOM方法创建同样的DOM结构快得多。

    var frag = document.createDocumentFragment();    for (var i = 0; i < 1000; i++) {      var el = document.createElement('p');      el.innerHTML = i;      frag.appendChild(el);    }    document.body.appendChild(frag);    //可以替换为:    var html = [];    for (var i = 0; i < 1000; i++) {      html.push('<p>' + i + '</p>');    }    document.body.innerHTML = html.join('');

通过模板元素clone,替代createElement

很多人喜欢在JavaScript中使用document.write来给页面生成内容。事实上这样的效率较低,如果需要直接插入HTML,可以找一个容器元素,比如指定一个div或者span,并设置他们的innerHTML来将自己的HTML代码插入到页面中。通常我们可能会使用字符串直接写HTML来创建节点,其实这样做,1无法保证代码的有效性2字符串操作效率低,所以应该是用document.createElement()方法,而如果文档中存在现成的样板节点,应该是用cloneNode()方法,因为使用createElement()方法之后,你需要设置多次元素的属性,使用cloneNode()则可以减少属性的设置次数――同样如果需要创建很多元素,应该先准备一个样板节点

var frag = document.createDocumentFragment();    for (var i = 0; i < 1000; i++) {      var el = document.createElement('p');      el.innerHTML = i;      frag.appendChild(el);    }    document.body.appendChild(frag);    //替换为:    var frag = document.createDocumentFragment();    var pEl = document.getElementsByTagName('p')[0];    for (var i = 0; i < 1000; i++) {      var el = pEl.cloneNode(false);      el.innerHTML = i;      frag.appendChild(el);    }    document.body.appendChild(frag);

使用firstChild和nextSibling代替childNodes遍历dom元素

var nodes = element.childNodes;    for (var i = 0, l = nodes.length; i < l; i++) {      var node = nodes[i];      //……    }    //可以替换为:    var node = element.firstChild;    while (node) {      //……      node = node.nextSibling;

删除DOM节点

删除dom节点之前,一定要删除注册在该节点上的事件,不管是用observe方式还是用attachEvent方式注册的事件,否则将会产生无法回收的内存。另外,在removeChild和innerHTML=”二者之间,尽量选择后者. 因为在sIEve(内存泄露监测工具)中监测的结果是用removeChild无法有效地释放dom节点

使用事件代理

任何可以冒泡的事件都不仅仅可以在事件目标上进行处理,目标的任何祖先节点上也能处理,使用这个知识就可以将事件处理程序附加到更高的地方负责多个目标的事件处理,同样,对于内容动态增加并且子节点都需要相同的事件处理函数的情况,可以把事件注册提到父节点上,这样就不需要为每个子节点注册事件监听了。另外,现有的js库都采用observe方式来创建事件监听,其实现上隔离了dom对象和事件处理函数之间的循环引用,所以应该尽量采用这种方式来创建事件监听

重复使用的调用结果,事先保存到局部变量

   //避免多次取值的调用开销    var h1 = element1.clientHeight + num1;    var h2 = element1.clientHeight + num2;    //可以替换为:    var eleHeight = element1.clientHeight;    var h1 = eleHeight + num1;    var h2 = eleHeight + num2;

 注意NodeList

最小化访问NodeList的次数可以极大的改进脚本的性能

    var images = document.getElementsByTagName('img');    for (var i = 0, len = images.length; i < len; i++) {    }

编写JavaScript的时候一定要知道何时返回NodeList对象,这样可以最小化对它们的访问

进行了对getElementsByTagName()的调用

获取了元素的childNodes属性
获取了元素的attributes属性
访问了特殊的集合,如document.forms、document.images等等
要了解了当使用NodeList对象时,合理使用会极大的提升代码执行速度

优化循环

可以使用下面几种方式来优化循环

减值迭代

大多数循环使用一个从0开始、增加到某个特定值的迭代器,在很多情况下,从最大值开始,在循环中不断减值的迭代器更加高效

简化终止条件

由于每次循环过程都会计算终止条件,所以必须保证它尽可能快,也就是说避免属性查找或者其它的操作,最好是将循环控制量保存到局部变量中,也就是说对数组或列表对象的遍历时,提前将length保存到局部变量中,避免在循环的每一步重复取值。

  var list = document.getElementsByTagName('p');    for (var i = 0; i < list.length; i++) {      //……    }    //替换为:    var list = document.getElementsByTagName('p');    for (var i = 0, l = list.length; i < l; i++) {      //……    }

简化循环体

循环体是执行最多的,所以要确保其被最大限度的优化

使用后测试循环

在JavaScript中,我们可以使用for(;;),while(),for(in)三种循环,事实上,这三种循环中for(in)的效率极差,因为他需要查询散列键,只要可以,就应该尽量少用。for(;;)和while循环,while循环的效率要优于for(;;),可能是因为for(;;)结构的问题,需要经常跳转回去。

    var arr = [1, 2, 3, 4, 5, 6, 7];    var sum = 0;    for (var i = 0, l = arr.length; i < l; i++) {      sum += arr[i];    }    //可以考虑替换为:    var arr = [1, 2, 3, 4, 5, 6, 7];    var sum = 0, l = arr.length;    while (l--) {      sum += arr[l];    }

最常用的for循环和while循环都是前测试循环,而如do-while这种后测试循环,可以避免最初终止条件的计算,因此运行更快。

展开循环

当循环次数是确定的,消除循环并使用多次函数调用往往会更快。

避免双重解释

如果要提高代码性能,尽可能避免出现需要按照JavaScript解释的字符串,也就是

尽量少使用eval函数

使用eval相当于在运行时再次调用解释引擎对内容进行运行,需要消耗大量时间,而且使用Eval带来的安全性问题也是不容忽视的。

不要使用Function构造器

不要给setTimeout或者setInterval传递字符串参数

var num = 0;    setTimeout('num++', 10);    //可以替换为:    var num = 0;    function addNum() {      num++;    }    setTimeout(addNum, 10);

缩短否定检测

    if (oTest != '#ff0000') {      //do something    }    if (oTest != null) {      //do something    }    if (oTest != false) {      //do something    }    //虽然这些都正确,但用逻辑非操作符来操作也有同样的效果:    if (!oTest) {      //do something    }

条件分支

将条件分支,按可能性顺序从高到低排列:可以减少解释器对条件的探测次数
在同一条件子的多(>2)条件分支时,使用switch优于if:switch分支选择的效率高于if,在IE下尤为明显。4分支的测试,IE下switch的执行时间约为if的一半。

使用三目运算符替代条件分支

if (a > b) {      num = a;    } else {      num = b;    }    //可以替换为:    num = a > b ? a : b;

使用常量

重复值:任何在多处用到的值都应该抽取为一个常量

用户界面字符串:任何用于显示给用户的字符串,都应该抽取出来以方便国际化
URLs:在Web应用中,资源位置很容易变更,所以推荐用一个公共地方存放所有的URL
任意可能会更改的值:每当你用到字面量值的时候,你都要问一下自己这个值在未来是不是会变化,如果答案是“是”,那么这个值就应该被提取出来作为一个常量。
避免与null进行比较

由于JavaScript是弱类型的,所以它不会做任何的自动类型检查,所以如果看到与null进行比较的代码,尝试使用以下技术替换

如果值应为一个引用类型,使用instanceof操作符检查其构造函数

如果值应为一个基本类型,作用typeof检查其类型

如果是希望对象包含某个特定的方法名,则使用typeof操作符确保指定名字的方法存在于对象上

避免全局量

全局变量应该全部字母大写,各单词之间用_下划线来连接。尽可能避免全局变量和函数, 尽量减少全局变量的使用,因为在一个页面中包含的所有JavaScript都在同一个域中运行。所以如果你的代码中声明了全局变量或者全局函数的话,后面的代码中载入的脚本文件中的同名变量和函数会覆盖掉(overwrite)你的。

//糟糕的全局变量和全局函数var current = null;function init(){//...}function change() {  //...}function verify() {  //...}//解决办法有很多,Christian Heilmann建议的方法是://如果变量和函数不需要在“外面”引用,那么就可以使用一个没有名字的方法将他们全都包起来。(function(){var current = null;function init() {  //...}function change() {  //...}function verify() {  //...}})();//如果变量和函数需要在“外面”引用,需要把你的变量和函数放在一个“命名空间”中//我们这里用一个function做命名空间而不是一个var,因为在前者中声明function更简单,而且能保护隐私数据myNameSpace = function() {  var current = null;  function init() {    //...  }  function change() {    //...  }  function verify() {    //...  }//所有需要在命名空间外调用的函数和属性都要写在return里面  return {    init: init,    //甚至你可以为函数和属性命名一个别名    set: change  };};

尊重对象的所有权

因为JavaScript可以在任何时候修改任意对象,这样就可以以不可预计的方式覆写默认的行为,所以如果你不负责维护某个对象,它的对象或者它的方法,那么你就不要对它进行修改,具体一点就是说:

不要为实例或原型添加属性
不要为实例或者原型添加方法
不要重定义已经存在的方法
不要重复定义其它团队成员已经实现的方法,永远不要修改不是由你所有的对象,你可以通过以下方式为对象创建新的功能:
创建包含所需功能的新对象,并用它与相关对象进行交互
创建自定义类型,继承需要进行修改的类型,然后可以为自定义类型添加额外功能

循环引用

如果循环引用中包含DOM对象或者ActiveX对象,那么就会发生内存泄露。内存泄露的后果是在浏览器关闭前,即使是刷新页面,这部分内存不会被浏览器释放。

简单的循环引用:

    var el = document.getElementById('MyElement');    var func = function () {      //…    }    el.func = func;    func.element = el;

但是通常不会出现这种情况。通常循环引用发生在为dom元素添加闭包作为expendo的时候。

    function init() {      var el = document.getElementById('MyElement');      el.onclick = function () {        //……      }    }    init();

init在执行的时候,当前上下文我们叫做context。这个时候,context引用了el,el引用了function,function引用了context。这时候形成了一个循环引用。

下面2种方法可以解决循环引用:

1)  置空dom对象

function init() {      var el = document.getElementById('MyElement');      el.onclick = function () {        //……      }    }    init();    //可以替换为:    function init() {      var el = document.getElementById('MyElement');      el.onclick = function () {        //……      }      el = null;    }    init(); 

将el置空,context中不包含对dom对象的引用,从而打断循环应用。

如果我们需要将dom对象返回,可以用如下方法:

   function init() {      var el = document.getElementById('MyElement');      el.onclick = function () {        //……      }      return el;    }    init();    //可以替换为:    function init() {      var el = document.getElementById('MyElement');      el.onclick = function () {        //……      }      try {        return el;      } finally {        el = null;      }    }    init();

2)  构造新的context

  function init() {      var el = document.getElementById('MyElement');      el.onclick = function () {        //……      }    }    init();    //可以替换为:    function elClickHandler() {      //……    }    function init() {      var el = document.getElementById('MyElement');      el.onclick = elClickHandler;    }    init();

 把function抽到新的context中,这样,function的context就不包含对el的引用,从而打断循环引用。

通过javascript创建的dom对象,必须append到页面中

IE下,脚本创建的dom对象,如果没有append到页面中,刷新页面,这部分内存是不会回收的!

    function create() {      var gc = document.getElementById('GC');      for (var i = 0; i < 5000; i++) {        var el = document.createElement('div');        el.innerHTML = "test";        //下面这句可以注释掉,看看浏览器在任务管理器中,点击按钮然后刷新后的内存变化        gc.appendChild(el);      }    }

释放dom元素占用的内存

将dom元素的innerHTML设置为空字符串,可以释放其子元素占用的内存。

在rich应用中,用户也许会在一个页面上停留很长时间,可以使用该方法释放积累得越来越多的dom元素使用的内存。

释放javascript对象

在rich应用中,随着实例化对象数量的增加,内存消耗会越来越大。所以应当及时释放对对象的引用,让GC能够回收这些内存控件。

对象:obj = null

对象属性:delete obj.myproperty

数组item:使用数组的splice方法释放数组中不用的item

避免string的隐式装箱

对string的方法调用,比如’xxx’.length,浏览器会进行一个隐式的装箱操作,将字符串先转换成一个String对象。推荐对声明有可能使用String实例方法的字符串时,采用如下写法:

var myString = new String(‘Hello World');

松散耦合

1、解耦HTML/JavaScript

JavaScript和HTML的紧密耦合:直接写在HTML中的JavaScript、使用包含内联代码的<script>元素、使用HTML属性来分配事件处理程序等

HTML和JavaScript的紧密耦合:JavaScript中包含HTML,然后使用innerHTML来插入一段html文本到页面

其实应该是保持层次的分离,这样可以很容易的确定错误的来源,所以我们应确保HTML呈现应该尽可能与JavaScript保持分离

2、解耦CSS/JavaScript

显示问题的唯一来源应该是CSS,行为问题的唯一来源应该是JavaScript,层次之间保持松散耦合才可以让你的应用程序更加易于维护,所以像以下的代码element.style.color=”red”尽量改为element.className=”edit”,而且不要在css中通过表达式嵌入JavaScript

3、解耦应用程序/事件处理程序

将应用逻辑和事件处理程序相分离:一个事件处理程序应该从事件对象中提取,并将这些信息传送给处理应用逻辑的某个方法中。这样做的好处首先可以让你更容易更改触发特定过程的事件,其次可以在不附加事件的情况下测试代码,使其更易创建单元测试

性能方面的注意事项

1、尽量使用原生方法

2、switch语句相对if较快

通过将case语句按照最可能到最不可能的顺序进行组织

3、位运算较快

当进行数字运算时,位运算操作要比任何布尔运算或者算数运算快

4、巧用||和&&布尔运算符

 function eventHandler(e) {      if (!e) e = window.event;    }    //可以替换为:    function eventHandler(e) {      e = e || window.event;    }    if (myobj) {      doSomething(myobj);    }    //可以替换为:    myobj && doSomething(myobj);

避免错误应注意的地方

1、每条语句末尾须加分号

在if语句中,即使条件表达式只有一条语句也要用{}把它括起来,以免后续如果添加了语句之后造成逻辑错误

2、使用+号时需谨慎

JavaScript 和其他编程语言不同的是,在 JavaScript 中,’+’除了表示数字值相加,字符串相连接以外,还可以作一元运算符用,把字符串转换为数字。因而如果使用不当,则可能与自增符’++’混淆而引起计算错误

    var valueA = 20;    var valueB = "10";    alert(valueA + valueB);   //ouput: 2010     alert(valueA + (+valueB)); //output: 30     alert(valueA + +valueB);  //output:30     alert(valueA ++ valueB);   //Compile error

3、使用return语句需要注意

一条有返回值的return语句不要用()括号来括住返回值,如果返回表达式,则表达式应与return关键字在同一行,以避免压缩时,压缩工具自动加分号而造成返回与开发人员不一致的结果

 function F1() {      var valueA = 1;      var valueB = 2;      return valueA + valueB;    }    function F2() {      var valueA = 1;      var valueB = 2;      return      valueA + valueB;    }    alert(F1()); //output: 3     alert(F2()); //ouput: undefined

==和===的区别

避免在if和while语句的条件部分进行赋值,如if (a = b),应该写成if (a == b),但是在比较是否相等的情况下,最好使用全等运行符,也就是使用===和!==操作符会相对于==和!=会好点。==和!=操作符会进行类型强制转换

var valueA = "1";    var valueB = 1;    if (valueA == valueB) {      alert("Equal");    }    else {      alert("Not equal");    }    //output: "Equal"    if (valueA === valueB) {      alert("Equal");    }    else {      alert("Not equal");    }    //output: "Not equal"

不要使用生偏语法

不要使用生偏语法,写让人迷惑的代码,虽然计算机能够正确识别并运行,但是晦涩难懂的代码不方便以后维护

函数返回统一类型

虽然JavaScript是弱类型的,对于函数来说,前面返回整数型数据,后面返回布尔值在编译和运行都可以正常通过,但为了规范和以后维护时容易理解,应保证函数应返回统一的数据类型

总是检查数据类型

要检查你的方法输入的所有数据,一方面是为了安全性,另一方面也是为了可用性。用户随时随地都会输入错误的数据。这不是因为他们蠢,而是因为他们很忙,并且思考的方式跟你不同。用typeof方法来检测你的function接受的输入是否合法

何时用单引号,何时用双引号

虽然在JavaScript当中,双引号和单引号都可以表示字符串, 为了避免混乱,我们建议在HTML中使用双引号,在JavaScript中使用单引号,但为了兼容各个浏览器,也为了解析时不会出错,定义JSON对象时,最好使用双引号

部署

用JSLint运行JavaScript验证器来确保没有语法错误或者是代码没有潜在的问
部署之前推荐使用压缩工具将JS文件压缩

SyntaxHighlighter.highlight();

Bootstrap每天必学之基础排版

本次主要来了解的是排版,这个大部分在HTML的基本标签中也是存在的,所以相对比较简单,为了保证系列的完整性,也顺带复习下,还是记录一下。主要内容如下:

  • 1.标题
  • 2.页面主体
  • 3.强调
  • 4.缩略语
  • 5.地址
  • 6.引用
  • 7.列表

一、标题
Html中的所有标题标签,从<h1>到<h6>均可使用。另外还提供了.h1到.h6的class,为的是给inline属性的文本赋予标题的样式。

<div class="container">   <h1 class="page-header">标题</h1>  <h1>h1. Bootstrap heading</h1>  <h2>h2. Bootstrap heading</h2>  <h3>h3. Bootstrap heading</h3>  <h4>h4. Bootstrap heading</h4>  <h5>h5. Bootstrap heading</h5>  <h6>h6. Bootstrap heading</h6> </div> 

直接看效果吧

在标题内还可以包含<small>标签或.small元素,可以用来标记副标题。

 <div class="container">   <h1 class="page-header">标题</h1>  <h1>h1. Bootstrap heading</h1><small>Secondary text</small>  <h2>h2. Bootstrap heading</h2><small>Secondary text</small>  <h3>h3. Bootstrap heading</h3><small>Secondary text</small>  <h4>h4. Bootstrap heading</h4><small>Secondary text</small>  <h5>h5. Bootstrap heading</h5><small>Secondary text</small>  <h6>h6. Bootstrap heading</h6><small>Secondary text</small> </div> 

二、页面主体
 Bootstrap将全局font-size设置为14px,line-height为1.428 。这些属性直接赋给<body>和所有段落元素。另外,<p>(段落)还被设置了等于1/2行高的底部外边距(margin)(即10px)。

 <h1 class="page-header">页面主体</h1>  <div style="border:1px solid ">   <p style="border:1px solid ">Nullam quis risus eget urna mollis ornare vel eu leo. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam id dolor id nibh ultricies vehicula.  Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec ullamcorper nulla non metus auctor fringilla. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla.  Maecenas sed diam eget risus varius blandit sit amet non magna. Donec id elit non mi porta gravida at eget metus. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit.</p> </div>

通过效果展示就很明显了。

 Lead body copy
 通过添加.lead可以让段落突出显示。 

 <h1 class="page-header">Lead Body Copy</h1>  <div style="border:1px solid ">   <p class="lead" style="border:1px solid ">Nullam quis risus eget urna mollis ornare vel eu leo. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam id dolor id nibh ultricies vehicula.  Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec ullamcorper nulla non metus auctor fringilla. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla.  Maecenas sed diam eget risus varius blandit sit amet non magna. Donec id elit non mi porta gravida at eget metus. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit.</p> </div> <h1></h1>

通过和上面页面主体的对比就可以看到明显的效果了。

三、强调
直接使用HTML中用于标注强调的标签,并给他们赋予少许的样式。
1.小号文本
  对于不需要强调的inline或block类型的文本,使用<small>标签包裹,其内的文本将被设置为父容器字体大小的85%。标题元素中嵌套的<small>元素被设置不同的font-size。
你还可以为行内元素赋予.small以代替任何<small>标签。

<small>This line of text is meant to be treated as fine print.</small>

2.着重
通过增加font-weight强调一段文本。

<strong>rendered as bold text</strong>

3.斜体
用斜体强调一段文本。

<em>rendered as italicized text</em>  

4.对齐class
通过文本对齐class,可以简单方便的将文字重新对齐。

<p class="text-left">Left aligned text.</p><p class="text-center">Center aligned text.</p><p class="text-right">Right aligned text.</p>

很明显第一行左对齐,第二行居中,第三行右对齐。
5.强调class
 这些class通过颜色来表示强调。也可以应用于链接,当鼠标盘旋于链接上时,其颜色会变深,就像默认的链接样式。

<h1>强调Class</h1> <p class="text-muted">Maecenas sed diam eget risus varius blandit sit amet non magna.</p> <p class="text-primary">Maecenas sed diam eget risus varius blandit sit amet non magna.</p> <p class="text-success">.Maecenas sed diam eget risus varius blandit sit amet non magna.</p> <p class="text-info">Maecenas sed diam eget risus varius blandit sit amet non magna..</p> <p class="text-warning">Maecenas sed diam eget risus varius blandit sit amet non magna..</p> <p class="text-danger">.Maecenas sed diam eget risus varius blandit sit amet non magna.</p> <h1></h1>

四、缩略图
   当鼠标悬停在缩写和缩写词上时就会显示完整内容,Bootstrap实现了对HTML的<abbr>元素的增强样式。缩略语元素带有title属性,外观表现为带有较浅的虚线框,鼠标移至上面时会变成带有“问号”的指针。如想看完整的内容可把鼠标悬停在缩略语上, 但需要包含title属性。
基本缩略语
如想看完整的内容可把鼠标悬停在缩略语上, 但需要包含title属性。

<abbr title="attribute">attr</abbr>

看到效果了,就是没办法截到图。
Initialism
为缩略语添加.initialism可以将其font-size设置的更小些。

<abbr title="HyperText Markup Language" class="initialism">HTML</abbr>

还是只上代码自己看效果。  
五、地址
 让联系信息以最接近日常使用的格式呈现。在每行结尾添加<br>可以保留需要的样式。

<address> <strong>Twitter, Inc.</strong><br> 795 Folsom Ave, Suite 600<br> San Francisco, CA 94107<br> <abbr title="Phone">P:</abbr> (123) 456-7890</address><address> <strong>Full Name</strong><br> <a href="mailto:#">first.last@example.com</a></address>

 

六、引用
在你的文档中引用其他来源的内容。
默认样式的引用
将任何HTML裹在<blockquote>之中即可表现为引用。对于直接引用,我们建议用<p>标签。

<blockquote> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.</p></blockquote>

 

引用选项
对于标准样式的<blockquote>,可以通过几个简单的变体就能改变风格和内容。
命名来源:添加<small>标签来注明引用来源。来源名称可以放在<cite>标签里面。

<blockquote> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.</p> <small>Someone famous in <cite title="Source Title">Source Title</cite></small></blockquote>

会多一个Source Title
另一种展示风格

使用.pull-right可以让引用展现出向右侧移动、对齐的效果。   

 <blockquote class="pull-right">  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.</p> </blockquote>

向右对齐移动了额,当然也有相应的pull-left。  
七、列表
 无序列表 
顺序无关紧要的一列元素。

 <ul>  <li>Lorem ipsum dolor sit amet</li>  <li>Lorem ipsum dolor sit amet</li>  <li>Lorem ipsum dolor sit amet</li>  <li>Lorem ipsum dolor sit amet</li>  <li>Lorem ipsum dolor sit amet</li> </ul>

这个也很明显和Html的一样。
有序列表
顺序至关重要的一组元素。

<ol>  <li>Lorem ipsum dolor sit amet</li>  <li>Lorem ipsum dolor sit amet</li>  <li>Lorem ipsum dolor sit amet</li>  <li>Lorem ipsum dolor sit amet</li>  <li>Lorem ipsum dolor sit amet</li> </ol>

同理有序列表
无样式列表
移除了默认的list-style样式和左侧外边距的一组元素(只针对直接子元素)。这这是针对直接子元素,也就是说,你需要对所有嵌套的列表都添加此class才能具有同样的样式。

<ul class="list-unstyled">  <li>Lorem ipsum dolor sit amet</li>  <li>Lorem ipsum dolor sit amet</li>  <li>Lorem ipsum dolor sit amet</li>  <li>Lorem ipsum dolor sit amet</li>  <li>Lorem ipsum dolor sit amet</li> </ul>

内联列表
通过设置display: inline-block;并添加少量的内补,将所有元素放置于同一列。

<ul class="list-inline">    <li>Lorem ipsum dolor sit amet</li>    <li>Lorem ipsum dolor sit amet</li>    <li>Lorem ipsum dolor sit amet</li>    <li>Lorem ipsum dolor sit amet</li>    <li>Lorem ipsum dolor sit amet</li>  </ul>

SyntaxHighlighter.highlight();

PHP中empty和isset对于参数结构的判断及empty()和isset()的区别

废话不多说了,直接给大家贴代码了。

<?php  class test{}  $a1 = null;  $a2 = "";  //$a3 =  $a4 = 0;  $a5 = '0';  $a6 = false;  $a7 = array();  //var $a8;  $a9 = new test();  for ($i=1; $i <=9 ; $i++) {    $s = 'a'.$i;    echo $i . ":";    var_dump(isset($$s));    echo '<br />';  }  echo '<br />';  for ($i=1; $i <=9 ; $i++) {    $s = 'a'.$i;    echo $i . ":";    var_dump(empty($$s));    echo '<br />';  }

PS:PHP中empty()和isset()的区别

 对于初学php的人来说,empty()和和isset()用法的区别是很难搞清楚的,他们的用法的差别不仔细去琢磨的话确实很难弄清楚。

先说一下他们的共同点:

都可以判定一个变量是否为空;

都返回boolean类型,即true或false。

下面具体说一下他们用法之间的区别:

isset()用来检测变量是否设置,只能用于变量,因为传递任何其它参数都将造成解析错误。若想检测常量是否已设置,可使用 defined() 函数。如果已经使用 unset() 释放了一个变量之后,它将不再是 isset()。若使用 isset() 测试一个被设置成 NULL 的变量,将返回 FALSE。(注意的是一个 NULL 字节(”/0″)并不等同于 PHP 的 NULL 常数)

<?php     var $a=0;      //empty($a)返回true      if(empty($a)){         echo "判断结果是空"      }     //isset($a)返回true      if(isset($a)){         echo "判断结果不是空"      }    ?>

SyntaxHighlighter.highlight();

php经典算法集锦

本文实例讲述了php几个经典算法。分享给大家供大家参考,具体如下:

有5个人偷了一堆苹果,准备在第二天分赃。晚上,有一人遛出来,把所有菜果分成5份,但是多了一个,顺手把这个扔给树上的猴了,自己先拿1/5藏了。没想到其他四人也都是这么想的,都如第一个人一样分成5份把多的那一个扔给了猴,偷走了1/5。第二天,大家分赃,也是分成5份多一个扔给猴了。最后一人分了一份。问:共有多少苹果?

for ($i = 1; ; $i++){  if ($i%5 == 1) {    //第一个人取五分之一,还剩$t    $t = $i - round($i/5) - 1;    if($t % 5 == 1)    {      //第二个人取五分之一,还剩$r      $r = $t - round($t/5) - 1;      if($r % 5 == 1)      {        //第三个人取五分之一,还剩$s        $s = $r - round($r/5) - 1;        if($s % 5 == 1)        {          //第四个人取五分之一,还剩$x          $x = $s - round($s/5) - 1;          if($x % 5 == 1)          {            //第五个人取五分之一,还剩$y            $y = $x - round($x/5) - 1;            if ($y % 5 == 1) {              echo $i;              break;            }          }        }      }    }  }}

一群猴子排成一圈,按1,2,…,n依次编号。然后从第1只开始数,数到第m只,把它踢出圈,从它后面再开始数,再数到第m只,在把它踢出去…,如此不停的进行下去,直到最后只剩下一只猴子为止,那只猴子就叫做大王。要求编程模拟此过程,输入m、n, 输出最后那个大王的编号。

function king($n, $m){  $monkeys = range(1, $n);  $i=0;  $k=$n;  while (count($monkeys)>1) {    if(($i+1)%$m==0) {      unset($monkeys[$i]);    } else {      array_push($monkeys,$monkeys[$i]);      unset($monkeys[$i]);    }    $i++;  }  return current($monkeys);}$a = king(5, 2);var_dump($a);

汉诺塔(又称河内塔)问题是印度的一个古老的传说。开天辟地的神勃拉玛在一个庙里留下了三根金刚石的棒,第一根上面套着64个圆的金片,最大的一个在底下,其余一个比一个小,依次叠上去,庙里的众僧不倦地把它们一个个地从这根棒搬到另一根棒上,规定可利用中间的一根棒作为帮助,但每次只能搬一个,而且大的不能放在小的上面。解答结果请自己运行计算,程序见尾部。面对庞大的数字(移动圆片的次数)18446744073709551615,看来,众僧们耗尽毕生精力也不可能完成金片的移动。

后来,这个传说就演变为汉诺塔游戏:

1.有三根杆子A,B,C。A杆上有若干碟子
2.每次移动一块碟子,小的只能叠在大的上面
3.把所有碟子从A杆全部移到C杆上

经过研究发现,汉诺塔的破解很简单,就是按照移动规则向一个方向移动金片:
如3阶汉诺塔的移动:A→C,A→B,C→B,A→C,B→A,B→C,A→C

此外,汉诺塔问题也是程序设计中的经典递归问题。

function hanoi($n,$x,$y,$z){  if($n==1){    echo 'move disk 1 from '.$x.' to '.$z."/n";  }else{    hanoi($n-1,$x,$z,$y);    echo 'move disk '.$n.' from '.$x.' to '.$z."/n";    hanoi($n-1,$y,$x,$z);  }   }hanoi(3,'A','B','C');

使用PHP描述冒泡排序和快速排序算法,对象可以是一个数组

//对数组冒泡排序function bubble_sort($array){  $count = count($array);  if ($count <= 0)     return false;    for($i=0; $i<$count; $i++){      for($j=$count-1; $j>$i; $j){      if ($array[$j] < $array[$j-1]){        $tmp = $array[$j];        $array[$j] = $array[$j-1];        $array[$j-1] = $tmp;    }  }}return $array;}function quick_sort($array) {  if (count($array) <= 1) return $array;  $key = $array[0];  $left_arr = array();  $right_arr = array();  for ($i=1; $i<count($array); $i++){  if ($array[$i] <= $key)    $left_arr[] = $array[$i];  else    $right_arr[] = $array[$i];  }  $left_arr = quick_sort($left_arr);  $right_arr = quick_sort($right_arr);  return array_merge($left_arr, array($key), $right_arr);}

使用PHP描述顺序查找和二分查找算法,顺序查找必须考虑效率,对象可以是一个有序数组

//使用二分查找数组中某个元素function bin_sch($array, $low, $high, $k){  if ($low <= $high){    $mid = intval(($low+$high)/2);    if ($array[$mid] == $k){      return $mid;    }elseif ($k < $array[$mid]){    return bin_sch($array, $low, $mid-1, $k);  }else{    return bin_sch($array, $mid+1, $high, $k);  }  }  return -1;}
function array_sort($arr, $keys, $order=0) {  if (!is_array($arr)) {    return false;  }  $keysvalue = array();  foreach($arr as $key => $val) {    $keysvalue[$key] = $val[$keys];  }  if($order == 0){    asort($keysvalue);  }else {    arsort($keysvalue);  }  reset($keysvalue);  foreach($keysvalue as $key => $vals) {    $keysort[$key] = $key;  }  $new_array = array();  foreach($keysort as $key => $val) {    $new_array[$key] = $arr[$val];  }  return $new_array;}

SyntaxHighlighter.highlight();

jquery实现手风琴效果

本文实例讲述了jquery实现手风琴效果的代码。分享给大家供大家参考。具体如下:

效果过程就是当鼠标覆盖图片时,这张图片的宽度变大,其他兄弟图片宽度变小,效果如下:

具体代码如下

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><title>jQuery横向手风琴图片展示动画DEMO演示</title><link href="css/style.css" type="text/css" rel="stylesheet"/><script type="text/javascript"" width=100% src="js/jquery.min.js"></script></head><body><!--手风琴效果--><div class="flash4"><ul><li class="first"><div class="imgTop"><img" width=100% src="images/ruili_img1.jpg" width="538" height="405" alt=""class="tm"/></div><div class="imgCen">给你15分钟做“对”的时尚人</div><div class="imgBot"><a href=""><p class="bt_1">服饰</p><p class="bt_2"><span>封面明星故事</span><span>2015春夏趋势</span><span>我爱海淘</span></p></a></div></li><li><div class="imgTop"><img" width=100% src="images/ruili_img2.jpg" width="538" height="405" alt=""/></div><div class="imgCen">蒂芙尼为你吟唱一曲自然颂</div><div class="imgBot"><a href=""><p class="bt_1">服饰</p><p class="bt_2"><span>封面明星故事</span><span>2015春夏趋势</span><span>我爱海淘</span></p></a></div></li><li><div class="imgTop"><img" width=100% src="images/ruili_img3.jpg" width="538" height="405" alt=""/></div><div class="imgCen">瑞丽・妆线上精品轻杂志</div><div class="imgBot"><a href=""><p class="bt_1">服饰</p><p class="bt_2"><span>封面明星故事</span><span>2015春夏趋势</span><span>我爱海淘</span></p></a></div></li><li><div class="imgTop"><img" width=100% src="images/ruili_img4.jpg" width="538" height="405" alt=""/></div><div class="imgCen">《ar》刘海造型女孩只需这样即刻焕然一新</div><div class="imgBot"><a href=""><p class="bt_1">服饰</p><p class="bt_2"><span>封面明星故事</span><span>2015春夏趋势</span><span>我爱海淘</span></p></a></div></li><li><div class="imgTop"><img" width=100% src="images/ruili_img5.jpg" width="538" height="405" alt=""/></div><div class="imgCen">电影×大明星见证传奇从戛纳诞生</div><div class="imgBot"><a href=""><p class="bt_1">服饰</p><p class="bt_2"><span>封面明星故事</span><span>2015春夏趋势</span><span>我爱海淘</span></p></a></div></li><li><div class="imgTop"><img" width=100% src="images/ruili_img6.jpg" width="538" height="405" alt=""/></div><div class="imgCen">重返20岁试用周</div><div class="imgBot"><a href=""><p class="bt_1">服饰</p><p class="bt_2"><span>封面明星故事</span><span>2015春夏趋势</span><span>我爱海淘</span></p></a></div></li><li class="last"><div class="imgTop"><img" width=100% src="images/ruili_img7.jpg" width="538" height="405" alt=""/></div><div class="imgCen">玩美女孩盖天天阳光女神进阶攻略</div><div class="imgBot"><a href=""><p class="bt_1">服饰</p><p class="bt_2"><span>封面明星故事</span><span>2015春夏趋势</span><span>我爱海淘</span></p></a></div></li></ul></div><!--手风琴结束--><script" width=100% src="js/script.js" type="text/javascript"></script><div style="text-align:center;clear:both;"></div></body></html>

 CSS代码:

@charset"utf-8";*{margin:0px;padding:0px;font-family:"微软雅黑";font-size:12px; text-decoration:none;list-style-type:none;}img{border:0px;}/*开始*/.flash4{width:1180px;height:450px;margin:0pxauto;margin-bottom:20px;position:relative;}.flash4 ul li{width:106px;height:450px;border-left:1px solid #000;position:relative;overflow:hidden;float:left;}.flash4 ul li .imgTop img{opacity:0.4;}.flash4 ul li .imgTop img.tm{opacity:1;}.flash4 ul li .imgCon{width:538px;height:405px;}.flash4 ul li .imgCen{width:538px;height:50px;background:rgba(0,0,0,0.5);color:#fff;font-size:20px;line-height:50px;position:absolute;left:0px;bottom:45px;text-indent:20px;display:none;}.flash4 ul li .imgBot{width:538px;height:45px;background:#222;}.flash4 ul li .imgBot p.bt_1{width:80px;line-height:45px;font-size:16px;color:#fff;text-indent:20px;float:left;}.flash4 ul li .imgBot p.bt_2{width:458px;height:45px;line-height:45px;float:left;display:none;}.flash4 ul li .imgBot p.bt_2 span{font-size:14px;color:#fff;padding-right:30px;background:url(../images/part2_icon.png)no-repeat left center;padding-left:10px;}.flash4 ul li.first{width:538px;}.flash4 ul li.last{position:absolute;right:0px;bottom:0px;}

jQuery代码:

//手风琴动画效果var index7 =0;//定义变量,赋值为0;$(".flash4 ul li").mouseenter(function(){index7 = $(this).index();$(this).stop().animate({width:538},500).siblings("li").stop().animate({width:106},500);$(".imgCen").eq(index7).css("display","block").siblings(".imgCen").css("display","none");$("p.bt_2").eq(index7).css("display","block").siblings("p.bt_2").css("display","none");$(".imgTop img").eq(index7).addClass("tm").siblings(".imgTop img").removeClass("tm");});$(".flash4 ul li").mouseleave(function(){$(this).eq(index7).stop().animate({width:538},500);$(".imgCen").css("display","none");$("p.bt_2").css("display","none");});

SyntaxHighlighter.highlight();

yii添删改查实例

一、数据访问对象 (DAO)

Yii

DAO 基于 PHP Data Objects (PDO) 构建。它是一个为众多流行的DBMS提供统一数据访问的扩展,这些 DBMS 包括

MySQL, PostgreSQL 等等。因此,要使用 Yii DAO,PDO 扩展和特定的 PDO 数据库驱动(例如 PDO_MYSQL)

必须安装。

Yii DAO 主要包含如下四个类:

CDbConnection: 代表一个数据库连接。

CDbCommand: 代表一条通过数据库执行的 SQL 语句。

CDbDataReader: 代表一个只向前移动的,来自一个查询结果集中的行的流。

CDbTransaction: 代表一个数据库事务。

1、建立数据库连接

要建立一个数据库连接,创建一个 CDbConnection

实例并将其激活。连接到数据库需要一个数据源的名字(DSN)以指定连接信息。用户名和密码也可能会用到。当连接到数据库的过程中发生错误时

(例如,错误的 DSN 或无效的用户名/密码),将会抛出一个异常。

$connection=new CDbConnection($dsn,$username,$password);// 建立连接。你可以使用 try...catch 捕获可能抛出的异常$connection->active=true;......$connection->active=false; // 关闭连接

DSN 的格式取决于所使用的 PDO 数据库驱动。总体来说, DSN 要含有 PDO 驱动的名字,跟上一个冒号,再跟上驱动特定的连接语法。可查阅 PDO 文档 获取更多信息。下面是一个常用DSN格式的列表。

* SQLite: sqlite:/path/to/dbfile

* MySQL: mysql:host=localhost;dbname=testdb

* PostgreSQL: pgsql:host=localhost;port=5432;dbname=testdb

* SQL Server: mssql:host=localhost;dbname=testdb

* Oracle: oci:dbname=//localhost:1521/testdb

由于 CDbConnection 继承自 CApplicationComponent,我们也可以将其作为一个 应用组件 使用。要这样做的话,请在 应用配置 中配置一个 db (或其他名字)应用组件如下:

array(......'components'=>array(......'db'=>array('class'=>'CDbConnection','connectionString'=>'mysql:host=localhost;dbname=testdb','username'=>'root','password'=>'password','emulatePrepare'=>true, // needed by some MySQL installations),),)

然后我们就可以通过 Yii::app()->db 访问数据库连接了。它已经被自动激活了,除非我们特意配置了 CDbConnection::autoConnect 为 false。通过这种方式,这个单独的DB连接就可以在我们代码中的很多地方共享。

2、执行SQL语句

数据库连接建立后,SQL 语句就可以通过使用 CDbCommand  执行了。你可以通过使用指定的SQL语句作为参数调用 CDbConnection::createCommand() 创建一个 CDbCommand 实例。

$connection=Yii::app()->db;  // 假设你已经建立了一个 "db" 连接// 如果没有,你可能需要显式建立一个连接:// $connection=new CDbConnection($dsn,$username,$password);$command=$connection->createCommand($sql);// 如果需要,此 SQL 语句可通过如下方式修改:// $command->text=$newSQL;

一条 SQL 语句会通过 CDbCommand 以如下两种方式被执行:

execute(): 执行一个无查询 (non-query)SQL语句,例如 INSERT, UPDATE 和 DELETE 。如果成功,它将返回此执行所影响的行数。

query(): 执行一条会返回若干行数据的 SQL 语句,例如 SELECT。如果成功,它将返回一个 CDbDataReader 实例,通过此实例可以遍历数据的结果行。为简便起见,(Yii)还实现了一系列 queryXXX() 方法以直接返回查询结果。

执行 SQL 语句时如果发生错误,将会抛出一个异常。

$rowCount=$command->execute();  // 执行无查询SQL$dataReader=$command->query();  // 执行一个SQL查询$rows=$command->queryAll();   // 查询并返回结果中的所有行$row=$command->queryRow();    // 查询并返回结果中的第一行$column=$command->queryColumn(); // 查询并返回结果中的第一列$value=$command->queryScalar(); // 查询并返回结果中第一行的第一个字段

3、获取查询结果

在CDbCommand::query()  生成 CDbDataReader  实例之后,你可以通过重复调用

CDbDataReader::read()  获取结果中的行。你也可以在 PHP 的 foreach 语言结构中使用

CDbDataReader  一行行检索数据。

$dataReader=$command->query();// 重复调用 read() 直到它返回 falsewhile(($row=$dataReader->read())!==false) { ... }// 使用 foreach 遍历数据中的每一行foreach($dataReader as $row) { ... }// 一次性提取所有行到一个数组$rows=$dataReader->readAll();

注意: 不同于query(), 所有的queryXXX()方法会直接返回数据。例如,queryRow()会返回代表查询结果第一行的一个数组。

4、使用事务

事务,在 Yii 中表现为 CDbTransaction  实例,可能会在下面的情况中启动:

* 开始事务.

* 一个个执行查询。任何对数据库的更新对外界不可见。

* 提交事务。如果事务成功,更新变为可见。

* 如果查询中的一个失败,整个事务回滚。

上述工作流可以通过如下代码实现:

$transaction=$connection->beginTransaction();try{$connection->createCommand($sql1)->execute();$connection->createCommand($sql2)->execute();//.... other SQL executions$transaction->commit();}catch(Exception $e) // 如果有一条查询失败,则会抛出异常{$transaction->rollBack();}

5、绑定参数

要避免 SQL 注入攻击 并提高重复执行的 SQL 语句的效率,你可以 “准备(prepare)”一条含有可选参数占位符的 SQL 语句,在参数绑定时,这些占位符将被替换为实际的参数。

参数占位符可以是命名的 (表现为一个唯一的标记) 或未命名的 (表现为一个问号)。调用 CDbCommand::bindParam() 或

CDbCommand::bindValue()

以使用实际参数替换这些占位符。这些参数不需要使用引号引起来:底层的数据库驱动会为你搞定这个。参数绑定必须在 SQL 语句执行之前完成。

// 一条带有两个占位符 ":username" 和 ":email"的 SQL$sql="INSERT INTO tbl_user (username, email) VALUES(:username,:email)";$command=$connection->createCommand($sql);// 用实际的用户名替换占位符 ":username"$command->bindParam(":username",$username,PDO::PARAM_STR);// 用实际的 Email 替换占位符 ":email"$command->bindParam(":email",$email,PDO::PARAM_STR);$command->execute();// 使用新的参数集插入另一行$command->bindParam(":username",$username2,PDO::PARAM_STR);$command->bindParam(":email",$email2,PDO::PARAM_STR);$command->execute();

方法 bindParam() 和 bindValue() 非常相似。唯一的区别就是前者使用一个PHP变量绑定参数,而后者使用一个值。对于那些内存中的大数据块参数,处于性能的考虑,应优先使用前者。

6、绑定列

当获取查询结果时,你也可以使用PHP变量绑定列。这样在每次获取查询结果中的一行时就会自动使用最新的值填充。

$sql="SELECT username, email FROM tbl_user";$dataReader=$connection->createCommand($sql)->query();// 使用 $username 变量绑定第一列 (username)$dataReader->bindColumn(1,$username);// 使用 $email 变量绑定第二列 (email)$dataReader->bindColumn(2,$email);while($dataReader->read()!==false){// $username 和 $email 含有当前行中的 username 和 email}

7、使用表前缀

要使用表前缀,配置 CDbConnection::tablePrefix  属性为所希望的表前缀。然后,在 SQL 语句中使用

{{TableName}} 代表表的名字,其中的 TableName  是指不带前缀的表名。例如,如果数据库含有一个名为 tbl_user

的表,而 tbl_ 被配置为表前缀,那我们就可以使用如下代码执行用户相关的查询:

$sql='SELECT * FROM {{user}}';$users=$connection->createCommand($sql)->queryAll();

二、Active Record

虽然Yii DAO可以处理几乎任何数据库相关的任务,但很可能我们会花费 90% 的时间以编写一些执行普通 CRUD(create, read,

update 和 delete)操作的SQL语句。而且我们的代码中混杂了SQL语句时也会变得难以维护。要解决这些问题,我们可以使用Active Record。

Active Record(AR)是一个流行的对象-关系映射(ORM)技术。每个 AR

类代表一个数据表(或视图),数据表(或视图)的列在 AR 类中体现为类的属性,一个AR实例则表示表中的一行。常见的 CRUD 操作作为 AR

的方法实现。因此,我们可以以一种更加面向对象的方式访问数据。例如,我们可以使用以下代码向tbl_post表中插入一个新行。

$post=new Post;$post->title='sample post';$post->content='post body content';$post->save();

注意: AR并非要解决所有数据库相关的任务。它的最佳应用是模型化数据表为PHP结构和执行不包含复杂SQL语句的查询。 对于复杂查询的场景,应使用Yii DAO。

1、建立数据库连接

AR依靠一个数据库连接以执行数据库相关的操作。默认情况下,它假定db应用组件提供了所需的CDbConnection数据库连接实例。如下应用配置提供了一个例子:

return array('components'=>array('db'=>array('class'=>'system.db.CDbConnection','connectionString'=>'sqlite:path/to/dbfile',// 开启表结构缓存(schema caching)提高性能// 'schemaCachingDuration'=>3600,),),);

提示: 由于Active Record依靠表的元数据(metadata)测定列的信息,读取元数据并解析需要时间。

如果你数据库的表结构很少改动,你应该通过配置CDbConnection::schemaCachingDuration属性的值为一个大于零的值开启表结构缓存。

如果你想使用一个不是db的应用组件,或者如果你想使用AR处理多个数据库,你应该覆盖CActiveRecord::getDbConnection()。CActiveRecord类是所有AR类的基类。

提示: 通过AR使用多个数据库有两种方式。如果数据库的结构不同,你可以创建不同的AR基类实现不同的getDbConnection()。否则,动态改变静态变量CActiveRecord::db是一个好主意。

2、定义AR类

要访问一个数据表,我们首先需要通过集成CActiveRecord定义一个AR类。每个AR类代表一个单独的数据表,一个AR实例则代表那个表中的一行。

如下例子演示了代表tbl_post表的AR类的最简代码:

class Post extends CActiveRecord{public static function model($className=__CLASS__){return parent::model($className);}public function tableName(){return 'tbl_post';}}

提示: 由于 AR 类经常在多处被引用,我们可以导入包含 AR 类的整个目录,而不是一个个导入。 例如,如果我们所有的 AR 类文件都在 protected/models 目录中,我们可以配置应用如下:

return array('import'=>array('application.models.*',),);

默认情况下,AR类的名字和数据表的名字相同。如果不同,请覆盖tableName()方法。

要使用表前缀功能,AR类的 tableName() 方法可以通过如下方式覆盖

public function tableName(){return '{{post}}';}

这就是说,我们将没有前缀的表名用双大括号括起来,这样Yii就能自动添加前缀,从而返回完整的表名。

数据表行中列的值可以作为相应AR实例的属性访问。例如,如下代码设置了 title 列 (属性):

$post=new Post;$post->title='a sample post';

虽然我们从未在Post类中显式定义属性title,我们还是可以通过上述代码访问。这是因为title是tbl_post表中的一个

列,CActiveRecord通过PHP的__get()魔术方法使其成为一个可访问的属性。如果我们尝试以同样的方式访问一个不存在的列,将会抛出一个异常。

如果一个表没有主键,则必须在相应的AR类中通过如下方式覆盖 primaryKey() 方法指定哪一列或哪几列作为主键。

public function primaryKey(){return 'id';// 对于复合主键,要返回一个类似如下的数组// return array('pk1', 'pk2');}

3、创建记录

要向数据表中插入新行,我们要创建一个相应 AR 类的实例,设置其与表的列相关的属性,然后调用 save()  方法完成插入:

$post=new Post;$post->title='sample post';$post->content='content for the sample post';$post->create_time=time();$post->save();

如果表的主键是自增的,在插入完成后,AR实例将包含一个更新的主键。在上面的例子中,id属性将反映出新插入帖子的主键值,即使我们从未显式地改变它。

如果一个列在表结构中使用了静态默认值(例如一个字符串,一个数字)定义。则AR实例中相应的属性将在此实例创建时自动含有此默认值。改变此默认值的一个方式就是在AR类中显示定义此属性:

class Post extends CActiveRecord{public $title='please enter a title';......}$post=new Post;echo $post->title; // 这儿将显示: please enter a title

记录在保存(插入或更新)到数据库之前,其属性可以赋值为 CDbExpression 类型。例如,为保存一个由MySQL的 NOW() 函数返回的时间戳,我们可以使用如下代码:

$post=new Post;$post->create_time=new CDbExpression('NOW()'); //CDbExpression类就是计算数据库表达式的值// $post->create_time='NOW()'; 不会起作用,因为// 'NOW()' 将会被作为一个字符串处理。$post->save();

提示: 由于AR允许我们无需写一大堆SQL语句就能执行数据库操作,我们经常会想知道AR在背后到底执行了什么SQL语句。这可以通过开启Yii的日志功能实现。例如,我们在应用配置中开启了CWebLogRoute,我们将会在每个网页的最后看到执行过的SQL语句。

我们也可以在应用配置中设置CDbConnection::enableParamLogging为true,这样绑定在SQL语句中的参数值也会被记录。

4、读取记录

要读取数据表中的数据,我们可以通过如下方式调用 find 系列方法中的一种:

// 查找满足指定条件的结果中的第一行$post=Post::model()->find($condition,$params);// 查找具有指定主键值的那一行$post=Post::model()->findByPk($postID,$condition,$params);// 查找具有指定属性值的行$post=Post::model()->findByAttributes($attributes,$condition,$params);// 通过指定的SQL语句查找结果中的第一行$post=Post::model()->findBySql($sql,$params);

如上所示,我们通过 Post::model() 调用 find 方法。请记住,静态方法 model() 是每个AR类所必须的。此方法返回在对象上下文中的一个用于访问类级别方法(类似于静态类方法的东西)的AR实例。

如果find方法找到了一个满足查询条件的行,它将返回一个Post实例,实例的属性含有数据表行中相应列的值。然后我们就可以像读取普通对象的属性那样读取载入的值,例如 echo $post->title;。

如果使用给定的查询条件在数据库中没有找到任何东西, find 方法将返回null。

调用find时,我们使用 $condition 和 $params 指定查询条件。此处 $condition 可以是 SQL 语句中的 WHERE 字符串,$params 则是一个参数数组,其中的值应绑定到 $condation 中的占位符。例如:

// 查找 postID=10 的那一行$post=Post::model()->find('postID=:postID', array(':postID'=>10));

注意: 在上面的例子中,我们可能需要在特定的 DBMS 中将 postID 列的引用进行转义。 例如,如果我们使用 PostgreSQL,我们必须将此表达式写为 “postID”=:postID,因为 PostgreSQL 在默认情况下对列名大小写不敏感。

我们也可以使用 $condition 指定更复杂的查询条件。不使用字符串,我们可以让 $condition 成为一个 CDbCriteria  的实例,它允许我们指定不限于 WHERE 的条件。例如:

$criteria=new CDbCriteria;$criteria->select='title'; // 只选择 'title' 列$criteria->condition='postID=:postID';$criteria->params=array(':postID'=>10);$post=Post::model()->find($criteria); // $params 不需要了

注意,当使用 CDbCriteria 作为查询条件时,$params 参数不再需要了,因为它可以在 CDbCriteria 中指定,就像上面那样。

一种替代 CDbCriteria 的方法是给 find 方法传递一个数组。数组的键和值各自对应标准(criterion)的属性名和值,上面的例子可以重写为如下:

$post=Post::model()->find(array('select'=>'title','condition'=>'postID=:postID','params'=>array(':postID'=>10),));

当一个查询条件是关于按指定的值匹配几个列时,我们可以使用 findByAttributes()。我们使 $attributes

参数是一个以列名做索引的值的数组。在一些框架中,此任务可以通过调用类似 findByNameAndTitle

的方法实现。虽然此方法看起来很诱人, 但它常常引起混淆,冲突和比如列名大小写敏感的问题。

当有多行数据匹配指定的查询条件时,我们可以通过下面的 findAll 方法将他们全部带回。每个都有其各自的 find 方法,就像我们已经讲过的那样。

// 查找满足指定条件的所有行$posts=Post::model()->findAll($condition,$params);// 查找带有指定主键的所有行$posts=Post::model()->findAllByPk($postIDs,$condition,$params);// 查找带有指定属性值的所有行$posts=Post::model()->findAllByAttributes($attributes,$condition,$params);// 通过指定的SQL语句查找所有行$posts=Post::model()->findAllBySql($sql,$params);

如果没有任何东西符合查询条件,findAll 将返回一个空数组。这跟 find 不同,find 会在没有找到什么东西时返回 null。

除了上面讲述的 find 和 findAll 方法,为了方便,(Yii)还提供了如下方法:

// 获取满足指定条件的行数$n=Post::model()->count($condition,$params);// 通过指定的 SQL 获取结果行数$n=Post::model()->countBySql($sql,$params);// 检查是否至少有一行复合指定的条件$exists=Post::model()->exists($condition,$params);

5、更新记录

在 AR 实例填充了列的值之后,我们可以改变它们并把它们存回数据表。

$post=Post::model()->findByPk(10);$post->title='new post title';$post->save(); // 将更改保存到数据库

正如我们可以看到的,我们使用同样的 save() 方法执行插入和更新操作。如果一个 AR 实例是使用 new 操作符创建的,调用save()

将会向数据表中插入一行新数据;如果 AR 实例是某个 find 或 findAll 方法的结果,调用 save()

将更新表中现有的行。实际上,我们是使用 CActiveRecord::isNewRecord 说明一个 AR 实例是不是新的。

直接更新数据表中的一行或多行而不首先载入也是可行的。 AR 提供了如下方便的类级别方法实现此目的:

// 更新符合指定条件的行Post::model()->updateAll($attributes,$condition,$params);// 更新符合指定条件和主键的行Post::model()->updateByPk($pk,$attributes,$condition,$params);// 更新满足指定条件的行的计数列Post::model()->updateCounters($counters,$condition,$params);

在上面的代码中, $attributes 是一个含有以 列名作索引的列值的数组; $counters 是一个由列名索引的可增加的值的数组;$condition 和 $params 在前面的段落中已有描述。

6、删除记录

如果一个 AR 实例被一行数据填充,我们也可以删除此行数据。

$post=Post::model()->findByPk(10); // 假设有一个帖子,其 ID 为 10$post->delete(); // 从数据表中删除此行

注意,删除之后, AR 实例仍然不变,但数据表中相应的行已经没了。

使用下面的类级别代码,可以无需首先加载行就可以删除它。

// 删除符合指定条件的行Post::model()->deleteAll($condition,$params);// 删除符合指定条件和主键的行Post::model()->deleteByPk($pk,$condition,$params);

7、数据验证

当插入或更新一行时,我们常常需要检查列的值是否符合相应的规则。如果列的值是由最终用户提供的,这一点就更加重要。总体来说,我们永远不能相信任何来自客户端的数据。

当调用 save() 时, AR 会自动执行数据验证。验证是基于在 AR 类的 rules() 方法中指定的规则进行的。关于验证规则的更多详情,请参考 声明验证规则 一节。下面是保存记录时所需的典型的工作流。

if($post->save()){// 数据有效且成功插入/更新}else{// 数据无效,调用 getErrors() 提取错误信息}

当要插入或更新的数据由最终用户在一个 HTML 表单中提交时,我们需要将其赋给相应的 AR 属性。我们可以通过类似如下的方式实现:

$post->title=$_POST['title'];$post->content=$_POST['content'];$post->save();

如果有很多列,我们可以看到一个用于这种复制的很长的列表。这可以通过使用如下所示的 attributes 属性简化操作。更多信息可以在 安全的特性赋值 一节和 创建动作 一节找到。

// 假设 $_POST['Post'] 是一个以列名索引列值为值的数组$post->attributes=$_POST['Post'];$post->save();

8、对比记录

类似于表记录,AR实例由其主键值来识别。因此,要对比两个AR实例,假设它们属于相同的AR类, 我们只需要对比它们的主键值。然而,一个更简单的方式是调用 CActiveRecord::equals()。

不同于AR在其他框架的执行, Yii在其 AR 中支持多个主键. 一个复合主键由两个或更多字段构成。相应地,主键值在Yii中表现为一个数组。primaryKey属性给出了一个 AR 实例的主键值。

9、自定义

CActiveRecord  提供了几个占位符方法,它们可以在子类中被覆盖以自定义其工作流。

beforeva lidate 和 afterValidate:这两个将在验证数据有效性之前和之后被调用。

beforeSave 和 afterSave: 这两个将在保存 AR 实例之前和之后被调用。

beforeDelete 和 afterDelete: 这两个将在一个 AR 实例被删除之前和之后被调用。

afterConstruct: 这个将在每个使用 new 操作符创建 AR 实例后被调用。

beforeFind: 这个将在一个 AR 查找器被用于执行查询(例如 find(), findAll())之前被调用。

afterFind: 这个将在每个 AR 实例作为一个查询结果创建时被调用。

10、使用AR处理事务

每个 AR 实例都含有一个属性名叫 dbConnection  ,是一个 CDbConnection 的实例,这样我们可以在需要时配合 AR 使用由 Yii DAO 提供的 事务 功能:

$model=Post::model();$transaction=$model->dbConnection->beginTransaction();try{// 查找和保存是可能由另一个请求干预的两个步骤// 这样我们使用一个事务以确保其一致性和完整性$post=$model->findByPk(10);$post->title='new post title';$post->save();$transaction->commit();}catch(Exception $e){$transaction->rollBack();}

11、命名范围

命名范围(named scope)表示一个命名的(named)查询规则,它可以和其他命名范围联合使用并应用于Active Record查询。

命名范围主要是在 CActiveRecord::scopes() 方法中以名字-规则对的方式声明。如下代码在Post模型类中声明了两个命名范围, published 和 recently。

class Post extends CActiveRecord{......public function scopes(){return array('published'=>array('condition'=>'status=1',),'recently'=>array('order'=>'create_time DESC','limit'=>5,),);}}

每个命名范围声明为一个可用于初始化 CDbCriteria 实例的数组。例如,recently 命名范围指定 order 属性为 create_time DESC , limit 属性为 5。他们翻译为查询规则后就会返回最近的5篇帖子。

命名范围多用作 find 方法调用的修改器。几个命名范围可以链到一起形成一个更有约束性的查询结果集。例如,要找到最近发布的帖子,我们可以使用如下代码:

$posts=Post::model()->published()->recently()->findAll();

总体来说,命名范围必须出现在一个 find 方法调用的左边。它们中的每一个都提供一个查询规则,并联合到其他规则,包括传递给 find 方法调用的那一个。最终结果就像给一个查询添加了一系列过滤器。

命名范围也可用于 update 和 delete 方法。例如,如下代码将删除所有最近发布的帖子:

Post::model()->published()->recently()->delete();

注意: 命名范围只能用于类级别方法。也就是说,此方法必须使用 ClassName::model() 调用。

12、参数化的命名范围

命名范围可以参数化。例如,我们想自定义 recently 命名范围中指定的帖子数量,要实现此目的,不是在CActiveRecord::scopes  方法中声明命名范围,而是需要定义一个名字和此命名范围的名字相同的方法:

public function recently($limit=5){$this->getDbCriteria()->mergeWith(array('order'=>'create_time DESC','limit'=>$limit,));return $this;}

然后,我们就可以使用如下语句获取3条最近发布的帖子。

$posts=Post::model()->published()->recently(3)->findAll();

上面的代码中,如果我们没有提供参数 3,我们将默认获取 5 条最近发布的帖子。

13、默认的命名范围

模型类可以有一个默认命名范围,它将应用于所有 (包括相关的那些)

关于此模型的查询。例如,一个支持多种语言的网站可能只想显示当前用户所指定的语言的内容。因为可能会有很多关于此网站内容的查询,我们可以定义一个默认

的命名范围以解决此问题。为实现此目的,我们覆盖 CActiveRecord::defaultScope  方法如下:

class Content extends CActiveRecord{public function defaultScope(){return array('condition'=>"language='".Yii::app()->language."'",);}}

现在,如果下面的方法被调用,将会自动使用上面定义的查询规则:

$contents=Content::model()->findAll();

注意,默认的命名范围只会应用于 SELECT 查询。INSERT, UPDATE 和 DELETE 查询将被忽略。

三、Relational Active Record(关联查询)

我们已经知道如何通过Active Record(AR)从单个数据表中取得数据了,在这一节中,我们将要介绍如何使用AR来连接关联的数据表获取数据。

在使用关联AR之前,首先要在数据库中建立关联的数据表之间的主键-外键关联,AR需要通过分析数据库中的定义数据表关联的元信息,来决定如何连接数据。

1、如何声明关联

在使用AR进行关联查询之前,我们需要告诉AR各个AR类之间有怎样的关联。

AR类之间的关联直接反映着数据库中这个类所代表的数据表之间的关联。从关系数据库的角度来说,两个数据表A,B之间可能的关联有三种:一对多,一对一,多对多。而在AR中,关联有以下四种:

BELONGS_TO: 如果数据表A和B的关系是一对多,那我们就说B属于A(B belongs to A)。

HAS_MANY: 如果数据表A和B的关系是多对一,那我们就说B有多个A(B has many A)。

HAS_ONE: 这是‘HAS_MANY’关系中的一个特例,当A最多有一个的时候,我们说B有一个A (B has one A)。

MANY_MANY:

这个相当于关系数据库中的多对多关系。因为绝大多数关系数据库并不直接支持多对多的关系,这时通常都需要一个单独的关联表,把多对多的关系分解为两个一对

多的关系。用AR的方式去理解的话,我们可以认为 MANY_MANY关系是由BELONGS_TO和HAS_MANY组成的。

在AR中声明关联,是通过覆盖(Override)父类CActiveRecord中的relations()方法来实现的。这个方法返回一个包含了关系定义的数组,数组中的每一组键值代表一个关联:

‘VarName’=>array(‘RelationType’, ‘ClassName’, ‘ForeignKey’, …additional options)

这里的VarName是这个关联的名称;RelationType指定了这个关联的类型,有四个常量代表了四种关联的类型:

self::BELONGS_TO,self::HAS_ONE,self::HAS_MANY和self::MANY_MANY;

ClassName是这个关系关联到的AR类的类名;ForeignKey指定了这个关联是通过哪个外键联系起来的。后面的additional

options可以加入一些额外的设置,后面会做介绍。

下面的代码演示了如何定义User和Post之间的关联。

class Post extends CActiveRecord {public function relations() {return array('author'=>array(self::BELONGS_TO,'User','authorID'),'categories'=>array(self::MANY_MANY,'Category','PostCategory(postID, categoryID)'),);}}class User extends CActiveRecord {public function relations() {return array('posts'=>array(self::HAS_MANY,'Post','authorID'),'profile'=>array(self::HAS_ONE,'Profile','ownerID'),);}}

说明: 有时外键可能由两个或更多字段组成,在这里可以将多个字段名由逗号或空格分隔,

一并写在这里。对于多对多的关系,关联表必须在外键中注明,例如在Post类的categories

关联中,外键就需要写成PostCategory(postID, categoryID)。

在AR类中声明关联时,每个关联会作为一个属性添加到AR类中,属性名就是关联的名称。在进行关联查询时,这些属性就会被设置为关联到的AR类的实例,例如在查询取得一个Post实例时,它的$author属性就是代表Post作者的一个User类的实例。

2、关联查询

进行关联查询最简单的方式就是访问一个关联AR对象的某个关联属性。如果这个属性之前没有被访问过,这时就会启动一个关联查询,通过当前AR对象的主键连接

相关的表,来取得关联对象的值,然后将这些数据保存在对象的属性中。这种方式叫做“延迟加载”,也就是只有等到访问到某个属性时,才会真正到数据库中把这

些关联的数据取出来。下面的例子描述了延迟加载的过程:

// retrieve the post whose ID is 10$post=Post::model()->findByPk(10);// retrieve the post's author: a relational query will be performed here$author=$post->author;

在不同的关联情况下,如果没有查询到结果,其返回的值也不同:BELONGS_TO 和 HAS_ONE 关联,无结果时返回null; HAS_MANY 和 MANY_MANY, 无结果时返回空数组。

延迟加载方法使用非常方便,但在某些情况下并不高效。例如,若我们要取得N个post的作者信息,使用延迟方法将执行N次连接查询。此时我们应当使用所谓的急切加载方法。

急切加载方法检索主要的 AR 实例及其相关的 AR 实例. 这通过使用 with() 方法加上 find 或 findAll 方法完

成。例如,

$posts=Post::model()->with('author')->findAll();

上面的代码将返回一个由 Post 实例组成的数组. 不同于延迟加载方法,每个Post 实例中的author 属性在我们访问此属性之前已经被关联的

User 实例填充。不是为每个post 执行一个连接查询, 急切加载方法在一个单独的连接查询中取出所有的 post 以及它们的author!

我们可以在with()方法中指定多个关联名字。例如, 下面的代码将取回 posts 以及它们的作者和分类:

$posts=Post::model()->with(‘author’,’categories’)->findAll();

我们也可以使用嵌套的急切加载。不使用一个关联名字列表, 我们将关联名字以分层的方式传递到 with() 方法, 如下,

$posts=Post::model()->with('author.profile','author.posts','categories')->findAll();

上面的代码将取回所有的 posts 以及它们的作者和分类。它也将取出每个作者的profile和 posts.

急切加载也可以通过指定 CDbCriteria::with 属性被执行, 如下:

$criteria=new CDbCriteria;$criteria->with=array('author.profile','author.posts','categories',);$posts=Post::model()->findAll($criteria);或$posts=Post::model()->findAll(array('with'=>array('author.profile','author.posts','categories',));

3、关联查询选项

之前我们提到额外的参数可以被指定在关联声明中。这些选项,指定为 name-value 对,被用来定制关联查询。它们被概述如下:

select: 为关联 AR 类查询的字段列表。默认是 ‘*’, 意味着所有字段。查询的字段名字可用别名表达式来消除歧义(例如:COUNT(??.name) AS nameCount)。

condition: WHERE 子语句。默认为空。注意, 列要使用别名引用(例如:??.id=10)。

params: 被绑定到 SQL 语句的参数. 应当为一个由 name-value 对组成的数组()。

on: ON 子语句. 这里指定的条件将使用 and 操作符被追加到连接条件中。此选项中的字段名应被消除歧义。此选项不适用于 MANY_MANY 关联。

order: ORDER BY 子语句。默认为空。注意, 列要使用别名引用(例如:??.age DESC)。

with: 应当和此对象一同载入的子关联对象列表. 注意, 不恰当的使用可能会形成一个无穷的关联循环。

joinType: 此关联的连接类型。默认是 LEFT OUTER JOIN。

aliasToken:列前缀占位符。默认是“??.”。

alias: 关联的数据表的别名。默认是 null, 意味着表的别名和关联的名字相同。

together: 是否关联的数据表被强制与主表和其他表连接。此选项只对于HAS_MANY 和 MANY_MANY 关联有意义。若此选项被设置为 false, ……(此处原文出错!).默认为空。此选项中的字段名以被消除歧义。

having: HAVING 子语句。默认是空。注意, 列要使用别名引用。

index: 返回的数组索引类型。确定返回的数组是关键字索引数组还是数字索引数组。不设置此选项, 将使用数字索引数组。此选项只对于HAS_MANY 和 MANY_MANY 有意义

此外, 下面的选项在延迟加载中对特定关联是可用的:

group: GROUP BY子句。默认为空。注意, 列要使用别名引用(例如:??.age)。 本选项仅应用于HAS_MANY 和 MANY_MANY 关联。

having: HAVING子句。默认为空。注意, 列要使用别名引用(例如:??.age)。本选项仅应用于HAS_MANY 和 MANY_MANY 关联。

limit: 限制查询的行数。本选项不能用于BELONGS_TO关联。

offset: 偏移。本选项不能用于BELONGS_TO关联。

class User extends CActiveRecord{public function relations(){return array('posts'=>array(self::HAS_MANY, 'Post', 'author_id','order'=>'posts.create_time DESC','with'=>'categories'),'profile'=>array(self::HAS_ONE, 'Profile', 'owner_id'),);}}

SyntaxHighlighter.highlight();

jQuery实现简单的文件上传进度条效果

本文实例讲述了jQuery实现文件上传进度条效果的代码。分享给大家供大家参考。具体如下:
运行效果截图如下:

具体代码如下:

<!DOCTYPE html><html><head><meta charset="utf-8"><title>upload</title><link rel="stylesheet" type="text/css" href="upload/upload.css"><script type="text/javascript"" width=100% src="upload/jquery.js"></script></head><body><span class="upload-span">开始上传文件</span><div class="upload-mask"></div><div class="upload-component"><div class="upload-close"><span class="upload-close-span">关闭</span></div><div class="upload-content"><div class="progress"><span class="upload-text"></span><span class="uploaded"></span></div><div class="confirm-cancel"><span class="confirm">确认</span><span class="cancel">取消</span></div></div></div><script type="text/javascript"" width=100% src="upload/upload.js"></script></body></html> 

CSS代码:

.upload-span{display:inline-block;width:120px;height:40px;color:#FFFFFF;text-align: center;line-height:40px;background-color: blue;border:2px solid blue;border-radius:5px;cursor: pointer;letter-spacing:2px;}.upload-mask{position: absolute;top:0;left:0;z-index:9;width:100%;height:100%;background-color: rgba(84,84,84,0.3);display: none;}.upload-component{position: absolute;z-index:99;top:50%;left:50%;margin-left:-120px;margin-top:-60px;width:240px;height:120px;background-color:#FFFFFF;display:none;}.upload-close{position: relative;height:30px;background-color: rgb(234,234,234);}.upload-close span{position: absolute;right:15px;line-height:30px;cursor: pointer;}.upload-content,.confirm-cancel{margin-top:15px;}.progress{position:relative;width:90%;height:22px;margin-left:4.88888%;text-align: center;line-height:22px;border:1px solid #ccc;}.upload-text{position:absolute;z-index:99999;color:red;}.uploaded{position:absolute;left:0;z-index:9999;width:0%;height:100%;background-color: blue;color:#FFFFFF;}.confirm-cancel span{display:inline-block;width:60px;height:30px;line-height:30px;text-align: center;background-color:#ccc;cursor:wait;}.confirm{margin-left:40%;}.cancel{margin-left:10px;} 
$(function (){var $uploadSpan = $('.upload-span');var $uploadMask = $('.upload-mask');var $uploadContent = $('.upload-component');var $closeConfirmCancel = $('.upload-close-span,.confirm,.cancel');var $uploadTextSpan = $('.upload-text');function showMask(){$(".upload-mask,.upload-component").css({display:'block'});progressBar();$uploadSpan.off('click',showMask);}function hiddenMask(){$uploadMask.css({display:'none'});$uploadSpan.on('click',showMask);}function closeConfirmCancel(){$uploadContent.css({display:'none'});$uploadTextSpan.text('').next().css({width:0});hiddenMask();}// 模拟进度function progressBar(){var max =100;var init =0;var uploaded;var test = setInterval(function(){init +=5;uploaded = parseInt(init / max *100)+'%';$uploadTextSpan.text(uploaded).next().css({width:uploaded});if(init ===100){clearInterval(test);$uploadTextSpan.text('上传完成');$('.confirm-cancel span').css({cursor:'pointer'});$('.confirm').css({backgroundColor:'rgb(111,197,293)'});$('.cancel').css({backgroundColor:'rgb(175,194,211)'})$closeConfirmCancel.on('click',closeConfirmCancel);}else{$closeConfirmCancel.off('click',closeConfirmCancel);$('.upload-close-span').on('click',function(){clearInterval(test);closeConfirmCancel();});}},1000);}$uploadSpan.on('click',showMask);})

SyntaxHighlighter.highlight();

Jquery Ajax Error 调试错误的技巧

JQuery使我们在开发Ajax应用程序的时候提高了效率,减少了许多兼容性问题,我们在Ajax项目中,遇到ajax异步获取数据出错怎么办,我们可以通过捕捉error事件来获取出错的信息。

在没给大家介绍正文之前先给分享Jquery中AJAX参数详细列表:

参数名 类型 描述
url String (默认: 当前页地址) 发送请求的地址。
type String (默认: “GET”) 请求方式 (“POST” 或 “GET”), 默认为 “GET”。注意:其它 HTTP 请求方法,如 PUT 和 DELETE 也可以使用,但仅部分浏览器支持。
timeout Number 设置请求超时时间(毫秒)。此设置将覆盖全局设置。
async Boolean (默认: true) 默认设置下,所有请求均为异步请求。如果需要发送同步请求,请将此选项设置为 false。注意,同步请求将锁住浏览器,用户其它操作必须等待请求完成才可以执行。
beforeSend Function 发送请求前可修改 XMLHttpRequest 对象的函数,如添加自定义 HTTP 头。XMLHttpRequest 对象是唯一的参数。

function (XMLHttpRequest) {
this; // the options for this ajax request
}
cache Boolean (默认: true) jQuery 1.2 新功能,设置为 false 将不会从浏览器缓存中加载请求信息。
complete Function 请求完成后回调函数 (请求成功或失败时均调用)。参数: XMLHttpRequest 对象,成功信息字符串。

function (XMLHttpRequest, textStatus) {
this; // the options for this ajax request
}
contentType String (默认: “application/x-www-form-urlencoded”) 发送信息至服务器时内容编码类型。默认值适合大多数应用场合。
data Object,
String
发 送到服务器的数据。将自动转换为请求字符串格式。GET 请求中将附加在 URL 后。查看 processData 选项说明以禁止此自动转换。必须为 Key/Value 格式。如果为数组,jQuery 将自动为不同值对应同一个名称。如 {foo:[“bar1”, “bar2”]} 转换为 ‘&foo=bar1&foo=bar2’。
dataType String

预期服务器返回的数据类型。如果不指定,jQuery 将自动根据 HTTP 包 MIME 信息返回 responseXML 或 responseText,并作为回调函数参数传递,可用值:

“xml”: 返回 XML 文档,可用 jQuery 处理。

“html”: 返回纯文本 HTML 信息;包含 script 元素。

“script”: 返回纯文本 JavaScript 代码。不会自动缓存结果。

“json”: 返回 JSON 数据 。

“jsonp”:  格式。使用  形式调用函数时,如 “myurl?callback=?” jQuery 将自动替换 ? 为正确的函数名,以执行回调函数。

error Function (默认: 自动判断 (xml 或 html)) 请求失败时将调用此方法。这个方法有三个参数:XMLHttpRequest 对象,错误信息,(可能)捕获的错误对象。

function (XMLHttpRequest, textStatus, errorThrown) {
// 通常情况下textStatus和errorThown只有其中一个有值
this; // the options for this ajax request
}
global Boolean (默认: true) 是否触发全局 AJAX 事件。设置为 false 将不会触发全局 AJAX 事件,如 ajaxStart 或 ajaxStop 。可用于控制不同的Ajax事件
ifModified Boolean (默认: false) 仅在服务器数据改变时获取新数据。使用 HTTP 包 Last-Modified 头信息判断。
processData Boolean (默认: true) 默认情况下,发送的数据将被转换为对象(技术上讲并非字符串) 以配合默认内容类型 “application/x-www-form-urlencoded”。如果要发送 DOM 树信息或其它不希望转换的信息,请设置为 false。
success Function 请求成功后回调函数。这个方法有两个参数:服务器返回数据,返回状态

function (data, textStatus) {
// data could be xmlDoc, jsonObj, html, text, etc...
this; // the options for this ajax request
}

jquery中ajax的常用用法类似于:

JScript 代码  

$(document).ready(function() {      jQuery("#clearCac").click(function() {        jQuery.ajax({          url: url,          type: "post",          data: { id: '0' },          dataType: "json",          success: function(msg) {            alert(msg);          },          error: function(XMLHttpRequest, textStatus, errorThrown) {            alert(XMLHttpRequest.status);            alert(XMLHttpRequest.readyState);            alert(textStatus);          },          complete: function(XMLHttpRequest, textStatus) {            this; // 调用本次AJAX请求时传递的options参数          }        });      });    });

当通过ajax异步调用成功时,会调用 success函数 。success函数语法为:

 //请求成功后回调函数。这个方法有两个参数:服务器返回数据,返回状态 function (data, textStatus) {  // data could be xmlDoc, jsonObj, html, text, etc...     this; // the options for this ajax request }当通过ajax异步调用出错时,会调用 error函数 。error函数语法为://(默 认: 自动判断 (xml 或 html)) 请求失败时调用时间。参数有以下三个:XMLHttpRequest 对象、错误信息、(可选)捕获的错误对象。如果发生了错误,错误信息(第二个参数)除了得到null之外,还可能是"timeout", "error", "notmodified" 和 "parsererror"。//textStatus: "timeout", "error", "notmodified" 和 "parsererror"。error:function (XMLHttpRequest, textStatus, errorThrown) { } 

error事件返回的第一个参数XMLHttpRequest:

XMLHttpRequest.readyState: 状态码的意思

0 - (未初始化)还没有调用send()方法
1 - (载入)已调用send()方法,正在发送请求
2 - (载入完成)send()方法执行完成,已经接收到全部响应内容
3 - (交互)正在解析响应内容
4 - (完成)响应内容解析完成,可以在客户端调用了

发送error可能有下面两张引起的,或者其他程序问题,需要我们认真仔细。

SyntaxHighlighter.highlight();

Yii2.0高级框架数据库增删改查的一些操作

yii2.0框架是PHP开发的一个比较高效率的框架,集合了作者的大量心血,下面通过用户为例给大家详解yii2使用中的一些基本的增删改查操作。

User::find()->all();    //返回所有用户数据;
User::findOne($id);   //返回 主键 id=1  的一条数据;
User::find()->where([‘name’ => ‘ttt’])->one();   //返回 [‘name’ => ‘ttt’] 的一条数据;
User::find()->where([‘name’ => ‘ttt’])->all();   //返回 [‘name’ => ‘ttt’] 的所有数据;
User::findBySql(‘SELECT * FROM user’)->all();  //用 sql  语句查询 user 表里面的所有数据;
User::findBySql(‘SELECT * FROM user’)->one();  此方法是用 sql  语句查询 user 表里面的一条数据;
User::find()->andWhere([‘sex’ => ‘女’, ‘age’ => ’18’])->count(‘id’);   //统计符合条件的总条数;
User::find()->one();    //返回一条数据;
User::find()->all();    //返回所有数据;
User::find()->count();    //返回记录的数量;
User::find()->average();   //返回指定列的平均值;
User::find()->min();    //返回指定列的最小值 ;
User::find()->max();   //返回指定列的最大值 ;
User::find()->scalar();    //返回值的第一行第一列的查询结果;
User::find()->column();    //返回查询结果中的第一列的值;
User::find()->exists();    //返回一个值指示是否包含查询结果的数据行;

yii2的group查询,以用户为例:

User::find()->addGroupBy(‘title’)->all();根据title进行分组

1、增加 (insert)

$model = new User();
$model->username = ‘小伙儿’;
$model->insert();

对数据库 删除的一些简单的操作,还是老样子,上面我写代码,如果样式混乱,我会附上截图。还是用 user 表为例

SyntaxHighlighter.highlight();

JQuery.Ajax()的data参数类型实例详解

假如现在有这样一个表单,是添加元素用的。

<form id='addForm' action='UserAdd.action' type='post'>   <label for='uname'>用户名</label>:<input type='text' name='uname' id='uname'><br>   <label for='mobileIpt'>手机号:</label><input type='text' name='mobileIpt' id='mobileIpt'><br>   <label for='birthday'>生日:</label><input type='text' name='birthday'><br>   <input type='button' value='提交' onclick='addUser()'></form>

我们不想使用表单submit的方式添加这个元素,我们就想使用ajax提交。

以前我们是这样实现的:

function addUser(){    var user = {      uname:$("#uname").val(),      mobileIpt:$("#mobileIpt").val(),      birthday:$("#birthday").val()    };    $.ajax({      url:'UserAdd.action',      data:user,      type:'post',      dataType:'text',      success:function(msg){        if(msg=='1'){          console.log('添加成功');        }else{          console.log('添加失败')        }      }    })  }

这没有什么错,就是获取表单元素的值实在是太麻烦….这里只有三项,很多项的时候就废了….

直到有一天,我发现了jquery的serializeArray方法

序列化表格元素 (类似 ‘.serialize()’ 方法) 返回 JSON 数据结构数据。
注意,此方法返回的是JSON对象而非JSON字符串。需要使用插件或者第三方库进行字符串化操作。
返回的JSON对象是由一个对象数组组成的,其中每个对象包含一个或两个名值对――name参数和value参数(如果value不为空的话)。

我们来试试看

$('#addForm').serializeArray();//返回数据结构,是json数组,每个对像分别name和value为key,代表这个表单元素的name和value[  {"name":"uname","value":""},  {"name":"mobileIpt","value":""},    {"name":"birthday","value":""}]

这个貌似用不上啊

我们使用JQuery.param()方法处理一下:

var arr = $('#addForm').serializeArray();$.param(arr);"uname=alice&mobileIpt=110&birthday=1983-05-12"

嘿这下符合我们的需求了吧,虽然不是json类型,但是至少可以作为data上传了。

这里我们可以直接在ajax的data处填上这个json数组,在jquery内部自己调用$.param()处理的。

我们来看下jquery.param()方法的说明:

返回值:StringjQuery.param(obj,[traditional])将表单元素数组或者对象序列化。参数obj,[traditional]数组或jQuery对象会按照name/value对进行序列化,普通对象按照key/value对进行序列化。traditional:是否使用传统的方式浅层序列化。demo:$.param({uanme:'vic',mobileIpt:'110',birthday:'2013-11-11'});"uanme=vic&mobileIpt=110&birthday=2013-11-11"

看着说明,貌似也跟我们没关系啊,我们换一个json数组来看

$.param([{uanme:'vic'},{mobileIpt:'110'},{birthday:'2013-11-11'}]);"undefined=&undefined=&undefined="

这个转换不成功了吧,为什么我们表单的那种数据能够成功转换成url参数呢?我们来看下jquery源码

//在ajax()方法中,对json类型的数据进行了$.param()处理if ( s.data && s.processData && typeof s.data !== "string" ) {  s.data = jQuery.param( s.data, s.traditional );}//param方法中if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {    // Serialize the form elements    jQuery.each( a, function() {      add( this.name, this.value );    });  } else {    // If traditional, encode the "old" way (the way 1.3.2 or older    // did it), otherwise encode params recursively.    for ( prefix in a ) {      buildParams( prefix, a[ prefix ], traditional, add );    }  }

这下明白了吧,如果是json数据,那么挨个循环,只取他们的name属性和value属性拼接字符串。

如果是普通对象,循环该对象的属性,然后拼接字符串。

总结:

所以,本文要说的是,在jquery的ajax函数中,可以传入3种类型的数据

1.文本:”uname=alice&mobileIpt=110&birthday=1983-05-12″

2.json对象:{uanme:’vic’,mobileIpt:’110′,birthday:’2013-11-11′}

3.json数组:

[  {"name":"uname","value":"alice"},  {"name":"mobileIpt","value":"110"},    {"name":"birthday","value":"2012-11-11"}]

所以,我们可以一键获取表单并提交,非常方便。

补充:

$.ajax({  type: post,  url: some.php,  data: name=john&location=boston, //第一种方式传参 // data: {name:john,location:boston} //第二种方式传参 // data: {foo:[bar1, bar2]} 转换为 '&foo=bar1&foo=bar2' /* 第一种我们用url传参,参数里面如果加带&这个符号地话,可能参数接收不到或不完整, 如“ data: name=john&location=boston,” 如果name地值是john&smith这样写可能就会有问题, 我们可以用js里面地encodeuricomponent()方法进行转义, 但如果用data: {name:john,location:boston}这种方式写地话就不需要进行转义, 如果转义地话,接收地将是转义后地字符串 */  success: function(msg){   alert( data saved: + msg );  }});

SyntaxHighlighter.highlight();

谈谈Jquery ajax中success和complete有哪些不同点

废话不多说了,先给大家贴一段代码看看吧,

$.ajax({   type: "post",   url: url,   dataType:'html',   success: function(data) { },   complete: function(XMLHttpRequest, textStatus) { },   error: function(){}});

success : 当请求成功时调用的函数。这个函数会得到一个参数:从服务器返回的数据。当请求成功时调用函数,即status==200。

complete :当请求完成时调用的函数。这个函数会得到两个参数:XMLHttpRequest对象和一个描述请求成功的类型的字符串。当请求完成时调用函数,即status==404、403、302…。

所以,在写success或者complete的方法时,注意传入的参数,和使用传进来参数对象来解决我们的问题

参数列表:

参数名 类型 描述
url String (默认: 当前页地址) 发送请求的地址。
type String (默认: “GET”) 请求方式 (“POST” 或 “GET”), 默认为 “GET”。注意:其它 HTTP 请求方法,如 PUT 和 DELETE 也可以使用,但仅部分浏览器支持。
timeout Number 设置请求超时时间(毫秒)。此设置将覆盖全局设置。
async Boolean (默认: true) 默认设置下,所有请求均为异步请求。如果需要发送同步请求,请将此选项设置为 false。注意,同步请求将锁住浏览器,用户其它操作必须等待请求完成才可以执行。
beforeSend Function 发送请求前可修改 XMLHttpRequest 对象的函数,如添加自定义 HTTP 头。XMLHttpRequest 对象是唯一的参数。

function (XMLHttpRequest) {     this; // the options for this ajax request     }
cache Boolean (默认: true) jQuery 1.2 新功能,设置为 false 将不会从浏览器缓存中加载请求信息。
complete Function 请求完成后回调函数 (请求成功或失败时均调用)。参数: XMLHttpRequest 对象,成功信息字符串。

function (XMLHttpRequest, textStatus) {     this; // the options for this ajax request     }
contentType String (默认: “application/x-www-form-urlencoded”) 发送信息至服务器时内容编码类型。默认值适合大多数应用场合。
data Object,
String
发送到服务器的数据。将自动转换为请求字符串格式。GET 请求中将附加在 URL 后。查看 processData 选项说明以禁止此自动转换。必须为 Key/Value 格式。如果为数组,jQuery 将自动为不同值对应同一个名称。如 {foo:[“bar1”, “bar2”]} 转换为 ‘&foo=bar1&foo=bar2’。
dataType String

预期服务器返回的数据类型。如果不指定,jQuery 将自动根据 HTTP 包 MIME 信息返回 responseXML 或 responseText,并作为回调函数参数传递,可用值:

“xml”: 返回 XML 文档,可用 jQuery 处理。

“html”: 返回纯文本 HTML 信息;包含 script 元素。

“script”: 返回纯文本 JavaScript 代码。不会自动缓存结果。

“json”: 返回 JSON 数据 。

“jsonp”:  格式。使用  形式调用函数时,如 “myurl?callback=?” jQuery 将自动替换 ? 为正确的函数名,以执行回调函数。

error Function (默认: 自动判断 (xml 或 html)) 请求失败时将调用此方法。这个方法有三个参数:XMLHttpRequest 对象,错误信息,(可能)捕获的错误对象。

function (XMLHttpRequest, textStatus, errorThrown) {     // 通常情况下textStatus和errorThown只有其中一个有值      this; // the options for this ajax request     }
global Boolean (默认: true) 是否触发全局 AJAX 事件。设置为 false 将不会触发全局 AJAX 事件,如 ajaxStart 或 ajaxStop 。可用于控制不同的Ajax事件
ifModified Boolean (默认: false) 仅在服务器数据改变时获取新数据。使用 HTTP 包 Last-Modified 头信息判断。
processData Boolean (默认: true) 默认情况下,发送的数据将被转换为对象(技术上讲并非字符串) 以配合默认内容类型 “application/x-www-form-urlencoded”。如果要发送 DOM 树信息或其它不希望转换的信息,请设置为 false。
success Function 请求成功后回调函数。这个方法有两个参数:服务器返回数据,返回状态

function (data, textStatus) {     // data could be xmlDoc, jsonObj, html, text, etc...     this; // the options for this ajax request     }

SyntaxHighlighter.highlight();

PHP Yii框架之表单验证规则大全

Yii是一个基于组件的高性能PHP框架,用于开发大型Web应用。Yii采用严格的OOP编写,并有着完善的库引用以及全面的教程。

废话不多说了,直接给大家贴代码了。

<?phpclass ContactForm extends CFormModel{  public $_id;  public $contact;//联系人  public $tel;//电话  public $fax;//传真  public $zipcode;//邮编  public $addr;//地址  public $mobile;//手机  public $email;//邮箱  public $website;//网址  public $qq;//QQ  public $msn;//MSN  public function rules()  {    return array(      array('contact','required','on'=>'edit','message'=>'联系人必须填写.'),      array('contact','length','on'=>'edit','min'=>2,'max'=>10,'tooShort'=>'联系人长度请控制在2-10个字符.','tooLong'=>'联系人长度请控制在2-10个字符.'),      array('tel', 'match','pattern' => '/^(/d{3}-|/d{4}-)(/d{8}|/d{7})?$/','message' => '请输入正确的电话号码.'),      array('fax', 'match','pattern' => '/^(/d{3}-|/d{4}-)(/d{8}|/d{7})?$/','message' => '请输入正确的传真号码.'),      array('mobile', 'match','pattern' => '/^13[0-9]{1}[0-9]{8}$|15[0189]{1}[0-9]{8}$|189[0-9]{8}$/','message' => '请输入正确的手机号码.'),      array('email','email','on'=>'edit','message'=>'邮箱输入有误.'),      array('zipcode','required','on'=>'edit','message'=>'邮编必须填写.'),      array('zipcode','numerical','on'=>'edit','message'=>'邮编是6位数字.'),      array('zipcode','length','on'=>'edit','min'=>6,'max'=>6,'tooShort'=>'邮编长度为6位数.','tooLong'=>'邮编长度为6位数.'),      array('website','url','on'=>'edit','message'=>'网址输入有误.'),      array('qq', 'match','pattern' => '/^[1-9]{1}[0-9]{4,11}$/','message' => '请输入正确的QQ号码.'),      array('msn','email','on'=>'edit','message'=>'MSN输入有误.'),    );  }}
public $password2;//非数据库的字段,但是在view中需要用到  public $verify; //手机验证码  public $fjg; //忘记号码  /**   * 映射数据库表名   * @return string the associated database table name<br><br>     * www.shouce.ren   */  public function tableName()  {    return 'adm_user';  }  /**   * 验证规则   * @return array validation rules for model attributes.   */  public function rules()  {    // NOTE: you should only define rules for those attributes that    // will receive user inputs.    return array(      //array('mobile_phone,name,status', 'required'),      array('mobile_phone', 'unique'),//'message' => '该手机号已经存在!'      array('mobile_phone', 'match','pattern' => '/^(13|15|18)[0-9]{9}$/','message' => '请输入正确的经办人手机号码.'),      //array('certificate_id', 'match','pattern' => '/(.jpg|.gif|.png|/d)$/','message' => '请重新选择证书图像并且后缀只能是jpg、gif、png格式.'),      array('phone', 'match','pattern' => '/^(/d{3}-|/d{4}-)?(/d{8}|/d{7})?$/','message' => '请输入正确的座机号码.'),      array('fax', 'match','pattern' => '/^(/d{3}-|/d{4}-)(/d{8}|/d{7})?$/','message' => '请输入正确的传真号码.'),      //array('email_address', 'match','pattern' => '/^[/w-]+(/.[/w-]+)*@[/w-]+(/.[/w-]+)+$/','message' => '请输入正确的邮箱.'),      array('email_address','email','message'=>'请输入正确的邮箱.'),      //验证密码和确认密码      array("password2","compare","compareAttribute"=>"password","message"=>"两次密码不一致",'on'=>'register'),      array("password2","compare","compareAttribute"=>"password","message"=>"两次密码不一致",'on'=>'regonter'),      array('qq', 'match','pattern' => '/^[1-9]{1}[0-9]{4,11}$/','message' => '请输入正确的QQ号码.'),      array('type,certificate_id,company_type, nationality,yyzz_id, status,level,create_by_id, create_time,update_time', 'numerical', 'integerOnly'=>true),      array('verify', 'numerical', 'message' => '验证码不正确','integerOnly'=>true),      array('name,user_type,tuijianren', 'length', 'max'=>20),      array('password', 'length', 'max'=>100),      array('email_address,business', 'length', 'max'=>50),      array('communication_address,money, yhzh,yhmc,industry, company, register_address,yhdh,shangbiao,zhuanli,gongshang', 'length', 'max'=>255),      array('role_id','default', 'setOnEmpty'=>true, 'value'=>10),      array('shangbiao','default', 'setOnEmpty'=>true, 'value'=>'0,0'),      array('zhuanli','default', 'setOnEmpty'=>true, 'value'=>'0,0'),      array('gongshang','default', 'setOnEmpty'=>true, 'value'=>'0,0'),      array('password','default', 'setOnEmpty'=>true, 'value'=>'123456'),      /*验证码*/      array('verify','checkVerify', 'on'=>'register'),      array('email_address','checkemail', 'on'=>'regonter'),//     array('certificate_id', 'file','allowEmpty'=>true,//         'types'=>'jpg, gif, png, doc, txt',//         'maxSize'=>1024 * 1024 * 10, // 10MB//         'tooLarge'=>'文件大小不能超过10M!',//         'message'=>'请先上传证书图像.'//     ),      // The following rule is used by search().      // @todo Please remove those attributes that should not be searched.      array('id,role_id,name, password,user_type, email_address,tuijianren,shangbiao,company_type,zhuanli,gongshang,money,yhzh,yhmc,yyzz_id,yhdh,type,level, phone, qq, mobile_phone, fax, communication_address, nationality, industry, company, business, register_address, certificate, status, create_by_id, create_time, update_time', 'safe', 'on'=>'search'),    );  }   /*   * 手机验证码校验    */  public function checkVerify($attribute,$params)  {       $model=new Mess();    $d_title = $model->find(array('condition'=>'suij=:suij and tel=:tel and type>:type and time>:time','params'=>array(':suij'=>$this->verify,':tel'=>$this->mobile_phone,':type'=>0,':time'=>(time()-3600)),'select'=>array('id')));    //$d_title = $model->findByAttributes(array('suij'=>$this->verify,'tel'=>$this->mobile_phone),array('select'=>array('id')));    if($d_title['id']<1)    {      $this->addError('verify', "验证码不正确。");    }    else    {      if($this->password == $this->password2)      {        $model->updateAll(array('type'=>0),array('condition'=>'suij=:sj','params'=>array(':sj'=>$this->verify)));      }    }  }

SyntaxHighlighter.highlight();

Jquery ajax基础教程

jQuery的Ajax带来了无需刷新的web页面革命。这里就详细介绍一下jQuery所涉及到的Ajax操作。(无需特殊说明,均需要有服务器配置,这里本人用的是Tomcat 7)

1.基于请求加载文件数据

这里的请求通常都是网页的某些操作,如点击等。

而其加载数据的类型归类为以下四种:a.加载HTML文件;b.加载JSON文件;c.加载Javascript文件;d.加载XML文件。

其对应的四种加载方法分别是:load、getJSON、getScript、get。

a.加载HTML文件

把编写好的HTML文件加载到网页中。例如:

//load方法加载html文件 $('#letter-a a').click(function(){     $('#dictionary').load('a.html');     return false; });

这里a.html也是放在服务器端的一个已经编写好的页面文件,直接调用load,就可以让所匹配的目标内载入HTML内容。

b.加载JSON文件

把编写好的JSON文件加载到网页中。例如:

//加载json文件 $('#letter-b a').click(function(){   $.getJSON('b.json',function(data){     var html = '';     $.each(data,function(entryIndex, entry){       html += "<div class='entry'>";       html += "<h3 class='term'>" + entry.term + "</h3>";       html += "<div class='part'>" + entry.part + "</div>";       html += "<div class='definition'>";       html += entry.definition;       if(entry.quote){         html += '<div class="quote">';         $.each(entry.quote, function(lineIndex, line){           html += '<div class="quote-line">' + line + '</div>';         });         if(entry.author){           html += '<div class="quote-author">' + entry.author + '</div>';         }       }       html += "</div>";       html += "</div>";     });     $('#dictionary').html(html);   });   return false;  }); 

getJSON方法第一个参数是指加载的文件路径,第二个参数是一个加载完成以后的回调函数。通过这个回调函数,就可以对加载好的data进行操作。重复的部分使用each循环处理。最后将拼凑好的html字符串使用html方法加入到目标id=dictionary的元素中。

c.加载Javascript文件

加载Javascript文件和加载HTML文件类似。这里需要注意的是,使用getScript方法加载进来的Javascript会根据当下Javascript环境直接运行。例如:

//执行脚本 $('#letter-c a').click(function(){     $.getScript('c.js');     return false; }); 

d.加载XML文件

jQuery中可以使用get方法加载XML文件。例如:

//加载XML文档 $('#letter-d a').click(function(){     $.get('d.xml',function(data){       $('#dictionary').empty();       $(data).find('entry').each(function(){         var $entry = $(this);         var html = '<div class="entry">';         html += '<h3 class="term">' + $entry.attr('term') + '</h3>';         html += '<div class="part">' + $entry.attr('part') + '</div>';         html += '<div class="definition">';         html += $entry.find('definition').text();         var $quote = $entry.find('quote');         if($quote.length)         {           html += '<div class="quote">';           $quote.find('line').each(function(){             html += '<div class="quote-line">';             html += $(this).text() + '</div>';           });           if($quote.attr('author')){             html += '<div class="quote-author">';             html += $quote.attr('author') + '</div>';           }           html += '</div>';         }         html += '</div>';         html += '</div>';         $('#dictionary').append($(html));       });     });     return false; }); 

XML文件有一个特点就是,你可以像用jQuery操作HTML那样操作XML的那些元素。如使用attr方法、text方法等等。

2.基于Get方法向服务器获取数据

之前的例子都是从服务器上静态的获取数据文件。而Ajax的价值不只于此,通过get方法从服务器动态的获取数据,为web页面无刷新的实现提供了莫大的帮助。

下面就使用get方法从服务器获取一段所需要的数据。这里,本人结合J2EE的Struts2框架和TOMCAT搭建的服务器端。具体服务器端多种多样,可以是php+apache或者其他什么的都可以。

操作如下,用户点击Eavesdrop则发送get方法到服务器,取得Eavesdrop的数据,并且返回json值,然后在jQuery中装配。

代码如下:

//GET方法加载服务器内容 $('#letter-e a').click(function(){     var requestData = {term:$(this).text().toUpperCase()};     $.get('EGet.action', requestData, function(data){             //返回的数据包结构根据Struts2配置如下:       //{"resultMSG":"{ 内部另一个json结构 }","success":"true"}       //先将返回的数据包拆包       var responseObj = eval("("+data+")");       if(responseObj.success == 'true')       {         $('#dictionary').empty();         //返回成功,接下来再次解包resultMSG         var dataObj = eval("("+responseObj.resultMSG+")");         var html = "";         html += "<div class='entry'>";         html += "<h3 class='term'>" + dataObj.term + "</h3>";         html += "<div class='part'>" + dataObj.part + "</div>";         html += "<div class='definition'>";         html += dataObj.definition;         if(dataObj.quote){           html += '<div class="quote">';           $.each(dataObj.quote, function(lineIndex, line){             html += '<div class="quote-line">' + line + '</div>';           });           if(dataObj.author){             html += '<div class="quote-author">' + dataObj.author + '</div>';           }         }         html += "</div>";         html += "</div>";         $('#dictionary').html(html);       }       else       {         var $warning = $('<div>Sorry, your term was not found!</div>');         $('#dictionary').html($warning);       }     });     return false; }); 

这里要说明的是由于struts2配置,返回的时候在需要的数据外又打了一层包,用于表示结果内容的resultMSG和是否ajax访问成功的success字段。所以使用了2次eval解包。

这里我后台java程序传递过来的并非配置好的HTML,而是仅仅是json类型的数据,本人认为在java层面编写html并传递不如直接传递数据方便,以后修改样式或者页面结构也都不如直接修改javascript方便。

通过get方法获得服务器数据,相当于向服务器提交如下这种请求:EGet.action?term=XXX

下面放出java后台文件代码:

1.EGet.java

package lhb; import com.opensymphony.xwork2.ActionSupport; public class EGet extends ActionSupport {   private String term;   private Terms sampleTerm;   private String success;   private String resultMSG;   /**    *    */   private static final long serialVersionUID = 1L;   public String execute() throws Exception   {     initData();     if(term.equals(sampleTerm.getTerm()))     {       success = "true";       resultMSG = "{/"term/": /""+sampleTerm.getTerm()+"/","+           "/"part/": /""+sampleTerm.getPart()+"/","+           "/"definition/": /""+sampleTerm.getDefinition()+"/","+           "/"quote/": ["+           "/"Is public worship, then, a sin,/","+           "/"That for devotions paid to Bacchus/","+           "/"The lictors dare to run us in,/","+           "/"And resolutely thump and whack us?/""+           "],"+           "/"author/": /""+sampleTerm.getAuthor()+"/"}";     }     else{       success = "false";       resultMSG = "fail";     }     return SUCCESS;   }   //初始化数据   private void initData()   {     String partEAVESDROP = "v.i.";     String definitionEAVESDROP = "Secretly to overhear a catalogue of the crimes and vices of another or yourself.";     String quoteEAVESDROP[] = {"A lady with one of her ears applied",         "To an open keyhole heard, inside,",         "Two female gossips in converse free ―",         "The subject engaging them was she.",         "/"I think,/" said one, /"and my husband thinks",         "That she's a prying, inquisitive minx!/"",         "As soon as no more of it she could hear",         "The lady, indignant, removed her ear.",         "/"I will not stay,/" she said, with a pout,",         "/"To hear my character lied about!/""};     String authorEAVESDROP = "Gopete Sherany";     Terms EAVESDROP = new Terms();     EAVESDROP.setTerm("EAVESDROP");     EAVESDROP.setPart(partEAVESDROP);     EAVESDROP.setDefinition(definitionEAVESDROP);     EAVESDROP.setQuote(quoteEAVESDROP);     EAVESDROP.setAuthor(authorEAVESDROP);     sampleTerm = EAVESDROP;   }   public String getTerm()   {     return term;   }   public void setTerm(String term)   {     this.term = term;   }   public String getSuccess()   {     return success;   }   public void setSuccess(String success)   {     this.success = success;   }   public String getResultMSG()   {     return resultMSG;   }   public void setResultMSG(String resultMSG)   {     this.resultMSG = resultMSG;   } } 

这个action中的数据本人直接配置了,这里只是做一个示范使用。真正的这些数据在项目中一般是存放在数据库中的。由于这主要是jQuery方面的小示例,就不弄那么麻烦了。

2.Terms.java

package lhb; public class Terms {   private String term;   private String part;   private String definition;   private String quote[];   private String author;   public String getTerm()   {     return term;   }   public void setTerm(String term)   {     this.term = term;   }   public String getPart()   {     return part;   }   public void setPart(String part)   {     this.part = part;   }   public String getDefinition()   {     return definition;   }   public void setDefinition(String definition)   {     this.definition = definition;   }   public String[] getQuote()   {     return quote;   }   public void setQuote(String[] quote)   {     this.quote = quote;   }   public String getAuthor()   {     return author;   }   public void setAuthor(String author)   {     this.author = author;   } } 

这个类纯粹就是一个pojo类。没有什么特别的方法。

3.struts.xml

这个是struts2的json方式传递配置

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC   "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"   "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts>   <!-- 指定全局国际化资源文件 -->   <constant name="struts.custom.i18n.resources" value="i18n"/>   <!-- 指定国际化编码所使用的字符集 -->   <constant name="struts.i18n.encoding" value="GBK"/>   <!-- JSON的action -->   <package name="jsonInfo" extends="json-default">     <action name="EGet" class="lhb.EGet">       <result type="json">         <param name="contentType">text/html</param>         <param name="includeProperties">success, resultMSG</param>       </result>     </action>   </package> </struts> 

这里可以看到includeProperties里所配置的外面一层json,success和resultMSG。这在实际中很好用。如果服务器中没有取得需要的值,虽然ajax访问成功,但是获得的结果并不算成功,因为没有取得需要的值。这里加入了success标示,方便前台jQuery操作。

基于其他方法获取服务器数据从写法上与get基本一致,如post方法、load方法。这里就不再赘述了。

3.动态提交表单

通过jQuery的AJAX支持,可以让我们很方便的动态提交表单而不用刷新页面。

如下面例子:

$('#letter-f form').submit(function(){     //调用preventDefault方法阻止事件冒泡,具体工作就是如果网页有脚本错误,那么则会阻止提交form表单     event.preventDefault();     var formValues = $('input[id="term"]').val();     var requestStr = {'term':formValues.toUpperCase()};     $.get('EGet.action', requestStr, function(data){       var responseObj = $.parseJSON(data);       if(responseObj.success == 'true')       {         var html = '';         var dataObj = $.parseJSON(responseObj.resultMSG);         html += "<div class='entry'>";         html += "<h3 class='term'>" + dataObj.term + "</h3>";         html += "<div class='part'>" + dataObj.part + "</div>";         html += "<div class='definition'>";         html += dataObj.definition;         if(dataObj.quote){           html += '<div class="quote">';           $.each(dataObj.quote, function(lineIndex, line){             html += '<div class="quote-line">' + line + '</div>';           });           if(dataObj.author){             html += '<div class="quote-author">' + dataObj.author + '</div>';           }         }         html += "</div>";         html += "</div>";         $('#dictionary').html(html);       }       else{         var warning = $('Sorry, your term was not found!');         $('#dictionary').html(warning);       }     }); }); 

这个例子援引的数据还是上一个EGet.action所用的那个数据。程序的操作过程基本是:

首先调用这个 preventDefault();这个方法在注释里也说明了,用于阻止事件冒泡带来的不便与麻烦。

接下来通过$()获得input的元素,使用val方法获得其值,接下来的使用方法与上例基本相同。

这里也可以使用serialize方法将input元素序列化成如下格式“term=xxx”这样。不过由于服务器端的java程序中的那些数据时硬编码的,所有,不是太方便用,就没用。

4.关于Ajax的观察员函数

jQuery包含了2个全局的ajax观察员函数:ajaxStart和ajaxStop。

分别在执行ajax操作的起始和结束时调用。例如:

//ajax的观察员函数 ajaxStart 和 ajaxStop   $('<div id="loading">Loading...</div>').insertBefore('#dictionary')     .ajaxStart(function(){       $(this).show();     }).ajaxStop(function(){       $(this).hide();     }); 

这里无论哪个a标签触发ajax操作,包括静态载入文件和动态服务器访问,都会触发ajaxStart和ajaxStop。
关于错误处理,常用的三个函数:success、complete、error。

下面以error为例:

.error(function(jqXHR){   $('#dictionary').html('An Error occurred:'+ jqXHR.status).append(jqXHR.responseText); }); 

可以以连缀的写法将error方法放置于get方法之后:“$.get().error()”这样。
刚才看了一下,这个可以将Tomcat的报错,加载到页面之上。这在有的时候还是很有用的。如图:

不过不知道为何这个将我原有样式也覆盖了一些,如果有哪位网友知道,麻烦指正一下问题所在。谢谢了。

5.Ajax和事件

Ajax动态访问服务器以后生成的元素,如果想绑定事件的话,一种方法是每次取到都重新绑定处理程序,这种相对来说比较简单,但是不适合DOM结构经常变化的场景。如果DOM结构经常变化,那么就需要用live方法,实现事件委托。

SyntaxHighlighter.highlight();

jQuery实现带玻璃流光质感的手风琴特效

jQuery实现带玻璃流光质感的手风琴特效是一款基于jQuery+CSS3实现的带玻璃流光质感的手风琴特效,分享给大家,具体如下

效果图:

具体代码如下:

html代码:

<section class="strips">   <article class="strips__strip">     <div class="strip__content">       <h1 class="strip__title" data-name="Lorem">Awesome</h1>     </div>   </article>   <article class="strips__strip">     <div class="strip__content">       <h1 class="strip__title" data-name="Ipsum">Words</h1>       <div class="strip__inner-text">         <h2>Ettrics</h2>          <script" width=100% src="http://www.w2bc.com/scripts/2bc/_gg_980_90.js" type="text/javascript"></script>          <p>           <a href="#" target="_blank"><i class="fa fa-qq"></i></a>         </p>       </div>     </div>   </article>   <article class="strips__strip">     <div class="strip__content">       <h1 class="strip__title" data-name="Dolor">Go</h1>       <div class="strip__inner-text">         <h2>Ettrics</h2>         <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Officia sapiente deserunt consectetur, quod reiciendis corrupti quo ea aliquid! Repellendus numquam quo, voluptate. Suscipit soluta omnis quibusdam facilis, illo voluptates odit!</p>         <p>           <a href="#" target="_blank"><i class="fa fa-weibo"></i></a>         </p>       </div>     </div>   </article>   <article class="strips__strip">     <div class="strip__content">       <h1 class="strip__title" data-name="Sit">Inside</h1>       <div class="strip__inner-text">         <h2>Ettrics</h2>         <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Officia sapiente deserunt consectetur, quod reiciendis corrupti quo ea aliquid! Repellendus numquam quo, voluptate. Suscipit soluta omnis quibusdam facilis, illo voluptates odit!</p>         <p>           <a href="#" target="_blank"><i class="fa fa-weixin"></i></a>         </p>       </div>     </div>   </article>   <article class="strips__strip">     <div class="strip__content">       <h1 class="strip__title" data-name="Amet">Here</h1>       <div class="strip__inner-text">         <h2>Ettrics</h2>         <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Officia sapiente deserunt consectetur, quod reiciendis corrupti quo ea aliquid! Repellendus numquam quo, voluptate. Suscipit soluta omnis quibusdam facilis, illo voluptates odit!</p>         <p>           <a href="#" target="_blank"><i class="fa fa-leaf"></i></a>         </p>       </div>     </div>   </article>   <i class="fa fa-close strip__close"></i> </section>
      var Expand = function () {      var tile = $('.strips__strip');      var tileLink = $('.strips__strip > .strip__content');      var tileText = tileLink.find('.strip__inner-text');      var stripClose = $('.strip__close');      var expanded = false;      var open = function () {        var tile = $(this).parent();        if (!expanded) {          tile.addClass('strips__strip--expanded');          tileText.css('transition', 'all .6s 1s cubic-bezier(0.23, 1, 0.32, 1)');          stripClose.addClass('strip__close--show');          stripClose.css('transition', 'all .6s 1s cubic-bezier(0.23, 1, 0.32, 1)');          expanded = true;        }      };      var close = function () {        if (expanded) {          tile.removeClass('strips__strip--expanded');          tileText.css('transition', 'all 0.15s 0 cubic-bezier(0.23, 1, 0.32, 1)');          stripClose.removeClass('strip__close--show');          stripClose.css('transition', 'all 0.2s 0s cubic-bezier(0.23, 1, 0.32, 1)');          expanded = false;        }      };      var bindActions = function () {        tileLink.on('click', open);        stripClose.on('click', close);      };      var init = function () {        bindActions();      };      return { init: init };    }();    Expand.init();

SyntaxHighlighter.highlight();

超详细的php用户注册页面填写信息完整实例(附源码)

注册页面是大多数网站必备的页面,所以很有必要对自己的注册页面做些精心的设计。下面三张图,第一张是注册的展示页面,第二张思维导图就一个简单的逻辑,第三张是通过firebug查看调用的JS文件。

一、给每个输入框写下说明

在用户看到这个输入框的时候,就能非常清晰的明白这个输入框是做啥用的,最大限度的降低他们产生疑惑的可能性。我们需要假设用户毫不了解注册需要输入的内容,随后给他们足够的信息以便帮助他们理解。

 二、小图标icon

Icon是增强内容的工具,而且能给访客一个很好的暗示。以前使用小图标都是用图片,用图片的话,很多时候那些对齐、宽度高度搞起来特麻烦,这次我将图标做成字体,操作字体可比操作图片容易很多。可以到国外的一个网站icomoon制作图标字体,不过这个网站打开起来比较慢,需要耐心等待。利用在线资源,接受新思想与新技术,让工作变得越来越简单。

这些小图标都是从icomoon网站上面导出的。这种方式操作对齐,大小非常方便,不过IE6和IE7不支持选择器before(关于选择器的浏览器兼容可以参考这里),所以在这两个浏览器中将不能显示这个图标。

<font class="ficomoon icon-signup"></font>注册新用户@font-face { font-family: 'icomoon'; src:url('fonts/icomoon.eot?-fl11l'); src:url('fonts/icomoon.eot?#iefix-fl11l') format('embedded-opentype'), url('fonts/icomoon.woff?-fl11l') format('woff'), url('fonts/icomoon.ttf?-fl11l') format('truetype'), url('fonts/icomoon.svg?-fl11l#icomoon') format('svg'); font-weight: normal; font-style: normal;}.ficomoon{font-family:'icomoon';}.icon-print:before {content: "/e601"}

 三、输入框还可输入的字符数

过去我会给输入框设置一个最大字数的属性限制,这是一种比较粗暴的方式,因为在输入到一定字符后,突然不能输入了,感觉就像是键盘一下子失灵了,没有任何征兆。

现在通过这个设置,首先能让用户知道这里有字数限制,其次能够让用户清楚的知道何时会达到这个限制,很好的提升了友好度。这里面还做了另外一个小操作,就是在输入到一些字符后,将出现变红色,警示用户马上要超过额定字数了。

这是一种poka-yoke的概念,意思就是防止错误,有两种含义:侦测机制与预防机制。

加入简单的字符计数器便能把一个潜在的错误转变成另一个“原来使用这个产品只需常识”的瞬间。

function _textLimit(options, value) { var length = value.length; var color = options.normal; var remind = options.len - length; if(remind > 0 && remind <= options.limit) { color = options.warnning; } if(remind < 0) { var txt = $('#' + options.inputId); txt.val(value.substr(0, options.len)); remind = 0; }  $('#' + options.digitalId).html(remind).css({"color": color, "font-size": options.fontSize}); }

四、输入正确与错误都给予反馈

除了检测到错误时立即显示友好的提示信息之外,告诉用户“一切都没有问题”同样也很重要。

设想一下,当你急切的在向某人询问一些信息的时候,肯定是希望能马上得到响应的回答。

当用户输入正确的时候,就应该表示表示,给他们一个绿色的勾,鼓励一下;当输入错误的时候,给他们一个红色的差,告诉他们错误的理由,让他们做相应的修改。这里的勾和差我都是使用的图标字体,对齐的时候特别方便。

.ico_correct{color:#01b60e;margin-left:10px;font-family:'icomoon';vertical-align:middle;font-size:1.25em}.ico_correct:before {content: "/f00c"}.ico_error{color:#ff0000;margin-left:10px;font-family:'icomoon';vertical-align:middle;font-size:1.25em}.ico_error:before {content: "/f00d"} 

五、邮箱做自动匹配

这种邮箱自动化匹配,既能减少用户的输入错误,也能提高用户输入的效率。让用户是在“做正确的事”。下拉列表中的红色能够凸显出匹配值与输入值的区别,便于识别。

从网上查到了相关的JS脚本代码,自己再做了一点小修改,集成到我的代码中。这里小编还有一篇超全的邮箱自动化匹配的文章分享给大家:

 

六、密码强度

密码强度检测是为了给用户一个善意的提醒,希望用户对自己信息有更强的保护心理。所以即使密码为弱,也不应该影响数据提交。三种等级在下面会显示不同的提示语,会提示用户增加密码强度,或鼓励用户将密码强度更进一步,或肯定这个密码的强度。

密码强度在网上有很多插件,但是这次我自己写CSS,然后自己做匹配强度,这样做是为了能更好的集成到我的网站页面中。不同强度显示不同的颜色块与提示。

regex.checkPwdStrong = function(str) {//密码强度是否为强 var rule = /^(?=.{8,})(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*/W).*$/g; return rule.test(str);};regex.checkPwdMedium = function(str) {//密码强度是否为中等 var rule = /^(?=.{7,})(((?=.*[A-Z])(?=.*[a-z]))|((?=.*[A-Z])(?=.*[0-9]))|((?=.*[a-z])(?=.*[0-9]))).*$/g; return rule.test(str);};regex.checkPwdPoor = function(str) {//密码强度是否为弱 var rule = /(?=.{6,}).*/g; return rule.test(str);};
.pwd_complex{padding:5px 0;height:15px}.pwd_complex span{height:6px;width:80px;margin-right:5px;display:inline-block;border:1px solid #c2c2c2;overflow:hidden}.pwd_complex .strong{border:1px solid #45ac07;background:#61cc1f}.pwd_complex .strong_word{color:#45ac07}.pwd_complex .medium{border:1px solid #45ac07;background:#9AF518}.pwd_complex .medium_word{color:#61cc1f}.pwd_complex .poor{border:1px solid #FF9C3A;background:#FFCA92}.pwd_complex .poor_word{color:#FF9C3A}

这里小编再给大家推荐一篇超全的邮箱密码强度验证的的文章分享给大家:

七、控制注册按钮

选中与不选中我本来做的是另外一个效果,就是没选中的时候将马上注册这个按钮隐藏掉,但后面觉得不妥,如果用户不小心将选中框取消,按钮又突然消失了,会使用户疑惑,有可能他们就终止注册或者刷新页面,重新输入相应的内容,无论做哪种操作,都会让用户感到不愉快。

后面我就想到将按钮变灰,在html中被禁用的 input 默认显示灰色,利用了一下用户的一些习惯。让按钮存在于页面上,暗示用户还有操作没完成,这里其实倒是可以再加些小提示,明确哪里没有做好,我偷懒了下没有做那种提示。

服务条款下面我用虚线标识了一下,并且在移上去的时候显示手的图标,暗示用户这里可以点击,点击服务条款弹出一个内容层,里面是协议内容,我没有做打开一张新页面那种提醒方式,我觉得这种时候用户的注意力应该集中在当前页面,而不是新开一个窗口,再去浏览那里的信息,分散了他的注意力,还有就是新开了一个窗口浏览器又多了个标签,挺占地方的。

 八、最后验证

当我点击提交按钮的时候,会用JS脚本做最后的验证,防止将错误信息提交到服务器端,如果有输入还没符合要求,会有一个小手定位到错误的输入框旁边,并做了来回移动的动画效果。一个会动的错误提示,我相信能更加吸引住用户的注意,然后做相应修改。这里使用了CSS3的新技术,一直想把一些已经学到了的东西应用到实际操作中,这里正好做了个尝试。这个动画提示还很粗糙,但给了我一个新的想法。唯一觉得变复杂的就是CSS代码一下子庞大了很多。

这个动画就是在控制margin-left的值,做来回移动。

.cssanimations .ico_prompt{ -moz-animation-duration: .7s; -moz-animation-name: prompt_hand; -moz-animation-iteration-count: infinite; -moz-animation-direction: alternate;  -webkit-animation-duration: .7s; -webkit-animation-name: prompt_hand; -webkit-animation-iteration-count: infinite; -webkit-animation-direction: alternate;  -o-animation-duration: .7s; -o-animation-name: prompt_hand; -o-animation-iteration-count: infinite; -o-animation-direction: alternate;  -ms-animation-duration: .7s; -ms-animation-name: prompt_hand; -ms-animation-iteration-count: infinite; -ms-animation-direction: alternate;  animation-duration: .7s; animation-name: prompt_hand; animation-iteration-count: infinite; animation-direction: alternate;}@keyframes prompt_hand { from {margin-left:10px} to {margin-left:2px}}@-moz-keyframes prompt_hand { from {margin-left:10px} to {margin-left:2px}}@-webkit-keyframes prompt_hand { from {margin-left:10px} to {margin-left:2px}}@-o-keyframes prompt_hand { from {margin-left:10px} to {margin-left:2px}@-ms-keyframes prompt_hand { from {margin-left:10px} to {margin-left:2px}}

九、按钮标记

按钮里面的文字我可以写成普通的“提交”字样,也能正常工作。但是意义更明显的按钮标记能更好地帮助用户建立对点击结果的期望,让用户清楚的知道我在这里用手指点击了一下,能得到什么结果。

 

十、做防重复提交限制

最后在用户点击提交后,我会有一个转动的圆圈出现,既能暗示用户系统正在提交,请耐心等待,又能防止用户重复提交服务器。一般有经验的用户看到这种圈圈就会意识到正在提交中,但对于没经验的用户,可以做到更好。我仅仅是做了个圈圈层特效,其实这里的“马上注册”几个字可以在点击后改变成“注册提交中”等提示,让用户能更加清晰的知道现在是什么情况。

为了完成这种效果,我使用了插件spin,能够兼容各个浏览器。在ajax做提交前显示,在ajax响应后去除这个等待层。

showAjaxLoading: function(btn) { var left = $(btn).offset().left; var top = $(btn).offset().top; var width = $(btn).width(); var height = $(btn).height(); var opts = { lines: 9, // The number of lines to draw length: 0, // The length of each line width: 10, // The line thickness radius: 15, // The radius of the inner circle corners: 1, // Corner roundness (0..1) rotate: 0, // The rotation offset direction: 1, // 1: clockwise, -1: counterclockwise color: '#000', // #rgb or #rrggbb or array of colors speed: 1, // Rounds per second trail: 81, // Afterglow percentage shadow: false, // Whether to render a shadow hwaccel: false, // Whether to use hardware acceleration className: 'spinner', // The CSS class to assign to the spinner zIndex: 2e9, // The z-index (defaults to 2000000000) top: '50%', // Top position relative to parent left: '50%' // Left position relative to parent }; $('#ajax_spin').remove(); $('body').append('<div id="ajax_spin" style="position:absolute;background:#FFF;filter:alpha(opacity=30);opacity:0.3"><div id="ajax_spin_inner" style="position:relative;height:50px;"></div></div>'); $('#ajax_spin').css({'top':top, 'left': left, 'width': width, 'height':height}); var target = document.getElementById('ajax_spin_inner');  var spinner = new Spinner(opts).spin(target); }

这个注册页面是我的一个初步的思路,以后有新的体会后,将会不断的做修改。

上面的这些步骤在某些情况下可能不是最好的解决方案,所以在实际情况中最相应的修改。没有最好,只有更好。

我想做到的一个目标是,当用户进入到这个页面后,能非常轻松的完成各个输入框,非常舒服流畅的完成各个框。

SyntaxHighlighter.highlight();

javascript实现Email邮件显示与删除功能

一、主要介绍:
这题采用之前的技术,根据table的rows属性,获得数组,然后对数组设置样式,所以颜色就出来了。

1).全选复选框,通过var nodess=document.getElementsByName(“mail”);

for(var x=0;x<nodess.length;x++){nodess[x].checked=nodes.checked;}

复选框函数进行

2).按钮全选,反选,和取消全选,可以用一个函数写,传入不同的参数AllBybtn(num)类型即可

函数里面 根据js的特性 非0 和 0 这两种状态,进行设置,为了同步,需要分别进行设置

3).为了显示出当全部选中就默认全选的复选框选中,所以需要对每一个复选框进行设置,采用函数checkBysingle()进行设置

4).删除所选项是需要主要,当前的是checkbox对象,上一级是td–tr–先拿到tr对象,然后对通过tr的父节点删除tr对象再删除之后,x会变化,可能有些删不到,所以需要将X的值还原,或者从后面开始删除。

2、样式设置:

<style type="text/css">       .one{         background-color:#00ff80;       }       .two{         background-color:#80ff00;       }       .three{         background-color:#0000ff;       }       table th{         background-color:#c0c0c0;       }       table{         width:400px;         border:solid 1px;       }       table tr{         height:50px;       }     </style> 

三、背景颜色以及鼠标移动的事件设置

  function toaddcolor(){           //颜色设置,           var nodes = document.getElementById("tabid");           var rows1 = nodes.rows;           for (var x = 1; x < rows1.length; x++) {             if (x % 2 == 0) {               rows1[x].className = "one";             }             else {               rows1[x].className = "two";             }           }         }           function addEvent(){             var name;             //当鼠标移上去之后发生相应的变化             var nodes=document.getElementById("tabid");             var rows1=nodes.rows;             for (var x = 1; x < rows1.length; x++) {             rows1[x].onmouseover = function(){               name = this.className;               this.className = "three";             }             rows1[x].onmouseout = function(){               this.className = name;             }              //             alert("bb"); //             alert(rows1[x].getElementsByTagName("input")[0].nodeName);             rows1[x].getElementsByTagName("input")[0].onclick=function(){//每一行的input对象               document.getElementsByName("allItem")[0].indeterminate=true;//让全选的复选框形状发生变化             }           }         }         onload=function(){//在网页加载时候调用           toaddcolor();           addEvent();         } 

四、复选框的全选

function allcheck(nodes){//全选checkbox的点击调用这个           var nodess=document.getElementsByName("mail");           for(var x=0;x<nodess.length;x++){             nodess[x].checked=nodes.checked;           }           //多个全选的时候,必须和其他的一个一样           var nodes1=document.getElementsByName("allItem");           for(var x=0;x<nodes1.length;x++){             nodes1[x].checked=nodes.checked;           }         } 

五、按钮的全选

function AllBybtn(num){//全选按钮设置           var nodess = document.getElementsByName("mail");           /*多重for 循环不太好,可以根据js里面的特性0  非0            for (var x = 0; x < nodess.length; x++) {             if (num == 1) {               nodess[x].checked = 1;             }else if (num == 2) {                 nodess[x].checked = !nodess[x].checked;               }else if (num == 3) {                   nodess[x].checked = false;                 }           }*/           for(var x=0;x<nodess.length;x++){             if(num<2){               nodess[x].checked=num;                 //让全选的复选框可以 单独写个函数                 var all=document.getElementsByName("allItem");                 for(var y=0;y<all.length;y++){                   if(num==1){                   all[y].checked=num;                 }else{                   all[y].checked=num;                 }               }             }else{               nodess[x].checked=!nodess[x].checked;               var all=document.getElementsByName("allItem");                 for (var y = 0; y < all.length; y++) {                   all[y].checked=0;                 }}}} 

六、所有项目都选中之后全选自动选中

function checkBysingle(){//全部入选之后,全选的自动选中                var flag = true;           var node = document.getElementsByName("mail");           for (var x = 0; x < node.length; x++) {             if (node[x].checked == false) {               flag = false;               break;             }           }                      var all = document.getElementsByName("allItem");           for (var y = 0; y < all.length; y++) {             if (flag) {               all[y].checked = true;             }             else {               all[y].checked = false;             }}} 

 七、删除邮件的函数(删除行)

function DelBybtn(){//删除行   var tdnode=document.getElementsByName("mail");     /*for(var x=0;x<tdnode.length;x++){     if(tdnode[x].checked){//对象是 checkbox 我们必须要那父级的父级 《tr》,我们需要移除的就是tr对象        var trnode=tdnode[x].parentNode.parentNode;//tr对象        trnode.parentNode.removeChild(trnode);//table对象移除tr对象     // alert("aa");     //节点容器跟Java当中的集合一样,只要是remove(),长度就会变的。这里,需要进行x的复位       x--;     }*/     for(var x=tdnode.length-1;x>=0;x--){       if(tdnode[x].checked){//对象是 checkbox 我们必须要那父级的父级 《tr》,我们需要移除的就是tr对象        var trnode=tdnode[x].parentNode.parentNode;//tr对象        trnode.parentNode.removeChild(trnode);//table对象移除tr对象     }     loading();//调用颜色的设置   } } 

现象一:

反选效果:

删除前:

删除后:

<!DOCTYPE html> <html>  <head>   <!--     这题采用之前的技术,根据table的rows属性,获得数组,然后对数组设置样式,所以颜色就出来了     1,全选复选框,通过var nodess=document.getElementsByName("mail");           for(var x=0;x<nodess.length;x++){             nodess[x].checked=nodes.checked;           }     复选框函数进行     2,按钮全选,反选,和取消全选,可以用一个函数写,传入不同的参数AllBybtn(num)类型即可     函数里面 根据js的特性 非0 和 0 这两种状态,进行设置,为了同步,需要分别进行设置     3,为了显示出当全部选中就默认全选的复选框选中,所以需要对每一个复选框进行设置,采用函数checkBysingle()进行设置     4,删除所选项是需要主要,当前的是checkbox对象,上一级是td--tr--先拿到tr对象,然后对通过tr的父节点删除tr对象       再删除之后,x会变化,可能有些删不到,所以需要将X的值还原,或者从后面开始删除   -->   <title>Mail.html</title>     <style type="text/css">       .one{         background-color:#00ff80;       }       .two{         background-color:#80ff00;       }       .three{         background-color:#0000ff;       }       table th{         background-color:#c0c0c0;       }       table{         width:400px;         border:solid 1px;       }       table tr{         height:50px;       }     </style>     <script type="text/javascript">         var name;         function toaddcolor(){           //颜色设置,           var nodes = document.getElementById("tabid");           var rows1 = nodes.rows;           for (var x = 1; x < rows1.length; x++) {             if (x % 2 == 0) {               rows1[x].className = "one";             }             else {               rows1[x].className = "two";             }           }         }           function addEvent(){             var name;             //当鼠标移上去之后发生相应的变化             var nodes=document.getElementById("tabid");             var rows1=nodes.rows;             for (var x = 1; x < rows1.length; x++) {             rows1[x].onmouseover = function(){               name = this.className;               this.className = "three";             }             rows1[x].onmouseout = function(){               this.className = name;             }              //             alert("bb"); //             alert(rows1[x].getElementsByTagName("input")[0].nodeName);             rows1[x].getElementsByTagName("input")[0].onclick=function(){//每一行的input对象               document.getElementsByName("allItem")[0].indeterminate=true;//让全选的复选框形状发生变化             }           }         }         onload=function(){//在网页加载时候调用           toaddcolor();           addEvent();         }         function allcheck(nodes){//全选checkbox的点击调用这个           var nodess=document.getElementsByName("mail");           for(var x=0;x<nodess.length;x++){             nodess[x].checked=nodes.checked;           }           //多个全选的时候,必须和其他的一个一样           var nodes1=document.getElementsByName("allItem");           for(var x=0;x<nodes1.length;x++){             nodes1[x].checked=nodes.checked;           }         }         function AllBybtn(num){//全选按钮设置           var nodess = document.getElementsByName("mail");           /*多重for 循环不太好,可以根据js里面的特性0  非0            for (var x = 0; x < nodess.length; x++) {             if (num == 1) {               nodess[x].checked = 1;             }else if (num == 2) {                 nodess[x].checked = !nodess[x].checked;               }else if (num == 3) {                   nodess[x].checked = false;                 }           }*/           for(var x=0;x<nodess.length;x++){             if(num<2){               nodess[x].checked=num;                 //让全选的复选框可以 单独写个函数                 var all=document.getElementsByName("allItem");                 for(var y=0;y<all.length;y++){                   if(num==1){                   all[y].checked=num;                 }else{                   all[y].checked=num;                 }               }             }else{               nodess[x].checked=!nodess[x].checked;               var all=document.getElementsByName("allItem");                 for (var y = 0; y < all.length; y++) {                   all[y].checked=0;                 }             }           }         }                  function checkBysingle(){//全部入选之后,全选的自动选中           var flag = true;           var node = document.getElementsByName("mail");           for (var x = 0; x < node.length; x++) {             if (node[x].checked == false) {               flag = false;               break;             }           }                      var all = document.getElementsByName("allItem");           for (var y = 0; y < all.length; y++) {             if (flag) {               all[y].checked = true;             }             else {               all[y].checked = false;             }}}              function DelBybtn(){//删除行         var tdnode=document.getElementsByName("mail");           /*for(var x=0;x<tdnode.length;x++){           if(tdnode[x].checked){//对象是 checkbox 我们必须要那父级的父级 《tr》,我们需要移除的就是tr对象              var trnode=tdnode[x].parentNode.parentNode;//tr对象              trnode.parentNode.removeChild(trnode);//table对象移除tr对象           // alert("aa");           //节点容器跟Java当中的集合一样,只要是remove(),长度就会变的。这里,需要进行x的复位             x--;           }*/                      for(var x=tdnode.length-1;x>=0;x--){             if(tdnode[x].checked){//对象是 checkbox 我们必须要那父级的父级 《tr》,我们需要移除的就是tr对象              var trnode=tdnode[x].parentNode.parentNode;//tr对象              trnode.parentNode.removeChild(trnode);//table对象移除tr对象           }           toaddcolor();           addEvent();         }       }     </script>     </head>    <body>   <table id="tabid">     <tr> <th><input type="checkbox" name="allItem" onclick="allcheck(this)"/> 全选 </th> <th>发件人</th> <th>邮件标题</th> <th>邮件附属信息</th></tr>     <tr> <td><input type="checkbox" name="mail" onclick="checkBysingle(this)" /> </td> <td>张三</td> <td>国庆祝福</td> <td>邮件附属信息1....</td></tr>     <tr> <td><input type="checkbox" name="mail" onclick="checkBysingle(this)" /> </td> <td>Jack</td> <td>假期堵车</td> <td>邮件附属信息2....</td></tr>     <tr> <td><input type="checkbox" name="mail" onclick="checkBysingle(this)" /> </td> <td>Jack</td> <td>假期堵车</td> <td>邮件附属信息3....</td></tr>     <tr> <td><input type="checkbox" name="mail" onclick="checkBysingle(this)" /> </td> <td>Jack</td> <td>假期堵车</td> <td>邮件附属信息4....</td></tr>     <tr> <td><input type="checkbox" name="mail" onclick="checkBysingle(this)" /> </td> <td>Jack</td> <td>假期堵车</td> <td>邮件附属信息5....</td></tr>     <tr> <td><input type="checkbox" name="mail" onclick="checkBysingle(this)" /> </td> <td>Tom</td> <td>一些广告</td> <td>邮件附属信息6....</td></tr>     <tr> <td><input type="checkbox" name="mail" onclick="checkBysingle(this)" /> </td> <td>Tom</td> <td>一些广告</td> <td>邮件附属信息6....</td></tr>     <tr> <td><input type="checkbox" name="mail" onclick="checkBysingle(this)" /> </td> <td>Tom</td> <td>一些广告</td> <td>邮件附属信息6....</td></tr>     <tr> <td><input type="checkbox" name="mail" onclick="checkBysingle(this)" /> </td> <td>Tom</td> <td>一些广告</td> <td>邮件附属信息6....</td></tr>     <tr><td><input type="checkbox"  name="allItem" onclick="allcheck(this)">全选</td>       <td colspan=3><input type="button" value="全选" name="btn1" onclick="AllBybtn(1)" />       <input type="button" value="反选" name="btn2" onclick="AllBybtn(2)"/>       <input type="button" value="取消全选" name="btn3" onclick="AllBybtn(0)"/>       <input type="button" value="删除所选项" name="btn4" onclick="DelBybtn()"/>                     </td>     </tr>   </table>  </body> </html> 

SyntaxHighlighter.highlight();

jQuery-1.9.1源码分析系列(十)事件系统之事件包装

在上篇文章给大家介绍了,本篇继续给大家介绍jquery1.9.1源码分析系列相关知识,具体内容请看下文吧。

首先需要明白,浏览器的原生事件是只读的,限制了jQuery对他的操作。举个简单的例子就能明白为什么jQuery非要构造一个新的事件对象。

  在委托处理中,a节点委托b节点在a被click的时候执行fn函数。当事件冒泡到b节点,执行fn的时候上下文环境需要保证正确,是a节点执行了fn而非b节点。如何保证执行fn的上下文环境是a节点的:看源码(红色部分)

//执行ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ).apply( matched.elem, args );

使用了apply将执行函数的上下文替换成了a节点(matched.elem)。还有一点args[0]即是事件对象event。又如何保证event是a节点的事件的?这就是event.currentTarget这个重要的属性的功能,所以在执行apply之前还做了一步操作

event.currentTarget = matched.elem;

直接更改事件对象的currentTarget属性,这在浏览器本地事件是做不到的。所以才有了基于本地事件构造jQuery的事件对象。

事件分两种:鼠标事件和键盘事件(不知道触摸事件何时能加进来)。看一下这两者的详细属性

  

  其中有些是浏览器自己的,非W3C标准的。jQuery将事件属性分为三块

鼠标和键盘事件共同拥有的属性jQuery.event.props: “altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which”.split(” “)

键盘事件专有的属性jQuery.event.keyHooks.props: “char charCode key keyCode”.split(” “)

鼠标事件专有的属性jQuery.event.mouseHooks.props: “button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement”.split(” “)

a. 构造新的事件对象jQuery.event.fix(originalEvent)

构造新的事件对象分三步完成

第一步,使用到event = new jQuery.Event( originalEvent ),构造新事件对象(不明白new的作用的请点击这里),并在创建事件的时候加上isDefaultPrevented、originalEvent、type 、timeStamp和事件已经被修正过的标记(优化使用,避免不必要的处理)。jQuery.Event(src, props)的源码如下

jQuery.Event = function( src, props ) {  // Allow instantiation without the 'new' keyword  if ( !(this instanceof jQuery.Event) ) {    return new jQuery.Event( src, props );  }  //src为事件对象  if ( src && src.type ) {    this.originalEvent = src;    this.type = src.type;    //事件冒泡的文档可能被标记为阻止默认事件发生;这个函数可以反应是否阻止的标志的正确值    this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||      src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;  //src为事件类型  } else {    this.type = src;  }  //将明确提供的特征添加到事件对象上  if ( props ) {    jQuery.extend( this, props );  }  //创建一个时间戳如果传入的事件不只一个  this.timeStamp = src && src.timeStamp || jQuery.now();  //标记事件已经修正过  this[ jQuery.expando ] = true;};

第一步构造后的事件对象

  

第二步,分辨出当前事件是那种事件,然后将对应的属性一一从浏览器本地事件originalEvent中拷贝过来

 //创建可写的事件对象副本,并格式化一些特征名称  var i, prop, copy,    type = event.type,    originalEvent = event,    fixHook = this.fixHooks[ type ];  if ( !fixHook ) {    this.fixHooks[ type ] = fixHook =    //rmouseEvent=/^(?:mouse|contextmenu)|click/    rmouseEvent.test( type ) ? this.mouseHooks :    //rkeyEvent=/^key/    rkeyEvent.test( type ) ? this.keyHooks :    {};  }  //获得要从原生事件中拷贝过来的属性列表  copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;  ...  //将原生的属性都拷贝到新的事件上  i = copy.length;  while ( i-- ) {    prop = copy[ i ];    event[ prop ] = originalEvent[ prop ];  }

第三步,相关属性的兼容处理

 // IE<9修正target特征值  if ( !event.target ) {    event.target = originalEvent.srcElement || document;  }  // Chrome 23+, Safari?,Target特征值不能是文本节点  if ( event.target.nodeType === 3 ) {    event.target = event.target.parentNode;  }  // IE<9,对于鼠标/键盘事件, 如果metaKey没有定义则设置metaKey==false  event.metaKey = !!event.metaKey;  //调用hooks的filter  return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;

最后那句代码针对鼠标事件和键盘事件做兼容适配处理。

  fixHook.filter可能是jQuery.event.keyHooks.filter

keyHooks.filter: function( event, original ) {  //给键盘事件添加which特征值  if ( event.which == null ) {    event.which = original.charCode != null ? original.charCode : original.keyCode;  }  return event;}

或这jQuery.event.mouseHooks.filter

mouseHooks.filter: function( event, original ) {  var body, eventDoc, doc,  button = original.button,  fromElement = original.fromElement;  //如果事件pageX/Y特征不见了,用可用的clientX/Y来计算出来  if ( event.pageX == null && original.clientX != null ) {    eventDoc = event.target.ownerDocument || document;    doc = eventDoc.documentElement;    body = eventDoc.body;    event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );    event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );  }  //如果必要的话添加relatedTarget特征  if ( !event.relatedTarget && fromElement ) {    event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;  }  //添加点击事件which特征值: 1 === left; 2 === middle; 3 === right  //备注:button不标准,因此不要是使用  if ( !event.which && button !== undefined ) {    event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );  }  return event;}

构建完成的最新事件对象如下(以鼠标事件为例)

  

原生的事件保存在了originalEvent中,target保存了目标节点(委托的节点、事件源),其他信息略过

b. 重载事件方法

  构建新的事件对象event = new jQuery.Event( originalEvent )时,事件会继承jQuery.event.prototype中的方法。来看一看有哪些方法

  

  前面分析了jQuery.event.prototype中重载了stopPropagation方法的作用:处了调用事件对象的阻止冒泡方法以外,还有一个作用就是被委托节点有多个被委托事件处理等待处理时,其中一个事件调用了event.stopPropagation()将阻止后续事件处理的执行。点击这里搜索关键字查看

  preventDefault函数也是有类似的作用。preventDefault函数中增加了这段代码

this.isPropagationStopped = returnTrue;

在触发事件trigger函数和模拟冒泡simulate函数中都会根据isPropagationStopped()判断是否要执行DOM节点的默认操作。源码如下

isImmediatePropagationStopped是stopPropagation特殊用法,isImmediatePropagationStopped会直接阻止掉当前的处理和后面等待执行的事件处理,而stopPropagation会执行完当前的处理,然后阻止后面等待执行的事件处理。

// jQuery.Event基于DOM事件所指定的ECMAScript语言绑定// http://www.w.org/TR//WD-DOM-Level--Events-/ecma-script-binding.htmljQuery.Event.prototype = {  isDefaultPrevented: returnFalse,  isPropagationStopped: returnFalse,  isImmediatePropagationStopped: returnFalse,  preventDefault: function() {    var e = this.originalEvent;    this.isDefaultPrevented = returnTrue;    if ( !e ) {return; }    if ( e.preventDefault ) {      e.preventDefault();    //IE支持    } else {      e.returnValue = false;    }  },  stopPropagation: function() {    var e = this.originalEvent;    this.isPropagationStopped = returnTrue;    if ( !e ) {return; }    if ( e.stopPropagation ) {      e.stopPropagation();    }    // IE支持    e.cancelBubble = true;  },  stopImmediatePropagation: function() {    this.isImmediatePropagationStopped = returnTrue;    this.stopPropagation();  }}

SyntaxHighlighter.highlight();

php实现简单的上传进度条

Web上传文件的三种解决方案分享给大家:

这里我要使用的是form法。通过为表单元素设置enctype=”multipart/form-data”属性,让表单提交的数据以二进制编码的方式提交,在接收此请求的Servlet中用二进制流来获取内容,就可以取得上传文件的内容,从而实现文件的上传。

表单元素的enctype属性指定的是表单数据的编码方式,该属性有3个值:

 

在网上找到了两种方式,PHP配合apc实现和利用uploadprogress实现,这次我要使用的是uploadprogress,点击地址可以下载到php相应版本的dll。安装php_uploadprogress.dll扩展,重启apache。

进度条实现原理:

这里用到了一个iframe无刷新上传文件的方法。

上传完成后的样子如图:

<body>  <div style="padding:20px">   <form action="action.php" enctype="multipart/form-data" method="post" target="iframeUpload">    <iframe name="iframeUpload" width="400" height="400" frameborder='1'></iframe>    <input type="hidden" name="UPLOAD_IDENTIFIER" value="1" />    <input id="file1" name="file1" type="file"/>     <input value="上传" type="submit" onclick="startProgress()"/>    </form>  </div>  <div style="width: 500px; height: 20px;border:1px solid red">    <div style="position: relative; height: 20px; background-color: purple; width: 0%;" class="barinner"></div>  </div>  <div id='showNum'></div>  <div class="prbar">   <div class="prpos barinner"></div>  </div> </body>

上面的HTML代码中要注意下UPLOAD_IDENTIFIER,这个是用来定位查看哪个文件的上传进度的。我这里就写死为一个1,大家可以写成一个php生成的随机数。下面是JS脚本:

 var proNum=0;   var loop=0;   var progressResult = "";   var interval;   function sendURL() {    $.ajax({     type : 'GET',     url : "getprogress.php",     async : true,     cache : false,     dataType : 'json',    data: "progress_key=" + $('input[name=UPLOAD_IDENTIFIER]').val(),    success : function(e) {      proNum=parseInt(e);      if(e){       $('.barinner').css('width', proNum+"%");      $('#showNum').html(proNum+"%");      setTimeout("getProgress()", 200);         }else{       if(interval == 1){        $('.barinner').css('width', "100%");       $('#showNum').html("100%");      }      }    }    });   }   function getProgress(){    loop++;    sendURL();   }   function startProgress(){    interval = 1;   $('.barinner').css('width', proNum+"%");    $('#showNum').html(proNum+"%");  setTimeout("getProgress()", 500);   }

下面是getprogress.php文件中的内容:

 <?php  if (function_exists("uploadprogress_get_info")) {  $info = uploadprogress_get_info($_GET['progress_key']);  if(!empty($info)){   if(($info['bytes_uploaded'] < $info['bytes_total']) && !empty($info['bytes_uploaded']) && !empty($info['bytes_total'])){    $proNum = floor(($info['bytes_uploaded']/$info['bytes_total'])*100);     }else{    $proNum = 100;   }   echo $proNum;  }else{   echo 0;  } }

在上传完成后,我展示了两种进度条的CSS,第二种是用最新的CSS3写的。用到了一些CSS3的动画属性。

 .prbar {  margin:5px;  width:500px;  background-color:#dddddd;  overflow:hidden;    /* 边框效果 */  border: 1px solid #bbbbbb;  -moz-border-radius: 15px;  border-radius: 15px;      /* 为进度条增加阴影效果 */  -webkit-box-shadow: 0px 2px 4px #555555;  -moz-box-shadow: 0px 2px 4px #555555;  box-shadow: 0px 2px 4px #555555;    } /* No rounded corners for Opera, because the overflow:hidden dont work with rounded corners */ doesnotexist:-o-prefocus, .prbar { border-radius:0px; } .prpos {  width:0%;  height:30px;  background-color:#3399ff;  border-right:1px solid #bbbbbb;  /* CSS3 进度条渐变 */  transition: width 2s ease;  -webkit-transition: width 0s ease;  -o-transition: width 0s ease;  -moz-transition: width 0s ease;  -ms-transition: width 0s ease;  /* CSS3 Stripes */  background-image: linear-gradient(135deg,#3399ff 25%,#99ccff 25%,#99ccff 50%, #3399ff 50%, #3399ff 75%,#99ccff 75%,#99ccff 100%);  background-image: -moz-linear-gradient(135deg,#3399ff 25%,#99ccff 25%,#99ccff 50%, #3399ff 50%, #3399ff 75%,#99ccff 75%,#99ccff 100%);  background-image: -ms-linear-gradient(135deg,#3399ff 25%,#99ccff 25%,#99ccff 50%, #3399ff 50%, #3399ff 75%,#99ccff 75%,#99ccff 100%);  background-image: -o-linear-gradient(135deg,#3399ff 25%,#99ccff 25%,#99ccff 50%, #3399ff 50%, #3399ff 75%,#99ccff 75%,#99ccff 100%);  background-image: -webkit-gradient(linear, 100% 100%, 0 0,color-stop(.25, #99ccff), color-stop(.25, #3399ff),color-stop(.5, #3399ff),color-stop(.5, #99ccff),color-stop(.75, #99ccff),color-stop(.75, #3399ff),color-stop(1, #3399ff));  background-image: -webkit-linear-gradient(135deg,#3399ff 25%,#99ccff 25%,#99ccff 50%, #3399ff 50%, #3399ff 75%,#99ccff 75%,#99ccff 100%);  background-size: 40px 40px;  /* Background stripes animation */  animation: bganim 3s linear 2s infinite;  -moz-animation: bganim 3s linear 2s infinite;  -webkit-animation: bganim 3s linear 2s infinite;  -o-animation: bganim 3s linear 2s infinite;  -ms-animation: bganim 3s linear 2s infinite; } @keyframes bganim {  from {background-position:0px;} to { background-position:40px;} } @-moz-keyframes bganim {  from {background-position:0px;} to { background-position:40px;} } @-webkit-keyframes bganim {  from {background-position:0px;} to { background-position:40px;} } @-o-keyframes bganim {  from {background-position:0px;} to { background-position:40px;} } @-ms-keyframes bganim {  from {background-position:0px;} to { background-position:40px;} }

SyntaxHighlighter.highlight();

JavaScript实现自动生成网页元素功能(按钮、文本等)

创建元素的方法:

  • 1、利用createTextNode()创建一个文本对象
  • 2、利用createElement()创建一个标签对象
  • 3、直接利用容器标签中的一个属性:innerHTML—–本质上改该标签容器中的“html代码”,不是我们认为的对象树的操作

详解代码:

<body>  <input type="button" value="创建并添加节点1" onclick="addNode1()"/>  <input type="button" value="创建并添加节点2" onclick="addNode2()"/>  <input type="button" value="创建并添加节点3" onclick="addNode3()"/>  <input type="button" value="remove节点1 " onclick='removenode()'/>  <input type="button" value="replaceNode节点2替换 " onclick='remove2()'/><!--1替换2,并且1没有保留-->  <input type="button" value="clone替换 " onclick='clone()'/>  <div id="div_id1">这是div模块--</div>  <div id="div_id2">必须好好地学习,这样才能让自己有很好的回报</div>  <div id="div_id3">好好干,加油(^ω^)</div>  <div id="div_id4">你懂得区域,实验区域</div>   </body> 

方式一 :创建文本文档

<span style="font-size:18px;">function addNode1(){    //1利用createTextNode()创建一个文本对象    var text=document.createTextNode("这是修改的,创建的文档");    //2获取div对象    var node1=document.getElementById("div_id1");    //添加成div对象的孩子    node1.appendChild(text);}</span><span style="font-size:24px;"> </span> 

方式二:利用createElement()创建一个标签对象

function addNode2(){    //1,利用createElement()创建一个标签对象    var nn=document.createElement("input");    nn.type="button"    nn.value="创建的按钮";    nn.target="_blank";    //2,获得div对象    var node2=document.getElementById("div_id2");    //添加成div对象的孩子    node2.appendChild(nn);   } 

方式三:直接利用容器标签中的一个属性:innerHTML—–本质上改该标签容器中的“html代码”,不是我们认为的对象树的操作

function addNode3(){     var mm=document.getElementById("div_id3");     mm.innerHTML="<a href='http://www.baidu.com'><input type='button' value='新建的按钮'></a>";     } 
  • 删除节点

使用 removeNode 和 removeChild 从元素上删除子结点两种方法,通常采用第二种方法

function removenode(){    var node =document.getElementById("div_id4"); //   alert(node.nodeName);//DIV //  自杀式 node.removeNode(true); //removeNode 从文档层次中删除对象。ie可以出现现象,一般不采用自杀式    node.parentNode.removeChild(node);////通过父节点去删除它的孩子,一般常用    alert("aa");   } 
  • 替换 没有保留替换的那个
function remove2(){    var node1 =document.getElementById("div_id1");    var node2 =document.getElementById("div_id2"); //   node1.replaceNode(node2);//自杀式不通用 ////通过父节点去替换它的孩子:用node1去替换node2    node1.parentNode.replaceChild(node1,node2);//object.replaceChild(oNewNode, oChildNode)   } 
  • clone节点
function clone(){   var node1 =document.getElementById("div_id1");   var node2 =document.getElementById("div_id2");   var node1_2=node1.cloneNode(true);//false只能clone基本的,不会clone下面的其他子节点   //克隆一个对象,默认参数为false。参数为true时,连子节点一起克隆   node1.parentNode.replaceChild(node1_2,node2);  } 

效果图:

<!DOCTYPE html> <html>  <head>  <title>DOM_operation.html</title>  <style type="text/css">   div{    border:#00f solid 1px;    width:200px;    height:100px;   }  </style>  <script type="text/javascript"> //AAAA 增    //方式一 创建文本文档   function addNode1(){    //1利用createTextNode()创建一个文本对象    var text=document.createTextNode("这是修改的,创建的文档");    //2获取div对象    var node1=document.getElementById("div_id1");    //添加成div对象的孩子    node1.appendChild(text);   }      function addNode2(){    //1,利用createElement()创建一个标签对象    var nn=document.createElement("input");    nn.type="button"    nn.value="创建的按钮";    nn.target="_blank";    //2,获得div对象    var node2=document.getElementById("div_id2");    //添加成div对象的孩子    node2.appendChild(nn);   }      //直接利用容器标签中的一个属性:innerHTML-----本质上改该标签容器中的“html代码”,不是我们认为的对象树的操作   function addNode3(){     var mm=document.getElementById("div_id3");     mm.innerHTML="<a href='http://www.baidu.com'><input type='button' value='新建的按钮'></a>";        } //BBBBBB-------删     //删除节点 使用 removeNode 和 removeChild 从元素上删除子结点两种方法,通常采用第二种方法   function removenode(){    var node =document.getElementById("div_id4"); //   alert(node.nodeName);//DIV //  自杀式 node.removeNode(true); //removeNode 从文档层次中删除对象。ie可以出现现象,一般不采用自杀式    node.parentNode.removeChild(node);////通过父节点去删除它的孩子,一般常用    alert("aa");   }   //替换 没有保留替换的那个   function remove2(){    var node1 =document.getElementById("div_id1");    var node2 =document.getElementById("div_id2"); //   node1.replaceNode(node2);//自杀式不通用 ////通过父节点去替换它的孩子:用node1去替换node2    node1.parentNode.replaceChild(node1,node2);//object.replaceChild(oNewNode, oChildNode)   }   function clone(){    var node1 =document.getElementById("div_id1");    var node2 =document.getElementById("div_id2");    var node1_2=node1.cloneNode(true);//false只能clone基本的,不会clone下面的其他子节点    //克隆一个对象,默认参数为false。参数为true时,连子节点一起克隆    node1.parentNode.replaceChild(node1_2,node2);   }  </script>  </head>   <body>  <input type="button" value="创建并添加节点1" onclick="addNode1()"/>  <input type="button" value="创建并添加节点2" onclick="addNode2()"/>  <input type="button" value="创建并添加节点3" onclick="addNode3()"/>  <input type="button" value="remove节点1 " onclick='removenode()'/>  <input type="button" value="replaceNode节点2替换 " onclick='remove2()'/><!--1替换2,并且1没有保留-->  <input type="button" value="clone替换 " onclick='clone()'/>  <div id="div_id1">这是div模块--</div>  <div id="div_id2">必须好好地学习,这样才能让自己有很好的回报</div>  <div id="div_id3">好好干,加油(^ω^)</div>  <div id="div_id4">你懂得区域,实验区域</div>      </body> </html> 

SyntaxHighlighter.highlight();

javascript实现动态统计图开发实例

本文实例讲述了javascript实现动态统计图的代码。分享给大家供大家参考。具体如下:
运行效果截图如下:

具体代码如下
html代码:

<div id="content">    <div class="legend">    <h1>汽车销量:</h1>    <div class="skills">    <ul>    <li class="jq">大众</li>    <li class="css">丰田</li>    <li class="html">别克</li>    <li class="php">福特</li>    <li class="sql">长安</li>    </ul>    </div>    </div>    <div id="diagram"></div>    </div>    <div class="get">    <div class="arc">    <span class="text">大众</span>    <input type="hidden" class="percent" value="95" />    <input type="hidden" class="color" value="#97BE0D" />    </div>    <div class="arc">    <span class="text">丰田</span>    <input type="hidden" class="percent" value="90" />    <input type="hidden" class="color" value="#D84F5F" />    </div>    <div class="arc">    <span class="text">别克</span>    <input type="hidden" class="percent" value="80" />    <input type="hidden" class="color" value="#88B8E6" />    </div>    <div class="arc">    <span class="text">福特</span>    <input type="hidden" class="percent" value="53" />    <input type="hidden" class="color" value="#BEDBE9" />    </div>    <div class="arc">    <span class="text">长安</span>    <input type="hidden" class="percent" value="45" />    <input type="hidden" class="color" value="#EDEBEE" />    </div>    </div>

css代码:

#content {position:absolute;top:50%;left:50%;margin:-340px 0 0 -450px;width:900px;height:600px;}.legend {float:left;width:250px;margin-top:140px;}#content h1 {font-family:'Cabin Sketch', arial, serif;text-shadow:3px 3px 0 #ddd;color:#193340;font-size:40px;margin-bottom:40px;text-align:right;}.skills {float:left;clear:both;width:100%;}.skills ul,.skills li {display:block;list-style:none;margin:0;padding:0;}.skills li {float:right;clear:both;padding:0 15px;height:35px;line-height:35px;color:#fff;margin-bottom:1px;font-size:18px;}
var o = {      init: function () {        this.diagram();      },      random: function (l, u) {        return Math.floor((Math.random() * (u - l + 1)) + l);      },      diagram: function () {        var r = Raphael('diagram', 600, 600),          rad = 73;        r.circle(300, 300, 85).attr({ stroke: 'none', fill: '#193340' });        var title = r.text(300, 300, 'loading...').attr({          font: '20px Arial',          fill: '#fff'        }).toFront();        r.customAttributes.arc = function (value, color, rad) {          var v = 3.6 * value,            alpha = v == 360 ? 359.99 : v,            random = o.random(91, 240),            a = (random - alpha) * Math.PI / 180,            b = random * Math.PI / 180,            sx = 300 + rad * Math.cos(b),            sy = 300 - rad * Math.sin(b),            x = 300 + rad * Math.cos(a),            y = 300 - rad * Math.sin(a),            path = [['M', sx, sy], ['A', rad, rad, 0, +(alpha > 180), 1, x, y]];          return { path: path, stroke: color }        }        $('.get').find('.arc').each(function (i) {          var t = $(this),            color = t.find('.color').val(),            value = t.find('.percent').val(),            text = t.find('.text').text();          rad += 30;          var z = r.path().attr({ arc: [value, color, rad], 'stroke-width': 26 });          z.mouseover(function () {            this.animate({ 'stroke-width': 50, opacity: .75 }, 1000, 'elastic');            if (Raphael.type != 'VML') //solves IE problem    this.toFront();            title.animate({ opacity: 0 }, 500, '>', function () {              this.attr({ text: text + '/n' + value + '%' }).animate({ opacity: 1 }, 500, '<');            });          }).mouseout(function () {            this.animate({ 'stroke-width': 26, opacity: 1 }, 1000, 'elastic');          });        });      }    }    $(function () { o.init(); });

SyntaxHighlighter.highlight();

php计算年龄精准到年月日

<?php /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ class Age {     /**   * 计算年龄精准到年月日   * @param type $birthday   * @return array   */   public function calAge($birthday) {    list($byear, $bmonth, $bday) = explode('-', $birthday);    list($year, $month, $day) = explode('-', date('Y-m-d'));    $bmonth = intval($bmonth);    $bday = intval($bday);    if ($bmonth < 10) {      $bmonth = '0' . $bmonth;    }    if ($bday < 10) {      $bday = '0' . $bday;    }    $bi = intval($byear . $bmonth . $bday);    $ni = intval($year . $month . $day);    $not_birth = 0;    if ($bi > $ni) {      $not_birth = 1;      $tmp = array($byear, $bmonth, $bday);      list($byear, $bmonth, $bday) = array($year, $month, $day);      list($year, $month, $day) = $tmp;      list($bi, $ni) = array($ni, $bi);    }    $years = 0;    while (($bi + 10000) <= $ni) {//先取岁数      $bi += 10000;      $years++;      $byear++;    }//得到岁数后 抛弃年    list($m, $d) = $this->getMD(array($year, $month, $day), array($byear, $bmonth, $bday));    return array('year' => $years, 'month' => $m, 'day' => $d, 'not_birth' => $not_birth);  }   /**   * 只能用于一年内计算   * @param type $ymd   * @param type $bymd   */  public function getMD($ymd, $bymd) {    list($y, $m, $d) = $ymd;    list($by, $bm, $bd) = $bymd;    if (($m . $d) < ($bm . $bd)) {      $m +=12;    }    $month = 0;    while ((($bm . $bd) + 100) <= ($m . $d)) {      $bm++;      $month++;    }    if ($bd <= $d) {//同处一个月      $day = $d - $bd;    } else {//少一个月      $mdays = $bm > 12 ? $this->_getMothDay( ++$by, $bm - 12) : $this->_getMothDay($by, $bm);      $day = $mdays - $bd + $d;    }    return array($month, $day);  }   private function _getMothDay($year, $month) {    switch ($month) {      case 1:      case 3:      case 5:      case 7:      case 8:      case 10:      case 12:        $day = 31;        break;      case 2:        $day = (intval($year % 4) ? 28 : 29); //能被4除尽的为29天其他28天        break;      default:        $day = 30;        break;    }    return $day;  } } $cage = new Age();$test = array(  '1990-06-12',  '1990-07-13',  '1990-08-16',  '1990-10-10',  '1990-10-13',  '1990-10-15',  '1990-11-9',  '1990-11-22',  '2016-11-22',  '2016-8-22',  '2016-10-13',);echo date('Y-m-d');echo '<pre>';foreach($test as $v){  $tmp = $cage->calAge($v);  echo $v , ':', $tmp['year'], '年', $tmp['month'], '月', $tmp['day'], '天', ';', $tmp['not_birth'], '<br>';}echo '</pre>' ;  /*  运行结果:  2015-10-13  1990-06-12:25年4月1天;0  1990-07-13:25年3月0天;0  1990-08-16:25年1月27天;0  1990-10-10:25年0月3天;0  1990-10-13:25年0月0天;0  1990-10-15:24年11月28天;0  1990-11-9:24年11月4天;0  1990-11-22:24年10月21天;0  2016-11-22:1年1月9天;1  2016-8-22:0年10月9天;1  2016-10-13:1年0月0天;1  *  */

SyntaxHighlighter.highlight();

js实现索引图片切换效果

本文实例讲述了js实现索引图片切换效果的代码。分享给大家供大家参考。具体如下:
运行效果截图如下:

具体代码如下

html代码:

<div id="slideshowHolder">    <img" width=100% src="img/1.jpg" />    <img" width=100% src="img/2.jpg" />    <img" width=100% src="img/3.jpg" />    </div>

css代码:

.ft-prev, .ft-next {      background-color: #000;      padding: 0 10px;      color:#fff;    }
$(document).ready(function () {      $('#slideshowHolder').jqFancyTransitions({        effect: '', // wave, zipper, curtain        width: 500, // width of panel        height: 700, // height of panel        strips: 20, // number of strips        delay: 5000, // delay between images in ms        stripDelay: 50, // delay beetwen strips in ms        titleOpacity: 0.7, // opacity of title        titleSpeed: 1000, // speed of title appereance in ms        position: 'alternate', // top, bottom, alternate, curtain        direction: 'fountainAlternate', // left, right, alternate, random, fountain, fountainAlternate        navigation: true, // prev and next navigation buttons        links: true // show images as links      });    });

SyntaxHighlighter.highlight();

超详细的javascript数组方法汇总

一、数组的常用方法
1:join();

将数组转为字符串显示。不输入参数,默认以逗号连接;输入参数,则以参数连接。

var arr=[1,2,3];console.log(arr.join());  // 1,2,3;console.log(arr.join("_")); // 1_2_3;console.log(arr);      // [1,2,3];

原数组不变。

2:reverse();

将数组逆序排列,原数组被修改。

var arr=[1,2,3];var arr2=arr.reverse();console.log(arr2); // [3,2,1];console.log(arr);  // [3,2,1];

3:sort();

默认按升序排列数组项,调用每个数组项的toString()方法,然后比较得到的字符串,从字符串首位开始比较。

var arr=[2,12,14,21,5];console.log(arr.sort());  //[12, 14, 2, 21, 5];

也可以传入一个比较函数作为参数。如果第一个参数应该在前,则比较函数返回一个小于0的值;反之第一个参数在后,这比较函数返回一个大于0的值;如果顺序无关紧要,则比较函数返回0;

var arr=[2,12,14,21,5];console.log(arr.sort(function(a,b){return a-b}));   // [2,5,12,14,21]; var arr1=[2,12,14,21,5];console.log(arr1.sort(function(a,b){return b-a}));  // [21,14,12,5,2];

4:concat();

数组合并,原数组不变。

var arr=[1,2,3];console.log(arr.concat(4,5));      // [1, 2, 3, 4, 5];console.log(arr.concat([4,5],6));    // [1, 2, 3, 4, 5, 6];console.log(arr.concat([[4,5],6]));   // [1, 2, 3, [4, 5],6];console.log(arr);            // [1, 2, 3];

5:slice();

返回部分数组,包含第一个参数所对应的数组项,但不包含第二个参数对应的数组项。如果传入的参数小于0,则从后往前数,最后一项为-1。如果只传入一个参数,则返回的数组包含起始位置到数组结尾的所有元素。原数组不变。

var arr=[1,2,3,4,5];console.log(arr.slice(1,3));   // [2,3];console.log(arr.slice(1));    // [2,3,4,5];console.log(arr.slice(1,-1));  // [2,3,4];console.log(arr);        // [1,2,3,4,5];

6:splice();

数组拼接:

1).删除-用于删除元素,两个参数,第一个参数(要删除第一项的位置),第二个参数(要删除的项数);

2).插入-向数组指定位置插入任意项元素。三个参数,第一个参数(其实位置),第二个参数(0),第三个参数(插入的项);

3).替换-向数组指定位置插入任意项元素,同时删除任意数量的项,三个参数。第一个参数(起始位置),第二个参数(删除的项数),第三个参数(插入任意数量的项);

splice()返回一个由删除元素组成的数组,或者如果没有删除元素就返回一个空数组。原数组被修改。

var arr=[1,2,3,4,5,6];console.log(arr.splice(2));        // [3,4,5,6];console.log(arr);             // [1,2];console.log(arr.splice(2,0,3,4,5,6));  // [];console.log(arr);               // [1,2,3,4,5,6];console.log(arr.splice(2,2));       // [3,4];console.log(arr);              // [1,2,5,6];

7:push()和pop()方法,unshift()和shift()方法;

push()和pop() 栈方法,后进先出。原数组都发生改变。

push()方法在数组的尾部添加一个或多个元素,并返回数组新的长度。

pop()方法删除数组的最后一个元素,减小数组的长度并返回删除的值。

unshift()和shift()队列方法,先进先出。原数组都发生改变。

unshift()方法在数组的头部添加一个或多个元素,并改变已存在元素的索引,然后返回数组新的长度。

shift()方法删除数组的第一个元素并将其返回,并改变已存在元素的索引。

var arr=[1,2,3];console.log(arr.push(4));  //4;console.log(arr);       //[1,2,3,4];console.log(arr.pop());   //4;console.log(arr);       //[1,2,3];console.log(arr.unshift(0)); //4;console.log(arr);       //[0,1,2,3];console.log(arr.shift());  //0;console.log(arr);       //[1,2,3];

8:forEach();

forEach()里第一个参数为该集合里的元素,第二个参数为集合里的索引,第三个参数为集合本身。

9:map();

map()对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组,原数组未被修改。

10:every()

对数组的每一项运行给定函数,如果该函数对每一项都返回true,则返回true。

11:some()

对数组的每一项运行给定函数,如果该函数对任一项返回true,则返回true。

12:filter()

对数组中的每一项运行给定函数,返回该函数会返回true的项组成的数组。

13:reduce()和reduceRight();
两个方法都会迭代数组的所有项,然后构建一个最终返回的值。其中reduce()方法从数组的第一项开始,逐个遍历到最后。而reduceRight()则从数组的最后一项开始,向前遍历到第一项。数组未被修改。

 

二、扩展方法
1:数组去重

function unique(array){  return array.filter(function(item,index){    return array.indexOf(item)==index;  })};var arr=[1,2,3,3,4,2,1,5];console.log(unique(arr));    //[1,2,3,4,5];function unique(arr){  var arr2=[arr[0]],    len=arr.length;  if(!len){    return;  }  for(var i=0;i<len;i++){    arr2.join(" ").indexOf(arr[i])<0?arr2.push(arr[i]):"";  }  return arr2;}var arr=[1,2,3,3,4,2,1,5];console.log(uniq(arr));   //[1,2,3,4,5]    
function deleteNullInArray(array){  return array.filter(function(item){    return item!=null;  }) }var arr=[1,2,null,,,5];console.log(deleteNullInArray(arr));  //[1,2,5];

SyntaxHighlighter.highlight();

使用PHP实现生成HTML静态页面

从PHP生成HTML静态页面并存储到以年份和月份为名称创建的目录。

读取全部数据批量生成,全部生成后弹出提示。

可指定批次生成数量,建议不超过800,否则执行速度会有问题。

(出于众所周知的原因,涉及到数据库的数据字段名称做了改动,并且为了代码明晰去掉了参数过滤的部分)

 说明:原动态地址为 moban.php?id=1 ,生成后地址为 html/200808/sell_1.html 。page.php为分页程序,本博客中有发布。

页面使用方式,将本代码保存为make.php,使用方法为浏览器访问 make.php?t=数量&pg=页面;例如 make.php?t=300&pg=2,即每次生成300条数据,从数据列表第2页开始生成,即跳过前面300条。如果不加任何参数,直接访问make.php,则默认每次生成200条,从第一页开始生成。

完整实例:

<?php if($_GET[pg]==''){ $aa=1; }else{ $aa=$_GET[pg]; } include("admin/conn.php"); require_once("page.php"); $result=mysql_query("select * from 2carsell "); $totle=mysql_num_rows($result); $pagelist = $_GET[t]; if($_GET[t]==''){ $pagelist='200'; }else{ $pagelist=$_GET[t]; } $pager = new Pager($totle,$pagelist); $datastat=" 共 <b>".$pager->countall."</b> 条,每次生成 <b>".$pager->countlist."</b> 条,共需生成 <b>".$pager->page."</b> 次";//数据统计 $bb=$pager->page; $pagenav=$pager->backstr.$pager->thestr.$pager->nextstr; $limitFrom = $pagelist*($pager->pg-1); $result=mysql_query("select * from 2carsell ORDER BY id DESC limit $limitFrom,$pagelist"); ?> <center><div style="font-size:14px;"><b>第 <font color=red><?echo $aa?></font > 次页面生成中..<? echo $datastat?></b></div><br> <? //php生成静态页面 print "<center><textarea name=textarea class=textarea style='width:520px;height:455px'>"; while($datauser=mysql_fetch_array($result)){   $iid=$datauser[id]; $html = file_get_contents("/moban.php?id=".$iid.""); $sql="select * from 2carsell where id=$iid"; $data=mysql_fetch_array(mysql_query($sql)); $path=date("Ym",$data[PutDate]); $testdir="html/".$path; if(file_exists ($testdir)): else: mkdir ($testdir, 0777); echo "目录".$testdir."创建成功!<br>"; endif;   $filename = "html/$path/sell_$iid.html";   // 使用写入模式打开$filename if (!$handle = fopen($filename, 'w')) { print "不能打开文件 $filename"; exit; }   if (is_writable($filename)) {   // 将$html写入到我们打开的文件中。 if (!fwrite($handle, $html)) { print "不能写入到文件 $filename"; exit; }   print "文件 $filename 更新成功!/n/r";   fclose($handle);   } else { print "文件 $filename 不可写"; } ?> <? }?> </textarea> <br><br> <div style="font-size=12px"><? echo $datastat." "?></div><br><br> <? $aa=$aa+1; if($aa>$bb){ echo '<font color=blue>恭喜,所有页面生成完毕!</font>'; echo "<script>alert('所有文档生成/更新完毕!')</script>"; }else{ echo "<Script> window.location='make.php?t=$pagelist&pg=$aa'; </script>"; } ?> 

看完实例,我们接着来分析分析

一般来说 用php转换输出html页面有两种办法 引用大虾的文章如下:

第一种:利用模板。目前PHP的模板可以说是很多了,有功能强大的smarty,还有简单易用的smarttemplate等。它们每一种模板,都有一个获取输出内容的函数。我们生成静态页面的方法,就是利用了这个函数。用这个方法的优点是,代码比较清晰,可读性好。

这里我用smarty做例子,说明如何生成静态页:

<?phprequire("smarty/Smarty.class.php");$t = new Smarty;$t->assign("title","Hello World!");$content = $t->fetch("templates/index.htm");//这里的 fetch() 就是获取输出内容的函数,现在$content变量里面,就是要显示的内容了$fp = fopen("archives/2005/05/19/0001.html", "w");fwrite($fp, $content);fclose($fp);?>

第二种方法:利用ob系列的函数。这里用到的函数主要是 ob_start(), ob_end_flush(), ob_get_content(),其中ob_start()是打开浏览器缓冲区的意思,打开缓冲后,所有来自PHP程序的非文件头信息均不会发送,而是保存在内部缓冲区,直到你使用了ob_end_flush().而这里最重要的一个函数,就是ob_get_contents(),这个函数的作用是获取缓冲区的内容,相当于上面的那个fetch(),道理一样的。

<?phpob_start();echo "Hello World!";$content = ob_get_contents();//取得php页面输出的全部内容$fp = fopen("archives/2005/05/19/0001.html", "w");fwrite($fp, $content);fclose($fp);?>

我选用的第2种方法 也就是用ob系列的函数

我刚开始看这个的时候有点不太明白 后来才知道ob是output buffering的意思 也就是输出缓存

当你准备输出的时候 所有的数据都保存在ob里面 服务器解析完php以后 把所有要输出到客户端的html代码都存放在ob里面 如果我们要输出html静态页面 只要把缓存取出来写入一个html页面即可

所以原理其实是很简单的

这里用到了几个函数 由于我初学php 很多函数我还不了解 所以这里也说明一下 希望可以帮助大家

ob_start():开始“捕捉”缓存 也就是从这里开始 打开浏览器的缓存

ob_end_flush():关闭浏览器缓存

ob_get_content():读取缓存内容

fopen(”文件路径”,”打开模式”)打开文件 这个函数的打开模式有好几种 下面介绍几种主要的模式:

“r” 只读方式打开,将文件指针指向文件头。

“r+” 读写方式打开,将文件指针指向文件头。

“w” 写入方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。

“w+” 读写方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。

fwrite(”文件名称”,”写入内容”) 写入文件

fclose() 关闭文件

<?phpob_start();//打开浏览器缓存 //下面是读取xml数据$parser = xml_parser_create(); //创建一个parser编辑器xml_set_element_handler($parser, "startElement", "endElement");//设立标签触发时的相应函数 这里分别为startElement和endElenmentxml_set_character_data_handler($parser, "characterData");//设立数据读取时的相应函数$xml_file="1.xml";//指定所要读取的xml文件,可以是url$filehandler = fopen($xml_file, "r");//打开文件  while ($data = fread($filehandler, 4096)) {xml_parse($parser, $data, feof($filehandler));}//每次取出4096个字节进行处理 fclose($filehandler);xml_parser_free($parser);//关闭和释放parser解析器  $name=false;$position=false;function startElement($parser_instance, $element_name, $attrs) //起始标签事件的函数{global $name,$position;if($element_name=="NAME"){$name=true;$position=false;echo "名字:";}if($element_name=="POSITION"){$name=false;$position=true;echo "职位:";}} function characterData($parser_instance, $xml_data) //读取数据时的函数{global $name,$position;if($position)echo $xml_data."<br />";if($name)echo $xml_data."<br />";} function endElement($parser_instance, $element_name) //结束标签事件的函数{global $name,$position;$name=false;$position=false;}//xml数据读取完毕 $htmlname=$id.".html";//$id可以自己定义 这里代表用户传来的id$htmlpath="archives/".$htmlname; //设置路径变量$content = ob_get_contents();//取得php页面输出的全部内容$fp = fopen($htmlpath, "w");fwrite($fp, $content);fclose($fp);?>

SyntaxHighlighter.highlight();

基于PHP给大家讲解防刷票的一些技巧

刷票行为,一直以来都是个难题,无法从根本上防止。

但是我们可以尽量减少刷票的伤害,比如:通过人为增加的逻辑限制。

基于 PHP,下面介绍防刷票的一些技巧:

1、使用CURL进行信息伪造

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, “”);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(‘X-FORWARDED-FOR:8.8.8.8’, ‘CLIENT-IP:8.8.8.8’));
curl_setopt($ch, CURLOPT_REFERER, ” “);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_USERAGENT, “Mozilla/5.0 (compatible; MSIE 6.0; Windows NT 5.0)”);
$out = curl_exec($ch);
curl_close($ch);

2、验证码:采用非常复杂的验证码

确切的说验证码的出现不是针对于人,而是针对于机器。通过复杂度和识别难易度的控制来阻拦掉一部分刷票机,从而减少刷票的发生。但随着软件技术、识别技术的发展越来越多的验证码面对着先进的刷票软件也失去了其防范的作用、但是专业刷票机可以攻破。如果不用验证码,投票基本就歇菜了,验证码获取方式,采用异步加载,即点击输入框时,才去请求,投票成功后,删除验证码的 Session

3、限时投票

比如:从早8点至晚23 点

4、设置投票间隔

用户投票后,需要隔多长时间才能继续投。

很多投票站点基本上都有这个限制,但是对于更改 IP的攻击,就没办法了

5、投票结果展示:延迟展示,友好展示

页面上投票,JS 立马加1,但是刷新页面,不一定立马展示最新投票结果,返回状态给页面(感谢您的投票!或者 投票成功!至于有没有成功,另说了!)

6、扣量逻辑:常见于一些软件评选之类的投票

这是个杀手锏,后台跑脚本实时监控异常增长(刷票)的项,然后实施扣量逻辑
即对于这个项,投 10 票才算一票

7、Cookie:常用的手段。比较低级

投票后,在客户端写入 Cookie,下次投票时判断 Cookie 是否存在
但是,这种方式非常容易攻破,因为 Cookie 可删除

8、加密选项 ID:对一些投票选项的ID,进行随机加密

加密算法,加Salt,并且设置有效时间,比如5分钟内
服务器端进行解密并且验证

9、nginx限制链接数

ngx_http_limit_conn_module
ngx_http_limit_req_module
nginx_limit_speed_module

可以使用这三个模块来限制,不过这不是一个好的解决方法

10、iptables限制

/sbin/iptables -A INPUT -p tcp –dport 80 –syn -m recent –name webpool –rcheck –seconds 60 –hitcount 10 -j DROP
/sbin/iptables -A INPUT -p tcp –dport 80 –syn -m recent –name webpool –set -j ACCEPT
/sbin/iptables -t filter -A INPUT -p tcp –dport 80 –tcp-flags FIN,SYN,RST,ACK SYN -m connlimit –connlimit-above 10 –connlimit-mask
 32 -j REJECT

#!/bin/bash# Date: 2015-09-29# # Author: cpz@erongtu.comshopt -s -o nounsetexport PATH=/usr/bin/:/biniptables_log="/tmp/iptables_conf.log"/sbin/iptables -A INPUT -p tcp --dport 80 --syn -m recent --name webpool --rcheck --seconds 60 --hitcount 10 -j DROP/sbin/iptables -A INPUT -p tcp --dport 80 --syn -m recent --name webpool --set -j ACCEPT/sbin/iptables -t filter -A INPUT -p tcp --dport 80 --tcp-flags FIN,SYN,RST,ACK SYN -m connlimit --connlimit-above 10 --connlimit-mask 32 -j REJECTwhile [ true ]; do  #sleep 1  for IP in `netstat -an | grep -i ':80 '|grep 'ESTAB' | awk '{print $5}' | cut -d : -f 1 | sort | uniq -c | awk '{if($1 > 30 && $2!="127.0.0.1" ) {print $2}}'`  do    /sbin/iptables -L -n | grep $IP >/dev/null || /sbin/iptables -A INPUT -p tcp --dport 80 -s $IP -j DROP    echo "/sbin/iptables -A INPUT -p tcp -s $IP -j DROP" >> ${iptables_log}  donedone

SyntaxHighlighter.highlight();

分享经典的JavaScript开发技巧

JavaScript开发经典技巧分享给大家:

1、首次为变量赋值时务必使用var关键字

变量没有声明而直接赋值得话,默认会作为一个新的全局变量,要尽量避免使用全局变量。
2、使用===取代==

==和!=操作符会在需要的情况下自动转换数据类型。但===和!==不会,它们会同时比较值和数据类型,这也使得它们要比==和!=快。
[10] === 10    // is false
[10]  == 10    // is true
’10’ == 10     // is true
’10’ === 10    // is false
 []   == 0     // is true
 [] ===  0     // is false
 ” == false   // is true but true == “a” is false
 ” === false  // is false
3、underfined、null、0、false、NaN、空字符串的逻辑结果均为false

4、行尾使用分号

实践中最好还是使用分号,忘了写也没事,大部分情况下JavaScript解释器都会自动添加。

5、使用对象构造器

function Person(firstName, lastName){ this.firstName = firstName; this.lastName = lastName;}var Saad = new Person("Saad", "Mousliki");

6、小心使用typeof、instanceof和contructor

  • typeof:JavaScript一元操作符,用于以字符串的形式返回变量的原始类型,注意,typeof null也会返回object,大多数的对象类型(数组Array、时间Date等)也会返回object
  • contructor:内部原型属性,可以通过代码重写
  • instanceof:JavaScript操作符,会在原型链中的构造器中搜索,找到则返回true,否则返回false
var arr = ["a", "b", "c"];typeof arr; // 返回 "object" arr instanceof Array // truearr.constructor(); //[]

7、使用自调用函数

函数在创建之后直接自动执行,通常称之为自调用匿名函数(Self-Invoked Anonymous Function)或直接调用函数表达式(Immediately Invoked Function Expression )。格式如下:

(function(){ // 置于此处的代码将自动执行})(); (function(a,b){ var result = a+b; return result;})(10,20)

8、从数组中随机获取成员

var items = [12, 548 , 'a' , 2 , 5478 , 'foo' , 8852, , 'Doe' , 2145 , 119];var randomItem = items[Math.floor(Math.random() * items.length)];

9、获取指定范围内的随机数

这个功能在生成测试用的假数据时特别有数,比如介与指定范围内的工资数。

代码如下:
var x = Math.floor(Math.random() * (max – min + 1)) + min;

10、生成从0到指定值的数字数组

var numbersArray = [] , max = 100;for( var i=1; numbersArray.push(i++) < max;); // numbers = [1,2,3 ... 100]

11、生成随机的字母数字字符串

function generateRandomAlphaNum(len) { var rdmString = ""; for( ; rdmString.length < len; rdmString += Math.random().toString(36).substr(2)); return rdmString.substr(0, len);}

12、打乱数字数组的顺序

var numbers = [5, 458 , 120 , -215 , 228 , 400 , 122205, -85411];numbers = numbers.sort(function(){ return Math.random() - 0.5});/* numbers 数组将类似于 [120, 5, 228, -215, 400, 458, -85411, 122205] */

这里使用了JavaScript内置的数组排序函数,更好的办法是用专门的代码来实现(如Fisher-Yates算法),可以参见StackOverFlow上的这个讨论。
13、字符串去空格

Java、C#和PHP等语言都实现了专门的字符串去空格函数,但JavaScript中是没有的,可以通过下面的代码来为String对象函数一个trim函数
String.prototype.trim = function(){return this.replace(/^/s+|/s+$/g, “”);};
新的JavaScript引擎已经有了trim()的原生实现。
14、数组之间追加

var array1 = [12 , "foo" , {name "Joe"} , -2458];var array2 = ["Doe" , 555 , 100];Array.prototype.push.apply(array1, array2);/* array1 值为 [12 , "foo" , {name "Joe"} , -2458 , "Doe" , 555 , 100] */

15、对象转换为数组

代码如下:
var argArray = Array.prototype.slice.call(arguments);

16、验证是否是数字

function isNumber(n){ return !isNaN(parseFloat(n)) && isFinite(n);}

17、验证是否是数组

function isArray(obj){ return Object.prototype.toString.call(obj) === '[object Array]' ;}

但如果toString()方法被重写过得话,就行不通了。也可以使用下面的方法:

代码如下:
Array.isArray(obj); // its a new Array method

如果在浏览器中没有使用frame,还可以用instanceof,但如果上下文太复杂,也有可能出错。

var myFrame = document.createElement('iframe');document.body.appendChild(myFrame);var myArray = window.frames[window.frames.length-1].Array;var arr = new myArray(a,b,10); // [a,b,10] // myArray 的构造器已经丢失,instanceof 的结果将不正常// 构造器是不能跨 frame 共享的arr instanceof Array; // false

18、获取数组中的最大值和最小值

var numbers = [5, 458 , 120 , -215 , 228 , 400 , 122205, -85411]; var maxInNumbers = Math.max.apply(Math, numbers); var minInNumbers = Math.min.apply(Math, numbers);

详细内容可以参考这篇文章:

19、清空数组

var myArray = [12 , 222 , 1000 ]; myArray.length = 0; // myArray will be equal to [].

20、不要直接从数组中delete或remove元素

如果对数组元素直接使用delete,其实并没有删除,只是将元素置为了undefined。数组元素删除应使用splice
切忌:

var items = [12, 548 ,'a' , 2 , 5478 , 'foo' , 8852, , 'Doe' ,2154 , 119 ]; items.length; // return 11 delete items[3]; // return true items.length; // return 11 /* items 结果为 [12, 548, "a", undefined × 1, 5478, "foo", 8852, undefined × 1, "Doe", 2154, 119] */

而应:

var items = [12, 548 ,'a' , 2 , 5478 , 'foo' , 8852, , 'Doe' ,2154 , 119 ]; items.length; // return 11 items.splice(3,1) ; items.length; // return 10 /* items 结果为 [12, 548, "a", 5478, "foo", 8852, undefined × 1, "Doe", 2154, 119]

删除对象的属性时可以使用delete。
21、使用length属性截断数组

前面的例子中用length属性清空数组,同样还可用它来截断数组:

var myArray = [12 , 222 , 1000 , 124 , 98 , 10 ]; myArray.length = 4; // myArray will be equal to [12 , 222 , 1000 , 124].

与此同时,如果把length属性变大,数组的长度值变会增加,会使用undefined来作为新的元素填充。length是一个可写的属性。

myArray.length = 10; // the new array length is 10 myArray[myArray.length - 1] ; // undefined

22、在条件中使用逻辑与或

var foo = 10; foo == 10 && doSomething(); // is the same thing as if (foo == 10) doSomething(); foo == 5 || doSomething(); // is the same thing as if (foo != 5) doSomething();

逻辑或还可用来设置默认值,比如函数参数的默认值。

function doSomething(arg1){  arg1 = arg1 || 10; // arg1 will have 10 as a default value if it's not already set}

23、使得map()函数方法对数据循环

var squares = [1,2,3,4].map(function (val) {  return val * val; }); // squares will be equal to [1, 4, 9, 16]

24、保留指定小数位数

var num =2.443242342;num = num.toFixed(4); // num will be equal to 2.4432

注意,toFixec()返回的是字符串,不是数字。
25、浮点计算的问题

0.1 + 0.2 === 0.3 // is false
9007199254740992 + 1 // is equal to 9007199254740992
9007199254740992 + 2 // is equal to 9007199254740994

为什么呢?因为0.1+0.2等于0.30000000000000004。JavaScript的数字都遵循IEEE 754标准构建,在内部都是64位浮点小数表示,具体可以参见JavaScript中的数字是如何编码的.
可以通过使用toFixed()和toPrecision()来解决这个问题。
26、通过for-in循环检查对象的属性

下面这样的用法,可以防止迭代的时候进入到对象的原型属性中。

for (var name in object) {  if (object.hasOwnProperty(name)) {   // do something with name } }

27、逗号操作符

var a = 0; var b = ( a++, 99 ); console.log(a); // a will be equal to 1 console.log(b); // b is equal to 99

28、临时存储用于计算和查询的变量

在jQuery选择器中,可以临时存储整个DOM元素。

var navright = document.querySelector('#right'); var navleft = document.querySelector('#left'); var navup = document.querySelector('#up'); var navdown = document.querySelector('#down');

29、提前检查传入isFinite()的参数

isFinite(0/0) ; // falseisFinite("foo"); // falseisFinite("10"); // trueisFinite(10); // trueisFinite(undefined); // falseisFinite(); // falseisFinite(null); // true,这点当特别注意

30、避免在数组中使用负数做索引

var numbersArray = [1,2,3,4,5]; var from = numbersArray.indexOf("foo") ; // from is equal to -1 numbersArray.splice(from,2); // will return [5]

注意传给splice的索引参数不要是负数,当是负数时,会从数组结尾处删除元素。
31、用JSON来序列化与反序列化

var person = {name :'Saad', age : 26, department : {ID : 15, name : "R&D"} };var stringFromPerson = JSON.stringify(person);/* stringFromPerson 结果为 "{"name":"Saad","age":26,"department":{"ID":15,"name":"R&D"}}" */var personFromString = JSON.parse(stringFromPerson);/* personFromString 的值与 person 对象相同 */

32、不要使用eval()或者函数构造器

eval()和函数构造器(Function consturctor)的开销较大,每次调用,JavaScript引擎都要将源代码转换为可执行的代码。

var func1 = new Function(functionCode);var func2 = eval(functionCode);

33、避免使用with()

使用with()可以把变量加入到全局作用域中,因此,如果有其它的同名变量,一来容易混淆,二来值也会被覆盖。
34、不要对数组使用for-in

避免:

var sum = 0; for (var i in arrayNumbers) {  sum += arrayNumbers[i]; }

而是:

var sum = 0; for (var i = 0, len = arrayNumbers.length; i < len; i++) {  sum += arrayNumbers[i]; }

另外一个好处是,i和len两个变量是在for循环的第一个声明中,二者只会初始化一次,这要比下面这种写法快:
for (var i = 0; i < arrayNumbers.length; i++)
35、传给setInterval()和setTimeout()时使用函数而不是字符串

如果传给setTimeout()和setInterval()一个字符串,他们将会用类似于eval方式进行转换,这肯定会要慢些,因此不要使用:

setInterval('doSomethingPeriodically()', 1000); setTimeout('doSomethingAfterFiveSeconds()', 5000);

而是用:

setInterval(doSomethingPeriodically, 1000); setTimeout(doSomethingAfterFiveSeconds, 5000);

36、使用switch/case代替一大叠的if/else

当判断有超过两个分支的时候使用switch/case要更快一些,而且也更优雅,更利于代码的组织,当然,如果有超过10个分支,就不要使用switch/case了。
37、在switch/case中使用数字区间

其实,switch/case中的case条件,还可以这样写:

function getCategory(age) {  var category = "";  switch (true) {   case isNaN(age):    category = "not an age";    break;   case (age >= 50):    category = "Old";    break;   case (age <= 20):    category = "Baby";    break;   default:    category = "Young";    break;  };  return category; } getCategory(5); // 将返回 "Baby"

38、使用对象作为对象的原型

下面这样,便可以给定对象作为参数,来创建以此为原型的新对象:

function clone(object) {  function OneShotConstructor(){};  OneShotConstructor.prototype = object;  return new OneShotConstructor(); } clone(Array).prototype ; // []

39、HTML字段转换函数

function escapeHTML(text) {  var replacements= {"<": "<", ">": ">","&": "&", "/"": """};       return text.replace(/[<>&"]/g, function(character) {   return replacements[character];  }); }

40、不要在循环内部使用try-catch-finally

try-catch-finally中catch部分在执行时会将异常赋给一个变量,这个变量会被构建成一个运行时作用域内的新的变量。
切忌:

var object = ['foo', 'bar'], i; for (i = 0, len = object.length; i <len; i++) {  try {   // do something that throws an exception  }  catch (e) {   // handle exception  } }

而应该:

var object = ['foo', 'bar'], i; try {  for (i = 0, len = object.length; i <len; i++) {   // do something that throws an exception  } } catch (e) {  // handle exception }

41、使用XMLHttpRequests时注意设置超时

XMLHttpRequests在执行时,当长时间没有响应(如出现网络问题等)时,应该中止掉连接,可以通过setTimeout()来完成这个工作:

var xhr = new XMLHttpRequest (); xhr.onreadystatechange = function () {  if (this.readyState == 4) {   clearTimeout(timeout);   // do something with response data  } } var timeout = setTimeout( function () {  xhr.abort(); // call error callback }, 60*1000 /* timeout after a minute */ ); xhr.open('GET', url, true); xhr.send();

同时需要注意的是,不要同时发起多个XMLHttpRequests请求。
42、处理WebSocket的超时

通常情况下,WebSocket连接创建后,如果30秒内没有任何活动,服务器端会对连接进行超时处理,防火墙也可以对单位周期没有活动的连接进行超时处理。
为了防止这种情况的发生,可以每隔一定时间,往服务器发送一条空的消息。可以通过下面这两个函数来实现这个需求,一个用于使连接保持活动状态,另一个专门用于结束这个状态。

var timerID = 0; function keepAlive() {  var timeout = 15000;  if (webSocket.readyState == webSocket.OPEN) {   webSocket.send('');  }  timerId = setTimeout(keepAlive, timeout); } function cancelKeepAlive() {  if (timerId) {   cancelTimeout(timerId);  } }

SyntaxHighlighter.highlight();

使用PHP uniqid函数生成唯一ID

生成唯一ID的应用场景非常普遍,如临时缓存文件名称,临时变量,临时安全码等,uniqid()函数基于以微秒计的当前时间,生成一个唯一的 ID。由于生成唯一ID与微秒时间关联,因此ID的唯一性非常可靠。

生成的唯一ID默认返回的字符串有 13 个字符串长,如果不定义唯一ID的前缀,最多可返回23个字符串长,如果再结合md5()函数,生成的唯一ID可靠性将更高,这种生成的ID比随机性的ID 最大优点在于可实现排序,特别是一些需要存储在数据库中的值。

一,函数原型

string uniqid ( [string prefix [, bool more_entropy]] )

可定义唯一ID的前缀与长度

二,版本兼容

PHP 3, PHP 4, PHP 5

三,函数基础用法与实例

1,生成一个唯一ID

<?php echo uniqid(); ?> 

2,结合md5()函数生成一个唯一ID

<?php echo md5(uniqid()); ?> 

输出:dfbc5c8c6438de075da28b3c8a413fd0

3,生成多个唯一ID,由于是以微秒计

<?php echo uniqid(); echo uniqid(); echo uniqid(); ?> 

输出:

4bfd0e375396b
4bfd0e3753981
4bfd0e3753983

由生成的结果来看,唯一ID之间具有可排序性的。
使用uniqid()函数生成唯一ID既能用于生成临时性ID也能用于生成永久性唯一ID(存储数据库)。

ps:php 生成唯一id的几种解决方法

下面小编给大家整理了三种解决办法,具体内容如下:

1、md5(time() . mt_rand(1,1000000));

  这种方法有一定的概率会出现重复

2、php内置函数uniqid()

  uniqid() 函数基于以微秒计的当前时间,生成一个唯一的 ID.

  w3school参考手册有一句话:”由于基于系统时间,通过该函数生成的 ID 不是最佳的。如需生成绝对唯一的 ID,请使用 md5() 函数”。

  下面方法返回结果类似:5DDB650F-4389-F4A9-A100-501EF1348872

function uuid() {  if (function_exists ( 'com_create_guid' )) {    return com_create_guid ();  } else {    mt_srand ( ( double ) microtime () * 10000 ); //optional for php 4.2.0 and up.随便数播种,4.2.0以后不需要了。    $charid = strtoupper ( md5 ( uniqid ( rand (), true ) ) ); //根据当前时间(微秒计)生成唯一id.    $hyphen = chr ( 45 ); // "-"    $uuid = '' . //chr(123)// "{"substr ( $charid, 0, 8 ) . $hyphen . substr ( $charid, 8, 4 ) . $hyphen . substr ( $charid, 12, 4 ) . $hyphen . substr ( $charid, 16, 4 ) . $hyphen . substr ( $charid, 20, 12 );    //.chr(125);// "}"    return $uuid;  }}
public function create_guid($namespace = '') {    static $guid = '';  $uid = uniqid("", true);  $data = $namespace;  $data .= $_SERVER['REQUEST_TIME'];  $data .= $_SERVER['HTTP_USER_AGENT'];  $data .= $_SERVER['LOCAL_ADDR'];  $data .= $_SERVER['LOCAL_PORT'];  $data .= $_SERVER['REMOTE_ADDR'];  $data .= $_SERVER['REMOTE_PORT'];  $hash = strtoupper(hash('ripemd128', $uid . $guid . md5($data)));  $guid = '{' .       substr($hash, 0, 8) .      '-' .      substr($hash, 8, 4) .      '-' .      substr($hash, 12, 4) .      '-' .      substr($hash, 16, 4) .      '-' .      substr($hash, 20, 12) .      '}';  return $guid; }

SyntaxHighlighter.highlight();

php生成唯一数字id的方法汇总

关于生成唯一数字ID的问题,是不是需要使用rand生成一个随机数,然后去数据库查询是否有这个数呢?感觉这样的话有点费时间,有没有其他方法呢?

当然不是,其实有两种方法可以解决。

1. 如果你只用php而不用数据库的话,那时间戳+随机数是最好的方法,且不重复;

2. 如果需要使用数据库,即你还需要给这个id关联一些其他的数据。那就给MySQL数据库中的表的id一个AUTO_INCREMENT(自增)属性,每次插入一条数据时,id自动+1,然后使用mysql_insert_id()或LAST_INSERT_ID()返回这个自增后的id。

当然,这个问题已经有现成的解决方法了,使用php uuid扩展就能完美解决这个问题,这个扩展能生成唯一的完全数字签名。。

如果你不使用composer请参考,

如果你的项目是基于composer搭建的,那么请参考

具体的源码我就不搬运了,小伙伴们自己取下来就可以直接使用了

PHP生成唯一标识符代码示例:

< ? //生成唯一标识符  //sha1()函数, "安全散列算法(SHA1)"  function create_unique() {  $data = $_SERVER['HTTP_USER_AGENT'] . $_SERVER['REMOTE_ADDR']  .time() . rand();  return sha1($data);  //return md5(time().$data);  //return $data;  } ?> 

PHP生成唯一标识符函数描述及例子

< ?  $newhash = create_unique();  echo $newhash;  ?>
/* * 信号量(Semaphore)。 * 这是一个包装类,用于解决不同平台下对“信号量”的不同实现方式。 * 目前这个类只是象征性的,在 Windows 平台下实际是空跑(并没有真的实现互斥)。 */class SemWrapper{  private $hasSemSupport;  private $sem;  const SEM_KEY = 1;  public function __construct()  {    $this->hasSemSupport = function_exists( 'sem_get' );    if ( $this->hasSemSupport ) {      $this->sem = sem_get( self::SEM_KEY );    }  }  public function acquire() {    if ( $this->hasSemSupport ) {      return sem_acquire( $this->sem );    }    return true;  }  public function release() {    if ( $this->hasSemSupport ) {      return sem_release( $this->sem );    }    return true;  }}/* * 顺序号发生器。 */class SeqGenerator{  const SHM_KEY = 1;  /**   * 对顺序号发生器进行初始化。   * 仅在服务器启动后的第一次调用有效,此后再调用此方法没有实际作用。   * @param int $start 产生顺序号的起始值。   * @return boolean 返回 true 表示成功。   */  static public function init( $start = 1 )  {    // 通过信号量实现互斥,避免对共享内存的访问冲突    $sw = new SemWrapper;    if ( ! $sw->acquire() ) {      return false;    }    // 打开共享内存    $shm_id = shmop_open( self::SHM_KEY, 'n', 0644, 4 );    if ( empty($shm_id) ) {      // 因使用了 'n' 模式,如果无法打开共享内存,可以认为该共享内存已经创建,无需再次初始化      $sw->release();      return true;    }    // 在共享内存中写入初始值    $size = shmop_write( $shm_id, pack( 'L', $start ), 0 );    if ( $size != 4 ) {      shmop_close( $shm_id );      $sw->release();      return false;    }    // 关闭共享内存,释放信号量    shmop_close( $shm_id );    $sw->release();    return true;  }  /**   * 产生下一个顺序号。   * @return int 产生的顺序号   */  static public function next()  {    // 通过信号量实现互斥,避免对共享内存的访问冲突    $sw = new SemWrapper;    if ( ! $sw->acquire() ) {      return 0;    }    // 打开共享内存    $shm_id = shmop_open( self::SHM_KEY, 'w', 0, 0 );    if ( empty($shm_id) ) {      $sw->release();      return 0;    }    // 从共享内存中读出顺序号    $data = shmop_read( $shm_id, 0, 4 );    if ( empty($data) ) {      $sw->release();      return 0;    }    $arr = unpack( 'L', $data );    $seq = $arr[1];    // 把下一个顺序号写入共享内存    $size = shmop_write( $shm_id, pack( 'L', $seq + 1 ), 0 );    if ( $size != 4 ) {      $sw->release();      return 0;    }    // 关闭共享内存,释放信号量    shmop_close( $shm_id );    $sw->release();    return $seq;  }}$a = SeqGenerator::init( time() );var_dump($a);for ( $i=0; $i < 10; $i++ ) {  $seq = SeqGenerator::next();  var_dump($seq);}

SyntaxHighlighter.highlight();

jQuery实现分隔条左右拖动功能

本文实例讲述了jQuery实现分隔条左右拖动功能的实现代码。分享给大家供大家参考。具体如下:
运行效果截图如下:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html> <head>  <title> New Document </title>  <script type="text/javascript"" width=100% src="jquery.min.js"></script>  <style type="text/css">    html, body, div {      margin: 0;      padding: 0;      border: 0;      -moz-user-select: none;      -webkit-user-select: none;    }    .gf_s {      float: left;      width: 4px;      cursor: e-resize;      background-color: #fff;      border: #99BBE8 1px solid;    }    .gf_s_g {      float: left;      width: 4px;      display: none;      cursor: e-resize;      position: absolute;      background-color: #F0F0F0;      border: #99BBE8 1px solid;      filter: alpha(opacity=60);      -moz-opacity: 0.6;      -khtml-opacity: 0.6;      opacity: 0.6;      z-index: 1000;    }  </style> </head> <body>   <div id="divP" style="width:100%; height:100%;">     <div id="divLeft" style="background-color: green; float: left; "></div>     <div id="divS" class="gf_s" style="float: left;"></div>     <div id="divSG" class="gf_s_g" style="float: left;"></div>     <div id="divRight" style="background-color: blue; float: left;"></div>   </div>   <script type="text/javascript">     var $sliderMoving = false;          //兼容各种浏览器的,获取鼠标真实位置     function mousePosition(ev) {       if (!ev) ev = window.event;       if (ev.pageX || ev.pageY) {         return { x: ev.pageX, y: ev.pageY };       }       return {         x: ev.clientX + document.documentElement.scrollLeft - document.body.clientLeft,         y: ev.clientY + document.documentElement.scrollTop - document.body.clientTop       };     };     //获取一个DIV的绝对坐标的功能函数,即使是非绝对定位,一样能获取到     function getElCoordinate(dom) {       var t = dom.offsetTop;       var l = dom.offsetLeft;       dom = dom.offsetParent;       while (dom) {         t += dom.offsetTop;         l += dom.offsetLeft;         dom = dom.offsetParent;       };       return { top: t, left: l };     };     //分隔条幽灵左右拖动(mousemove)     function sliderGhostMoving(e) {       $("#divSG").css({ left: mousePosition(e).x - 2, display: "block" });     };     //完成分隔条左右拖动(mouseup)     function sliderHorizontalMove(e) {       var lWidth = getElCoordinate($("#divSG")[0]).left - 2;       var rWidth = $(window).width() - lWidth - 6;       $("#divLeft").css("width", lWidth + "px");       $("#divRight").css("width", rWidth + "px");       $("#divSG").css("display", "none");     };     function reinitSize() {       var width = $(window).width() - 6;       var height = $(window).height();       $("#divLeft").css({ height: height + "px", width: width * 0.75 + "px" });       $("#divS").css({ height: height - 2 + "px", width: "4px" });       $("#divSG").css({ height: height - 2 + "px", width: "4px" });       $("#divRight").css({ height: height + "px", width: width * 0.25 + "px" });     }     $(document).ready(function () {       reinitSize();       $("#divS").on("mousedown", function (e) {         $sliderMoving = true;         $("divP").css("cursor", "e-resize");       });       $("#divP").on("mousemove", function (e) {         if ($sliderMoving) {           sliderGhostMoving(e);         }       });       $("#divP").on("mouseup", function (e) {         if ($sliderMoving) {           $sliderMoving = false;           sliderHorizontalMove(e);           $("#divP").css("cursor", "default");         }       });     });     $(window).resize(function () {       reinitSize();     });   </script> </body></html>

SyntaxHighlighter.highlight();

jquery实现select选择框内容左右移动代码分享

本文实例讲述了select选择框内容左右移动添加删除。分享给大家供大家参考。具体如下:
select选择框内容左右移动,简单实用,选中选项内容,点击移动按钮可进行内容左右移动,运行效果图:

<!DOCTYPE html><html><head> <meta charset="utf-8" /> <title>index</title></head><body> <div>  <select id="leftSelector" multiple="multiple" name="SmsListOnLeft" style="height:100px; width:50px">   <option value="0">0</option>   <option value="1">1</option>   <option value="2">2</option>   <option value="3">3</option>   <option value="4">4</option>   <option value="5">5</option>  </select>  <span style="top: 30px; position: fixed;">   <input type="button" value="<<" id="btnLeft" />   <input type="button" value=">>" id="btnRight" />  </span>    <select id="rightSelector" multiple="multiple" name="SmsListOnRight" style="height:100px; width:50px; margin-left:80px">   <option value="6">A</option>   <option value="7">B</option>   <option value="8">C</option>   <option value="9">D</option>   <option value="10">E</option>  </select>     </div>  <br>  <input type="button" onclick="selectAll()" id="btnSelectAll" value="selectAll" />  <script" width=100% src="js/jquery-2.1.4.js"></script> <script>  $("#btnRight").click(function () {   //数据option选中的数据集合赋值给变量vSelect   var vSelect = $("#leftSelector option:selected");   //克隆数据添加到 rightSelector 中   vSelect.clone().appendTo("#rightSelector");   //同时移除 leftSelector 中的 option   vSelect.remove();  });    //right move  $("#btnLeft").click(function () {   var vSelect = $("#rightSelector option:selected");   vSelect.clone().appendTo("#leftSelector");   vSelect.remove();  });  function selectAll() {   var lst1 = window.document.getElementById("rightSelector");   var length = lst1.options.length;   for (var i = 0; i < length; i++) {    var v = lst1.options[i].value; //option内的value    var t = lst1.options[i].text; //显示的文本    alert(v + ":" + t);   }  } </script> </body></html>

SyntaxHighlighter.highlight();

如何使用php脚本给html中引用的js和css路径打上版本号

在搜索引擎中搜索关键字.htaccess 缓存,你可以搜索到很多关于设置网站文件缓存的教程,通过设置可以将css、js等不太经常更新的文件缓存在浏览器端,这样访客每次访问你的网站的时候,浏览器就可以从浏览器的缓存中获取css、js等,而不必从你的服务器读取,这样在一定程度上加快了网站的打开速度,又可以节约一下你的服务器流量。

具体文字说明不给大家多说了,下面通过代码实例给大家讲解。

比如

<link rel="stylesheet" type="text/css" href="./css/globel.css"><script" width=100% src="./js/config.js"></script>

中的href和src加上版本

<link rel="stylesheet" type="text/css" href="./css/globel.css?eslc-app=3-0-2"><script" width=100% src="./js/config.js?eslc-app=3-0-2"></script>

当然如果不是前后端 分离得干干净净的,就没必要这么额外的这样自己在写个脚本去打版本。

打版本的好处:

解决外部引用文件实时更新问题。比如

pc端上主要体现在 iframe中的外部引用文件不会实时更新。

wap端上部分app也是比如微信。 如果你的网页是嵌到自己的app,那也更不用说了。

用php写了个类

//生成版本//清除版本class ReplaceVersion{ protected $filePostFixs = array(); protected $versionName = null; protected $version = null; protected $path = null; /**  * @param mixed $configs   * @param [type] $profix [description]  * @param [type] $path  [description]  */ public function __construct($configs, $profix, $path){  if (!$this->isCanRun()) {   $this->error('必须在内网环境 10.10.0开头才可运行'); //exit;  }  $this->setVersion($configs);  $this->setFilePostFix($profix);  $this->path = $path; } protected function isCanRun(){  if (strpos($_SERVER['HTTP_HOST'], '10.10.0') !== false) {   return true;  }  return false; } /**  * 匹配到script节点  * @param array $match 匹配到的script  * @return string 处理好的script  */ protected function callbackScript($match){  //["<script" width=100% src="../js/config.js?is=new"></script>", "../js/config.js", "?is=new"]  /*/<script.*?src=/"(.*?)(/?.*?|/?)?/".*?><//script>/*/  $str = $match[0];  $pattern = '/(<script.*?src=/")(.*)?(/"><//script>)/';  return $this->callbackMatch($str, $pattern); } /**  * 匹配到css节点  * @param array $match 匹配到的css  * @return string 处理好的css  */ protected function callbackCss($match){  // '<link rel="stylesheet" type="text/css" href="../css/globel.css">';  $str = $match[0];  $pattern = '/(<link.*?href=/")(.*)?(/".*?>)/';  return $this->callbackMatch($str, $pattern); } /**  * 回调模式匹配  * @param string $str   * @param string $pattern  * @return string    */ protected function callbackMatch($str, $pattern){  switch ($this->dealFlag) {   case 'replace':    return $this->replaceCallbackMatch($str, $pattern);   case 'clean':    return $this->cleanCallbackMatch($str, $pattern);   default:    $this->error('非法模式');  } } /**  * 替换版本  * @param string $str 待处理的string  * @param string $pattern 正则  * @return string  处理后的string  */ protected function replaceCallbackMatch($str, $pattern){  if (!preg_match($pattern, $str, $third)) {   return $str;  }  $arr  = explode('?', $third[2]);  $len  = count($arr);  $versionName = $this->versionName;  $version = $this->version;  if ($len === 1) {//没有问号   $arr[0] .= '?'. $versionName. '='. $version;  }else{//有问号   if (preg_match('/(^|/&)'. $versionName.'=(.*?)($|/&)/', $arr[1])) {//存在    $arr[1] = preg_replace('/(^|/&)'. $versionName.'=(.*?)($|/&)/', '$1'. $versionName.'='. $version. '$3', $arr[1]);    $arr[0] .= '?'. $arr[1];   }else{//不存在    $arr[0] .= '?'. $arr[1]. '&'. $versionName. '='. $version;   }  }  return $third[1]. $arr[0]. $third[3]; } /**  * 清除版本  * @param string $str 待清除的版本  * @param string $pattern 正则  * @return string  清除后的string  */ protected function cleanCallbackMatch($str, $pattern){  if (!preg_match($pattern, $str, $third)) {   return $str;  }  $arr  = explode('?', $third[2]);  $len  = count($arr);  $versionName = $this->versionName;  if ($len > 1 && strpos($arr[1], $versionName. '=') !== false) {   $arr[1] = preg_replace('/(^|/&)'. $versionName.'=(.*?)($|/&)/', '$1', $arr[1]);   substr($arr[1], -1) === '&' && ($arr[1] = substr($arr[1], 0, -1));   $arr[0] .= strlen($arr[1]) > 0 ? '?'. $arr[1] : '';   $str = $third[1]. $arr[0]. $third[3];  }  return $str; } /**  * 执行  */ protected function run(){  if ($this->path == '') {   $this->error('empty path');   return ;  }  if (is_dir($this->path)) {   $this->setDirFilesVersion( $this->path );  }else if(is_file($this->path)){   $this->setFileVersion( $this->path );  }else{   $this->error('error path');  } } /**  * 添加版本  */ public function replace(){  $this->dealFlag = 'replace';  $this->run();  echo 'replace success'; } /**  * 清除版本  */ public function clean(){  $this->dealFlag = 'clean';  $this->run();  echo 'clean success'; } protected function success(){ } protected function error($errorMsg){  echo $errorMsg;  exit(); } protected function setDirFilesVersion($dir){  $handle = null;  $file  = null;  if ( $handle = opendir($dir)) {   while ( false !== ($file = readdir($handle)) ) {    if ($file === '.' || $file === '..' || strpos($file, '.') === -1 ) {continue;}    $this->setFileVersion($file);   }  } } protected function setFileVersion($file){  $temp = null;  /*$pattern = '/<script.*?src=/"(.*?)(/?.*?|/?)?/".*?><//script>/';*/  $temp = explode('.', $file) ;  if ( ! $this->isNeedReplacePostFix(array_pop( $temp )) ) {return;}  $content = null;  $content = file_get_contents($file);  $content = preg_replace_callback('/<script.*?><//script>/', array(&$this, 'callbackScript'), $content);  $content = preg_replace_callback('/<link.*?type="text//css".*?>/', array(&$this, 'callbackCss'), $content);  // highlight_string($content);  file_put_contents($file, $content); } /**  * 获得版本  * @param mixed $configs array( 'versionName' : 'version') || $versionName  */ protected function setVersion($configs){  // last_wap_version  = '3-0-0',   // wap_version = '3-0-1',  if (is_array($configs) && $configs > 0) {   foreach ($configs as $key => $value) {    $this->version = $value;    $this->versionName = $key;   }  }else if(is_string($configs) && $configs != ''){   $configs = explode(',', $configs);   $this->versionName = $configs[0];   count($configs) == 2 && ($this->version = $configs[1]);  }else{   $this->error('the version is empty');  } } /**  * 通过后缀判断该文件是否要添加或清除版本  * @param string $profix 后缀  * @return boolean  true | false  */ protected function isNeedReplacePostFix($profix){  if (in_array($profix, $this->filePostFixs)) {   return true;  }  return false; } /**  * 设置需要操作的后缀  */ public function setFilePostFix($profix){  if (is_array($profix)) {   count($profix) > 0 && ( $this->filePostFixs = array_merge($this->filePostFixs, $profix) );  }else if(is_string($profix)){   $this->filePostFixs[] = $profix;  } }}
$dir  = __DIR__;$is_clean = false;//$is_clean = true;//第一个参就是版本信息, 第二个就是要匹配的文件后缀, 第三个是要匹配的目录或者文件if ($is_clean) {//清除版本 $configs = 'eslc-wap'; $replaceObj = new ReplaceVersion($configs, array('html'), $dir); $replaceObj->clean();}else{//添加或替换版本 $configs = array('eslc-wap' => '1.0.1');//也可以写成 $configs = 'eslc-wap, 1.0.1'; $replaceObj = new ReplaceVersion($configs, array('html'), $dir); $replaceObj->replace();}

SyntaxHighlighter.highlight();

解决jquery插件:TypeError:$.browser is undefined报错的方法

首先先说一说$.browser
browser就是用来获取浏览器基本信息的。
jQuery 从 1.9 版开始,移除了 $.browser 和 $.browser.version , 取而代之的是 $.support 。 在更新的 2.0 版本中,将不再支持 IE 6/7/8。 以后,如果用户需要支持 IE 6/7/8,只能使用 jQuery 1.9。
解决方法:加入以下js即可

(function(jQuery){   if(jQuery.browser) return;   jQuery.browser = {};  jQuery.browser.mozilla = false;  jQuery.browser.webkit = false;  jQuery.browser.opera = false;  jQuery.browser.msie = false;   var nAgt = navigator.userAgent;  jQuery.browser.name = navigator.appName;  jQuery.browser.fullVersion = ''+parseFloat(navigator.appVersion);  jQuery.browser.majorVersion = parseInt(navigator.appVersion,10);  var nameOffset,verOffset,ix;   // In Opera, the true version is after "Opera" or after "Version"  if ((verOffset=nAgt.indexOf("Opera"))!=-1) {  jQuery.browser.opera = true;  jQuery.browser.name = "Opera";  jQuery.browser.fullVersion = nAgt.substring(verOffset+6);  if ((verOffset=nAgt.indexOf("Version"))!=-1)  jQuery.browser.fullVersion = nAgt.substring(verOffset+8);  }  // In MSIE, the true version is after "MSIE" in userAgent  else if ((verOffset=nAgt.indexOf("MSIE"))!=-1) {  jQuery.browser.msie = true;  jQuery.browser.name = "Microsoft Internet Explorer";  jQuery.browser.fullVersion = nAgt.substring(verOffset+5);  }  // In Chrome, the true version is after "Chrome"  else if ((verOffset=nAgt.indexOf("Chrome"))!=-1) {  jQuery.browser.webkit = true;  jQuery.browser.name = "Chrome";  jQuery.browser.fullVersion = nAgt.substring(verOffset+7);  }  // In Safari, the true version is after "Safari" or after "Version"  else if ((verOffset=nAgt.indexOf("Safari"))!=-1) {  jQuery.browser.webkit = true;  jQuery.browser.name = "Safari";  jQuery.browser.fullVersion = nAgt.substring(verOffset+7);  if ((verOffset=nAgt.indexOf("Version"))!=-1)  jQuery.browser.fullVersion = nAgt.substring(verOffset+8);  }  // In Firefox, the true version is after "Firefox"  else if ((verOffset=nAgt.indexOf("Firefox"))!=-1) {  jQuery.browser.mozilla = true;  jQuery.browser.name = "Firefox";  jQuery.browser.fullVersion = nAgt.substring(verOffset+8);  }  // In most other browsers, "name/version" is at the end of userAgent  else if ( (nameOffset=nAgt.lastIndexOf(' ')+1) <  (verOffset=nAgt.lastIndexOf('/')) )  {  jQuery.browser.name = nAgt.substring(nameOffset,verOffset);  jQuery.browser.fullVersion = nAgt.substring(verOffset+1);  if (jQuery.browser.name.toLowerCase()==jQuery.browser.name.toUpperCase()) {  jQuery.browser.name = navigator.appName;  }  }  // trim the fullVersion string at semicolon/space if present  if ((ix=jQuery.browser.fullVersion.indexOf(";"))!=-1)  jQuery.browser.fullVersion=jQuery.browser.fullVersion.substring(0,ix);  if ((ix=jQuery.browser.fullVersion.indexOf(" "))!=-1)  jQuery.browser.fullVersion=jQuery.browser.fullVersion.substring(0,ix);   jQuery.browser.majorVersion = parseInt(''+jQuery.browser.fullVersion,10);  if (isNaN(jQuery.browser.majorVersion)) {  jQuery.browser.fullVersion = ''+parseFloat(navigator.appVersion);  jQuery.browser.majorVersion = parseInt(navigator.appVersion,10);  }  jQuery.browser.version = jQuery.browser.majorVersion;  })(jQuery);  

SyntaxHighlighter.highlight();

php+ajax实现无刷新分页

本文实例讲述了php+ajax实现无刷新分页实现方法。分享给大家供大家参考。具体如下:

    limit  偏移量,长度;
    limit  0,7;   第一页
    limit  7,7;   第二页
    limit  14,7;  第三页
每页信息条数:7
信息总条数:select count(*) from table
信息总页数:ceil向上取整(总条数/每页条数)
1、分页类具体使用

<?phpclass Pagination {  private $total; //数据表中总记录数  private $listRows; //每页显示行数  private $limit; //mysql 数据库的limit  private $uri; //分页信息前面的uri地址  private $pageNum; //页数  private $config = array('header' => "个记录", "prev" => "【上一页】", "next" => "【下一页】", "first" => "【首 页】", "last" => "【尾 页】");  private $listNum = 8;  /*   * $total 当前信息总条数   * $listRows 每页显示的条数   * $pa 下面的page    http://网址/index.php?page=5   */  public function __construct($total, $listRows = 10, $pa = "") {    $this->total = $total;    $this->listRows = $listRows;    $this->uri = $this->getUri($pa);    $this->page = !empty($_GET["page"]) ? $_GET["page"] : 1;//不传入page,则默认显示首页    $this->pageNum = ceil($this->total / $this->listRows);    $this->limit = $this->setLimit();  }  //设置每页显示的条数  private function setLimit() {    return "Limit " . ($this->page - 1) * $this->listRows . ", {$this->listRows}";  }  //获得URL地址  private function getUri($pa) {    $url = $_SERVER["REQUEST_URI"] . (strpos($_SERVER["REQUEST_URI"], '?') ? '' : "?") . $pa;    $parse = parse_url($url);    if (isset($parse["query"])) {      parse_str($parse['query'], $params);      unset($params["page"]);      $url = $parse['path'] . '?' . http_build_query($params);    }    return $url;  }  //魔术方法,  public function __get($args) {    if ($args == "limit")      return $this->limit;    else      return null;  }  //页面开始的条数  private function start() {    if ($this->total == 0)      return 0;    else      return ($this->page - 1) * $this->listRows + 1;  }  //页面结束的条数  private function end() {    return min($this->page * $this->listRows, $this->total);  }  /*设置首页*/  private function first() {    $html = "";    if ($this->page == 1)      $html.=' '.$this->config["first"].' ';    else      $html.=" <a href='javascript:void(0)' onclick='showPage(/"{$this->uri}&page=1/")'>{$this->config["first"]}</a> ";      //$html.=" <a href='{$this->uri}&page=1'>{$this->config["first"]}</a> ";    return $html;  }  /*设置上一页*/  private function prev() {    $html = "";    if ($this->page == 1)      $html.=' '.$this->config["prev"].' ';    else      $html.=" <a href='javascript:void(0)' onclick='showPage(/"{$this->uri}&page=" . ($this->page - 1) . "/")'>{$this->config["prev"]}</a> ";      //$html.=" <a href='{$this->uri}&page=".($this->page-1)."'>{$this->config["prev"]}</a> ";    return $html;  }  //页码列表【首页】【2】【3】…………【尾页】  private function pageList() {    $linkPage = "";    $inum = floor($this->listNum / 2);    for ($i = $inum; $i >= 1; $i--) {      $page = $this->page - $i;      if ($page < 1)        continue;      $linkPage.=" <a href='javascript:void(0)' onclick='showPage(/"{$this->uri}&page={$page}/")'>{$page}</a> ";    }    $linkPage.=" {$this->page} ";    for ($i = 1; $i <= $inum; $i++) {      $page = $this->page + $i;      if ($page <= $this->pageNum)        $linkPage.=" <a href='javascript:void(0)' onclick='showPage(/"{$this->uri}&page={$page}/")'>{$page}</a> ";      else        break;    }    return $linkPage;  }  /*设置下一页*/   private function next() {    $html = "";    if ($this->page == $this->pageNum)      $html.=' '.$this->config["next"].' ';    else      $html.=" <a href='javascript:void(0)' onclick='showPage(/"{$this->uri}&page=" . ($this->page + 1) . "/")'>{$this->config["next"]}</a> ";      //$html.=" <a href='{$this->uri}&page=".($this->page + 1)."'>{$this->config["next"]}</a> ";    return $html;  }  /*设置尾页*/  private function last() {    $html = "";    if ($this->page == $this->pageNum)      $html.=' '.$this->config["last"].' ';    else      $html.=" <a href='javascript:void(0)' onclick='showPage(/"{$this->uri}&page=" . ($this->pageNum) . "/")'>{$this->config["last"]}</a> ";      //$html.=" <a href='{$this->uri}&page=.(this->pageNum).'>{$this->config["last"]}</a> ";    return $html;  }  /*设置页面跳转*/  private function goPage() {    return     ' <input type="text" onkeydown="javascript:if(event.keyCode==13){var page=(this.value>' . $this->pageNum . ')?' . $this->pageNum . ':this.value;showPage(/'' . $this->uri . '&page=/'+page+/'/')}" value="' . $this->page . '" style="width:25px">    <input type="button" value="GO" onclick="javascript:var page=(this.previousSibling.value>' . $this->pageNum . ')?' . $this->pageNum . ':this.previousSibling.value;showPage(/'' . $this->uri . '&page=/'+page+/'/')"> ';  }  //页面列表配置选项  function fpage($display = array(0, 1, 2, 3, 4, 5, 6, 7, 8)) {    $html[0] = " 共有<b>{$this->total}</b>{$this->config["header"]} ";    $html[1] = " 每页显示<b>" . ($this->end() - $this->start() + 1) . "</b>条,本页<b>{$this->start()}-{$this->end()}</b>条 ";    $html[2] = " <b>{$this->page}/{$this->pageNum}</b>页 ";    $html[3] = $this->first();    $html[4] = $this->prev();    $html[5] = $this->pageList();    $html[6] = $this->next();    $html[7] = $this->last();    $html[8] = $this->goPage();    $fpage = '';    foreach ($display as $index) {      $fpage.=$html[$index];    }    return $fpage;  }}

2 数据显示

<?php//链接数据库//获得具体信息//分页显示header("content-type:text/html;charset=utf-8");$link = mysql_connect('localhost','root','111111');mysql_select_db('shop', $link);mysql_query("set names utf8");$css = <<<eof<style type="text/css">  table {border:1px solid black; width:700px; margin:auto; border-collapse:collapse;}  td {border:1px solid black; }</style>eof;echo $css;echo "<table>  <tr><td>序号</td><td>名称</td><td>数量</td><td>价格</td><td>时间</td></tr>";//1 引入分页类include "./Pagination.php";//2. 获得信息总条数$sql = "select * from sw_goods";$qry = mysql_query($sql);$total = mysql_num_rows($qry);$per  = 7;//3. 实例化分页类对象$page_obj = new Pagination($total,$per);//4. 拼装sql语句,获得每页信息//利用page_obj实现limit的灵活设置//$page_obj -> limit;$sqla = "select * from sw_goods ".$page_obj->limit;$qrya = mysql_query($sqla);//5. 获得页面列表$pagelist = $page_obj -> fpage(array(3,4,5,6,7,8));$i=1;while($rsta = mysql_fetch_assoc($qrya)){  echo "<tr>";  echo "<td>".$i++."</td>";  echo "<td>".$rsta['goods_name']."</td>";  echo "<td>".$rsta['goods_number']."</td>";  echo "<td>".$rsta['goods_price']."</td>";  echo "<td>".date("Y-m-d H:i:s",$rsta['goods_create_time'])."</td>";  echo "</tr>";}echo "<tr><td colspan=5>".$pagelist."</td></tr>";echo "</table>";

3 ajax无刷新分页实现

open(‘get’,’http://网址/index.php?page=2′)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html>  <head>    <title>新建网页</title>    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />    <meta name="description" content="" />    <meta name="keywords" content="" />    <script type="text/javascript">//获得分页信息ajax函数function showPage(myurl){  var xhr = new XMLHttpRequest();  xhr.onreadystatechange = function(){    if(xhr.readyState==4){      var rst = document.getElementById("result");      rst.innerHTML = xhr.responseText;    }  }  xhr.open("get",myurl);  xhr.send(null);}window.onload = function(){  showPage("./data1.php"); //获得分页信息  //showPage("./data.php?page=2");}    </script>    <style type="text/css">    </style>  </head>  <body>    <h2>ajax无刷新分页效果</h2>    <div id="result"></div>  </body></html><script type="text/javascript">  document.write(new Date()+"<br />");  document.write(new Date()+"<br />");  document.write(new Date()+"<br />");  document.write(new Date()+"<br />");</script>

SyntaxHighlighter.highlight();

带你了解PHP7 性能翻倍的关键

20岁老牌网页程序语言PHP,最快将在10月底释出PHP 7新版,这是十年来的首次大改版,最大特色是在性能上的大突破,能比前一版PHP 5快上一倍,PHP之父Rasmus Lerdorf表示,甚至能比HHVM虚拟机下的PHP程序性能更快。

HHVM 是脸书为自家网站特性而量身客制的PHP优化机制,不见得适用任何网站。但Rasmus Lerdorf表示,新版目标之一就是要让任何网站开发者,就连使用开发框架Drupal、开源电子商务系统Opencart时,都能有不输使用HHVM 技术的性能。在新版发表前夕,他也趁来台参加PHPConf Taiwan年会时,分享PHP 7性能大突破的关键。

一个20年来历经了多 次改版和无数次优化的成熟语言,还能有性能提高一倍的突破绝非易事,Rasmus Lerdorf坦言,不像一般新项目多半容易找出许多改进空间,新版PHP并非修改部分程序就达到了如此的成果。反而是,透过大量细节优化和性能累加 后,PHP 7才具备了不输HHVM的执行性能。

Rasmus Lerdorf与PHP核心贡献团队花了许多心力减少程序运作时搬动的内存位数,由此加速执行的性能。例如,PHP中储存变量的数据架构zval从24位 缩减至16位、Hashtable从72位减少至56位,并检视PHP中的函式,思考有无任何改进性能的空间。

除了从减少内存的使用着手 外,Rasmus Lerdorf更检视CPU的Cache line的运作原理,了解程序代码如何与CPU互动、编译程序如何在新CPU架构下编译程序代码等细节,确保PHP 7的程序代码符合现代CPU的架构。虽然每个项目的优化对性能贡献都低于0.5%,但由于优化的项目很多,或是某项改善的功能会被应用程序反复呼叫,整体 修正的综效结果就能有如此大的进展。

受HHVM刺激,决定打造兼具性能与功能的PHP

Facebook为了优化PHP运作,搭配JIT编 译而打造出虚拟机HHVM。而HHVM虽然拥有快速的执行性能,其为特定用途优化的设计,只能满足小部分的开发者。反之,Rasmus Lerdorf除了想提升PHP的性能表现外,也想要同时满足高端使用者以及业余使用者的需求,让PHP 7成为兼备性能表现及通用功能的程序语言。

然而,开发符合市场上少部分人使用的程序语言并不是难事,但是PHP项目瞄准许多对象,必须同时符合业余使用者及专业开发者需求的原则下,开发难以面面俱 到,因为总是会有部分族群的需求无法被满足,「这就像拿水管大范围的喷洒,而每个人衣服都会被水沾湿一点,但是不会有人的衣服完全湿透。」Rasmus Lerdorf比喻。

不使用外挂框架的PHP的运算性能表现都很优异,但是受到外加框架的影响,原本可以在数秒内处理上千个网页要求的 PHP,性能大幅下降,变为只能处理数十个要求。Rasmus Lerdorf表示,在HHVM出现之前,相较于对PHP性能表现的要求,使用者比较在意PHP能否降低网页开发的难度,而这些框架能让开发者的工作变得 比较简单。但是在Facebook推出HHVM后,引出许多重视PHP性能表现的使用者,让Rasmus Lerdorf意识到许多使用者有性能表现的需求。他开始思考如何将HHVM的JIT架构与PHP融合。

但Rasmus Lerdorf表示,PHP与HHVM两者在架构设计上相当不同,例如,HHVM的多线程架构并不是很稳固。此外,HHVM的可移植性并不佳,离可以在 Windows平台上运作还有很大一段路,而PHP有很多开发者在Windows环境开发,而HHVM无法照顾到那些使用者。

Rasmus Lerdorf表示,他不能放弃PHP的主要架构,虽然他们曾经考虑过融合两者,但是,HHVM在使用上有很多的限制。虽然HHVM对Facebook及 许多开发者是非常好的工具,但对于PHP项目来说,HHVM的使用范畴还不够宽广,只能符合Facebook或是Wikipedia等特定项目的需求。

非强型别语言的PHP,导入JIT是难上加难

然而,在PHP中加入JIT编译是件非常困难的事情。Rasmus Lerdorf表示,JIT必须学会辨认程序的运作模(Patterns),例如了解哪些部份为重要的程序代码,并且在程序运作前,预测程序被呼叫的时 机,或是哪些部分的程序会呼叫。

Rasmus Lerdorf比喻,在许多汽车中,JIT必须能预测哪部分的车子会右转、哪部分的车子会左转或是某些颜色汽车会直行,「而JIT必须要全部预测正确,否则性能会大大的降低。」但是,如果预测正确,程序执行性能则会大大提升。

在一般的程序语言的编译中加入JIT已属不易,Rasmus Lerdorf表示,由于PHP的动态属性(dynamic)让加入JIT是难上加难。他举例,开发者宣告参数$a值为1,但不代表程序所有的$ a的值都为1,由于PHP中参数值可以很轻易地重新定义。在C语言中,当开发者宣告参数a为整数,则a永远为整数。如果程序中有任何地方宣告a是整数以外 的类型,连编译都无法执行。而因为C语言此种强型别的程序语言,「JIT可以预测变量a为整数,但是在PHP中,我们没有这种奢侈。」他解释,HHVM的 做法为当JIT得知a是整数型别后,则假设a永远为整数。

而HHVM为了在使用JIT编译,某种程度上受限了PHP的发展。HHVM的用户 必须清楚宣告变量的性质,但是使用PHP的开发者,可以先宣告没有性质的类别(Class),后续再指定类别的变量属性。「在没有任何限制下,将JIT加 入PHP是我们要做的事。」他表示,PHP必须顾及Wordpress、Drupal等框架的开发者,不能任意停止对此些框架的支持。故与HHVM相 比,PHP在打造JIT的条件限制更多。

但是,「这不代表我们不能做JIT。此外,我们也要控制PHP的发展走向。」Rasmus Lerdorf表示。

目前,PHP核心贡献者之一的Dmitry Stogov开发一个原型JIT,并且使用某些实验性的应用程序去测试运作。Rasmus Lerdorf表示,如果将此JIT用于执行某些重复性的运算或是循环程序,得以让PHP 7性能又再快上10倍。

不过他也坦承,当此实验性的JIT用于Wordpress时,并未得到任何加速效果,「我们想要打造的JIT不是要在大学课本上学到的东西,而是能在真实世 界中运作的JIT。」他表示。因为PHP一直都抱持如此的理想,试图解决人们生活中的问题,并且能真实世界中在线环境中运作,而不只是存在课本中的理论。

SyntaxHighlighter.highlight();

php curl模拟post请求和提交多维数组的示例代码

下面一段代码给大家介绍php curl模拟post请求的示例代码,具体代码如下:

<?php$uri = "http://www.cnblogs.com/test.php";//这里换成自己的服务器的地址// 参数数组$data = array ( 'name' => 'tanteng'// 'password' => 'password');$ch = curl_init ();// print_r($ch);curl_setopt ( $ch, CURLOPT_URL, $uri );curl_setopt ( $ch, CURLOPT_POST, 1 );curl_setopt ( $ch, CURLOPT_HEADER, 0 );curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, 1 );curl_setopt ( $ch, CURLOPT_POSTFIELDS, $data );$return = curl_exec ( $ch );curl_close ( $ch );print_r($return);

2,远程服务器:

<?phpif(isset($_POST['name'])){ if(!empty($_POST['name'])){ echo '您好,',$_POST['name'].'!'; }}

下面给大家介绍php中curl模拟post提交多维数组。

今天需要用curl模拟post提交参数,请求同事提供的一个接口;但是传递的参数中,有一个参数的值为数组,用普通的curl post代码提交,会报错误

PHP Notice:  Array to string conversion in /test/functions.php on line 30
Notice: Array to string conversion in /test/functions.php on line 30

<?php        $param = array(                'uid' => 123,                 'uids' => array(12,455),                 'msgType' => 'WITH',                  'nick' => 'aaa',                  );        $url = "http://cx.com/t.php";        //通过curl的post方式发送接口请求        SendDataByCurl($url,$param);       //通过curl模拟post的请求;function SendDataByCurl($url,$data=array()){  //对空格进行转义  $url = str_replace(' ','+',$url);  $ch = curl_init();  //设置选项,包括URL  curl_setopt($ch, CURLOPT_URL, "$url");  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);  curl_setopt($ch, CURLOPT_HEADER, 0);  curl_setopt($ch,CURLOPT_TIMEOUT,3); //定义超时3秒钟    // POST数据  curl_setopt($ch, CURLOPT_POST, 1);  // 把post的变量加上  curl_setopt($ch, CURLOPT_POSTFIELDS, $data);  //执行并获取url地址的内容  $output = curl_exec($ch);  //释放curl句柄  curl_close($ch);  return $output;}
//通过curl模拟post的请求;function SendDataByCurl($url,$data=array()){  //对空格进行转义  $url = str_replace(' ','+',$url);  $ch = curl_init();  //设置选项,包括URL  curl_setopt($ch, CURLOPT_URL, "$url");  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);  curl_setopt($ch, CURLOPT_HEADER, 0);  curl_setopt($ch,CURLOPT_TIMEOUT,3); //定义超时3秒钟    // POST数据  curl_setopt($ch, CURLOPT_POST, 1);  // 把post的变量加上  curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));  //所需传的数组用http_bulid_query()函数处理一下,就ok了  //执行并获取url地址的内容  $output = curl_exec($ch);  $errorCode = curl_errno($ch);  //释放curl句柄  curl_close($ch);  if(0 !== $errorCode) {    return false;  }  return $output;}

SyntaxHighlighter.highlight();

phpStudy访问速度慢和启动失败的解决办法

下面给大家介绍phpstudy访问速度慢的解决办法。

1、修改mysql数据库链接地址为ip地址127.0.0.1。

2、使用最新版本,这个坑了我好久时间。

下面一段内容是关于phpstudy启动失败的解决办法。

php5.3、5.4和apache都是用vc9编译,电脑必须安装vc9运行库才能运行。

php5.5、5.6是vc11编译,如用php5.5、5.6必须安装vc11运行库。

vc9和vc11运行库下载:

php5.5以上才有64位的,其他均为32位。所以64位的系统最好把32位的运行库也安装上。

===================================

phpStudy启动失败时的解决方法

phpStudy启动失败,原因一是防火墙拦截,二是80端口已经被别的程序占用,如IIS,迅雷等;三是没有安装VC9运行库,php和apache都是VC9编译。解决以上三个问题,99%能一次性安装成功

为了减少出错安装路径不得有汉字,如有防火墙开启,会提示是否信任httpd、mysqld运行,请选择全部允许。

由于牵扯到注册服务、自解压等,个别弱智傻xx杀毒卫士,xx狗会误报病毒,不放心的可以安装后自行扫描。

最好关闭xx杀毒卫士,xx狗后安装phpStudy。

32位的VC9运行库下载:
64位的VC9运行库下载:

不是管理员administrator的用户,请右键以管理员身份运行。牵扯到服务进程的管理必须以管理员身份运行。

端口问题无法启动时,请使用菜单『环境端口检测』进行端口检测,尝试启动。最重要的一点,你的机子一定要安装VC9运行库,phpStudy安装路径不得有汉字。

SyntaxHighlighter.highlight();

标准版Eclipse搭建PHP环境的详细步骤

一、下载Eclipse的PHP插件

  百度搜索phpeclipse,看到某条结果是带有SourceForge.net字样的,点进去,找到Download按钮,点击之后,等待5秒就会开始下载了。

二、安装Eclipse的PHP插件

  插件下载完成之后,解压,然后把site.xml删掉(大部分插件不用删这个文件,但是PHPEclipse必须删),最后把整个文件夹复制到Eclipse的dropins文件夹里面,重启Eclipse即可。

三、在Eclipse新建PHP工程和新建PHP文件

  在插件安装成功的前提下,新建工程和新建文件的时候,都能见到PHP相关的选项。然后在PHP文件里随便写几行代码。

四、下载PHP的运行环境

  百度搜索xampp,在第一个结果,点击普通下载。

五、安装PHP的运行环境

  运行安装包,如果见到有警告,可以忽略,点击OK进入下一步。

  安装选项,初学者简单点,全部选上就可以了,这些tomcat、mysql全部安装在xampp的目录下,不会对自己的原有tomcat、mysql搞混的,不使用它们就行了。

六、配置PHP的运行环境

  安装成功之后,运行xampp/xampp-control.exe,在Apache那一行,点击Config,会弹出下拉框,选第三个Apache(httpd-xmapp.conf),然后插入如图的一段配置,第一句是给这个PHP工程设置一个别名,方便在浏览器的地址栏访问,下面的是指定PHP工程的位置。保存一下。

七、运行PHP

  在Apache那一行,点击Start,然后见到PID(s)和Port(s)出现了几个数字,就说明启动成功了。

  在浏览器输入http://127.0.0.1/myphp/home.php,能看到之前写的页面。

SyntaxHighlighter.highlight();

php有效防止同一用户多次登录

【问题描述】:同一用户在同一时间多次登录如果不能检测出来,是危险的。因为,你无法知道是否有其他用户在登录你的账户。如何禁止同一用户多次登录呢?
【解决方案】
(1) 每次登录,身份认证成功后,重新产生一个session_id。

session_regenerate_id(); session_register ("username") ; 

(2) 在用户数据库中开一个sessionid字段,重新产生session_id后,都更新该字段。

$sessionid = session_id(); $db = new PDO('sqlite:softToken.db'); $sql = "update userinfo set sessionid ='$sessionid' where username='$username' and passwd='$passwd';"; $query = $db->prepare($sql); $query->execute(); 

(3) 建立一个session保存用户名

$_SESSION["username"] = $username; 

(4) 利用url重写,传递session_id

$url = "main.php?sid=".session_id(); unset($db); echo "<font color=blue>登录成功,正在跳转!</font>" ; header ("Location:$url"); 
<?php header('Content-type:text/html; charset=utf-8'); $sessionid = $_GET['sid']; session_id($sessionid); session_start (); $username = $_SESSION["username"]; $db = new PDO('sqlite:softToken.db'); $sql = "select * from userinfo where username='$username' and sessionid='$sessionid';"; $query = $db->prepare($sql); $query->execute(); $user = $query->fetch(PDO::FETCH_OBJ);  if ($user->username == ""){ session_destroy(); echo "<script language='javascript' type='text/javascript'>" ; echo "window.location.href = 'index.html';" ; echo "</script>" ; exit () ; } ?>  <html> <body> ...... </body> </html>

SyntaxHighlighter.highlight();

PHP传值到不同页面的三种常见方式及php和html之间传值问题

在项目开发中经常见到不同页面之间传值在web工作中,本篇文章给大家列出了三种常见的方式。

接触PHP也有几个月了,本文总结一下这段日子中,在编程过程里常用的3种不同页面传值方法,希望可以给大家参考。有什么意见也希望大家一起讨论。

      一. POST传值

post传值是用于html的<form>表单跳转的方法,很方便使用。例如:

 <html> <form action='' method=''> <input type='text' name='name1'> <input type='hidden' name='name2' value='value'> <input type='submit' value='提交'> </form> </html>

      form中的action填入的是跳转页面的url路径,method填入post方法。form表单中的提交按钮按下后,就会把form中有name的内容都传到填入的url中,可以通过$_POST[‘name’]获取,例如:

<?php$a=$_POST['name1'];$b=$_POST['name2'];?>

这里有个很方便的小技巧,在input标签中把type选为’hidden’时,这个input标签会隐藏起来,不在页面显示,但这input标签在form中,并且有name值和value值,同样会跟随提交按钮传递过去,这种隐藏标签可以传递一些不想显示出来的内容。

    二.GET传值

GET传值是通过跟随url传递的,在页面跳转时,跟着url跳转。常用于<a>标签的使用。例如:

<a href='delete.php?id=value'>点我跳转</a>

跳转进入xxx.php后,就能通过$_GET[‘id’]获取传递的值。GET方法常用于URL的目的是删除或读取某个id的php文件。

   三.SESSION传值

SESSION是全局变量的一种,经常用于用户登陆后保存用户id之类的常用数据。一旦保存到SESSION中,其他页面都可以通过SESSION获取,SESSION的使用要开启session:

<?php//session赋值  session_start();  $_SESSION['one']=value1;  $_SESSION['two']=value2;//session值的读取:  $one = $_SESSION['one'];  //session值的销毁  unset($_SESSION['one']);?>

以上是小编给大家列出的三种方式,供大家参考,同时希望大家喜欢。 

php和html之间的传值问题

这样的表单我用form怎么向php传值,重点是php怎么接收这样的数据

——解决思路———————-

form表单里两个参数,一个action,表明了往哪个文件传,不写默认传自己;一个method,表明用什么方式传,有get和post两种

SyntaxHighlighter.highlight();

PHP中使用array函数新建一个数组

PHP 中的数组实际上是一个有序映射。映射是一种把 values 关联到 keys 的类型。此类型在很多方面做了优化,因此可以把它当成真正的数组,或列表(向量),散列表(是映射的一种实现),字典,集合,栈,队列以及更多可能性。由于数组元素的值也可以是另一个数组,树形结构和多维数组也是允许的。

array

(PHP 4, PHP 5)

array ― 新建一个数组

说明

代码如下:

array array ([ mixed $… ] )

返回根据参数建立的数组。参数可以用 => 运算符给出索引。关于数组是什么的信息请阅读数组一节。

Note:

array() 是一个语言结构,用于字面上表示数组,不是常规的函数。

语法“index => values”,用逗号分开,定义了索引和值。索引可以是字符串或数字。如果省略了索引,会自动产生从 0 开始的整数索引。如果索引是整数,则下一个产生的索引将是目前最大的整数索引 + 1。注意如果定义了两个完全一样的索引,则后面一个会覆盖前一个。

在最后一个定义的数组项目之后加一个逗号虽然不常见,却是合法的语法。

下面的例子演示了怎样建立一个二维数组,怎样给相应的数组指定键名,以及怎样在普通数组中略过和继续数字索引。

Example #1 array() 例子

<?php$fruits = array (  "fruits" => array("a" => "orange", "b" => "banana", "c" => "apple"),  "numbers" => array(1, 2, 3, 4, 5, 6),  "holes"  => array("first", 5 => "second", "third"));?>

Example #2 array() 的自动索引

<?php$array = array(1, 1, 1, 1, 1, 8 => 1, 4 => 1, 19, 3 => 13);print_r($array);?>

以上例程会输出:

 Array
(
    [0] => 1
    [1] => 1
    [2] => 1
    [3] => 13
    [4] => 1
    [8] => 1
    [9] => 19
)

注意索引 3 被定义了两次,保留了最后的值 13。索引 4 在 索引 8 之后定义,下一个自动生成的索引(值为 19 那个)为 9,因为最大的索引是 8。

本例建立了从 1 开始的数组。

Example #3 从 1 开始索引的 array()

<?php$firstquarter = array(1 => 'January', 'February', 'March');print_r($firstquarter);?>

以上例程会输出:

 Array(  [1] => January  [2] => February  [3] => March)

在 Perl 中,可以访问在双引号内的数组的值。但在 PHP 中需要将数组用花括号括起来。

Example #4 访问双引号内的数组

<?php$foo = array('bar' => 'baz');echo "Hello {$foo['bar']}!"; // Hello baz!?>

PHP Array 函数

PHP:指示支持该函数的最早的 PHP 版本。

函数 描述 PHP
创建数组。 3
返回其键均为大写或小写的数组。 4
把一个数组分割为新的数组块。 4
通过合并两个数组来创建一个新数组。 5
用于统计数组中所有值出现的次数。 4
返回两个数组的差集数组。 4
比较键名和键值,并返回两个数组的差集数组。 4
比较键名,并返回两个数组的差集数组。 5
通过用户提供的回调函数做索引检查来计算数组的差集。 5
用回调函数对键名比较计算数组的差集。 5
用给定的值填充数组。 4
用回调函数过滤数组中的元素。 4
交换数组中的键和值。 4
计算数组的交集。 4
比较键名和键值,并返回两个数组的交集数组。 4
使用键名比较计算数组的交集。 5
带索引检查计算数组的交集,用回调函数比较索引。 5
用回调函数比较键名来计算数组的交集。 5
检查给定的键名或索引是否存在于数组中。 4
返回数组中所有的键名。 4
将回调函数作用到给定数组的单元上。 4
把一个或多个数组合并为一个数组。 4
递归地合并一个或多个数组。 4
对多个数组或多维数组进行排序。 4
用值将数组填补到指定长度。 4
将数组最后一个单元弹出(出栈)。 4
计算数组中所有值的乘积。 5
将一个或多个单元(元素)压入数组的末尾(入栈)。 4
从数组中随机选出一个或多个元素,并返回。 4
用回调函数迭代地将数组简化为单一的值。 4
将原数组中的元素顺序翻转,创建新的数组并返回。 4
在数组中搜索给定的值,如果成功则返回相应的键名。 4
删除数组中的第一个元素,并返回被删除元素的值。 4
在数组中根据条件取出一段值,并返回。 4
把数组中的一部分去掉并用其它值取代。 4
计算数组中所有值的和。 4
用回调函数比较数据来计算数组的差集。 5
带索引检查计算数组的差集,用回调函数比较数据。 5
带索引检查计算数组的差集,用回调函数比较数据和索引。 5
计算数组的交集,用回调函数比较数据。 5
带索引检查计算数组的交集,用回调函数比较数据。 5
带索引检查计算数组的交集,用回调函数比较数据和索引。 5
删除数组中重复的值。 4
在数组开头插入一个或多个元素。 4
返回数组中所有的值。 4
对数组中的每个成员应用用户函数。 3
对数组中的每个成员递归地应用用户函数。 5
对数组进行逆向排序并保持索引关系。 3
对数组进行排序并保持索引关系。 3
建立一个数组,包括变量名和它们的值。 4
计算数组中的元素数目或对象中的属性个数。 3
返回数组中的当前元素。 3
返回数组中当前的键/值对并将数组指针向前移动一步。 3
将数组的内部指针指向最后一个元素。 3
从数组中将变量导入到当前的符号表。 3
检查数组中是否存在指定的值。 4
从关联数组中取得键名。 3
对数组按照键名逆向排序。 3
对数组按照键名排序。 3
把数组中的值赋给一些变量。 3
用“自然排序”算法对数组进行不区分大小写字母的排序。 4
用“自然排序”算法对数组排序。 4
将数组中的内部指针向前移动一位。 3
current() 的别名。 3
将数组的内部指针倒回一位。 3
建立一个包含指定范围的元素的数组。 3
将数组的内部指针指向第一个元素。 3
对数组逆向排序。 3
把数组中的元素按随机顺序重新排列。 3
count() 的别名。 3
对数组排序。 3
使用用户自定义的比较函数对数组中的值进行排序并保持索引关联。 3
使用用户自定义的比较函数对数组中的键名进行排序。 3
使用用户自定义的比较函数对数组中的值进行排序。 3

SyntaxHighlighter.highlight();

MySQL中使用SHOW PROFILE命令分析性能的用法整理

show profile是由Jeremy Cole捐献给MySQL社区版本的。默认的是关闭的,但是会话级别可以开启这个功能。开启它可以让MySQL收集在执行语句的时候所使用的资源。为了统计报表,把profiling设为1
 

mysql> SET profiling = 1;

 
之后在运行一个查询

mysql> SELECT COUNT(DISTINCT actor.first_name) AS cnt_name, COUNT(*) AS cnt-> FROM sakila.film_actor-> INNER JOIN sakila.actor USING(actor_id)-> GROUP BY sakila.film_actor.film_id-> ORDER BY cnt_name DESC;...997 rows in set (0.03 sec)

 
这个执行语句的剖析信息存储在这个会话中。使用SHOW PROFILES进行查看。

mysql> SHOW PROFILES/G
*************************** 1. row ***************************Query_ID: 1Duration: 0.02596900Query: SELECT COUNT(DISTINCT actor.first_name) AS cnt_name,...

 
你可以使用SHOW PROFILE语句来获取已经存储的剖析数据。如果不加参数,会显示状态以及它们持续的时间。

mysql> SHOW PROFILE;
+------------------------+-----------+| Status | Duration |+------------------------+-----------+| (initialization) | 0.000005 || Opening tables | 0.000033 || System lock | 0.000037 || Table lock | 0.000024 || init | 0.000079 || optimizing | 0.000024 || statistics | 0.000079 || preparing | 0.00003 || Creating tmp table | 0.000124 || executing | 0.000008 || Copying to tmp table | 0.010048 || Creating sort index | 0.004769 || Copying to group table | 0.0084880 || Sorting result | 0.001136 || Sending data | 0.000925 || end | 0.00001 || removing tmp table | 0.00004 || end | 0.000005 || removing tmp table | 0.00001 || end | 0.000011 || query end | 0.00001 || freeing items | 0.000025 || removing tmp table | 0.00001 || freeing items | 0.000016 || closing tables | 0.000017 || logging slow query | 0.000006 |+------------------------+-----------+

 
每行都是状态变化的过程以及它们持续的时间。Status那一列和SHOW FULL PROCESSLIST的State应该是一致的。
这个值是来自于thd->proc_info变量。因此你所查看的这个值是直接来自MySQL内部的。尽管这些数值是比较直接易懂的,你也可以查看MySQL手册。
 
你可以给SHOW PROFILES指定一个Query_ID来查看指定的语句,还可以给输出添加新的列。如,查看用户和CPU使用。可以用如下命令。
 

mysql> SHOW PROFILE CPU FOR QUERY 1;

 
SHOW PROFILE可以深入的查看服务器执行语句的工作情况。以及也能帮助你理解执行语句消耗时间的情况。一些限制是它没有实现的功能,不能查看和剖析其他连接的语句,以及剖析时所引起的消耗。

SHOW PROFILES显示最近发给服务器的多条语句,条数根据会话变量profiling_history_size定义,默认是15,最大值为100。设为0等价于关闭分析功能。

SHOW PROFILE FOR QUERY n,这里的n就是对应SHOW PROFILES输出中的Query_ID。

例如:

mysql> show profiles;
+----------+-------------+---------------------------------------+| Query_ID | Duration | Query     |+----------+-------------+---------------------------------------+| 1 | 0.00037700 | alter table table1 drop column c3 int || 2 | 70.37123800 | alter table table1 drop column c3 || 3 | 0.00124500 | show tables    || 4 | 0.00569800 | select * from table1 where id=2 || 5 | 0.00068500 | select count(1) from tables1  || 6 | 0.00197900 | select count(1) from table1  || 7 | 0.00105900 | alter table tables1 drop c1  || 8 | 0.00800200 | alter table table1 drop c1  |+----------+-------------+---------------------------------------+8 rows in set (0.00 sec)

 

mysql> SHOW PROFILE FOR QUERY 2; #查看alter table table1 drop column c3的分析
+------------------------------+-----------+| Status   | Duration |+------------------------------+-----------+| starting   | 0.000183 || checking permissions  | 0.000057 || checking permissions  | 0.000059 || init    | 0.000060 || Opening tables  | 0.000071 || System lock   | 0.000062 || setup   | 0.000080 || creating table  | 0.005052 || After create   | 0.000220 || copy to tmp table  | 0.000244 || rename result table  | 70.364027 || end    | 0.000575 || Waiting for query cache lock | 0.000062 || end    | 0.000075 || query end   | 0.000057 || closing tables  | 0.000061 || freeing items  | 0.000080 || logging slow query  | 0.000056 || logging slow query  | 0.000098 || cleaning up   | 0.000059 |+------------------------------+-----------+20 rows in set (0.00 sec)

如果没有指定FOR QUERY,那么输出最近一条语句的信息。

LIMIT部分的用法与SELECT中LIMIT子句一致,不赘述。

type是可选的,取值范围可以如下:

  • ALL 显示所有性能信息
  • BLOCK IO 显示块IO操作的次数
  • CONTEXT SWITCHES 显示上下文切换次数,不管是主动还是被动
  • CPU 显示用户CPU时间、系统CPU时间
  • IPC 显示发送和接收的消息数量
  • MEMORY [暂未实现]
  • PAGE FAULTS 显示页错误数量
  • SOURCE 显示源码中的函数名称与位置
  • SWAPS 显示SWAP的次数

例:

mysql> show profile cpu for query 2;
+------------------------------+-----------+----------+------------+| Status   | Duration | CPU_user | CPU_system |+------------------------------+-----------+----------+------------+| starting   | 0.000183 | 0.000000 | 0.000000 || checking permissions  | 0.000057 | 0.000000 | 0.000000 || checking permissions  | 0.000059 | 0.000000 | 0.000000 || init    | 0.000060 | 0.000000 | 0.000000 || Opening tables  | 0.000071 | 0.000000 | 0.000000 || System lock   | 0.000062 | 0.000000 | 0.000000 || setup   | 0.000080 | 0.000000 | 0.001000 || creating table  | 0.005052 | 0.003000 | 0.001000 || After create   | 0.000220 | 0.000000 | 0.000000 || copy to tmp table  | 0.000244 | 0.000000 | 0.000000 || rename result table  | 70.364027 | 7.470864 | 41.612674 || end    | 0.000575 | 0.000000 | 0.001000 || Waiting for query cache lock | 0.000062 | 0.000000 | 0.000000 || end    | 0.000075 | 0.000000 | 0.000000 || query end   | 0.000057 | 0.000000 | 0.000000 || closing tables  | 0.000061 | 0.000000 | 0.000000 || freeing items  | 0.000080 | 0.000000 | 0.000000 || logging slow query  | 0.000056 | 0.000000 | 0.000000 || logging slow query  | 0.000098 | 0.000000 | 0.000000 || cleaning up   | 0.000059 | 0.000000 | 0.000000 |+------------------------------+-----------+----------+------------+20 rows in set (0.00 sec)

ps:
SHOW PROFILE ALL FOR QUERY 2;的信息还可以通过SELECT * FROM information_schema.profiling WHERE query_id = 2 ORDER BY seq;获取。

作用范围
这个命令只是在本会话内起作用,即无法分析本会话外的语句。

开启分析功能后,所有本会话中的语句都被分析(甚至包括执行错误的语句),除了SHOW PROFILE和SHOW PROFILES两句本身。

应用示例:使用SHOW PROFILE分析查询缓存命中的性能优势。

mysql> set profiling=1;
Query OK, 0 rows affected (0.00 sec)

 

mysql> select count(1) as cnt from tran_excution;
+-------+| cnt |+-------+| 14225 |+-------+1 row in set (0.00 sec)

由于已经启用了查询缓存,相同查询(非分区表)可以直接在查询缓存中命中。

mysql> select count(1) as cnt from tran_excution;

我们仔细看下两个同样的语句的分析。

mysql> show profile source for query 1;
+--------------------------------+----------+-----------------------+---------------+-------------+| Status    | Duration | Source_function | Source_file | Source_line |+--------------------------------+----------+-----------------------+---------------+-------------+| starting   | 0.000048 | NULL   | NULL  | NULL || Waiting for query cache lock | 0.000013 | try_lock  | sql_cache.cc |  454 || checking query cache for query | 0.000040 | send_result_to_client | sql_cache.cc | 1561 || checking permissions  | 0.000023 | check_access  | sql_parse.cc | 4751 || Opening tables   | 0.000040 | open_tables  | sql_base.cc | 4831 || System lock   | 0.000020 | mysql_lock_tables | lock.cc |  299 || Waiting for query cache lock | 0.000037 | try_lock  | sql_cache.cc |  454 || init    | 0.000020 | mysql_select  | sql_select.cc | 2579 || optimizing   | 0.000015 | optimize  | sql_select.cc |  865 || statistics   | 0.000017 | optimize  | sql_select.cc | 1056 || preparing   | 0.000016 | optimize  | sql_select.cc | 1078 || executing   | 0.000015 | exec   | sql_select.cc | 1836 || Sending data   | 0.003875 | exec   | sql_select.cc | 2380 || end    | 0.000018 | mysql_select  | sql_select.cc | 2615 || query end   | 0.000015 | mysql_execute_command | sql_parse.cc | 4440 || closing tables   | 0.000016 | mysql_execute_command | sql_parse.cc | 4492 || freeing items   | 0.000016 | mysql_parse  | sql_parse.cc | 5640 || Waiting for query cache lock | 0.000012 | try_lock  | sql_cache.cc |  454 || freeing items   | 0.000032 | NULL   | NULL  | NULL || Waiting for query cache lock | 0.000017 | try_lock  | sql_cache.cc |  454 || freeing items   | 0.000016 | NULL   | NULL  | NULL || storing result in query cache | 0.000017 | end_of_result  | sql_cache.cc | 1020 || logging slow query  | 0.000018 | log_slow_statement | sql_parse.cc | 1461 || logging slow query  | 0.000050 | log_slow_statement | sql_parse.cc | 1470 || cleaning up   | 0.000018 | dispatch_command | sql_parse.cc | 1417 |+--------------------------------+----------+-----------------------+---------------+-------------+25 rows in set (0.00 sec)
mysql> show profile source for query 2;
+--------------------------------+----------+-----------------------+--------------+-------------+| Status    | Duration | Source_function | Source_file | Source_line |+--------------------------------+----------+-----------------------+--------------+-------------+| starting   | 0.000051 | NULL   | NULL  | NULL || Waiting for query cache lock | 0.000014 | try_lock  | sql_cache.cc |  454 || checking query cache for query | 0.000016 | send_result_to_client | sql_cache.cc | 1561 || checking privileges on cached | 0.000013 | send_result_to_client | sql_cache.cc | 1652 || checking permissions  | 0.000015 | check_access  | sql_parse.cc | 4751 || sending cached result to clien | 0.000036 | send_result_to_client | sql_cache.cc | 1749 || logging slow query  | 0.000017 | log_slow_statement | sql_parse.cc | 1461 || cleaning up   | 0.000018 | dispatch_command | sql_parse.cc | 1417 |+--------------------------------+----------+-----------------------+--------------+-------------+8 rows in set (0.00 sec)

SyntaxHighlighter.highlight();

MySQL中的alter table命令的基本使用方法及提速优化

一、基本用法

1. 增加列

alter table tbl_name add col_name type

例如,  给pet的表增加一列 weight,

mysql>alter table pet add weight int;

2. 删除列

alter table tbl_name drop col_name

例如, 删除pet表中的weight这一列

mysql>alter table pet drop weight;

3. 改变列

分为改变列的属性和改变列的名字

改变列的属性――方法1:

alter table tbl_name modify col_name type

例如,改变weight的类型

mysql>alter table pet modify weight varchar(30);

改变列的属性――方法2:

alter table tbl_name change old_col_name col_name type

例如,改变weight的类型

alter table pet change weight weight varchar(30);

改变列的名字:

alter table tbl_name change old_col_name col_name

例如改变pet表中weight的名字:

mysql>alter table pet change weight wei;

4. 改变表的名字

alter table tbl_name rename new_tbl

例如, 把pet表更名为animal

mysql>alter table pet rename animal;

二、对ALTER TABLE的优化
在系统的日常维护中,经常需要对表结构进行更新,例如添加/删除一个字段,改变一个VARCHAR的字段长度等等。MySQL针对这种修改表结构的处理方式是先创建一张新的结构的表,接着会通过执行Insert语句将旧表的内容插入到新表中,最后删除整张旧表。这种处理方式在数据量比较小的时候,不会有什么问题,可是当数据量很大的时候可能需要很多时间来处理该过程。
 
执行一个更新表结构的操作花费了几个小时的时间,这是无法忍受的。如果你用的是5.1之前的版本的话,还会在执行表结构更新是数据库往往是停止服务的,幸好在最新的版本中这个问题得到了改善
 
如果在进行表结构更新的时候你采用了恰当的方法,也并不是所有的更新操作会占用你很久的时间。
例如 你想更新用户表的默认密码为“666666”,通常采用的做法是

mysql> ALTER TABLE user      -> MODIFY COLUMN pwd VARCHAR NOT NULL DEFAULT ‘666666';

 
通过SHOW STATUS你可以发现在执行这个操作的过程中进行了大量的Insert操作,当用户的数量很大时 例如百万,千万条的数据时,必然会消耗很多的时间。
 
可是如果你采用下边的方式来更新的话,时间会大大的缩短

mysql> ALTER TABLE user     -> ALTER COLUMN pwd varchar not null SETDEFAULT 5;

 
执行SHOW STATUS操作发现大量的插入操作不存在了,且时间也大大的缩短了(需要先进行FLUSH STATUS)
 之所以可能缩短时间是因为
(1)表字段的默认值是放在表的frm(.frm:表结构文件  .MYD:表数据文件  .MYI:表索引)文件中
(2)ALTER COLUMN会更新frm文件,而不会涉及到表的内容
(3)MODIFY COLUMN会涉及到表数据的内容
 
 从前面的列子可以看出如果操作的过程中只涉及到frm文件的改动的话,表结构的更新效率会大大的提高,但是很多时候在没有必要的时候mysql也会进行表的重建。如果你愿意承担风险,可以用修改frm文件的方式以达到提速修改表结的目的
 
并不是所有的表结构改动都可以通过修改frm文件的方式来提高修改的效率,下面的一些改动可以通过修改frm文件的方式达到更新的目的:
(1)      更改字段的默认值
(2)      增加/删除字段的AUTO_INCREMENT属性
(3)      增加/删除/修改 ENUM的常量值。对于删除操作,如果有字段引用了这个常量值,则在删除后查询的结构为空字符串
 
下面以更新字段的默认值属性为例,分别通过使用ALTER COLUMN和修改frm文件的方式来提高修改表结构的效率
 
1 执行ALTER COLUMN
1.1   首先准备一张字典表

CREATETABLE IF NOT EXISTS dictionary (    id int(10) unsigned NOT NULLAUTO_INCREMENT,  word varchar(100) NOT NULL,  mean varchar(300) NOT NULL,   PRIMARY KEY (`id`)   );  

1.2    插入一些测试数据

mysql>DELIMITER $$mysql>DROP PROCEDURE IF EXISTS SampleProc$$Query OK, 0rows affected, 1 warning (0.01 sec)
CREATEPROCEDURE SampleProc()
BEGIN
DECLARE xINT;
SET x = 1;
WHILEx  <= 110000 DO
insert intodictionary (word, mean) values(concat('a',x),concat('a means',x));
SET  x = x + 1;
END WHILE;
END
mysql>    DELIMITER ;
mysql>call SampleProc();

1.3    SHOW STATUS 观察结果Modify Column 以及Alter Column的区别
首先使用MODIFY COLUMN

mysql> flush status;Query OK, 0 rows affected (0.00 sec)mysql> alter table dictionary      ->modify column mean varchar(20) NOT null default 'DEFAULT1';Query OK, 110002 rows affected (3.07 sec)Records: 110002 Duplicates: 0 Warnings: 0mysql> SHOW STATUS WHERE Variable_name LIKE'Handler%'  ->OR Variable_name LIKE 'Created%';+----------------------------+--------+| Variable_name       | Value |+----------------------------+--------+| Handler_read_rnd_next   | 110003 || Handler_rollback      | 0   || Handler_savepoint     | 0   || Handler_savepoint_rollback | 0   || Handler_update       | 0   || Handler_write       | 110002 |+----------------------------+--------+

 
在使用ALTER COLUMN

 mysql> flush status;mysql> alter table dictionary     -> alter column mean set default'DEFAULT2';Query OK, 0 rowsaffected (0.03 sec)Records: 0 Duplicates: 0 Warnings: 0 mysql> SHOW STATUSWHERE Variable_name LIKE 'Handler%'     -> OR Variable_name LIKE 'Created%';+----------------------------+-------+| Variable_name       | Value |+----------------------------+-------+|Handler_read_rnd_next   | 0   ||Handler_savepoint_rollback | 0   || Handler_update       | 0   || Handler_write       | 0   |

2    修改frm文件
通过修改frm文件的方式来提高修改表结构效率的步骤大概如下
1.       备份相关的数据库文件
2.       创建一张和旧表完全相同的表结构

mysql>create table dictionary_new like dictionary;

3.       执行FLUSH TABLES WITH READ LOCK. 所有的表都被关闭

mysql> alter table dictionary_new     -> modify column mean varchar(30)default 'DEFAULR#';mysql> flush table with read lock;

 
5.       把dictionary_new.frm文件重名为dictionary.frm
6.       执行UNLOCK TABLES

       mysql> unlock tables;       mysql> insert into dictionary(word) values('Random');       mysql> select * from dictionarywhere word='Random';
+--------+--------+----------+| id   | word | mean   |+--------+--------+----------+| 110004 |Random | DEFAULR# |+--------+--------+----------+

SyntaxHighlighter.highlight();

MySQL的常用命令集锦

下面是我们经常会用到且非常有用的MySQL命令。下面你看到#表示在Unix命令行下执行命令,看到mysql>表示当前已经登录MySQL服务器,是在mysql客户端执行mysql命令。
登录MySQL,如果连接远程数据库,需要用-h指定hostname。

登录MySQL,如果连接远程数据库,需要用-h指定hostname。

# [mysql dir]/bin/mysql -h hostname -u root -p

创建一个数据库。

mysql> create database [databasename];

列出所有数据库。

mysql> show databases;

切换到一个数据库。

mysql> use [db name];

显示一个数据库的所有表。

mysql> show tables;

查看数据表的字段格式。

mysql> describe [table name];

删除一个数据库。

mysql> drop database [database name];

删除一个数据表。

mysql> drop table [table name];

显示一个数据表的所有数据。

mysql> SELECT * FROM [table name];

返回指定数据表的各列信息。

mysql> show columns from [table name];

使用值“whatever”过滤显示选定的某些行。

mysql> SELECT * FROM [table name] WHERE [field name] = "whatever";

显示所有包含name为”Bob”和phone number为“3444444”的记录。

mysql> SELECT * FROM [table name] WHERE name = "Bob" AND phone_number = '3444444';

显示所有不包含name为”Bob”和phone number为“3444444”的记录,并以phone_number字段排序。

mysql> SELECT * FROM [table name] WHERE name != "Bob" AND phone_number = '3444444' order by phone_number;

显示所有的name以字母“bob”开头和phone number为“3444444”的记录。

mysql> SELECT * FROM [table name] WHERE name like "Bob%" AND phone_number = '3444444';

显示name以字母“bob”开头和phone number为“3444444”的第1至第5条记录。

mysql> SELECT * FROM [table name] WHERE name like "Bob%" AND phone_number = '3444444' limit 1,5;

使用正则表达式查找记录。使用“正则表达式二进制”强制区分大小写。此命令查找以a开头的任何记录。

mysql> SELECT * FROM [table name] WHERE rec RLIKE "^a";

返回唯一不同的记录。

mysql> SELECT DISTINCT [column name] FROM [table name];

以升序或降序显示选定的记录。

mysql> SELECT [col1],[col2] FROM [table name] ORDER BY [col2] DESC;

返回行数。

mysql> SELECT COUNT(*) FROM [table name];

统计指定列值的总和。

mysql> SELECT SUM(*) FROM [table name];

联结表。

mysql> select lookup.illustrationid, lookup.personid,person.birthday from lookup left join person on lookup.personid=person.personid=statement to join birthday in person table with primary illustration id;
# mysql -u root -pmysql> use mysql;mysql> INSERT INTO user (Host,User,Password) VALUES('%','username',PASSWORD('password'));mysql> flush privileges;
# [mysql dir]/bin/mysqladmin -u username -h hostname.blah.org -p password 'new-password'
从mysql命令行更改用户密码。以root登录,设置密码,更新权限。
# /etc/init.d/mysql stop# mysqld_safe --skip-grant-tables &# mysql -u root
mysql> use mysql;mysql> update user set password=PASSWORD("newrootpassword") where User='root';mysql> flush privileges;mysql> quit
# /etc/init.d/mysql stop# /etc/init.d/mysql start
root密码为空时,设置root密码。
# mysqladmin -u root password newpassword
更新root密码。
# mysqladmin -u root -p oldpassword newpassword
允许用户“bob”从localhost以密码“passwd”连接服务器。以root登录,切换mysql数据库。设置权限,更新权限。
# mysql -u root -p
mysql> use mysql;mysql> grant usage on *.* to bob@localhost identified by 'passwd';mysql> flush privileges;
如果不想手工输入密码 请使用–password 参数
 mysqldump -h database_ip -u Username --password=123456 --opt databasename > backup-file.sql mysqldump -h database_ip -d -u Username --password=123456 databasename >database_structure.sql
为数据库db设置权限。以root登录,切换到mysql数据库,授予权限,更新权限。
# mysql -u root -p
mysql> use mysql;mysql> INSERT INTO db (Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv) VALUES ('%','databasename','username','Y','Y','Y','Y','Y','N');mysql> flush privileges;
或者
mysql> grant all privileges on databasename.* to username@localhost;mysql> flush privileges;
更新已存在表的数据。
mysql> UPDATE [table name] SET Select_priv = 'Y',Insert_priv = 'Y',Update_priv = 'Y' where [field name] = 'user';
删除表中[field name] = ‘whatever’的行。
mysql> DELETE from [table name] where [field name] = 'whatever';
更新数据库的权限/特权。
mysql> flush privileges;
删除列。
mysql> alter table [table name] drop column [column name];
新增列到db。
mysql> alter table [table name] add column [new column name] varchar (20);
更改列名。
mysql> alter table [table name] change [old column name] [new column name] varchar (50);
增加唯一的列。
mysql> alter table [table name] add unique ([column name]);
设置列值大点。
mysql> alter table [table name] modify [column name] VARCHAR(3);
删除唯一列。
mysql> alter table [table name] drop index [colmn name];
导入一个CSV文件到表。
mysql> LOAD DATA INFILE '/tmp/filename.csv' replace INTO TABLE [table name] FIELDS TERMINATED BY ',' LINES TERMINATED BY '/n' (field1,field2,field3);
导出所有数据库到sql文件。
# [mysql dir]/bin/mysqldump -u root -ppassword --opt >/tmp/alldatabases.sql
导出一个数据库。
# [mysql dir]/bin/mysqldump -u username -ppassword --databases databasename >/tmp/databasename.sql
从一个数据库导出一个表。
# [mysql dir]/bin/mysqldump -c -u username -ppassword databasename tablename > /tmp/databasename.tablename.sql
从sql文件还原数据库(数据表)。
# [mysql dir]/bin/mysql -u username -ppassword databasename < /tmp/databasename.sql
创建数据表例1。
mysql> CREATE TABLE [table name] (firstname VARCHAR(20), middleinitial VARCHAR(3), lastname VARCHAR(35),suffix VARCHAR(3),officeid VARCHAR(10),userid VARCHAR(15),username VARCHAR(8),email VARCHAR(35),phone VARCHAR(25), groups VARCHAR(15),datestamp DATE,timestamp time,pgpemail VARCHAR(255));
创建数据表例2。
mysql> create table [table name] (personid int(50) not null auto_increment primary key,firstname varchar(35),middlename varchar(50),lastnamevarchar(50) default 'bato');
将查询结果保存到文件
 select title from book into outfile '/tmp/outfile.txt';
查找表中多余的重复记录,重复记录是根据某个字段(peopleId)来判断
 select * from people where peopleId in (select peopleId from people group by  peopleId having count(peopleId) > 1);
查询表中不重复记录(排除重复记录)
 select * from phome_ecms_wma where title in (select distinct title from phome_ecms_wma);
删除表中重复记录,重复记录是根据某个字段(title)来判断
 select *,count(distinct title) INTO OUTFILE '/tmp/table.bak' from phome_ecms_wma group by title; delete from phome_ecms_wma; LOAD DATA INFILE '/tmp/table.bak' REPLACE INTO TABLE phome_ecms_wma character set utf8;
随机选取记录
 SELECT *FROM url ORDER BY RAND() LIMIT 5;
查询数据库当前编码
 mysql> show variables like "character_set%";
修改表字段类型
 mysql> alter table table_name change last_action last_action datetime NOT NULL default '0000-00-00 00:00:00';
给表添加一个新字段
 mysql> ALTER TABLE host ADD ks_mac VARCHAR(100);
从表中删除一个字段
 mysql> ALTER TABLE table_name DROP field_name; 
重命名表
 mysql>alter table t1 rename t2;
给字段加索引
 mysql> alter table tablename add index 索引名 (字段名1[,字段名2 …]); mysql> alter table tablename add index emp_name (name);
加主关键字的索引
 mysql> alter table tablename add primary key(id);
加唯一限制条件的索引
 mysql> alter table tablename add unique emp_name2(cardnumber);
删除某个索引
 mysql>alter table tablename drop index emp_name;
远程访问mysql 设置
 mysql> GRANT ALL PRIVILEGES ON database_test.* to root@192.168.1.9 IDENTIFIED BY '123456'; mysql> FLUSH PRIVILEGES;

SyntaxHighlighter.highlight();

关于Mysql中文乱码问题该如何解决(乱码问题完美解决方案)

最近两天做项目总是被乱码问题困扰着,这不刚把mysql中文乱码问题解决了,下面小编把我的解决方案分享给大家,供大家参考,也方便以后自己查阅。

首先:

用show variables like “%colla%”;show varables like “%char%”;这两条命令查看数据库与服务端的字符集设置

如果查看出来都是gbk2312,或 gbk,那么就只能支持简体中文,繁体和一些特殊符号是不能插入的,我们只有修改字符集为UTF-8,

修改方法如下:

用记事本或UitraEdit打开mysql数据库安装目录下的my.ini文件打开, 然后Ctrl+F搜索default-character-set,将后面的字符集修改为UTF8,注意要修改两个地方,一个事客户端的,一个是服务端的。

然后保存,重启mysql服务、、进去继续用show variables like “%colla%”;show varables like “%char%”;着两条语句查询一下字符集。 如图:

到此就配置完成了。

注意:

如果以前建有数据库没有删除的 请用 show database 数据库名;和 show create table 表名;查看一下数据库和表的字符集是否为UTF8 , 因为修改my.ini文件,它不能修改原来数据库的的字符集。在命令行下面可以用

alter database 数据库名 character set “字符集”; 命令来修改数据库字符集

还有一点要注意的是,修改为UTF8以后,在命令行下面中文是乱码的,只输出到页面或控制台是正常的,这个问题我也上网查了一下,貌似命令行下面不支持UTF8,我也不太清楚。

当修改以后,在命令行下面如果要插入中文,可以在插入语句之前执行,set names gbk2312;就可以插入中文了,但是不能插入繁体和一些特殊符号。

以上就是这几天解决乱码的成果。希望各位大虾多多指教。

下面抽点空给大家整理些关于MySQL会出现中文乱码的原因不外乎下列几点。

1.server本身设定问题,例如还停留在latin1
2.table的语系设定问题(包含character与collation)
3.客户端程式(例如php)的连线语系设定问题

强烈建议使用utf8!!!!

utf8可以兼容世界上所有字符!!!!

一、避免创建数据库及表出现中文乱码和查看编码方法

1、创建数据库的时候:

CREATE DATABASE `test` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'; 

2、建表的时候

CREATE TABLE `database_user` ( `ID` varchar(40) NOT NULL default '', `UserID` varchar(40) NOT NULL default '', ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

这3个设置好了,基本就不会出问题了,即建库和建表时都使用相同的编码格式。

但是如果你已经建了库和表可以通过以下方式进行查询。

1.查看默认的编码格式:

mysql> show variables like "%char%"; +--------------------------+---------------+ | Variable_name | Value | +--------------------------+---------------+ | character_set_client | gbk | | character_set_connection | gbk | | character_set_database | utf8 | | character_set_filesystem | binary | | character_set_results | gbk | | character_set_server | utf8 | | character_set_system | utf8 | +--------------------------+-------------+ 

注:以前2个来确定,可以使用set names utf8,set names gbk设置默认的编码格式;

执行SET NAMES utf8的效果等同于同时设定如下:

SET character_set_client='utf8'; SET character_set_connection='utf8'; SET character_set_results='utf8'; 

2.查看test数据库的编码格式:

mysql> show create database test; +------------+------------------------------------------------------------------------------------------------+ | Database | Create Database | +------------+------------------------------------------------------------------------------------------------+ | test | CREATE DATABASE `test` /*!40100 DEFAULT CHARACTER SET gbk */ | +------------+------------------------------------------------------------------------------------------------+ 

3.查看yjdb数据表的编码格式:

mysql> show create table yjdb; | yjdb | CREATE TABLE `yjdb` ( `sn` int(5) NOT NULL AUTO_INCREMENT, `type` varchar(10) NOT NULL, `brc` varchar(6) NOT NULL, `teller` int(6) NOT NULL, `telname` varchar(10) NOT NULL, `date` int(10) NOT NULL, `count` int(6) NOT NULL, `back` int(10) NOT NULL, PRIMARY KEY (`sn`), UNIQUE KEY `sn` (`sn`), UNIQUE KEY `sn_2` (`sn`) ) ENGINE=MyISAM AUTO_INCREMENT=1826 DEFAULT CHARSET=gbk ROW_FORMAT=DYNAMIC | 

二、避免导入数据有中文乱码的问题

1:将数据编码格式保存为utf-8

设置默认编码为utf8:

set names utf8;

设置数据库db_name默认为utf8:

ALTER DATABASE `db_name` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; 

设置表tb_name默认编码为utf8:

ALTER TABLE `tb_name` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; 

导入:

LOAD DATA LOCAL INFILE ‘C://utf8.txt’ INTO TABLE yjdb; 

2:将数据编码格式保存为ansi(即GBK或GB2312)

设置默认编码为gbk:

set names gbk;

设置数据库db_name默认编码为gbk:

ALTER DATABASE `db_name` DEFAULT CHARACTER SET gbk COLLATE gbk_chinese_ci; 

设置表tb_name默认编码为gbk:

ALTER TABLE `tb_name` DEFAULT CHARACTER SET gbk COLLATE gbk_chinese_ci; 

导入:

LOAD DATA LOCAL INFILE ‘C://gbk.txt’ INTO TABLE yjdb; 

注:1.UTF8不要导入gbk,gbk不要导入UTF8;

2.dos下不支持UTF8的显示;

三、解决网页中乱码的问题

将网站编码设为 utf-8,这样可以兼容世界上所有字符。

  如果网站已经运作了好久,已有很多旧数据,不能再更改简体中文的设定,那么建议将页面的编码设为 GBK, GBK与GB2312的区别就在于:GBK能比GB2312显示更多的字符,要显示简体码的繁体字,就只能用GBK。

1.编辑/etc/my.cnf ,在[mysql]段加入default_character_set=utf8;

SyntaxHighlighter.highlight();

MySQL的子查询及相关优化学习教程

一、子查询
1、where型子查询
(把内层查询结果当作外层查询的比较条件)

#不用order by 来查询最新的商品select goods_id,goods_name from goods where goods_id = (select max(goods_id) from goods);
#取出每个栏目下最新的产品(goods_id唯一)select cat_id,goods_id,goods_name from goods where goods_id in(select max(goods_id) from goods group by cat_id); 

2、from型子查询
(把内层的查询结果供外层再次查询)
#用子查询查出挂科两门及以上的同学的平均成绩
思路:

#先查出哪些同学挂科两门以上select name,count(*) as gk from stu where score < 60 having gk >=2;#以上查询结果,我们只要名字就可以了,所以再取一次名字select name from (select name,count(*) as gk from stu having gk >=2) as t;#找出这些同学了,那么再计算他们的平均分select name,avg(score) from stu where name in (select name from (select name,count(*) as gk from stu having gk >=2) as t) group by name;

3、exists型子查询
(把外层查询结果拿到内层,看内层的查询是否成立)

#查询哪些栏目下有商品,栏目表category,商品表goodsselect cat_id,cat_name from category where exists(select * from goods where goods.cat_id = category.cat_id);

二、优化
从句式的形式看,子查询分为特殊格式子查询和非特殊格式子查询,特殊格式的子查询中又包括IN、ALL、ANY、SOME、EXISTS等类型的子查询,对于有的类型的子查询,MySQL有的支持优化,有的不支持,具体情况如下。

 

示例一,MySQL不支持对EXISTS类型的子查询的优化:

EXISTS类型的相关子查询,查询执行计划如下:

mysql> EXPLAIN EXTENDED SELECT * FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t1.a1= t2.a2 AND t2.a2>10);

+----+--------------------+-------+------+------+-------------+| id | select_type    | table | type | key | Extra    |+----+--------------------+-------+------+------+-------------+| 1 | PRIMARY      | t1  | ALL | NULL | Using where || 2 | DEPENDENT SUBQUERY | t2  | ALL | NULL | Using where |+----+--------------------+-------+------+------+-------------+2 rows in set, 2 warnings (0.00 sec)

被查询优化器处理后的语句为:

/* select#1 */ select `test`.`t1`.`id1` AS `id1`,`test`.`t1`.`a1` AS `a1`,  `test`.`t1`.`b1` AS `b1`from `test`.`t1`where exists(/* select#2 */  select 1  from `test`.`t2`  where ((`test`.`t1`.`a1` = `test`.`t2`.`a2`) and (`test`.`t2`.`a2` > 10)))

从查询执行计划看,子查询存在,MySQL没有进一步做子查询的优化工作。

另外的一个EXISTS类型的相关子查询,查询执行计划如下:

mysql> EXPLAIN EXTENDED SELECT * FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t1.b1= t2.b2 AND t1.a1=10);
+----+--------------------+-------+------+------+-------------+| id | select_type    | table | type | key | Extra    |+----+--------------------+-------+------+------+-------------+| 1 | PRIMARY      | t1  | ALL | NULL | Using where || 2 | DEPENDENT SUBQUERY | t2  | ALL | NULL | Using where |+----+--------------------+-------+------+------+-------------+2 rows in set, 3 warnings (0.02 sec)

被查询优化器处理后的语句为:

/* select#1 */ select `test`.`t1`.`id1` AS `id1`,`test`.`t1`.`a1` AS `a1`,  `test`.`t1`.`b1` AS `b1`from `test`.`t1`where exists(/* select#2 */  select 1  from `test`.`t2`  where ((`test`.`t1`.`b1` = `test`.`t2`.`b2`) and (`test`.`t1`.`a1` = 10)))

从查询执行计划看,子查询存在,MySQL没有进一步做子查询的优化工作。

 

示例二,MySQL不支持对NOT EXISTS类型的子查询的优化:

NOT EXISTS类型的相关子查询,查询执行计划如下:

mysql> EXPLAIN EXTENDED SELECT * FROM t1 WHERE NOT EXISTS (SELECT 1 FROM t2 WHERE t1.a1= t2.a2 AND t2.a2>10);
+----+--------------------+-------+------+------+-------------+| id | select_type    | table | type | key | Extra    |+----+--------------------+-------+------+------+-------------+| 1 | PRIMARY      | t1  | ALL | NULL | Using where || 2 | DEPENDENT SUBQUERY | t2  | ALL | NULL | Using where |+----+--------------------+-------+------+------+-------------+2 rows in set, 2 warnings (0.00 sec)

被查询优化器处理后的语句为:

/* select#1 */ select `test`.`t1`.`id1` AS `id1`,`test`.`t1`.`a1` AS `a1`,  `test`.`t1`.`b1` AS `b1`from `test`.`t1`where (not(exists(  /* select#2 */ select 1  from `test`.`t2`  where ((`test`.`t1`.`a1` = `test`.`t2`.`a2`) and (`test`.`t2`.`a2` > 10)))))

从查询执行计划看,子查询存在,MySQL没有进一步做子查询的优化工作。

 

另外的一个NOT EXISTS类型的相关子查询,查询执行计划如下:

mysql> EXPLAIN EXTENDED SELECT * FROM t1 WHERE NOT EXISTS (SELECT 1 FROM t2 WHERE t1.b1= t2.b2 AND t1.a1=10);
+----+--------------------+-------+------+------+-------------+| id | select_type    | table | type | key | Extra    |+----+--------------------+-------+------+------+-------------+| 1 | PRIMARY      | t1  | ALL | NULL | Using where || 2 | DEPENDENT SUBQUERY | t2  | ALL | NULL | Using where |+----+--------------------+-------+------+------+-------------+2 rows in set, 3 warnings (0.00 sec)

被查询优化器处理后的语句为:

/* select#1 */ select `test`.`t1`.`id1` AS `id1`,`test`.`t1`.`a1` AS `a1`,  `test`.`t1`.`b1` AS `b1`from `test`.`t1`where (not(exists(  /* select#2 */ select 1  from `test`.`t2`  where ((`test`.`t1`.`b1` = `test`.`t2`.`b2`) and (`test`.`t1`.`a1` = 10)))))

从查询执行计划看,子查询存在,MySQL没有进一步做子查询的优化工作。

 

示例三,MySQL支持对IN类型的子查询的优化,按也有不支持的情况存在:

IN非相关子查询,查询执行计划如下:

mysql> EXPLAIN EXTENDED SELECT * FROM t1 WHERE t1.a1 IN (SELECT a2 FROM t2 WHERE t2.a2>10);
+----+--------------+-------------+------+------+----------------------------------------------------+| id | select_type | table    | type | key | Extra  |+----+--------------+-------------+------+------+----------------------------------------------------+| 1 | SIMPLE    | <subquery2> | ALL | NULL | NULL  || 1 | SIMPLE    | t1     | ALL | NULL | Using where; Using join buffer (Block Nested Loop) || 2 | MATERIALIZED | t2     | ALL | NULL | Using where  |+----+--------------+-------------+------+------+----------------------------------------------------+3 rows in set, 1 warning (0.00 sec)

被查询优化器处理后的语句为:

/* select#1 */ select `test`.`t1`.`id1` AS `id1`,`test`.`t1`.`a1` AS `a1`,  `test`.`t1`.`b1` AS `b1`from `test`.`t1` semi join (`test`.`t2`)where ((`test`.`t1`.`a1` = `<subquery2>`.`a2`) and (`test`.`t2`.`a2` > 10))

从查询执行计划看,表t2被物化后,与表t1执行了半连接(semi join)。尽管有“subquery2”这样的内容看起来是子查询,但是表t2已经被上拉到表t1层执行了半连接,所以MySQL支持IN子查询优化为半连接操作。

 

另外一个IN非相关子查询,查询执行计划如下:

mysql> EXPLAIN EXTENDED SELECT * FROM t1 WHERE t1.a1 IN (SELECT a2 FROM t2 WHERE t2.a2=10);
+----+--------------+-------------+------+------+----------------------------------------------------+| id | select_type | table    | type | key | Extra  |+----+--------------+-------------+------+------+----------------------------------------------------+| 1 | SIMPLE    | <subquery2> | ALL | NULL | Using where  || 1 | SIMPLE    | t1     | ALL | NULL | Using where; Using join buffer (Block Nested Loop) || 2 | MATERIALIZED | t2     | ALL | NULL | Using where  |+----+--------------+-------------+------+------+----------------------------------------------------+3 rows in set, 1 warning (0.02 sec)

被查询优化器处理后的语句为:

/* select#1 */ select `test`.`t1`.`id1` AS `id1`,`test`.`t1`.`a1` AS `a1`,  `test`.`t1`.`b1` AS `b1`from `test`.`t1` semi join (`test`.`t2`)where ((`<subquery2>`.`a2` = 10) and (`test`.`t1`.`a1` = 10) and (`test`.`t2`.`a2` = 10))

从查询执行计划看,子查询不存在,表t1和t2直接做了块嵌套循环半连接(Block Nested Loop),把子查询上拉到父查询中用嵌套循环半连接完成IN操作。另外,由于子查询上拉,使得增加连接条件“a1=a2”,而原先的条件“a2=10”可以利用常量传递优化技术,使得“a1=a2=10”,所以查询执行计划中,两个索引扫描的条件分别为:a1 = 10、a2 = 10。

 

另外一个IN非相关子查询,查询执行计划如下:

mysql> EXPLAIN EXTENDED SELECT * FROM t1 WHERE t1.a1 IN (SELECT a2 FROM t2 WHERE t1.a1=10);
+----+-------------+-------+------+------------------------------------------------------------------+| id | select_type | table | type | Extra      |+----+-------------+-------+------+------------------------------------------------------------------+| 1 | SIMPLE   | t2  | ALL | Using where; Start temporary      || 1 | SIMPLE   | t1  | ALL | Using where; End temporary; Using join buffer (Block Nested Loop)|+----+-------------+-------+------+------------------------------------------------------------------+2 rows in set, 2 warnings (0.00 sec)

被查询优化器处理后的语句为:

/* select#1 */ select `test`.`t1`.`id1` AS `id1`,`test`.`t1`.`a1` AS `a1`,  `test`.`t1`.`b1` AS `b1`from `test`.`t1` semi join (`test`.`t2`)where ((`test`.`t2`.`a2` = 10) and (`test`.`t1`.`a1` = 10))

从查询执行计划看,子子查询不存在,表t1和t2直接做了块嵌套循环连接(Block Nested Loop),但属于半连接操作(semi join),把子查询上拉到父查询中用嵌套循环半连接完成IN操作。

 

示例四,MySQL支持对NOT IN类型的子查询的优化

NOT IN非相关子查询,查询执行计划如下:

mysql> EXPLAIN EXTENDED SELECT * FROM t1 WHERE t1.a1 NOT IN (SELECT a2 FROM t2 WHERE t2.a2>10);
+----+-------------+-------+------+------+-------------+| id | select_type | table | type | key | Extra    |+----+-------------+-------+------+------+-------------+| 1 | PRIMARY   | t1  | ALL | NULL | Using where || 2 | SUBQUERY  | t2  | ALL | NULL | Using where |+----+-------------+-------+------+------+-------------+2 rows in set, 1 warning (0.02 sec)

被查询优化器处理后的语句为:

/* select#1 */ select `test`.`t1`.`id1` AS `id1`,`test`.`t1`.`a1` AS `a1`,`test`.`t1`.`b1` AS `b1`from `test`.`t1`where (not(<in_optimizer>(  `test`.`t1`.`a1`,`test`.`t1`.`a1` in (    <materialize> (/* select#2 */      select `test`.`t2`.`a2`      from `test`.`t2`      where (`test`.`t2`.`a2` > 10)      having 1    ),    <primary_index_lookup>(      `test`.`t1`.`a1` in <temporary table> on <auto_key>      where ((`test`.`t1`.`a1` = `materialized-subquery`.`a2`))    )   )  )))

从查询执行计划看,表t2做了子查询(SUBQUERY)。而子查询被物化(materialize)。所以,MySQL对于NOT IN子查询采用了物化的优化方式,但不支持子查询的消除。

 

另外一个NOT IN非相关子查询,查询执行计划如下:

mysql> EXPLAIN EXTENDED SELECT * FROM t1 WHERE t1.a1 NOT IN (SELECT a2 FROM t2 WHERE t2.a2=10);
+----+-------------+-------+------+------+-------------+| id | select_type | table | type | key | Extra    |+----+-------------+-------+------+------+-------------+| 1 | PRIMARY   | t1  | ALL | NULL | Using where || 2 | SUBQUERY  | t2  | ALL | NULL | Using where |+----+-------------+-------+------+------+-------------+2 rows in set, 1 warning (0.00 sec)

被查询优化器处理后的语句为:

/* select#1 */ select `test`.`t1`.`id1` AS `id1`,`test`.`t1`.`a1` AS `a1`,`test`.`t1`.`b1` AS `b1`from `test`.`t1`where (not(<in_optimizer>(  `test`.`t1`.`a1`,`test`.`t1`.`a1` in (    <materialize> (/* select#2 */      select `test`.`t2`.`a2`      from `test`.`t2`      where (`test`.`t2`.`a2` = 10)      having 1    ),    <primary_index_lookup>(      `test`.`t1`.`a1` in <temporary table> on <auto_key>      where ((`test`.`t1`.`a1` = `materialized-subquery`.`a2`))    )  )  )))

从查询执行计划看,表t2做了子查询(SUBQUERY)。而子查询被物化(materialize)。所以,MySQL对于NOT IN子查询采用了物化的优化方式,但不支持子查询的消除。

 

示例五,MySQL支持对ALL类型的子查询的优化:

不相关的ALL子查询,查询执行计划如下:

mysql> EXPLAIN EXTENDED SELECT * FROM t1 WHERE t1.a1 >ALL (SELECT a2 FROM t2 WHERE t2.a2>10);
+----+-------------+-------+------+------+-------------+| id | select_type | table | type | key | Extra    |+----+-------------+-------+------+------+-------------+| 1 | PRIMARY   | t1  | ALL | NULL | Using where || 2 | SUBQUERY  | t2  | ALL | NULL | Using where |+----+-------------+-------+------+------+-------------+2 rows in set, 1 warning (0.00 sec)

被查询优化器处理后的语句为:

/* select#1 */ select `test`.`t1`.`id1` AS `id1`,`test`.`t1`.`a1` AS `a1`,`test`.`t1`.`b1` AS `b1`from `test`.`t1`where <not>((`test`.`t1`.`a1` <= <max>(  /* select#2 */  select `test`.`t2`.`a2`  from `test`.`t2`  where (`test`.`t2`.`a2` > 10)  )))

从查询执行计划看,出现了子查询(SUBQUERY),但是,子查询被“<= <max>”操作符限制,而子查询中的被查询列a2上存在唯一索引,所以可以利用索引求最值,所以MySQL支持“>ALL”式的子查询优化,子查询只被执行一次即可求得最大值。

 

不相关的ALL子查询,查询执行计划如下:

mysql> EXPLAIN EXTENDED SELECT * FROM t1 WHERE t1.a1 =ALL (SELECT a2 FROM t2 WHERE t2.a2=10);
+----+--------------------+-------+------+------+-------------+| id | select_type    | table | type | key | Extra    |+----+--------------------+-------+------+------+-------------+| 1 | PRIMARY      | t1  | ALL | NULL | Using where || 2 | DEPENDENT SUBQUERY | t2  | ALL | NULL | Using where |+----+--------------------+-------+------+------+-------------+2 rows in set, 1 warning (0.00 sec)

被查询优化器处理后的语句为:

/* select#1 */ select `test`.`t1`.`id1` AS `id1`,`test`.`t1`.`a1` AS `a1`,`test`.`t1`.`b1` AS `b1`from `test`.`t1`where <not>(<in_optimizer>(  `test`.`t1`.`a1`,<exists>(    /* select#2 */ select 1 from `test`.`t2`    where ((`test`.`t2`.`a2` = 10) and      <if>(outer_field_is_not_null,        ((<cache>(`test`.`t1`.`a1`) <> 10) or <cache>(isnull(10))),        true      )    )    having <if>(outer_field_is_not_null, <is_not_null_test>(`test`.`t2`.`a2`), true)  )))

从查询执行计划看,出现了子查询(SUBQUERY),但是被查询优化器处理后的语句中包含“exists”,这表明MySQL对于“=ALL”式的子查询优化用“EXISTS strategy”方式优化,所以MySQL支持“=ALL”式的子查询优化。

 

不相关的ALL子查询,查询执行计划如下:

mysql> EXPLAIN EXTENDED SELECT * FROM t1 WHERE t1.a1 <ALL (SELECT a2 FROM t2 WHERE t2.a2=10);
+----+-------------+-------+------+------+-------------+| id | select_type | table | type | key | Extra    |+----+-------------+-------+------+------+-------------+| 1 | PRIMARY   | t1  | ALL | NULL | Using where || 2 | SUBQUERY  | t2  | ALL | NULL | Using where |+----+-------------+-------+------+------+-------------+2 rows in set, 1 warning (0.00 sec)

被查询优化器处理后的语句为:

/* select#1 */ select `test`.`t1`.`id1` AS `id1`,`test`.`t1`.`a1` AS `a1`,`test`.`t1`.`b1` AS `b1`from `test`.`t1`where <not>((`test`.`t1`.`a1` >= <min>  (/* select#2 */    select `test`.`t2`.`a2`    from `test`.`t2`    where (`test`.`t2`.`a2` = 10)  )))

从查询执行计划看,出现了子查询(SUBQUERY),但是,子查询被“>= <min>”操作符限制,而子查询中的被查询列a2上存在唯一索引,所以可以利用索引求最值,所以MySQL支持“<ALL”式的子查询优化,子查询只被执行一次即可求得最小值。

 

示例六,MySQL支持对SOME类型的子查询的优化:

使用了“>SOME”式子的子查询被优化,查询执行计划如下:

mysql> EXPLAIN EXTENDED SELECT * FROM t1 WHERE t1.a1 >SOME (SELECT a2 FROM t2 WHERE t2.a2>10);
+----+-------------+-------+------+------+-------------+| id | select_type | table | type | key | Extra    |+----+-------------+-------+------+------+-------------+| 1 | PRIMARY   | t1  | ALL | NULL | Using where || 2 | SUBQUERY  | t2  | ALL | NULL | Using where |+----+-------------+-------+------+------+-------------+2 rows in set, 1 warning (0.05 sec)

被查询优化器处理后的语句为:

 /* select#1 */ select `test`.`t1`.`id1` AS `id1`,`test`.`t1`.`a1` AS `a1`,   `test`.`t1`.`b1` AS `b1`from `test`.`t1`where <nop>((`test`.`t1`.`a1` > (  /* select#2 */  select min(`test`.`t2`.`a2`)  from `test`.`t2`  where (`test`.`t2`.`a2` > 10))))

从查询执行计划看,出现了子查询(SUBQUERY),但是,子查询被“min”函数限制,而子查询中的被查询列a2上存在唯一索引,所以可以利用索引求最值,所以MySQL支持“>SOME”式的子查询优化,子查询只被执行一次即可求得最大值。

 

使用了“=SOME”式子的子查询被优化,查询执行计划如下:

mysql> EXPLAIN EXTENDED SELECT * FROM t1 WHERE t1.a1 =SOME (SELECT a2 FROM t2 WHERE t2.a2=10);
+----+--------------+-------------+------+------+----------------------------------------------------+| id | select_type | table    | type | key | Extra  |+----+--------------+-------------+------+------+----------------------------------------------------+| 1 | SIMPLE    | <subquery2> | ALL | NULL | Using where  || 1 | SIMPLE    | t1     | ALL | NULL | Using where; Using join buffer (Block Nested Loop) || 2 | MATERIALIZED | t2     | ALL | NULL | Using where  |+----+--------------+-------------+------+------+----------------------------------------------------+3 rows in set, 1 warning (0.01 sec)

被查询优化器处理后的语句为:

/* select#1 */ select `test`.`t1`.`id1` AS `id1`,`test`.`t1`.`a1` AS `a1`,`test`.`t1`.`b1` AS `b1`from `test`.`t1` semi join (`test`.`t2`)where ((`<subquery2>`.`a2` = 10) and (`test`.`t1`.`a1` = 10) and (`test`.`t2`.`a2` = 10))

从查询执行计划看,没有出现了子查询,表t2被物化,与表t1进行了半连接。

 

使用了“<SOME”式子的子查询被优化,查询执行计划如下:

mysql> EXPLAIN EXTENDED SELECT * FROM t1 WHERE t1.a1 <SOME (SELECT a2 FROM t2 WHERE t2.a2=10);
+----+-------------+-------+------+------+-------------+| id | select_type | table | type | key | Extra    |+----+-------------+-------+------+------+-------------+| 1 | PRIMARY   | t1  | ALL | NULL | Using where || 2 | SUBQUERY  | t2  | ALL | NULL | Using where |+----+-------------+-------+------+------+-------------+2 rows in set, 1 warning (0.00 sec)

被查询优化器处理后的语句为:

/* select#1 */ select `test`.`t1`.`id1` AS `id1`,`test`.`t1`.`a1` AS `a1`,  `test`.`t1`.`b1` AS `b1`from `test`.`t1`where <nop>(  (    `test`.`t1`.`a1` < (/* select#2 */      select max(`test`.`t2`.`a2`)      from `test`.`t2`      where (`test`.`t2`.`a2` = 10)    )  ))

从查询执行计划看,出现了子查询(SUBQUERY),但是,子查询被“max”函数限制,而子查询中的被查询列a2上存在唯一索引,所以可以利用索引求最值,所以MySQL支持“<SOME”式的子查询优化,子查询只被执行一次即可求得最大值。

 

示例七,MySQL支持对ANY类型的子查询的优化:

使用了“>ANY”式子的子查询被优化,查询执行计划如下:

mysql> EXPLAIN EXTENDED SELECT * FROM t1 WHERE t1.a1 >ANY (SELECT a2 FROM t2 WHERE t2.a2>10);
+----+-------------+-------+------+------+-------------+| id | select_type | table | type | key | Extra    |+----+-------------+-------+------+------+-------------+| 1 | PRIMARY   | t1  | ALL | NULL | Using where || 2 | SUBQUERY  | t2  | ALL | NULL | Using where |+----+-------------+-------+------+------+-------------+2 rows in set, 1 warning (0.00 sec)

被查询优化器处理后的语句为:

/* select#1 */ select `test`.`t1`.`id1` AS `id1`,`test`.`t1`.`a1` AS `a1`,  `test`.`t1`.`b1` AS `b1`from `test`.`t1`where <nop>(  (    `test`.`t1`.`a1` > (/* select#2 */      select min(`test`.`t2`.`a2`)      from `test`.`t2`      where (`test`.`t2`.`a2` > 10)    )  ))

从查询执行计划看,出现了子查询(SUBQUERY),但是,子查询被“min”函数限制,而子查询中的被查询列a2上存在唯一索引,所以可以利用索引求最值,所以MySQL支持“>ANY”式的子查询优化,子查询只被执行一次即可求得最小值。

 

使用了“=ANY”式子的子查询被优化,查询执行计划如下:

mysql> EXPLAIN EXTENDED SELECT * FROM t1 WHERE t1.a1 =ANY (SELECT a2 FROM t2 WHERE t2.a2>10);
+----+--------------+-------------+------+------+----------------------------------------------------+| id | select_type | table    | type | key | Extra  |+----+--------------+-------------+------+------+----------------------------------------------------+| 1 | SIMPLE    | <subquery2> | ALL | NULL | NULL  || 1 | SIMPLE    | t1     | ALL | NULL | Using where; Using join buffer (Block Nested Loop) || 2 | MATERIALIZED | t2     | ALL | NULL | Using where  |+----+--------------+-------------+------+------+----------------------------------------------------+3 rows in set, 1 warning (0.02 sec)

被查询优化器处理后的语句为:

/* select#1 */ select `test`.`t1`.`id1` AS `id1`,`test`.`t1`.`a1` AS `a1`,  `test`.`t1`.`b1` AS `b1`from `test`.`t1` semi join (`test`.`t2`)where ((`test`.`t1`.`a1` = `<subquery2>`.`a2`) and (`test`.`t2`.`a2` > 10))

从查询执行计划看,没有出现了子查询,表t2被物化,与表t1进行了半连接。

 

使用了“<ANY”式子的子查询被优化,查询执行计划如下:

mysql> EXPLAIN EXTENDED SELECT * FROM t1 WHERE t1.a1 <ANY (SELECT a2 FROM t2 WHERE t2.a2>10);
+----+-------------+-------+------+------+-------------+| id | select_type | table | type | key | Extra    |+----+-------------+-------+------+------+-------------+| 1 | PRIMARY   | t1  | ALL | NULL | Using where || 2 | SUBQUERY  | t2  | ALL | NULL | Using where |+----+-------------+-------+------+------+-------------+2 rows in set, 1 warning (0.00 sec)
/* select#1 */ select `test`.`t1`.`id1` AS `id1`,`test`.`t1`.`a1` AS `a1`,  `test`.`t1`.`b1` AS `b1`from `test`.`t1`where <nop>(  (    `test`.`t1`.`a1` < (/* select#2 */      select max(`test`.`t2`.`a2`)      from `test`.`t2`      where (`test`.`t2`.`a2` > 10)    )  ))

SyntaxHighlighter.highlight();

MySQL中的事件调度基础学习教程

经常需要有一些定时任务在MySQL表上执行,例如统计、迁移、删除无用数据等。之前的作法是利用Linux cron定时运行脚本,但是发现这样的额外依赖有时并不方便,例如单机多实例部署时,就需要分别手动分别配置不同的cron任务,需要额外配置相应的用户和权限;新环境部署时容易遗漏cron任务等。

MySQL提供了Event Scheduler,与Linux下的crontab类似,可以根据时间调度来运行任务,运行一次或多次。

完整的Event Schduler创建语句如下:

CREATE  [DEFINER = { user | CURRENT_USER }]  EVENT  [IF NOT EXISTS]  event_name  ON SCHEDULE schedule  [ON COMPLETION [NOT] PRESERVE]  [ENABLE | DISABLE | DISABLE ON SLAVE]  [COMMENT 'comment']  DO event_body;schedule:  AT timestamp [+ INTERVAL interval] …  | EVERY interval  [STARTS timestamp [+ INTERVAL interval] …]  [ENDS timestamp [+ INTERVAL interval] …]interval:  quantity {YEAR | QUARTER | MONTH | DAY | HOUR | MINUTE |       WEEK | SECOND | YEAR_MONTH | DAY_HOUR | DAY_MINUTE |       DAY_SECOND | HOUR_MINUTE | HOUR_SECOND | MINUTE_SECOND}

一、调度Scheduler
MySQL中的调度可以是只运行一次,也可以指定时间间隔重复运行。其定义是在event定义的ON SCHEDULE子句中。该子句格式如下:

ON SCHEDULEAT timestamp [+ INTERVAL interval] …| EVERY interval  [STARTS timestamp [+ INTERVAL interval] …]  [ENDS timestamp [+ INTERVAL interval] …]

其中,timestamp必须包括”年月日时分秒“,它参与表达式计算后,结果是datetime或者timestamp类型。

而时间间隔interval可以如下:

<数字> {YEAR | QUARTER | MONTH | DAY | HOUR | MINUTE |      WEEK | SECOND | YEAR_MONTH | DAY_HOUR | DAY_MINUTE |      DAY_SECOND | HOUR_MINUTE | HOUR_SECOND | MINUTE_SECOND}

其含义很清晰,如YEAR 年;QUARTER 季度;YEAR_MONTH 年+月;MINUTE_SECOND 分钟+秒。

补充:

YEAR | QUARTER | MONTH | YEAR_MONTH 后台都转换成MONTH,其他时间间隔都转换成SECOND
ON SCHEDULE中的时间使用创建时本会话中的时区信息time_zone,这个时区默认是服务端的全局time_zone,也可能后续手动更新掉。这些时间会转化成UTC时间,存储到mysql.event表中。
1.一次运行
AT直接指定时间,或者使用时间表达式计算得出确定的时间点。

示例:

AT ‘2006-02-10 23:59:00′   指定确切运行时间,本地时区。
AT current_timestamp + INTERVAL ‘1:15′ MINUTE_SECOND  指定1分15秒后运行。
2.多次运行
EVERY设置运行的时间间隔,这里不能再指定[+ INTERVAL interval]。

指定STARTS、ENDS是可选的。

STARTS是指定重复运行的第一次是什么时候。不指定的情况下,会在事件创建时运行第一次,即等价于STARTS CURRENT_TIMESTAMP!
ENDS告知MySQL结束重复运行的时间点。不指定的情况下,MySQL会永远重复运行下去。
示例:

EVERY 5 WEEK  每5周运行一次,创建时运行第一次。
EVERY 3 DAY STARTS ‘2013-12-4 09:10:00′  从’2013-12-4 09:10:00′开始运行第一次,每隔3天运行一次。
EVERY 2 MONTH STARTS CURRENT_TIMESTAMP + INTERVAL 10 MINUTE ENDS ‘2014-12-31 23:59:59′ 10分钟后开始到2014年底,每两个月运行一次。
二、事件Event
1.启用Event Scheduler功能
Event是由一个特定的Event Scheduler线程执行的,运行过程中可以通过show full processlist查看其当前状态信息,如:

7384313     event_scheduler     localhost     [NULL]     Daemon     3     Waiting on empty queue     [NULL]

默认事件调度Event Scheduler功能是未启用的,需要配置全局参数event_scheduler,本参数可以动态设置,即时生效。

event_scheduler有如下三种取值:

OFF/0 关闭,默认值。不运行Event Scheduler线程,也就无法进行事件调度。设置为ON可以立即启用。
ON/1 启用。
DISABLED 禁用。同样不运行Event Scheduler线程。只有在MySQL服务启动时设置才有用。当event_scheduler是ON或者OFF时,不能在运行时设置event_scheduler为DISABLED。如果启动时配置了event-scheduler=DISABLED,则运行时就不能设置为ON/OFF。换句话中,可以在MySQL服务启动时设置为DISABLED,然后完全禁用了event_scheduler,不能动态调整。
所以,要启用event_scheduler,运行时执行:

set global event_scheduler=on

要随MySQL服务一起启用,则在/etc/my.cnf中添加

[mysqld]event-scheduler=on

2.创建事件的语法

CREATE  [DEFINER = { user | CURRENT_USER }]  EVENT  [IF NOT EXISTS]  event_name  ON SCHEDULE schedule  [ON COMPLETION [NOT] PRESERVE]  [ENABLE | DISABLE | DISABLE ON SLAVE]  [COMMENT 'comment']  DO event_body; schedule:  AT timestamp [+ INTERVAL interval] ...   | EVERY interval  [STARTS timestamp [+ INTERVAL interval] ...]  [ENDS timestamp [+ INTERVAL interval] ...]interval: quantity {YEAR | QUARTER | MONTH | DAY | HOUR | MINUTE |       WEEK | SECOND | YEAR_MONTH | DAY_HOUR |DAY_MINUTE |DAY_SECOND | HOUR_MINUTE |HOUR_SECOND | MINUTE_SECOND}

参数详细说明:
DEFINER: 定义事件执行的时候检查权限的用户。
ON SCHEDULE schedule: 定义执行的时间和时间间隔。
ON COMPLETION [NOT] PRESERVE: 定义事件是一次执行还是永久执行,默认为一次执行,即NOT PRESERVE。
ENABLE | DISABLE | DISABLE ON SLAVE: 定义事件创建以后是开启还是关闭,以及在从上关闭。如果是从服务器自动同步主上的创建事件的语句的话,会自动加上DISABLE ON SLAVE。
COMMENT ‘comment’: 定义事件的注释。
 
3.更改事件的语法

ALTER  [DEFINER = { user | CURRENT_USER }]  EVENT event_name  [ON SCHEDULE schedule]  [ON COMPLETION [NOT] PRESERVE]  [RENAME TO new_event_name]  [ENABLE | DISABLE | DISABLE ON SLAVE]  [COMMENT 'comment']  [DO event_body]

4.删除事件的语法

DROP EVENT [IF EXISTS] event_name

5.Do子句
在Do子句中实现事件的具体逻辑,几乎所有可以在存储程序中运行的MySQL语句都可以在event中使用。

1)简单SQL示例:

CREATE EVENT e_hourly  ON SCHEDULE   EVERY 1 HOUR  COMMENT ‘Clears out sessions table each hour.'  DO   DELETE FROM site_activity.sessions;

2)复杂SQL示例:

delimiter |CREATE EVENT e  ON SCHEDULE   EVERY 5 SECOND  DO   BEGIN    DECLARE v INTEGER;    DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END;    SET v = 0;    WHILE v < 5 DO     INSERT INTO t1 VALUES (0);     UPDATE t2 SET s1 = s1 + 1;     SET v = v + 1;    END WHILE;  END |delimiter ;

3)Do子句中SQL的限制

基本上Do中可以使用任何在存储程序(Stored Routine)中允许的SQL语句,而存储程序中有些限制,event还有些额外的限制。

Stored Routine中如下语句不允许:

  • LOCK TABLES/UNLOCK TABLES
  • LOAD DATA与LOAD TABLE

支持动态SQL(PREPARE, EXECUTE, DEAALOCATE PREPARE)!但是PREPARE本身有些语句不允许执行。

INSERT DELAYED不会生效
EVENT的限制:

如果Do子句中包含ALTER EVENT子句,虽然能够创建,但是运行时会出错。
不要在Do子句中使用SELECT或SHOW这样仅仅是查询的语句,因为其输出无法从外部获取到。可以使用SELECT … INTO 这样的形式将查询结果保存起来。

5.查看EVENT
有如下方式可以查看event的信息:

mysql.eventinformation_schema.eventsshow eventsshow create event

三、event schedule其他注意点
MySQL保存了事件创建时的sql_mode作为其运行时的sql_mode;
如果在一个调度区间内任务没有处理完成,新的调度依然会生成,这样就会出现同时又多个任务在运行的情况。如果要避免多个任务同时存在,可以使用GET_LOCK()函数或者行锁、表锁。

四、    Mysql事件实战
测试环境
创建一个用于测试的test表:

CREATE TABLE `test` ( `id` int(11) NOT NULL AUTO_INCREMENT, `t1` datetime DEFAULT NULL, `id2` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=106 DEFAULT CHARSET=utf8

实战1
Ø  创建一个每隔3秒往test表中插入一条数据的事件,代码如下:

CREATE EVENT IF NOT EXISTS test ON SCHEDULE EVERY 3 SECONDON COMPLETION PRESERVEDO INSERT INTO test(id,t1) VALUES('',NOW());

Ø  创建一个10分钟后清空test表数据的事件

CREATE EVENT IF NOT EXISTS testON SCHEDULEAT CURRENT_TIMESTAMP + INTERVAL 1 MINUTEDO TRUNCATE TABLE test.aaa;

Ø  创建一个在2012-08-23 00:00:00时刻清空test表数据的事件,代码如下:

CREATE EVENT IF NOT EXISTS testON SCHEDULEAT TIMESTAMP '2012-08-23 00:00:00'DO TRUNCATE TABLE test;

Ø  创建一个从2012年8月22日21点45分开始到10分钟后结束,运行每隔3秒往test表中插入一条数据的事件,代码如下:

CREATE EVENT IF NOT EXISTS test ON SCHEDULE EVERY 3 SECONDSTARTS '2012-08-22 21:49:00' ENDS '2012-08-22 21:49:00'+ INTERVAL 10 MINUTEON COMPLETION PRESERVEDO INSERT INTO test(id,t1) VALUES('',NOW());

 
 实战2
通常的应用场景是通过事件来定期的调用存储过程,下面是一个简单的示例:
创建一个让test表的id2字段每行加基数2的存储过程,存储过程代码如下:

DROP PROCEDURE IF EXISTS test_add;DELIMITER //CREATE PROCEDURE test_add()BEGINDECLARE 1_id INT DEFAULT 1;DECLARE 1_id2 INT DEFAULT 0;DECLARE error_status INT DEFAULT 0;DECLARE datas CURSOR FOR SELECT id FROM test;DECLARE CONTINUE HANDLER FOR NOT FOUND SET error_status=1;OPEN datas;FETCH datas INTO 1_id;REPEATSET 1_id2=1_id2+2;UPDATE test SET id2=1_id2 WHERE id=1_id;FETCH datas INTO 1_id;UNTIL error_statusEND REPEAT;CLOSE datas;END//
CREATE EVENT test ON SCHEDULE EVERY 1 DAYSTARTS '2012-08-22 00:00:00'ENDS '2012-08-22 00:00:00'+INTERVAL 40 DAYON COMPLETION PRESERVE DOCALL test_add();

SyntaxHighlighter.highlight();

安全地关闭MySQL服务的教程

普通关闭
我的mysql是自己下载的tar包,自己设定安装目录来安装的。

停止mysql服务,说来简单,但不知道的话,还真是挠头。在这和mysql入门的同学们共享:)

正确方法是,进入mysql的bin目录下,然后执行

./mysqladmin -uroot -p shutdown

然后输入你的密码就可以了。

ps:当然,如果你的root没有密码,就不需要-p选项喽。

ps:有人问启动的方法,正确的启动方法是:进入mysql的bin目录,然后nohup ./mysqld_safe &就可以了。

安全地关闭MySQL实例
关闭过程:

1、发起shutdown,发出  SIGTERM信号
2、有必要的话,新建一个关闭线程(shutdown thread)
如果是客户端发起的关闭,则会新建一个专用的关闭线程

如果是直接收到 SIGTERM 信号进行关闭的话,专门负责信号处理的线程就会负责关闭工作,或者新建一个独立的线程负责这个事

当无法创建独立的关闭线程时(例如内存不足),MySQL Server会发出类似下面的告警信息:

Error: Can't create thread to kill server

3、MySQL Server不再响应新的连接请求
关闭TCP/IP网络监听,关闭Unix Socket等渠道

4、逐渐关闭当前的连接、事务
空闲连接,将立刻被终止;

当前还有事务、SQL活动的连接,会将其标识为 killed,并定期检查其状态,以便下次检查时将其关闭;(参考 KILL 语法)

当前有活跃事务的,该事物会被回滚,如果该事务中还修改了非事务表,则已经修改的数据无法回滚,可能只会完成部分变更;

如果是Master/Slave复制场景里的Master,则对复制线程的处理过程和普通线程也是一样的;

如果是Master/Slave复制场景里的Slave,则会依次关闭IO、SQL线程,如果这2个线程当前是活跃的,则也会加上 killed 标识,然后再关闭;

Slave服务器上,SQL线程是允许直接停止当前的SQL操作的(为了避免复制问题),然后再关闭该线程;

在MySQl 5.0.80及以前的版本里,如果SQL线程当时正好执行一个事务到中间,该事务会回滚;从5.0.81开始,则会等待所有的操作结束,除非用户发起KILL操作。

当Slave的SQL线程对非事务表执行操作时被强制 KILL了,可能会导致Master、Slave数据不一致;

5、MySQL Server进程关闭所有线程,关闭所有存储引擎;
刷新所有表cache,关闭所有打开的表;

每个存储引擎各自负责相关的关闭操作,例如MyISAM会刷新所有等待写入的操作;InnoDB会将buffer pool刷新到磁盘中(从MySQL 5.0.5开始,如果innodb_fast_shutdown不设置为 2 的话),把当前的LSN记录到表空间中,然后关闭所有的内部线程。

6、MySQL Server进程退出
关于KILL指令

从5.0开始,KILL 支持指定  CONNECTION | QUERY两种可选项:

KILL CONNECTION和原来的一样,停止回滚事务,关闭该线程连接,释放相关资源;
KILL QUERY则只停止线程当前提交执行的操作,其他的保持不变;
提交KILL操作后,该线程上会设置一个特殊的 kill标记位。通常需要一段时间后才能真正关闭线程,因为kill标记位只在特定的情况下才检查:

1、执行SELECT查询时,在ORDER BY或GROUP BY循环中,每次读完一些行记录块后会检查 kill标记位,如果发现存在,该语句会终止;
2、执行ALTER TABLE时,在从原始表中每读取一些行记录块后会检查 kill 标记位,如果发现存在,该语句会终止,删除临时表;
3、执行UPDATE和DELETE时,每读取一些行记录块并且更新或删除后会检查 kill 标记位,如果发现存在,该语句会终止,回滚事务,若是在非事务表上的操作,则已发生变更的数据不会回滚;
4、GET_LOCK() 函数返回NULL;
5、INSERT DELAY线程会迅速内存中的新增记录,然后终止;
6、如果当前线程持有表级锁,则会释放,并终止;
7、如果线程的写操作调用在等待释放磁盘空间,则会直接抛出“磁盘空间满”错误,然后终止;
8、当MyISAM表在执行REPAIR TABLE 或 OPTIMIZE TABLE 时被 KILL的话,会导致该表损坏不可用,指导再次修复完成。
安全关闭MySQL几点建议

SyntaxHighlighter.highlight();

mysql忘记密码怎么办(windows linux)

先给大家介绍windows下mysql忘记密码的解决方案。

  Windows下的实际操作如下

  1.关闭正在运行的MySQL。

  2.打开DOS窗口,转到mysql/bin目录。

  3.输入mysqld –skip-grant-tables回车。如果没有出现提示信息,那就对了。

  4.再开一个DOS窗口(因为刚才那个DOS窗口已经不能动了),转到mysql/bin目录。

  5.输入mysql回车,如果成功,将出现MySQL提示符 >

  6. 连接权限数据库>use mysql; (>是本来就有的提示符,别忘了最后的分号)

  6.改密码:> update user set password=password(“520″) where user=”root”; (别忘了最后的分号)

  7.刷新权限(必须的步骤)>flush privileges;

  8.退出 > /q

  9.注销系统,再进入,开MySQL,使用用户名root和刚才设置的新密码123456登陆。

  第一步

  C:/Documents and Settings/Administrator>cd D:/web/www.php100.com/Mysql/MySQL Se
  rver5.5/bin
  C:/Documents and Settings/Administrator>d:
  D:/web/www.php100.com/Mysql/MySQL Server5.5/bin>mysqld –skip-grant-tables

  第二步

  Microsoft Windows [版本 5.2.3790]
  (C) 版权所有 1985-2003 Microsoft Corp.
  C:/Documents and Settings/Administrator>cd D:/web/www.php100.com/Mysql/MySQL Se
  rver5.5/bin
  C:/Documents and Settings/Administrator>d:
  D:/web/www.php100.com/Mysql/MySQL Server5.5/bin>mysql
  Welcome to the MySQL monitor. Commands end with ; or /g.
  Your MySQL connection id is 1
  Server version: 5.5.10 MySQL Community Server (GPL)
  Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
  Oracle is a registered trademark of Oracle Corporation and/or its
  affiliates. Other names may be trademarks of their respective
  owners.
  Type ‘help;’ or ‘/h’ for help. Type ‘/c’ to clear the current input statement.
  mysql> use mysql;
  Database changed
  mysql> update user set password=password(“520″) where user=”root”;
  Query OK, 1 row affected (0.00 sec)
  Rows matched: 1 Changed: 1 Warnings: 0
  mysql> flush privileges;
  Query OK, 0 rows affected (0.00 sec)
  mysql> /q
  Bye
  D:/web/www.php100.com/Mysql/MySQL Server5.5/bin>

下面给大家介绍linux下mysql的root密码忘记解决方案

1.首先确认服务器出于安全的状态,也就是没有人能够任意地连接MySQL数据库。

因为在重新设置MySQL的root密码的期间,MySQL数据库完全出于没有密码保护的状态下,其他的用户也可以任意地登录和修改MySQL的信息。可以采用将MySQL对外的端口封闭,并且停止Apache以及所有的用户进程的方法实现服务器的准安全状态。最安全的状态是到服务器的Console上面操作,并且拔掉网线。

2.修改MySQL的登录设置:

# vi /etc/my.cnf

在[mysqld]的段中加上一句:skip-grant-tables

例如:

[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
skip-grant-tables

保存并且退出vi。

3.重新启动mysqld
# /etc/init.d/mysqld restart
Stopping MySQL: [ OK ]
Starting MySQL: [ OK ]

4.登录并修改MySQL的root密码

# /usr/bin/mysql
Welcome to the MySQL monitor. Commands end with ; or /g.
Your MySQL connection id is 3 to server version: 3.23.56
Type ‘help;’ or ‘/h’ for help. Type ‘/c’ to clear the buffer.
mysql> USE mysql ;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> UPDATE user SET Password = password ( ‘new-password’ ) WHERE User = ‘root’ ;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 2 Changed: 0 Warnings: 0
mysql> flush privileges ;
Query OK, 0 rows affected (0.01 sec)
mysql> quit
Bye

5.将MySQL的登录设置修改回来

# vi /etc/my.cnf
将刚才在[mysqld]的段中加上的skip-grant-tables删除
保存并且退出vi。

SyntaxHighlighter.highlight();

对MySQL日志操作的一些基本命令总结

MySQL日志主要包含:错误日志、查询日志、慢查询日志、事务日志、二进制日志;

日志是mysql数据库的重要组成部分。日志文件中记录着mysql数据库运行期间发生的变化;也就是说用来记录mysql数据库的客户端连接状况、SQL语句的执行情况和错误信息等。当数据库遭到意外的损坏时,可以通过日志查看文件出错的原因,并且可以通过日志文件进行数据恢复。

错误日志

在mysql数据库中,错误日志功能是默认开启的。并且,错误日志无法被禁止。默认情况下,错误日志存储在mysql数据库的数据文件中。错误日志文件通常的名称为hostname.err。其中,hostname表示服务器主机名。

错误日志信息可以自己进行配置的,错误日志所记录的信息是可以通过log-error和log-warnings来定义的,其中log-err是定义是否启用错误日志的功能和错误日志的存储位置,log-warnings是定义是否将警告信息也定义至错误日志中。默认情况下错误日志大概记录以下几个方面的信息:服务器启动和关闭过程中的信息(未必是错误信息,如mysql如何启动InnoDB的表空间文件的、如何初始化自己的存储引擎的等等)、服务器运行过程中的错误信息、事件调度器运行一个事件时产生的信息、在从服务器上启动服务器进程时产生的信息。

下面我们来定义mysql错误日志的功能:

一般而言,日志级别的定义没有回话变量都只是在全局级别下进行定义。

是否启用了日志

mysql>show variables like 'log_bin';
怎样知道当前的日志
mysql> show master status;
二进制日志文件
看二进制日志文件用mysqlbinlog
shell>mysqlbinlog mail-bin.000001
或者
shell>mysqlbinlog mail-bin.000001 | tail
备注:日志目录如果没有指定,则默认在datadir配置目录下,通过my.ini查看该配置目录

Windows 下用类似的命令。

在5.6及以上版本一定要手动指定。5.6以下版本默认file_name为$datadir/mysqld-binlog
二进制日志用于记录所有更改数据的语句。主要用于复制和即时点恢复。
查看二进制日志的工具为:mysqlbinlog
二进制日志包含了所有更新了数据或者已经潜在更新了数据(例如,没有匹配任何行的一个DELETE)的所有语句。语句以“事件”的形式保存,它描述数据更改。二进制日志还包含关于每个更新数据库的语句的执行时间信息。它不包含没有修改任何数据的语句。
二进制日志的主要目的是在数据库存在故障时,恢复时能够最大可能地更新数据库(即时点恢复),因为二进制日志包含备份后进行的所有更新。二进制日志还用于在主复制服务器上记录所有将发送给从服务器的语句。
那么二进制日志是记录执行的语句还是执行后的结果数据呢?
第一种情况:
加入一个表有10万行数据,而现在要执行一个如下语句将amount字段的值全部在原来的基础上增加1000:

UPDATE sales.january SET amount=amount+1000;

此时如果要记录执行后的结果数据的话,日志会非常大。
因此在这种情况下应记录执行语句。这种方式就是基于语句的二进制日志。
第二种情况:
如果向某个字段插入的是当前的时间呢?如下:

INSERT INTO tb SET Birthdate=CURRENT_TIME();

此时就不能记录语句了,因为不同时间执行的结果是不一样的。这是应该记录这一行的值,这种就是基于行(row)的二进制日志。
在有些情况,可能会结合两种方式来记录,这种叫做混合方式的二进制日志。
二进制日志记录时间:
默认情况下,并不是每次写入时都将二进制日志与硬盘同步。因此如果操作系统或机器(不仅仅是MySQL服务器)崩溃,有可能二进制日志中最后的语句丢失了。要想防止这种情况,你可以使用sync_binlog全局变量(1是最安全的值,但也是最慢的),使二进制日志在每N次二进制日志写入后与硬盘同步。
对非事务表的更新执行完毕后立即保存到二进制日志中。对于事务表,例如BDB或InnoDB表,所有更改表的更新(UPDATE、DELETE或INSERT) 被缓存起来,直到服务器接收到COMMIT语句。在该点,执行完COMMIT之前,mysqld将整个事务写入二进制日志。当处理事务的线程启动时,它为缓冲查询分配binlog_cache_size大小的内存。如果语句大于该值,线程则打开临时文件来保存事务。线程结束后临时文件被删除。

日志恢复:(数据库备份时间:2013-02-30 10:10:10 数据出错前一刻时间:2013-02-30 10:10:10)
利用mysqlbinlog.exe工具
(1)打开cmd,进入到日志目录下
(2)恢复备份数据库
(3)重新执行从备份数据库开始到出错前一刻日志,
例如1:用时间段恢复

mysqlbinlog --start-datetime="2013-02-30 10:10:10" --stop-datetime="2013-02-30 10:10:10" log.00001 | mysql -uroot -p123456

由于在测试中发现,用时间进行恢复,恢复这个时间段sql并不准确,特此标注(待研究)
例如2:用日志位置进行恢复(必须打开日志,确定开始恢复日志位置和出错前日志的位置)
     (A):

mysqlbinlog log.00001 >F:log.sql
mysqlbinlog --start-position="5230766" --stop-position="5231104" PC-201304011235-bin.000001 | mysql -uroot -p111111

SyntaxHighlighter.highlight();

Yii 连接、修改 MySQL 数据库及phpunit 测试连接

>>>database<<<

1. 修改 protected/config/main.php

去掉mysql数据库连接方式的注释,并且修改用户名,密码以及连接的数据库。

2. 新建 protected/tests/unit/DbTest.php

内容如下:

<?phpclass DbTest extends CTestCase{  public function testConnection()  {    $this->assertNotEquals(NULL, Yii::app()->db);  }}

3. 执行

  C:/xampp/yii/power/protected/tests> phpunit ./unit/DbTest.php

>>>end of datebase<<<

Yii MySQL修改数据库的数据

最新学习Yii框架,分享一些学习心得,适合初学者,大神请按ctrl + w

<?php  /*   * $id 代表主键,可以是一个也可以是一个集合。   * $attributes 代表是要修改的字段的集合。   * $condition 代表条件。   * $params 传入的值。   */  $count = Model::model()->updateByPk($id,$attributes,$condition,$params);  if($count > 0) {    echo '修改成功';  } else {    echo '修改失败';  }?>
<?php  $model = Model::model()->findByPk($id);  $model->username = 'yiistudy';  $model->password = 'mysql';  $count = $model->update(array('username','password'));  if($count>0) {    echo '修改成功';  } else {    echo '修改失败';  }?>

SyntaxHighlighter.highlight();

MySQL中的联合索引学习教程

联合索引又叫复合索引。对于复合索引:Mysql从左到右的使用索引中的字段,一个查询可以只使用索引中的一部份,但只能是最左侧部分。例如索引是key index (a,b,c). 可以支持a | a,b| a,b,c 3种组合进行查找,但不支持 b,c进行查找 .当最左侧字段是常量引用时,索引就十分有效。

两个或更多个列上的索引被称作复合索引。
利用索引中的附加列,您可以缩小搜索的范围,但使用一个具有两列的索引 不同于使用两个单独的索引。复合索引的结构与电话簿类似,人名由姓和名构成,电话簿首先按姓氏对进行排序,然后按名字对有相同姓氏的人进行排序。如果您知 道姓,电话簿将非常有用;如果您知道姓和名,电话簿则更为有用,但如果您只知道名不姓,电话簿将没有用处。
所以说创建复合索引时,应该仔细考虑列的顺序。对索引中的所有列执行搜索或仅对前几列执行搜索时,复合索引非常有用;仅对后面的任意列执行搜索时,复合索引则没有用处。
如:建立 姓名、年龄、性别的复合索引。

create table test(a int,b int,c int,KEY a(a,b,c));

复合索引的建立原则:

 如果您很可能仅对一个列多次执行搜索,则该列应该是复合索引中的第一列。如果您很可能对一个两列索引中的两个列执行单独的搜索,则应该创建另一个仅包含第二列的索引。
如上图所示,如果查询中需要对年龄和性别做查询,则应当再新建一个包含年龄和性别的复合索引。
包含多个列的主键始终会自动以复合索引的形式创建索引,其列的顺序是它们在表定义中出现的顺序,而不是在主键定义中指定的顺序。在考虑将来通过主键执行的搜索,确定哪一列应该排在最前面。
请注意,创建复合索引应当包含少数几个列,并且这些列经常在select查询里使用。在复合索引里包含太多的列不仅不会给带来太多好处。而且由于使用相当多的内存来存储复合索引的列的值,其后果是内存溢出和性能降低。

         
 复合索引对排序的优化:

 复合索引只对和索引中排序相同或相反的order by 语句优化。
 在创建复合索引时,每一列都定义了升序或者是降序。如定义一个复合索引:

CREATE INDEX idx_example  ON table1 (col1 ASC, col2 DESC, col3 ASC) 

 
 其中 有三列分别是:col1 升序,col2 降序, col3 升序。现在如果我们执行两个查询
 1:

Select col1, col2, col3 from table1 order by col1 ASC, col2 DESC, col3 ASC

  和索引顺序相同
 2:

Select col1, col2, col3 from table1 order by col1 DESC, col2 ASC, col3 DESC 

 和索引顺序相反
 查询1,2 都可以别复合索引优化。
 如果查询为:
 

Select col1, col2, col3 from table1 order by col1 ASC, col2 ASC, col3 ASC

  排序结果和索引完全不同时,此时的 查询不会被复合索引优化。

查询优化器在在where查询中的作用:

 如果一个多列索引存在于 列 Col1 和 Col2 上,则以下语句:Select   * from table where   col1=val1 AND col2=val2 查询优化器会试图通过决定哪个索引将找到更少的行。之后用得到的索引去取值。
 1. 如果存在一个多列索引,任何最左面的索引前缀能被优化器使用。所以联合索引的顺序不同,影响索引的选择,尽量将值少的放在前面。
如:一个多列索引为 (col1 ,col2, col3)
    那么在索引在列 (col1) 、(col1 col2) 、(col1 col2 col3) 的搜索会有作用。

SELECT * FROM tb WHERE col1 = val1 SELECT * FROM tb WHERE col1 = val1 and col2 = val2 SELECT * FROM tb WHERE col1 = val1 and col2 = val2 AND col3 = val3 

 

 2. 如果列不构成索引的最左面前缀,则建立的索引将不起作用。
如:

SELECT * FROM tb WHERE col3 = val3 SELECT * FROM tb WHERE col2 = val2 SELECT * FROM tb WHERE col2 = val2 and col3=val3 

 
 3. 如果一个 Like 语句的查询条件不以通配符起始则使用索引。
如:%车 或 %车%   不使用索引。
    车%              使用索引。
索引的缺点:
1.       占用磁盘空间。
2.       增加了插入和删除的操作时间。一个表拥有的索引越多,插入和删除的速度越慢。如 要求快速录入的系统不宜建过多索引。

下面是一些常见的索引限制问题

1、使用不等于操作符(<>, !=)
下面这种情况,即使在列dept_id有一个索引,查询语句仍然执行一次全表扫描
select * from dept where staff_num <> 1000;
但是开发中的确需要这样的查询,难道没有解决问题的办法了吗?
有!
通过把用 or 语法替代不等号进行查询,就可以使用索引,以避免全表扫描:上面的语句改成下面这样的,就可以使用索引了。

select * from dept shere staff_num < 1000 or dept_id > 1000; 

 

2、使用 is null 或 is not null
使用 is null 或is nuo null也会限制索引的使用,因为数据库并没有定义null值。如果被索引的列中有很多null,就不会使用这个索引(除非索引是一个位图索引,关于位图索引,会在以后的blog文章里做详细解释)。在sql语句中使用null会造成很多麻烦。
解决这个问题的办法就是:建表时把需要索引的列定义为非空(not null)

3、使用函数
如果没有使用基于函数的索引,那么where子句中对存在索引的列使用函数时,会使优化器忽略掉这些索引。下面的查询就不会使用索引:

select * from staff where trunc(birthdate) = '01-MAY-82'; 

 
但是把函数应用在条件上,索引是可以生效的,把上面的语句改成下面的语句,就可以通过索引进行查找。

select * from staff where birthdate < (to_date('01-MAY-82') + 0.9999); 

 

4、比较不匹配的数据类型
比较不匹配的数据类型也是难于发现的性能问题之一。
下面的例子中,dept_id是一个varchar2型的字段,在这个字段上有索引,但是下面的语句会执行全表扫描。

select * from dept where dept_id = 900198; 

 
这是因为oracle会自动把where子句转换成to_number(dept_id)=900198,就是3所说的情况,这样就限制了索引的使用。
把SQL语句改为如下形式就可以使用索引

select * from dept where dept_id = '900198'; 

 

恩,这里还有要注意的:

 比方说有一个文章表,我们要实现某个类别下按时间倒序列表显示功能:

 SELECT * FROM articles WHERE category_id = ... ORDER BY created DESC LIMIT ...

 这样的查询很常见,基本上不管什么应用里都能找出一大把类似的SQL来,学院派的读者看到上面的SQL,可能会说SELECT *不好,应该仅仅查询需要的字段,那我们就索性彻底点,把SQL改成如下的形式:

 

SELECT id FROM articles WHERE category_id = ... ORDER BY created DESC LIMIT ...

 

 我们假设这里的id是主键,至于文章的具体内容,可以都保存到memcached之类的键值类型的缓存里,如此一来,学院派的读者们应该挑不出什么毛病来了,下面我们就按这条SQL来考虑如何建立索引:

 不考虑数据分布之类的特殊情况,任何一个合格的WEB开发人员都知道类似这样的SQL,应该建立一个”category_id, created“复合索引,但这是最佳答案不?不见得,现在是回头看看标题的时候了:MySQL里建立索引应该考虑数据库引擎的类型!

 如果我们的数据库引擎是InnoDB,那么建立”category_id, created“复合索引是最佳答案。让我们看看InnoDB的索引结构,在InnoDB里,索引结构有一个特殊的地方:非主键索引在其BTree的叶节点上会额外保存对应主键的值,这样做一个最直接的好处就是Covering Index,不用再到数据文件里去取id的值,可以直接在索引里得到它。

SyntaxHighlighter.highlight();

MySQL入门完全指南及Linux系统下基本的安装教程

我们来了解一下MySQL的基本特性:

1.内部构件和可移植性

  • 使用C和C++编写
  • 用众多不同的编译器进行了测试
  • 能够工作在众多不同的平台上。请参见2.1.1 “MySQL支持的操作系统”。
  • 使用GNU Automake、Autoconf和Libtool进行移植。
  • 提供了用于C、C++、Eiffel、Java、Perl、PHP、Python、Ruby和Tcl的API。
  • 采用核心线程的完全多线程 如果有多个CPU,它能方便地使用这些CPU。
  • 提供了事务性和非事务性存储引擎。
  • 使用了极快的“B树”磁盘表(MyISAM)和索引压缩。
  • 添加另一个存储引擎相对简单。如果打算为内部数据库添加一个SQL接口,该特性十分有用。
  • 极快的基于线程的内存分配系统。
  • 通过使用优化的“单扫描多连接”,能实现极快的连接。
  • 存储器中的哈希表用作临时表。
  • SQL函数是使用高度优化的类库实现的,运行很快。通常,在完成查询初始化后,不存在存储器分配。
  • 采用Purify(商业内存溢出检测器)以及GPL工具Valgrind(http://developer.kde.org/~sewardj/)测试了MySQL代码。
  • 服务器可作为单独程序运行在客户端/服务器联网环境下。它也可作为库提供,可嵌入(链接)到独立的应用程序中。这类应用程序可单独使用,也能在网络环境下使用。

2.列类型

众多列类型: 带符号/无符号整数,1、2、3、4、8字节长,FLOAT,DOUBLE,CHAR,VARCHAR,TEXT,BLOB,DATE,TIME,DATETIME,TIMESTAMP,YEAR,SET,ENUM,以及OpenGIS空间类型。请参见第11章:列类型。

定长和可变长度记录。

3.语句和函数

在SELECT和查询的WHERE子句中,提供完整的操作符和函数支持。例如:

mysql> SELECT CONCAT(first_name, ' ', last_name)  -> FROM citizen  -> WHERE income/dependents > 10000 AND age > 30;

对SQL GROUP BY和ORDER BY子句的全面支持。支持聚合函数(COUNT(), COUNT(DISTINCT …),AVG(),STD(),SUM(),MAX(),MIN()和GROUP_CONCAT())。

支持LEFT OUTER JOIN和RIGHT OUTER JOIN,采用标准的SQL和ODBC语法。

按照标准SQL的要求,支持表别名和列别名。

DELETE、INSERT、REPLACE和UPDATE返回更改(影响)的行数。连接到服务器时,可通过设置标志返回匹配的行数。

MySQL的SHOW命令可用于检索关于数据库、数据库引擎、表和索引的信息。EXPLAIN命令可用于确定优化器处理查询的方式。

函数名与表名或列名不冲突。例如,ABS是有效的列名。唯一的限制在于,调用函数时,函数名和随后的符号“(”之间不得有空格。请参见9.6 “MySQL中保留字的处理”。

可以将不同数据库的表混合在相同的查询中(就像MySQL 3.22中那样)。

4.安全

十分灵活和安全的权限和密码系统,允许基于主机的验证。连接到服务器时,所有的密码传输均采用加密形式,从而保证了密码安全。

5.可伸缩性和限制

处理大型数据库: 我们使用了MySQL服务器和含5千万条记录的数据库。我们还听说,有些用户将MySQL用于含60000个表和约50亿行的数据库。

每个表可支持高达64条索引(在MySQL 4.1.2之前为32条)。每条索引可由1~16个列或列元素组成。最大索引宽度为1000字节(在MySQL 4.1.2之前为500)。索引可使用具备CHAR、VARCHAR、BLOB或TEXT列类型的列前缀。

6.连接性

在任何平台上,客户端可使用TCP/IP协议连接到MySQL服务器。在Windows系统的NT系列中(NT、2000、XP或2003),客户端可使用命名管道进行连接。在Unix系统中,客户端可使用Unix域套接字文件建立连接。

在MySQL 4.1和更高的版本中,如果是以“–shared-memory”选项开始,Windows服务器还支持共享内存连接。客户端可使用“–protocol=memory”选项,通过共享内存建立连接。

Connector/ODBC (MyODBC)接口为使用ODBC(开放式数据库连接性)连接的客户端程序提供了MySQL支持。例如,可以使用MS Access连接到你的MySQL服务器。客户端可运行在Windows或Unix平台上。提供了MyODBC源。支持所有的ODBC 2.5函数,以及众多其他函数。

Connector/J接口为使用JDBC连接的Java客户端程序提供了MySQL支持。客户端可运行在Windows或Unix平台上。提供了Connector/J源码。

7.本地化

服务器可使用多种语言向客户端提供错误消息。请参见5.10.2节,“设置错误消息语言”。

对数种不同字符集的全面支持,包括latin1 (cp1252)、german、big5、ujis等。例如,在表名和列名中允许使用斯堪的纳维亚字符‘å’、‘ä’和‘ö’。从MySQL 4.1开始,提供了Unicode支持。

所有数据均以所选的字符集保存。正常字符串列的比较不区分大小写。

分类是根据所选的字符集(默认情况下,使用瑞典校对)进行的。启动MySQL服务器时,可更改该项设置。要想查看高级分类的示例,请参见Czech分类代码。MySQL服务器支持众多不同的字符集,这类字符集可在编译时和运行时指定。

8.客户端和工具

MySQL服务器提供了对SQL语句的内部支持,可用于检查、优化和修复表。通过mysqlcheck客户端,可在命令行上使用这类语句。MySQL还包括myisamchk,这是一种很快的命令行实用工具,可用于在MyISAM表上执行这类操作。请参见第5章:数据库管理。

对于所有MySQL程序,均能通过“-help”或“-?”选项调用,以获取联机帮助信息。

MySQL在Linux上的安装:

1,rpm包形式
(1) 操作系统发行商提供的
(2) MySQL官方提供的(版本更新,修复了更多常见BUG)www.mysql.com/downloads
关于MySQL中rpm包类型的介绍:
 MySQL-client         客户端组件
 
 MySQL-debuginfo      调试MySQL的组件 
 
 MySQL-devel          想针对于MySQL编译安装PHP等依赖于MySQL的组件包
 
 MySQL-embedded       MySQL的嵌入式版本
 
 MySQL-server         共享库
 
 MySQL-shared         共享库
 
 MySQL-shared-dompat  为了兼容老版本的共享库
 
 MySQL-test           MySQL的测试组件(在线处理功能)
安装方法:
首先可以从安装光盘中或者到mysql的网站上下载对应版本的rpm包如下:

MySQL-server-community-5.5.28-1.rhel5.i386.rpm  MySQL-client-community-5.5.28-1.rhel5.i386.rpm 
接着我们可以使用rpm命令进行安装:
rpm -ivh MySQL-server-community-5.5.28-1.rhel5.i386.rpm  rpm -ivh MySQL-client-community-5.5.28-1.rhel5.i386.rpm 

补充一点: 
-h 使用符号#显示安装进度
 
-v 报告每一步操作的情况

2,通用二进制包
(1)新建用户以安全方式运行进程:

# groupadd -r mysql  # useradd -g mysql -r -s /sbin/nologin -M -d /mydata/data mysql  # chown -R mysql:mysql /mydata/data 

(2)安装并初始化mysql-5.5.28
首先下载平台对应的mysql版本至本地,这里是32位平台,因此,选择的为mysql-5.5.28-linux2.6-i686.tar.gz
#

 tar xf mysql-5.5.28-linux2.6-i686.tar.gz -C /usr/local  # cd /usr/local/  # ln -sv mysql-5.5.28-linux2.6-i686 mysql  # cd mysql  # chown -R mysql:mysql .  # scripts/mysql_install_db --user=mysql --datadir=/mydata/data  # chown -R root . 

(3)为mysql提供主配置文件:

# cd /usr/local/mysql  # cp support-files/my-large.cnf /etc/my.cnf 

(4)修改配置文件:
修改此文件中thread_concurrency的值为你的CPU个数乘以2,比如这里使用如下行: 
 

thread_concurrency = 2 

 
另外还需要添加如下行指定mysql数据文件的存放位置: 
 

datadir = /mydata/data 

(5)为mysql提供sysv服务脚本:

# cd /usr/local/mysql  # cp support-files/mysql.server /etc/rc.d/init.d/mysqld 

(6)添加至服务列表:

# chkconfig --add mysqld  # chkconfig mysqld on 

(7)而后就可以启动服务测试使用了。

# service mysqld start

 
3,源码编译
(安装编译方式有点改变,配置过程无太大变动,所以后面不详细介绍各个步骤了)
若想在5.0系列的红帽系统上进行源码编译安装MySQL必须借助一个跨平台编译器cmake
所以:
(1)首先安装cmake
安装cmake需要用make

# tar xf cmake-2.8.8.tar.gz  # cd cmake-2.8.8  # ./bootstrap 

     使用此脚本来检测编译环境 
 

# make  # make install 

(2)编译安装mysql-5.5.28
使用cmake编译mysql-5.5.28,选项的方式有所改变简单介绍一下。。。
cmake指定编译选项的方式不同于make,其实现方式如下:
cmake .
 
cmake . -LH 或 ccmake .        查找可以使用的相关选项
指定安装文件的安装路径时常用的选项:
-DCMAKE_INSTALL_PREFIX=/usr/local/mysql         指定安装路径
 
-DMYSQL_DATADIR=/data/mysql                     数据安装路径
 
-DSYSCONFDIR=/etc                               配置文件的安装路径
由于MySQL支持很多的存储引擎而默认编译的存储引擎包括:csv、myisam、myisammrg和heap。若要安装其它存储引擎,可以使用类似如下编译选项:
-DWITH_INNOBASE_STORAGE_ENGINE=1          安装INNOBASE存储引擎
 
-DWITH_ARCHIVE_STORAGE_ENGINE=1           安装ARCHIVE存储引擎
 
-DWITH_BLACKHOLE_STORAGE_ENGINE=1         安装BLACKHOLE存储引擎
 
-DWITH_FEDERATED_STORAGE_ENGINE=1         安装FEDERATED存储引擎
 
若要明确指定不编译某存储引擎,可以使用类似如下的选项:
-DWITHOUT_<ENGINE>_STORAGE_ENGINE=1 
比如:
-DWITHOUT_EXAMPLE_STORAGE_ENGINE=1        不启用或不编译EXAMPLE存储引擎
 
-DWITHOUT_FEDERATED_STORAGE_ENGINE=1
 
-DWITHOUT_PARTITION_STORAGE_ENGINE=1
如若要编译进其它功能,如SSL等,则可使用类似如下选项来实现编译时使用某库或不使用某库:
-DWITH_READLINE=1
 
-DWITH_SSL=system           表示使用系统上的自带的SSL库
 
-DWITH_ZLIB=system
 
-DWITH_LIBWRAP=0
其它常用的选项:
-DMYSQL_TCP_PORT=3306                       设置默认端口的
 
-DMYSQL_UNIX_ADDR=/tmp/mysql.sock           MySQL进程间通信的套接字的位置
 
-DENABLED_LOCAL_INFILE=1                    是否启动本地的LOCAL_INFILE
 
-DEXTRA_CHARSETS=all                        支持哪些额外的字符集
 
-DDEFAULT_CHARSET=utf8                      默认字符集
 
-DDEFAULT_COLLATION=utf8_general_ci         默认的字符集排序规则
 
-DWITH_DEBUG=0                              是否启动DEBUG功能
 
-DENABLE_PROFILING=1                        是否启用性能分析功能
如果想清理此前的编译所生成的文件,则需要使用如下命令:

make clean  rm CMakeCache.txt 

编译安装

# tar xf mysql-5.5.28.tar.gz  # cd mysql-5.5.28  # groupadd -r mysql  # useradd -g -r mysql mysql  # mkdir -pv /data/mydata  # chown -R mysql:mysql /data/mydata  # cmake . -DCMAKE_INSTALL_PREFIX=/usr/local/mysql -DMYSQL_DATADIR=/data/mydata -DSYSCONFDIR=/etc -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_ARCHIVE_STORAGE_ENGINE=1 -DWITH_BLACKHOLE_STORAGE_ENGINE=1 -DWITH_READLINE=1 -DWITH_SSL=system -DWITH_ZLIB=system -DWITH_LIBWRAP=0 -DMYSQL_UNIX_ADDR=/tmp/mysql.sock -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci  # make  # make install # cd /usr/local/mysql 

 

# chown -R :mysql

    更改属组 
 

# scripts/mysql_install_db --user=mysql --datadir=/data/mydata/

  指定数据存放位置
 

# cp support-files/my-large.cnf /etc/my.cnf 

    创建配置文件
 
编辑配置文件

#vim /etc/my.cnf 

添加如下行指定mysql数据文件的存放位置: 
 

datadir = /mydata/data 

创建执行脚本和启动服务

# cp support-files/mysql.server /etc/rc.d/init.d/mysqld 

复制脚本 
 

# chmod +x /etc/rc.d/init.d/mysqld 

 执行权限 
 

# chkconfig -add mysql 

添加到服务列表中 
 

# service mysqld start 
# bin/mysql  

SyntaxHighlighter.highlight();

MySQL的日志基础知识及基本操作学习教程

MySQL日志主要包含:错误日志、查询日志、慢查询日志、事务日志、二进制日志;

日志是mysql数据库的重要组成部分。日志文件中记录着mysql数据库运行期间发生的变化;也就是说用来记录mysql数据库的客户端连接状况、SQL语句的执行情况和错误信息等。当数据库遭到意外的损坏时,可以通过日志查看文件出错的原因,并且可以通过日志文件进行数据恢复。

错误日志

在mysql数据库中,错误日志功能是默认开启的。并且,错误日志无法被禁止。默认情况下,错误日志存储在mysql数据库的数据文件中。错误日志文件通常的名称为hostname.err。其中,hostname表示服务器主机名。

错误日志信息可以自己进行配置的,错误日志所记录的信息是可以通过log-error和log-warnings来定义的,其中log-err是定义是否启用错误日志的功能和错误日志的存储位置,log-warnings是定义是否将警告信息也定义至错误日志中。默认情况下错误日志大概记录以下几个方面的信息:服务器启动和关闭过程中的信息(未必是错误信息,如mysql如何启动InnoDB的表空间文件的、如何初始化自己的存储引擎的等等)、服务器运行过程中的错误信息、事件调度器运行一个事件时产生的信息、在从服务器上启动服务器进程时产生的信息。

下面我们来定义mysql错误日志的功能:

一般而言,日志级别的定义没有回话变量都只是在全局级别下进行定义。

mysql> SHOW GLOBAL VARIABLES LIKE '%log%';
+-----------------------------------------+-----------------------------------+| Variable_name              | Value               |+-----------------------------------------+-----------------------------------+| back_log                | 50                || binlog_cache_size            | 32768               || binlog_direct_non_transactional_updates | OFF                || binlog_format              | MIXED               || binlog_stmt_cache_size          | 32768               || expire_logs_days             | 0                 || general_log               | OFF                || general_log_file             | /mydata/data/stu18.log       || innodb_flush_log_at_trx_commit      | 1                 || innodb_locks_unsafe_for_binlog      | OFF                || innodb_log_buffer_size          | 8388608              || innodb_log_file_size           | 5242880              || innodb_log_files_in_group        | 2                 || innodb_log_group_home_dir        | ./                 || innodb_mirrored_log_groups        | 1                 || log                   | OFF                || log_bin                 | ON                || log_bin_trust_function_creators     | OFF                || log_error                | /mydata/data/stu18.magedu.com.err |定义错误日志| log_output               | FILE               || log_queries_not_using_indexes      | OFF                || log_slave_updates            | OFF                || log_slow_queries            | OFF                || log_warnings               | 1        是否把警告信息写入错误日志中| max_binlog_cache_size          | 18446744073709547520        || max_binlog_size             | 1073741824             || max_binlog_stmt_cache_size        | 18446744073709547520        || max_relay_log_size            | 0                 || relay_log                |                  || relay_log_index             |                  || relay_log_info_file           | relay-log.info           || relay_log_purge             | ON                 || relay_log_recovery            | OFF                || relay_log_space_limit          | 0                 || slow_query_log              | OFF                || slow_query_log_file           | /mydata/data/stu18-slow.log    || sql_log_bin               | ON                || sql_log_off               | OFF                || sync_binlog               | 0                 || sync_relay_log              | 0                 || sync_relay_log_info           | 0                 |+-----------------------------------------+-----------------------------------+

其中,log_error可以直接定义为文件路径,也可以为ON|OFF;log_warings只能使用1|0来定义开关启动。

更改错误日志位置可以使用log_error来设置形式如下:

[root@stu18 data]# vim /etc/my.cnf[mysqld]Log_error=DIR/[filename]

解析:其中,DIR参数指定错误日志的路径filename参数是错误日志的名称,没有指定该参数时默认为主机名。重启mysql服务器即可生效。

查看mysql错误日志:

[root@stu18 data]# tail -20 stu18.magedu.com.err
130813 15:30:50 InnoDB: Starting shutdown...130813 15:30:51 InnoDB: Shutdown completed; log sequence number 1630920130813 15:30:51 [Note] /usr/local/mysql/bin/mysqld: Shutdown complete130813 15:30:52 mysqld_safe mysqld from pid file /mydata/data/stu18.magedu.com.pid ended130813 15:30:53 mysqld_safe Starting mysqld daemon with databases from /mydata/data130813 15:30:54 InnoDB: The InnoDB memory heap is disabled   #禁用了InnoDB memory的堆功能。130813 15:30:54 InnoDB: Mutexes and rw_locks use GCC atomic builtins #Mutexes(互斥量)和rw_locks(行级锁)是GCC编译的是InnoDB内置的。130813 15:30:54 InnoDB: Compressed tables use zlib 1.2.3   #默认压缩工具是zlib130813 15:30:55 InnoDB: Initializing buffer pool, size = 128.0M  #InnoDB引擎的缓冲池(buffer pool)的值大小130813 15:30:55 InnoDB: Completed initialization of buffer pool130813 15:30:55 InnoDB: highest supported file format is Barracuda.130813 15:30:57 InnoDB: Waiting for the background threads to start130813 15:30:58 InnoDB: 5.5.33 started; log sequence number 1630920130813 15:30:58 [Note] Server hostname (bind-address): '0.0.0.0'; port: 3306130813 15:30:58 [Note]  - '0.0.0.0' resolves to '0.0.0.0'; #0.0.0.0会反解主机名,这里反解失败130813 15:30:58 [Note] Server socket created on IP: '0.0.0.0'.130813 15:30:58 [Note] Event Scheduler: Loaded 0 events  #事件调度器没有任何事件,因为没有装载。130813 15:30:58 [Note] /usr/local/mysql/bin/mysqld: ready for connections. #mysql启动完成等待客户端的请求。Version: '5.5.33-log' socket: '/tmp/mysql.sock' port: 3306 Source distribution #创建一个本地sock用于本地连接。

删除错误日志:

在mysql5.5.7之前:数据库管理员可以删除很长时间之前的错误日志,以保证mysql服务器上的硬盘空间。mysql数据库中,可以使用mysqladmin命令开启新的错误日志。mysqladmin命令的语法如下:mysqladmin u root pflush-logs也可以使用登录mysql数据库中使用FLUSHLOGS语句来开启新的错误日志。

在mysql5.5.7之后:服务器将关闭此项功能。只能使用重命名原来的错误日志文件,手动冲洗日志创建一个新的:方式如下:

[root@stu18 data]# mv stu18.magedu.com.err stu18.magedu.com.err.old [root@stu18 data]# mysqladmin flush-logs[root@stu18 data]# ls
hellodb myclass mysql-bin.000003 mysql-bin.index      stu18.magedu.com.pid   ibdata1   mysql   mysql-bin.000004 performance_schema  ib_logfile0 mysql-bin.000001 stu18.magedu.com.err      test  ib_logfile1 mysql-bin.000002 stu18.magedu.com.err.old

查询日志:

默认情况下查询日志是关闭的。由于查询日志会记录用户的所有操作,其中还包含增删查改等信息,在并发操作大的环境下会产生大量的信息从而导致不必要的磁盘IO,会影响mysql的性能的。如若不是为了调试数据库的目的建议不要开启查询日志。

查看查询日志是否开启:

mysql> SHOW GLOBAL VARIABLES LIKE '%log%';
+-----------------------------------------+-----------------------------------+| Variable_name              | Value               |+-----------------------------------------+-----------------------------------+| back_log                | 50                || binlog_cache_size            | 32768               || binlog_direct_non_transactional_updates | OFF                || binlog_format              | MIXED               || binlog_stmt_cache_size          | 32768               || expire_logs_days             | 0                 || general_log               | OFF    #定义查询日志是否开启 |          | general_log_file             | /mydata/data/stu18.log  #定义查询日志的文件地址名称             || innodb_flush_log_at_trx_commit      | 1                 || innodb_locks_unsafe_for_binlog      | OFF                || innodb_log_buffer_size          | 8388608              || innodb_log_file_size           | 5242880              || innodb_log_files_in_group        | 2                 || innodb_log_group_home_dir        | ./                 || innodb_mirrored_log_groups        | 1                 || log                   | OFF     #是否开启日志 (若开启则表示开启所有的日志功能)         || log_bin                 | ON                || log_bin_trust_function_creators     | OFF                || log_error                | /mydata/data/stu18.magedu.com.err || log_output               | FILE  #日志的输出的位置     |    | log_queries_not_using_indexes      | OFF                || log_slave_updates            | OFF                || log_slow_queries             | OFF                || log_warnings               | 1                 || max_binlog_cache_size          | 18446744073709547520        || max_binlog_size             | 1073741824             || max_binlog_stmt_cache_size        | 18446744073709547520        || max_relay_log_size            | 0                 || relay_log                |                  || relay_log_index             |                  || relay_log_info_file           | relay-log.info           || relay_log_purge             | ON                || relay_log_recovery            | OFF                || relay_log_space_limit          | 0                 || slow_query_log              | OFF                || slow_query_log_file           | /mydata/data/stu18-slow.log    || sql_log_bin               | ON                || sql_log_off               | OFF                || sync_binlog               | 0                 || sync_relay_log              | 0                 || sync_relay_log_info           | 0                 |+-----------------------------------------+-----------------------------------+41 rows in set (0.00 sec)

拓展解析:日志的输出位置一般有三种方式:file(文件),table(表),none(不保存);其中前两个输出位置可以同时定义,none表示是开启日志功能但是记录日志信息。file就是通过general_log_file |/mydata/data/stu18.log 等方式定义的,而输出位置定义为表时查看日志的内容方式为:

mysql> use mysql;          #在此数据库中
Database changed
mysql> show tables;
+---------------------------+| Tables_in_mysql      |+---------------------------+| columns_priv       || db            || event           || func           || general_log        |   #这个就是查询日志的表输出位置| help_category       || help_keyword       || help_relation       || help_topic………………        |+---------------------------+

慢查询日志:

慢查询日志是用来记录执行时间超过指定时间的查询语句。通过慢查询日志,可以查找出哪些查询语句的执行效率很低,以便进行优化。一般建议开启,它对服务器性能的影响微乎其微,但是可以记录mysql服务器上执行了很长时间的查询语句。可以帮助我们定位性能问题的。

查看慢查询日志的定义:

mysql> SHOW GLOBAL VARIABLES LIKE '%log%';| slow_query_log     | OFF  #定义慢查询日志的| slow_query_log_file  |/mydata/data/stu18-slow.log  #输出方式为file(文件)时定义慢查询日志的位置

启动和设置慢查询日志:

1、通过配置文件my.cnf中的log-slow-queries选项可以开启慢查询日志;形式如下:

[root@stu18 data]# vim /etc/my.cnf[mysqld]slow_query_log=1log-slow-queries [= DIR/[filename] ]

其中,DIR参数指定慢查询日志的存储路径;filename参数指定日志的文件名,生成日志文件的完成名称为filename-slow.log。如果不指定存储路径,慢查询日志默认存储到mysql数据库的数据文件下,如果不指定文件名,默认文件名为hostname-slow.log。

2、通过登录mysql服务器直接定义,方式如下:

首先要有全局权限;然后执行mysql>set global slow_query_log=1;

时间默认超过多少的称为慢查询日志?

一般都是通过long_query_time选项来设置这个时间值,时间以秒为单位,可以精确到微秒。如果查询时间超过了这个时间值(默认为10秒),这个查询语句将被记录到慢查询日志中。查看服务器默认时间值方式如下:

mysql> SHOW GLOBAL VARIABLES LIKE 'long%';
+-----------------+-----------+| Variable_name  | Value   |+-----------------+-----------+| long_query_time | 10.000000 |+-----------------+-----------+1 row in set (0.04 sec)

注释:其中这个慢查询时间并不是只表示语句自身执行超过10秒还包含由于其他资源被征用造成阻塞的查询执行时间或其他原因等都被记录到慢查询中。所以这个慢查的时长表示从查询开始到查询结束中间包含可能的任何原因所经历的所有时间。

测试是否可以记录日志:

mysql> set globalslow_query_log=1;    #开启慢查询日志
Query OK, 0 rowsaffected (0.35 sec)
mysql> setsession long_query_time=0.001;   #更改时间(当前session中,退出则重置)
Query OK, 0 rowsaffected (0.00 sec)
mysql> set globallong_query_time=0.001;   #更改时间(全局中,重启服务则重置)mysql> SHOWVARIABLES LIKE 'long%';     #查询定义时间
+-----------------+----------+| Variable_name  | Value  |+-----------------+----------+| long_query_time |0.001000 |+-----------------+----------+1 row in set (0.00sec)mysql> showglobal variables like "%slow%"; #查看慢查询日志开启状态+---------------------+-----------------------------+| Variable_name    | Value            |+---------------------+-----------------------------+|log_slow_queries  | ON             ||slow_launch_time  | 2              ||slow_query_log   | ON             ||slow_query_log_file | /mydata/data/stu18-slow.log |+---------------------+-----------------------------+4 rows in set (0.03sec)

查看慢查询日志:

mysql> use mysqlmysql> selectuser,host,password from user where user="root";
+------+------------------+----------+| user | host       | password |+------+------------------+----------+| root |localhost    |     || root |stu18.magedu.com |     || root |127.0.0.1    |     || root | ::1       |     |+------+------------------+----------+4 rows in set (0.08sec)    #查询时间为0.08mysql> systemmore /mydata/data/stu18_slow.log     #查询慢查询日志记录信息/usr/local/mysql/bin/mysqld,Version: 5.5.33-log (Source distribution). startedwith:Tcp port: 3306 Unix socket: /tmp/mysql.sockTime         Id Command  Argument>>>>>>>>>>>>>>>>部分已省略>>>>>>>>>>>>>># Time: 13100723:46:33# User@Host:root[root] @ localhost []# Query_time:0.108459 Lock_time: 0.000216 Rows_sent:4 Rows_examined: 6SETtimestamp=1381160793;selectuser,host,password from user where user="root";

事务日志:

事务日志(InnoDB特有的日志)可以帮助提高事务的效率。使用事务日志,存储引擎在修改表的数据时只需要修改其内存拷贝,再把改修改行为记录到持久在硬盘上的事务日志中,而不用每次都将修改的数据本身持久到磁盘。事务日志采用追加的方式,因此写日志的操作是磁盘上一小块区域内的顺序I/O,而不像随机I/O需要在磁盘的多个地方移动磁头,所以采用事务日志的方式相对来说要快得多。事务日志持久以后,内存中被修改的数据在后台可以慢慢的刷回到磁盘。目前大多数的存储引擎都是这样实现的,我们通常称之为预写式日志,修改数据需要写两次磁盘。

如果数据的修改已经记录到事务日志并持久化,但数据本身还没有写回磁盘,此时系统崩溃,存储引擎在重启时能够自动恢复这部分修改的数据。具有的恢复方式则视存储引擎而定。

查看事务日志的定义:

mysql> SHOWGLOBAL VARIABLES LIKE '%log%';
+-----------------------------------------+-----------------------------------+| Variable_name              | Value               |+-----------------------------------------+-----------------------------------+| innodb_flush_log_at_trx_commit     | 1    #在事务提交时innodb是否同步日志从缓冲到文件中1表示事务以提交就同步不提交每隔一秒同步一次,性能会很差造成大量的磁盘I/O;定义为2表示只有在事务提交时才会同步但是可能会丢失整个事务   ||innodb_locks_unsafe_for_binlog     |OFF                || innodb_log_buffer_size         | 8388608              ||innodb_log_file_size          |5242880              || innodb_log_files_in_group        | 2   #至少有两个        ||innodb_log_group_home_dir        |./   #定义innodb事务日志组的位置    ||innodb_mirrored_log_groups       |1  #表示对日志组做镜像     |

每个事务日志都是大小为5兆的文件:

[root@stu18 data]#ls -lh
-rw-rw---- 1 mysqlmysql 5.0M Oct 7 23:36 ib_logfile0-rw-rw---- 1 mysqlmysql 5.0M Aug 12 01:06 ib_logfile1

二进制日志:

二进制日志也叫作变更日志,主要用于记录修改数据或有可能引起数据改变的mysql语句,并且记录了语句发生时间、执行时长、操作的数据等等。所以说通过二进制日志可以查询mysql数据库中进行了哪些变化。一般大小体积上限为1G。

二进制开启状态:

mysql> showglobal variables like "%log_bin%";
+---------------------------------+-------+| Variable_name          | Value |+---------------------------------+-------+| log_bin             | ON  | #已开启|log_bin_trust_function_creators | OFF  || sql_log_bin           | ON  |+---------------------------------+-------+

二进制日志相关的参数:

mysql> showglobal variables like "%log%";
sql_log_bin ={ON|OFF}   #用于控制会话级别二进制日志功能的开启或关闭。默认为ON,表示启用记录功能。用户可以在会话级别修改此变量的值,但其必须具有SUPER权限。binlog_cache_size =32768  #默认值32768 Binlog Cache用于在打开了二进制日志(binlog)记录功能的环境,是MySQL 用来提高binlog的记录效率而设计的一个用于短时间内临时缓存binlog数据的内存区域。一般来说,如果我们的数据库中没有什么大事务,写入也不是特别频繁,2MB~4MB是一个合适的选择。但是如果我们的数据库大事务较多,写入量比较大,可与适当调高binlog_cache_size。同时,我们可以通过binlog_cache_use 以及 binlog_cache_disk_use来分析设置的binlog_cache_size是否足够,是否有大量的binlog_cache由于内存大小不够而使用临时文件(binlog_cache_disk_use)来缓存了。binlog_stmt_cache_size= 32768    #当非事务语句使用二进制日志缓存,但是超出binlog_stmt_cache_size时,使用一个临时文件来存放这些语句。log_bin = mysql-bin#指定binlog的位置,默认在数据目录下。binlog-format= {ROW|STATEMENT|MIXED}   #指定二进制日志的类型,默认为MIXED。如果设定了二进制日志的格式,却没有启用二进制日志,则MySQL启动时会产生警告日志信息并记录于错误日志中。sync_binlog = 10#设定多久同步一次二进制日志至磁盘文件中,0表示不同步,任何正数值都表示对二进制每多少次写操作之后同步一次。当autocommit的值为1时,每条语句的执行都会引起二进制日志同步,否则,每个事务的提交会引起二进制日志同步max_binlog_cache_size= {4096 .. 18446744073709547520}   #二进定日志缓存空间大小,5.5.9及以后的版本仅应用于事务缓存,其上限由max_binlog_stmt_cache_size决定。max_binlog_stmt_cache_size= {4096 .. 18446744073709547520}  #二进定日志缓存空间大小,5.5.9及以后的版本仅应用于事务缓存expire_log_days ={0..99}  #设定二进制日志的过期天数,超出此天数的二进制日志文件将被自动删除。默认为0,表示不启用过期自动删除功能。如果启用此功能,自动删除工作通常发生在MySQL启动时或FLUSH日志时。

二进制日志定义方式:

其一、log_bin可以直接定义为文件路径,也可以为ON|OFF。

其二、通过编辑my.cnf中的log-bin选项可以开启二进制日志;形式如下:

[root@stu18 ~]#my.cnf[mysqld]log-bin [=DIR / [filename]]

其中,DIR参数指定二进制文件的存储路径;filename参数指定二级制文件的文件名,其形式为filename.number,number的形式为000001、000002等。每次重启mysql服务或运行mysql> flush logs;都会生成一个新的二进制日志文件,这些日志文件的number会不断地递增。除了生成上述的文件外还会生成一个名为filename.index的文件。这个文件中存储所有二进制日志文件的清单又称为二进制文件的索引。

[root@stu18 ~]# cd /mydata/data/[root@stu18 data]#ls -lh
-rw-rw---- 1 mysqlmysql 14K Aug 13 15:30 mysql-bin.000001-rw-rw---- 1 mysqlmysql 150 Aug 13 17:05 mysql-bin.000002-rw-rw---- 1 mysqlmysql 150 Aug 13 17:06 mysql-bin.000003-rw-rw---- 1 mysqlmysql 150 Aug 13 17:07 mysql-bin.000004-rw-rw---- 1 mysqlmysql 150 Aug 13 17:39 mysql-bin.000005-rw-rw---- 1 mysqlmysql 126 Aug 13 19:03 mysql-bin.000006-rw-rw---- 1 mysqlmysql 126 Aug 13 19:03 mysql-bin.000007-rw-rw---- 1 mysqlmysql 126 Aug 13 19:05 mysql-bin.000008-rw-rw---- 1 mysqlmysql 107 Aug 13 19:05 mysql-bin.000009-rw-rw---- 1 mysqlmysql 353 Oct 7 23:40 mysql-bin.000010-rw-rw---- 1 mysqlmysql 190 Oct 7 20:43 mysql-bin.index
[root@stu18 data]#cat mysql-bin.index
./mysql-bin.000001./mysql-bin.000002./mysql-bin.000003./mysql-bin.000004./mysql-bin.000005./mysql-bin.000006./mysql-bin.000007./mysql-bin.000008./mysql-bin.000009./mysql-bin.000010

如果说我们向某个表的某个字段插入一个数据而这个数据为当前时间(日期时间型);过段时间将此二进制文件应用到另一台服务器上数据就会变动从而导致数据的不一致性所以说对于这种非确定性的数据使用默认的语句定义并不是可靠的;

二进制日志中常用的定义格式:

1、语句(statement):默认的记录格式;

2、行(row):定义的并非数据本身而是这一行的数据是什么;

3、混合模式(mixed):交替使用行和语句、由mysql服务器自行判断。

其中基于行的定义格式数据量会大一些但是可以保证数据的精确性。

查看二进制日志:

二进制日志的定义方式为二进制格式;使用此格式可以存储更多的信息,并且可以使写入二进制日志的效率更高。但是不能直接使用查看命令打开并查看二进制日志。

mysql> showbinary logs;   #显示当前服务器使用的二进制文件及大小
+------------------+-----------+| Log_name     | File_size |+------------------+-----------+| mysql-bin.000001|   13814 || mysql-bin.000002|    150 || mysql-bin.000003|    150 || mysql-bin.000004|    150 || mysql-bin.000005|    150 || mysql-bin.000006|    126 || mysql-bin.000007|    126 || mysql-bin.000008|    126 || mysql-bin.000009|    107 || mysql-bin.000010|    353 |+------------------+-----------+10 rows in set (0.07sec)
mysql> showmaster logs;   #显示主服务器使用的二进制文件及大小
+------------------+-----------+| Log_name     | File_size |+------------------+-----------+| mysql-bin.000001|   13814 || mysql-bin.000002|    150 || mysql-bin.000003|    150 || mysql-bin.000004|    150 || mysql-bin.000005|    150 || mysql-bin.000006|    126 || mysql-bin.000007|    126 || mysql-bin.000008|    126 || mysql-bin.000009|    107 || mysql-bin.000010|    353 |+------------------+-----------+10 rows in set (0.02sec)mysql> showmaster status;  #当前使用的二进制文件及所处位置+------------------+----------+--------------+------------------+| File       | Position | Binlog_Do_DB |Binlog_Ignore_DB |+------------------+----------+--------------+------------------+| mysql-bin.000010|   353 |       |         |+------------------+----------+--------------+------------------+1 row in set (0.00sec)

小扩展:二进制日志的记录位置:通常为上一个事件执行结束时间的位置,每一个日志文件本身也有自己的元数据所以说对于当前版本的mysql来说二进制的开始位置通常为107;

mysql> flushlogs;
Query OK, 0 rowsaffected (0.23 sec)

注意:flush logs一般只会滚动中继日志和二进制日志。

mysql> showmaster status;
+------------------+----------+--------------+------------------+| File       | Position | Binlog_Do_DB |Binlog_Ignore_DB |+------------------+----------+--------------+------------------+| mysql-bin.000011|   107 |       |         |+------------------+----------+--------------+------------------+1 row in set (0.00sec)

查看当前二进制文件的信息:

mysql> createdatabase yong;
Query OK, 1 rowaffected (0.12 sec)
mysql> createtable yong.tb1 (id int,name char(20));
Query OK, 0 rowsaffected (0.44 sec)
mysql> insertinto yong.tb1 values(1,'tom');
Query OK, 1 rowaffected (0.14 sec)
mysql> showmaster status;
+------------------+----------+--------------+------------------+| File       | Position | Binlog_Do_DB |Binlog_Ignore_DB |+------------------+----------+--------------+------------------+| mysql-bin.000011|   479 |       |         |+------------------+----------+--------------+------------------+1 row in set (0.00sec)

查看二进制日志信息的命令:

SHOW BINLOG EVENTS[IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count]
mysql> showbinlog events/G     #查看所有的二进制信息
***************************87. row ***************************  Log_name: mysql-bin.000001    Pos: 13580 Event_type: Query Server_id: 1End_log_pos: 13688    Info: use `hellodb`; /*!40000 ALTERTABLE `toc` DISABLE KEYS */***************************88. row ***************************  Log_name: mysql-bin.000001    Pos: 13688 Event_type: Query Server_id: 1End_log_pos: 13795    Info: use `hellodb`; /*!40000 ALTERTABLE `toc` ENABLE KEYS */***************************89. row ***************************  Log_name: mysql-bin.000001    Pos: 13795 Event_type: Stop Server_id: 1End_log_pos: 13814    Info:89 rows in set (0.00sec)mysql> showbinlog events in 'mysql-bin.000011';  #查看指定日志的二进制信息+------------------+-----+-------------+-----------+-------------+----------------------------------------------+| Log_name     | Pos | Event_type | Server_id | End_log_pos | Info                     |+------------------+-----+-------------+-----------+-------------+----------------------------------------------+| mysql-bin.000011|  4 | Format_desc |     1 |     107 | Server ver: 5.5.33-log, Binlogver: 4    || mysql-bin.000011 |107 | Query    |     1 |     190 | create database yong             || mysql-bin.000011 |190 | Query    |     1 |     293 | create table yong.tb1 (idint,name char(20)) || mysql-bin.000011 |293 | Query    |     1 |     357 | BEGIN                    || mysql-bin.000011 |357 | Query    |     1 |     452 | insert into yong.tb1values(1,'tom')     || mysql-bin.000011 |452 | Xid     |     1 |     479 | COMMIT /* xid=103 */             |+------------------+-----+-------------+-----------+-------------+----------------------------------------------+6 rows in set (0.00sec)mysql> showbinlog events in 'mysql-bin.000011' from 190; #从指定的事件位置开始+------------------+-----+------------+-----------+-------------+----------------------------------------------+| Log_name     | Pos | Event_type | Server_id |End_log_pos | Info                     |+------------------+-----+------------+-----------+-------------+----------------------------------------------+| mysql-bin.000011 |190 | Query   |     1 |     293 | create table yong.tb1 (idint,name char(20)) || mysql-bin.000011 |293 | Query   |     1 |     357 | BEGIN                    || mysql-bin.000011 |357 | Query   |     1 |     452 | insert into yong.tb1values(1,'tom')     || mysql-bin.000011 |452 | Xid    |     1 |     479 | COMMIT /* xid=103 */             |+------------------+-----+------------+-----------+-------------+----------------------------------------------+4 rows in set (0.00sec)mysql> showbinlog events in 'mysql-bin.000011' from 190 limit 3; #指定偏移量(不是语句,是事件)+------------------+-----+------------+-----------+-------------+----------------------------------------------+| Log_name     | Pos | Event_type | Server_id |End_log_pos | Info                     |+------------------+-----+------------+-----------+-------------+----------------------------------------------+| mysql-bin.000011 |190 | Query   |     1 |     293 | create table yong.tb1 (idint,name char(20)) || mysql-bin.000011 |293 | Query   |     1 |     357 | BEGIN                    || mysql-bin.000011 |357 | Query   |     1 |     452 | insert into yong.tb1values(1,'tom')     |+------------------+-----+------------+-----------+-------------+----------------------------------------------+3 rows in set (0.00sec)

命令行下查看二进制日志:

由于无法使用cat等方式直接打开并查看二进制日志;所以必须使用mysqlbinlog命令。但是当正在执行mysql读写操作时建议不要使用此打开正在使用的二进制日志文件;若非要打开可flush logs。mysqlbinlog命令的使用方式:

[root@stu18 data]#mysqlbinlog mysql-bin.000017    #必须在数据目录下

/

*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;/*!40019 SET@@session.max_insert_delayed_threads=0*/;/*!50003 SET@OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;DELIMITER /*!*/;# at 4    #事件开始处#131009 0:25:59 server id 1 end_log_pos 107  Start: binlog v 4, server v 5.5.33-log created 131009 0:25:59 # Warning: thisbinlog is either in use or was not closed properly.BINLOG 'FzJUUg8BAAAAZwAAAGsAAAABAAQANS41LjMzLWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEzgNAAgAEgAEBAQEEgAAVAAEGggAAAAICAgCAA=='/*!*/;# at 107#131009 0:26:36 server id 1 end_log_pos 192  Query  thread_id=12  exec_time=0 error_code=0   #131009 0:26:36年月日的简写方式;end_log_pos事件结束处;thread_id=12 哪个会话线程创建的此语句;exec_time=0 执行时长单位为秒;error_code=0 错误代码0表示没有SET TIMESTAMP=1381249596/*!*/;   #预设信息(环境设定)

导出此数据库的信息:

[root@stu18 data]#mysqlbinlog mysql-bin.000017 > /tmp/a.sql

导入此数据库的信息:

[root@stu18 data]#mysql < a.sql

删除二进制日志信息:

二进制日志会记录大量的信息(其中包含一些无用的信息)。如果很长时间不清理二进制日志,将会浪费很多的磁盘空间。但是,删除之后可能导致数据库崩溃时无法进行恢复,所以若要删除二进制日志首先将其和数据库备份一份,其中也只能删除备份前的二进制日志,新产生的日志信息不可删(可以做即时点还原)。也不可在关闭mysql服务器之后直接删除因为这样可能会给数据库带来错误的。若非要删除二进制日志需要做如下操作:导出备份数据库和二进制日志文件进行压缩归档存储。删除二进制文件的方法如下:

1、删除所有的二进制日志(不可效仿):

使用RESET MASTER语句可以删除所有的二进制日志。该语句的形式如下:

mysql> resetmaster;  

      

Query OK, 0 rowsaffected (0.17 sec)mysql> showbinary logs;+------------------+-----------+| Log_name     | File_size |+------------------+-----------+| mysql-bin.000001|    107 |+------------------+-----------+1 row in set (0.04sec)

解析:首先不建议在生产环境下使用此操作;删除所有的二进制日志后,Mysql将会重新创建新的二进制日志。新二进制日志的编号从000001开始。

2、根据文件或时间点来删除二进制日志:

语法形式:

mysql> PURGE { BINARY | MASTER } LOGS {TO 'log_name' | BEFORE datetime_expr }

其中TO’log_name’表示把这个文件之前的其他文件都删除掉,也可使用BEFORE datetime_expr指定把哪个时间之前的二进制文件删除了。

mysql> PURGEBINARY LOGS TO 'mysql-bin.000007';
Query OK, 0 rowsaffected (0.11 sec)
mysql> showbinary logs;
+------------------+-----------+| Log_name     | File_size |+------------------+-----------+| mysql-bin.000007|    150 || mysql-bin.000008|    150 || mysql-bin.000009|    150 || mysql-bin.000010|    150 || mysql-bin.000011|    150 || mysql-bin.000012|    150 || mysql-bin.000013|    150 || mysql-bin.000014|    150 || mysql-bin.000015|    150 || mysql-bin.000016|    150 || mysql-bin.000017|    483 |+------------------+-----------+11 rows in set (0.04sec)
[root@stu18 data]#cat mysql-bin.index
./mysql-bin.000007./mysql-bin.000008./mysql-bin.000009./mysql-bin.000010./mysql-bin.000011./mysql-bin.000012./mysql-bin.000013./mysql-bin.000014./mysql-bin.000015./mysql-bin.000016./mysql-bin.000017

由此可以看出这种清理二进制日志文件的方式是非常合理的,不会导致数据库的错误发生。

mysql> PURGEBINARY LOGS BEFORE '13-10-19 10:26:36'; #使用时间来删除二进制日志
Query OK, 0 rowsaffected (0.05 sec)

 

PS:MySQL中日志相关常用的服务器变量说明:
expire_logs_days={0..99}
设定二进制日志的过期天数,超出此天数的二进制日志文件将被自动删除。默认为0,表示不启用过期自动删除功能。如果启用此功能,自动删除工作通常发生在MySQL启动时或FLUSH日志时。作用范围为全局,可用于配置文件,属动态变量。

general_log={ON|OFF}
设定是否启用查询日志,默认值为取决于在启动mysqld时是否使用了–general_log选项。如若启用此项,其输出位置则由–log_output选项进行定义,如果log_output的值设定为NONE,即使用启用查询日志,其也不会记录任何日志信息。作用范围为全局,可用于配置文件,属动态变量。

general_log_file=FILE_NAME
查询日志的日志文件名称,默认为“hostname.log”。作用范围为全局,可用于配置文件,属动态变量。

binlog-format={ROW|STATEMENT|MIXED}
指定二进制日志的类型,默认为STATEMENT,建议更改为MIXED。如果设定了二进制日志的格式,却没有启用二进制日志,则MySQL启动时会产生警告日志信息并记录于错误日志中。作用范围为全局或会话,可用于配置文件,且属于动态变量。

log={YES|NO}
是否启用记录所有语句的日志信息于一般查询日志(general query log)中,默认通常为OFF。MySQL 5.6已经弃用此选项。

log-bin={YES|NO}
是否启用二进制日志,如果为mysqld设定了–log-bin选项,则其值为ON,否则则为OFF。其仅用于显示是否启用了二进制日志,并不反应log-bin的设定值。作用范围为全局级别,属非动态变量。

log_bin_trust_function_creators={TRUE|FALSE}
此参数仅在启用二进制日志时有效,用于控制创建存储函数时如果会导致不安全的事件记录二进制日志条件下是否禁止创建存储函数。默认值为0,表示除非用户除了CREATE ROUTING或ALTER ROUTINE权限外还有SUPER权限,否则将禁止创建或修改存储函数,同时,还要求在创建函数时必需为之使用DETERMINISTIC属性,再不然就是附带READS SQL DATA或NO SQL属性。设置其值为1时则不启用这些限制。作用范围为全局级别,可用于配置文件,属动态变量。

log_error=/PATH/TO/ERROR_LOG_FILENAME
定义错误日志文件。作用范围为全局或会话级别,可用于配置文件,属非动态变量。

log_output={TABLE|FILE|NONE}
定义一般查询日志和慢查询日志的保存方式,可以是TABLE、FILE、NONE,也可以是TABLE及FILE的组合(用逗号隔开),默认为TABLE。如果组合中出现了NONE,那么其它设定都将失效,同时,无论是否启用日志功能,也不会记录任何相关的日志信息。作用范围为全局级别,可用于配置文件,属动态变量。

log_query_not_using_indexes={ON|OFF}
设定是否将没有使用索引的查询操作记录到慢查询日志。作用范围为全局级别,可用于配置文件,属动态变量。

log_slave_updates
用于设定复制场景中的从服务器是否将从主服务器收到的更新操作记录进本机的二进制日志中。本参数设定的生效需要在从服务器上启用二进制日志功能。

log_slow_queries={YES|NO}
是否记录慢查询日志。慢查询是指查询的执行时间超出long_query_time参数所设定时长的事件。MySQL 5.6将此参数修改为了slow_query_log。作用范围为全局级别,可用于配置文件,属动态变量。

log_warnings=#
设定是否将警告信息记录进错误日志。默认设定为1,表示启用;可以将其设置为0以禁用;而其值为大于1的数值时表示将新发起连接时产生的“失败的连接”和“拒绝访问”类的错误信息也记录进错误日志。

long_query_time=#
设定区别慢查询与一般查询的语句执行时间长度。这里的语句执行时长为实际的执行时间,而非在CPU上的执行时长,因此,负载较重的服务器上更容易产生慢查询。其最小值为0,默认值为10,单位是秒钟。它也支持毫秒级的解析度。作用范围为全局或会话级别,可用于配置文件,属动态变量。

max_binlog_cache_size{4096 .. 18446744073709547520}
二进定日志缓存空间大小,5.5.9及以后的版本仅应用于事务缓存,其上限由max_binlog_stmt_cache_size决定。作用范围为全局级别,可用于配置文件,属动态变量。

max_binlog_size={4096 .. 1073741824}
设定二进制日志文件上限,单位为字节,最小值为4K,最大值为1G,默认为1G。某事务所产生的日志信息只能写入一个二进制日志文件,因此,实际上的二进制日志文件可能大于这个指定的上限。作用范围为全局级别,可用于配置文件,属动态变量。

max_relay_log_size={4096..1073741824}
设定从服务器上中继日志的体积上限,到达此限度时其会自动进行中继日志滚动。此参数值为0时,mysqld将使用max_binlog_size参数同时为二进制日志和中继日志设定日志文件体积上限。作用范围为全局级别,可用于配置文件,属动态变量。

innodb_log_buffer_size={262144 .. 4294967295}
设定InnoDB用于辅助完成日志文件写操作的日志缓冲区大小,单位是字节,默认为8MB。较大的事务可以借助于更大的日志缓冲区来避免在事务完成之前将日志缓冲区的数据写入日志文件,以减少I/O操作进而提升系统性能。因此,在有着较大事务的应用场景中,建议为此变量设定一个更大的值。作用范围为全局级别,可用于选项文件,属非动态变量。

innodb_log_file_size={108576 .. 4294967295}
设定日志组中每个日志文件的大小,单位是字节,默认值是5MB。较为明智的取值范围是从1MB到缓存池体积的1/n,其中n表示日志组中日志文件的个数。日志文件越大,在缓存池中需要执行的检查点刷写操作就越少,这意味着所需的I/O操作也就越少,然而这也会导致较慢的故障恢复速度。作用范围为全局级别,可用于选项文件,属非动态变量。

innodb_log_files_in_group={2 .. 100}
设定日志组中日志文件的个数。InnoDB以循环的方式使用这些日志文件。默认值为2。作用范围为全局级别,可用于选项文件,属非动态变量。

innodb_log_group_home_dir=/PATH/TO/DIR
设定InnoDB重做日志文件的存储目录。在缺省使用InnoDB日志相关的所有变量时,其默认会在数据目录中创建两个大小为5MB的名为ib_logfile0和ib_logfile1的日志文件。作用范围为全局级别,可用于选项文件,属非动态变量。

innodb_support_xa={TRUE|FLASE}
存储引擎事务在存储引擎内部被赋予了ACID属性,分布式(XA)事务是一种高层次的事务,它利用“准备”然后“提交”(prepare-then-commit)两段式的方式将ACID属性扩展到存储引擎外部,甚至是数据库外部。然而,“准备”阶段会导致额外的磁盘刷写操作。XA需要事务协调员,它会通知所有的参与者准备提交事务(阶段1)。当协调员从所有参与者那里收到“就绪”信息时,它会指示所有参与者进行真正的“提交”操作。
此变量正是用于定义InnoDB是否支持两段式提交的分布式事务,默认为启用。事实上,所有启用了二进制日志的并支持多个线程同时向二进制日志写入数据的MySQL服务器都需要启用分布式事务,否则,多个线程对二进制日志的写入操作可能会以与原始次序不同的方式完成,这将会在基于二进制日志的恢复操作中或者是从服务器上创建出不同原始数据的结果。因此,除了仅有一个线程可以改变数据以外的其它应用场景都不应该禁用此功能。而在仅有一个线程可以修改数据的应用中,禁用此功能是安全的并可以提升InnoDB表的性能。作用范围为全局和会话级别,可用于选项文件,属动态变量。

relay_log=file_name
设定中继日志的文件名称,默认为host_name-relay-bin。也可以使用绝对路径,以指定非数据目录来存储中继日志。作用范围为全局级别,可用于选项文件,属非动态变量。

relay_log_index=file_name
设定中继日志的索引文件名,默认为为数据目录中的host_name-relay-bin.index。作用范围为全局级别,可用于选项文件,属非动态变量。

relay-log-info-file=file_name
设定中继服务用于记录中继信息的文件,默认为数据目录中的relay-log.info。作用范围为全局级别,可用于选项文件,属非动态变量。

relay_log_purge={ON|OFF}
设定对不再需要的中继日志是否自动进行清理。默认值为ON。作用范围为全局级别,可用于选项文件,属动态变量。

relay_log_space_limit=#
设定用于存储所有中继日志文件的可用空间大小。默认为0,表示不限定。最大值取决于系统平台位数。作用范围为全局级别,可用于选项文件,属非动态变量。

slow_query_log={ON|OFF}
设定是否启用慢查询日志。0或OFF表示禁用,1或ON表示启用。日志信息的输出位置取决于log_output变量的定义,如果其值为NONE,则即便slow_query_log为ON,也不会记录任何慢查询信息。作用范围为全局级别,可用于选项文件,属动态变量。

slow_query_log_file=/PATH/TO/SOMEFILE
设定慢查询日志文件的名称。默认为hostname-slow.log,但可以通过–slow_query_log_file选项修改。作用范围为全局级别,可用于选项文件,属动态变量。

sql_log_bin={ON|OFF}
用于控制二进制日志信息是否记录进日志文件。默认为ON,表示启用记录功能。用户可以在会话级别修改此变量的值,但其必须具有SUPER权限。作用范围为全局和会话级别,属动态变量。

SyntaxHighlighter.highlight();

MySQL日志分析软件mysqlsla的安装和使用教程

一、下载 mysqlsla

[root@localhost tmp]# wget http://hackmysql.com/scripts/mysqlsla-2.03.tar.gz
--19:45:45-- http://hackmysql.com/scripts/mysqlsla-2.03.tar.gzResolving hackmysql.com... 64.13.232.157Connecting to hackmysql.com|64.13.232.157|:80... connected.HTTP request sent, awaiting response... 200 OKLength: 33674 (33K) [application/x-tar]Saving to: `mysqlsla-2.03.tar.gz.2'100%[===========================================================================================>] 33,674   50.2K/s  in 0.7s  19:45:47 (50.2 KB/s) - `mysqlsla-2.03.tar.gz.2' saved [33674/33674]

二、解压

[root@localhost tmp]# tar -zxvf mysqlsla-2.03.tar.gz
mysqlsla-2.03/mysqlsla-2.03/Changesmysqlsla-2.03/INSTALLmysqlsla-2.03/READMEmysqlsla-2.03/Makefile.PLmysqlsla-2.03/bin/mysqlsla-2.03/bin/mysqlslamysqlsla-2.03/META.ymlmysqlsla-2.03/lib/mysqlsla-2.03/lib/mysqlsla.pmmysqlsla-2.03/MANIFEST
[root@localhost tmp]# cd mysqlsla-2.03[root@localhost mysqlsla-2.03]# ls
bin Changes INSTALL lib Makefile.PL MANIFEST META.yml README

三、执行perl脚本检查包依赖关系

[root@localhost mysqlsla-2.03]# perl Makefile.PL 
Checking if your kit is complete...Looks goodWriting Makefile for mysqlsla

四、安装

[root@localhost mysqlsla-2.03]# make && make install;
cp lib/mysqlsla.pm blib/lib/mysqlsla.pmcp bin/mysqlsla blib/script/mysqlsla/usr/bin/perl "-MExtUtils::MY" -e "MY->fixin(shift)" blib/script/mysqlslaManifying blib/man3/mysqlsla.3pmInstalling /usr/lib/perl5/site_perl/5.8.8/mysqlsla.pmInstalling /usr/share/man/man3/mysqlsla.3pmInstalling /usr/bin/mysqlslaWriting /usr/lib/perl5/site_perl/5.8.8/i386-linux-thread-multi/auto/mysqlsla/.packlistAppending installation info to /usr/lib/perl5/5.8.8/i386-linux-thread-multi/perllocal.pod

五、基本使用
1.使用参数说明
(1).   –log-type (-lt) type logs:
通过这个参数来制定log的类型,主要有slow, general, binary, msl, udl,分析slow log时通过制定为slow.
(2).   –sort:
 制定使用什么参数来对分析结果进行排序,默认是按照t_sum来进行排序。
 t_sum按总时间排序, c_sum按总次数排序
(3).   –top:
显示sql的数量,默认是10,表示取按规则排序的前多少条
(4).   –statement-filter (-sf) [+-][TYPE]:
过滤sql语句的类型,比如select、update、drop. [TYPE]有SELECT, CREATE, DROP, UPDATE, INSERT,例如”+SELECT,INSERT”,不出现的默认是-,即不包括。
(5).   –databases db:
要处理哪个库的日志:
2.统计参数说明
(1). queries total: 总查询次数 
(2). unique:去重后的sql数量 
(3). sorted by : 输出报表的内容排序 最重大的慢sql统计信息, 包括 平均执行时间, 等待锁时间, 结果行的总数, 扫描的行总数.
(4). Count: sql的执行次数及占总的slow log数量的百分比. 
(5). Time: 执行时间, 包括总时间, 平均时间, 最小, 最大时间, 时间占到总慢sql时间的百分比. 
(6). 95% of Time: 去除最快和最慢的sql, 覆盖率占95%的sql的执行时间. 
(7). Lock Time: 等待锁的时间. 
(8).95% of Lock: 95%的慢sql等待锁时间. 
(9).Rows sent: 结果行统计数量, 包括平均, 最小, 最大数量. 
(10).Rows examined: 扫描的行数量. 
(11).Database: 属于哪个数据库.
(12).Users: 哪个用户,IP, 占到所有用户执行的sql百分比.
(13). Query abstract: 抽象后的sql语句.
(14). Query sample: sql语句.
3.使用范例
(1).统计慢查询文件为dowload_server1-slow.log的所有select的慢查询sql,并显示执行时间最长的10条sql,并写到sql_time.sql中去

mysqlsla -lt slow -sf "+select" -top 10 dowload_server1-slow.log >test_time.log
mysqlsla -lt slow -sf "+select,update" -top 100 -sort c_sum -db ultraxsmutf8 dowload_server1-slow.log >num_time.log

SyntaxHighlighter.highlight();

MySQL下常见的启动失败与备份失败问题的解决教程

启动失败
重启服务器后–>重启应用服务(Confluence)–>报错,数据库连接失败(mysql设置了开机自启动)–>查看mysql数据库状态:

[root@fisheye ~]# ps -ef | grep mysqlroot   25555 21974 0 11:28 pts/0  00:00:00 grep mysql

启动mysql服务器

[root@fisheye data]# service mysql start
MySQL server PID file could not be found![失败]Starting MySQL.............. ERROR! The server quit without updating PID file (/mydata/data/fisheye..pid).[失败]

查看错误日志:

[root@fisheye data]# tail -100 fisheye.err
InnoDB: Last MySQL binlog file position 0 337403929, file name ./mysql-bin.000016141013 1:13:28 InnoDB: Waiting for the background threads to start141013 1:13:29 InnoDB: 5.5.33 started; log sequence number 100664715217:13:29 UTC - mysqld got signal 11 ;This could be because you hit a bug. It is also possible that this binaryor one of the libraries it was linked against is corrupt, improperly built,or misconfigured. This error can also be caused by malfunctioning hardware.We will try our best to scrape up some info that will hopefully helpdi141013 01:13:29 mysqld_safe mysqld from pid file /mydata/data/fisheye.pid ended

未发现明显性错误提示,所以手动创建一个pid文件试试

[root@fisheye data]# touch /mydata/data/fisheye.pi

再进行重启服务:

[root@fisheye data]# service mysql restart
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)

突然想到之前看过此类报错的文章,记得有可能是磁盘空间不足导致的mysql无法启动。

[root@fisheye data]# df -h

(文件系统              容量  已用 可用 已用% 挂载点)

/dev/sda1       9.5G 9.5G 0 100% //dev/sda4       5.5G 1.3G 4.0G 24% /mnt/backup/dev/mapper/IhuilianVG-IhuilianLV00            22G 4.2G  17G 20% /var/www/apptmpfs         1.3G   0 1.3G  0% /dev/shm

果然如此,下面罗列一些类似问题(无法启动)的解决思路:
1.可能是datadir目录存在的分区满了(df -h )
       解决方法:打开配置文件/etc/my.cnf,在[mysqld]节下重新指定数据目录(datadir),并将原来的数据目录迁移到重新制定的数据目录处
       关于迁移:(1)、cp或者tar的时候一定要把权限给带上,但是为防止意外建议再授权一次;(2)、数据比较大时一定要先压缩再迁移,保证完整性,特别是scp到其他机器时可能会超时所以一定要压缩(tar.gz);(3)、若是移动至另外的服务器一定要保证mysql版本一致。

2.可能是/mydata/data/fisheye.pid文件没有写的权限
      解决方法 :给予权限,执行 “chown -R mysql:mysql /mydata/data/”  然后重新启动mysqld!

3.可能进程里已经存在mysql进程
      解决方法:用命令“ps -ef|grep mysqld”查看是否有mysqld进程,如果有使用“kill -9  进程号”杀死,然后重新启动mysqld!

4.可能是第二次在机器上安装mysql,有残余数据影响了服务的启动。
       解决方法:去mysql的数据目录/data看看,如果存在mysql-bin.index,就赶快把它删除掉吧,它就是罪魁祸首了。

5.skip-federated字段问题(报错信息:[ERROR] /mydata/data/mysql/libexec/mysqld: unknown option ‘–skip-federated’)
       解决方法:检查一下/etc/my.cnf文件中有没有没被注释掉的skip-federated字段,如果有就立即注释掉吧。

6.selinux惹的祸,如果是centos系统,默认会开启selinux
       解决方法:关闭它,打开/etc/selinux/config,把SELINUX=enforcing改为SELINUX=disabled后存盘退出重启机器试试。

备份失败
说明
执行 mysqldump 时出现找不到某一个 tables 并且中断执行?及锁表后延伸出现的问题记录!
问题及方案如下
Error Meaage: 执行mysqldump 时出现找不到某一个 tables 并且中断执行

[root@test100 data]# mysqldump fx > fx.sql
mysqldump: Got error: 1146: Table 'user_suggest_report' doesn't exist when using LOCK TABLES

考虑加上 –skip-lock-tables或者-R进行锁表试试,也是不行,信息如下

[root@test100 data]#mysqldump --skip-lock-tables fx > fx.sql
Error: Couldn't read status information for table vote_results () mysqldump: Couldn't execute 'show create table `user_suggest_report`': Table 'fx.user_suggest_report' doesn't exist (1146)

登陆服务器查看是否存在此表

 [root@test100 data]#mysql -h127.0.0.1 -D fx mysql> show tables;      #查看所有的表 --> 发现是表存在的
+--------------------------------+| Tables_in_fx          |+--------------------------------+| user_suggest_report      |+--------------------------------+80 rows in set (0.00 sec)

删除此表

mysql> drop table user_suggest_report;      #既然是存在的,但是系统却认定不存在说明存在问题,索性想删除试试
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'user_suggest_report' at line 1

进入mysql存储目录下将其数据表移动或删除

[root@test100 data]# cat /etc/my.cnf | grep datadirdatadir=/var/lib/mysql[root@test100 data]# cd /var/lib/mysql/fx/[root@test100 fx]# mv user_suggest_report.frm /data

重启mysql服务器

[root@test100 fx]# service mysqld restart
[root@test100 data]# mysqldump fx > fx.150109.sql  #操作成功

SyntaxHighlighter.highlight();

MySQL的InnoDB引擎入门学习教程

MySQL发展到今天,InnoDB引擎已经作为绝对的主力,除了像大数据量分析等比较特殊领域需求外,它适用于众多场景。然而,仍有不少开发者还在“执迷不悟”的使用MyISAM引擎,觉得对InnoDB无法把握好,还是MyISAM简单省事,还能支持快速COUNT(*)。本文是由于最近几天帮忙处理discuz论坛有感而发,希望能对广大开发者有帮助。

1. 快速认识InnoDB
InnoDB是MySQL下使用最广泛的引擎,它是基于MySQL的高可扩展性和高性能存储引擎,从5.5版本开始,它已经成为了默认引擎。
InnODB引擎支持众多特性:

a) 支持ACID,简单地说就是支持事务完整性、一致性;
b) 支持行锁,以及类似ORACLE的一致性读,多用户并发;
c) 独有的聚集索引主键设计方式,可大幅提升并发读写性能;
d) 支持外键;
e) 支持崩溃数据自修复;
InnoDB有这么多特性,比MyISAM来的优秀多了,还犹豫什么,果断的切换到InnoDB引擎吧 🙂

2. 修改InnoDB配置选项
可以选择官方版本,或者Percona的分支,如果不知道在哪下载,就google吧。
安装完MySQL后,需要适当修改下my.cnf配置文件,针对InnoDB相关的选项做一些调整,才能较好的运行InnoDB。
相关的选项有:

#InnoDB存储数据字典、内部数据结构的缓冲池,16MB 已经足够大了。innodb_additional_mem_pool_size = 16M#InnoDB用于缓存数据、索引、锁、插入缓冲、数据字典等#如果是专用的DB服务器,且以InnoDB引擎为主的场景,通常可设置物理内存的50%#如果是非专用DB服务器,可以先尝试设置成内存的1/4,如果有问题再调整#默认值是8M,非常坑X,这也是导致很多人觉得InnoDB不如MyISAM好用的缘故innodb_buffer_pool_size = 4G#InnoDB共享表空间初始化大小,默认是 10MB,也非常坑X,改成 1GB,并且自动扩展innodb_data_file_path = ibdata1:1G:autoextend#如果不了解本选项,建议设置为1,能较好保护数据可靠性,对性能有一定影响,但可控innodb_flush_log_at_trx_commit = 1#InnoDB的log buffer,通常设置为 64MB 就足够了innodb_log_buffer_size = 64M#InnoDB redo log大小,通常设置256MB 就足够了innodb_log_file_size = 256M#InnoDB redo log文件组,通常设置为 2 就足够了innodb_log_files_in_group = 2#启用InnoDB的独立表空间模式,便于管理innodb_file_per_table = 1#启用InnoDB的status file,便于管理员查看以及监控等innodb_status_file = 1#设置事务隔离级别为 READ-COMMITED,提高事务效率,通常都满足事务一致性要求transaction_isolation = READ-COMMITTED 在这里,其他配置选项也需要注意:#设置最大并发连接数,如果前端程序是PHP,可适当加大,但不可过大#如果前端程序采用连接池,可适当调小,避免连接数过大max_connections = 60#最大连接错误次数,可适当加大,防止频繁连接错误后,前端host被mysql拒绝掉max_connect_errors = 100000#设置慢查询阀值,建议设置最小的 1 秒long_query_time = 1#设置临时表最大值,这是每次连接都会分配,不宜设置过大 max_heap_table_size 和 tmp_table_size 要设置一样大max_heap_table_size = 96Mtmp_table_size = 96M#每个连接都会分配的一些排序、连接等缓冲,一般设置为 2MB 就足够了sort_buffer_size = 2Mjoin_buffer_size = 2Mread_buffer_size = 2Mread_rnd_buffer_size = 2M#建议关闭query cache,有些时候对性能反而是一种损害query_cache_size = 0#如果是以InnoDB引擎为主的DB,专用于MyISAM引擎的 key_buffer_size 可以设置较小,8MB 已足够#如果是以MyISAM引擎为主,可设置较大,但不能超过4G#在这里,强烈建议不使用MyISAM引擎,默认都是用InnoDB引擎key_buffer_size = 8M#设置连接超时阀值,如果前端程序采用短连接,建议缩短这2个值#如果前端程序采用长连接,可直接注释掉这两个选项,是用默认配置(8小时)interactive_timeout = 120wait_timeout = 120

3. 开始使用InnoDB引擎
修改完配置文件,即可启动MySQL。启动完毕后,在MySQL的datadir目录下,若产生以下几个文件,则表示应该可以使用InnoDB引擎了。

-rw-rw---- 1 mysql mysql 1.0G Sep 21 17:25 ibdata1-rw-rw---- 1 mysql mysql 256M Sep 21 17:25 ib_logfile0-rw-rw---- 1 mysql mysql 256M Sep 21 10:50 ib_logfile1

登录MySQL后,执行命令,确认已启用InnoDB引擎:

(root:imysql.cn:Thu Oct 15 09:16:22 2009)[mysql]> show engines;+------------+---------+----------------------------------------------------------------+--------------+------+------------+| Engine   | Support | Comment                            | Transactions | XA  | Savepoints |+------------+---------+----------------------------------------------------------------+--------------+------+------------+| InnoDB   | YES   | Supports transactions, row-level locking, and foreign keys   | YES     | YES | YES    |

接下来创建一个InnoDB表:

(root:imysql.cn:Thu Oct 15 09:16:22 2009)[mysql]> CREATE TABLE my_innodb_talbe(id INT UNSIGNED NOT NULL AUTO_INCREMENT,name VARCHAR(20) NOT NULL DEFAULT '',passwd VARCHAR(32) NOT NULL DEFAULT '',PRIMARY KEY(id),UNIQUE KEY `idx_name`(name)) ENGINE = InnoDB;

有几个和MySQL(尤其是InnoDB引擎)数据表设计相关的建议,希望开发者朋友能遵循:

a) 所有InnoDB数据表都创建一个和业务无关的自增数字型作为主键,对保证性能很有帮助;
b) 杜绝使用text/blob,确实需要使用的,尽可能拆分出去成一个独立的表;
c) 时间戳建议使用 TIMESTAMP 类型存储;
d) IPV4 地址建议用 INT UNSIGNED 类型存储;
e) 性别等非是即非的逻辑,建议采用 TINYINT 存储,而不是 CHAR(1);
f) 存储较长文本内容时,建议采用JSON/BSON格式存储;

4.了解InnoDB的存储结构
从物理意义上来讲,InnoDB表由共享表空间、日志文件组(redo文件组)、表结构定义文件组成。若将innodb_file_per_table设置为on,则系统将为每一个表单独的生成一个table_name.ibd的文件,在此文件中,存储与该表相关的数据、索引、表的内部数据字典信息。表结构文件则以.frm结尾,这与存储引擎无关。

  以下为InnoDB的表空间结构图:

在InnoDB存储引擎中,默认表空间文件是ibdata1,初始化为10M,且可以扩展,如下图所示:

实际上,InnoDB的表空间文件是可以修改的,使用以下语句就可以修改:

Innodb_data_file_path=ibdata1:370M;ibdata2:50M:autoextend

  使用共享表空间存储方式时,Innodb的所有数据保存在一个单独的表空间里面,而这个表空间可以由很多个文件组成,一个表可以跨多个文件存在,所以其大小限制不再是文件大小的限制,而是其自身的限制。从Innodb的官方文档中可以看到,其表空间的最大限制为64TB,也就是说,Innodb的单表限制基本上也在64TB左右了,当然这个大小是包括这个表的所有索引等其他相关数据。

  而在使用单独表空间存储方式时,每个表的数据以一个单独的文件来存放,这个时候的单表限制,又变成文件系统的大小限制了。

  以下即为不同平台下,单独表空间文件最大限度。

Operating System  File-size Limit
Win32 w/ FAT/FAT32  2GB/4GB
Win32 w/ NTFS          2TB (possibly larger)
Linux 2.4+          (using ext3 file system) 4TB
Solaris 9/10          16TB
MacOS X w/ HFS+         2TB
NetWare w/NSS file system  8TB

以下是MySQL文档中的内容:
Windows用户请注意: FAT和VFAT (FAT32)不适合MySQL的生产使用。应使用NTFS。

共享表空间与独占表空间可以通过参数innodb_file_per_table来转换,若为1,则开启独占表空间,否则,开启共享表存储。

在服务器资源有限,单表数据不是特别多的情况下, 独立表空间明显比共享方式效率更高 . 但是MySQL 默认是共享表空间 。

具体的共享表空间和独立表空间优缺点如下:

共享表空间:
优点:
可以放表空间分成多个文件存放到各个磁盘上(表空间文件大小不受表大小的限制,如一个表可以分布在不同步的文件上)。数据和文件放在一起方便管理。
缺点:
所有的数据和索引存放到一个文件中以为着将有一个很常大的文件,虽然可以把一个大文件分成多个小文件,但是多个表及索引在表空间中混合存储,这样对于一个表做了大量删除操作后表空间中将会有大量的空隙,特别是对于统计分析,日值系统这类应用最不适合用共享表空间。

独立表空间:在配置文件(my.cnf)中设置: innodb_file_per_table

优点:
1.  每个表都有自已独立的表空间。
2.  每个表的数据和索引都会存在自已的表空间中。
3.  可以实现单表在不同的数据库中移动。
4.  空间可以回收(除drop table操作处,表空不能自已回收)
a)         Drop table操作自动回收表空间,如果对于统计分析或是日值表,删除大量数据后可以通过:alter table TableName engine=innodb;回缩不用的空间。
b)         对于使innodb-plugin的Innodb使用truncate table也会使空间收缩。
c)         对于使用独立表空间的表,不管怎么删除,表空间的碎片不会太严重的影响性能,而且还有机会处理。
缺点:
单表增加过大,如超过100个G。

对于启用了innodb_file_per_table 的参数选项之后,在每个表对应的.idb文件内只是存放了数据、索引和插入缓冲,而撤销(undo)信息,系统事务信息,二次写缓冲等还是存放在了原来的共享表空间内。

数据段即B+树的叶节点,索引段即为B+树的非索引节点。

InnoDB存储引擎的管理是由引擎本身完成的,表空间是由分散的页和段组成。

SyntaxHighlighter.highlight();

安装和使用percona-toolkit来辅助操作MySQL的基本教程

一、percona-toolkit简介
percona-toolkit是一组高级命令行工具的集合,用来执行各种通过手工执行非常复杂和麻烦的mysql和系统任务,这些任务包括:

  • 检查master和slave数据的一致性
  • 有效地对记录进行归档
  • 查找重复的索引
  • 对服务器信息进行汇总
  • 分析来自日志和tcpdump的查询
  • 当系统出问题的时候收集重要的系统信息

percona-toolkit源自Maatkit 和Aspersa工具,这两个工具是管理mysql的最有名的工具,现在Maatkit工具已经不维护了,请大家还是使用percona-toolkit吧!这些工具主要包括开发、性能、配置、监控、复制、系统、实用六大类,作为一个优秀的DBA,里面有的工具非常有用,如果能掌握并加以灵活应用,将能极大的提高工作效率。

二、percona-toolkit工具包安装
1.  软件包下载
访问http://www.percona.com/software/percona-toolkit/下载最新版本的Percona Toolkit 或者通过如下命令行来获取最新的版本:

wget percona.com/get/percona-toolkit.tar.gzwget percona.com/get/percona-toolkit.rpm

我这里选择直接从网站上找到最新版本下载:

wget http://www.percona.com/redir/downloads/percona-toolkit/2.1.1/percona-toolkit-2.1.1-1.noarch.rpmwget http://www.percona.com/redir/downloads/percona-toolkit/2.1.1/percona-toolkit-2.1.1.tar.gz

 从http://pkgs.repoforge.org/perl-TermReadKey/下载最新的TermReadKey包

wget http://pkgs.repoforge.org/perl-TermReadKey/perl-TermReadKey-2.30-1.el5.rf.x86_64.rpm

 
2. 软件包安装
我的环境是Centos 5.5 64 BIT
A. percona-toolkit的rpm安装方式

rpm -ivh perl-TermReadKey-2.30-1.el5.rf.x86_64.rpmrpm -ivh percona-toolkit-2.1.1-1.noarch.rpm

注意:需要安装Term::ReadKey 包,否则会报perl(Term::ReadKey) >= 2.10 is needed by percona-toolkit-2.1.1-1.noarch错误
B. percona-toolkit的编译安装方式
tar xzvf percona-toolkit-2.1.1.tar.gz
cd percona-toolkit-2.1.1
perl Makefile.PL
make
make test
make install

三、常用功能
1. pt-duplicate-key-checker
功能介绍:
功能为从mysql表中找出重复的索引和外键,这个工具会将重复的索引和外键都列出来,并生成了删除重复索引的语句,非常方便
用法介绍:
pt-duplicate-key-checker [OPTION…] [DSN]
包含比较多的选项,具体的可以通过命令pt-duplicate-key-checker  –help来查看具体支持那些选项,我这里就不一一列举了。DNS为数据库或者表。
使用示例:
查看test数据库的重复索引和外键使用情况使用如下命令

pt-duplicate-key-checker --host=localhost --user=root --password=zhang@123 --databases=test

       
2. pt-online-schema-change
功能介绍:
功能为在alter操作更改表结构的时候不用锁定表,也就是说执行alter的时候不会阻塞写和读取操作,注意执行这个工具的时候必须做好备份,操作之前最好详细读一下官方文档http://www.percona.com/doc/percona-toolkit/2.1/pt-online-schema-change.html。
工作原理是创建一个和你要执行alter操作的表一样的空表结构,执行表结构修改,然后从原表中copy原始数据到表结构修改后的表,当数据copy完成以后就会将原表移走,用新表代替原表,默认动作是将原表drop掉。在copy数据的过程中,任何在原表的更新操作都会更新到新表,因为这个工具在会在原表上创建触发器,触发器会将在原表上更新的内容更新到新表。如果表中已经定义了触发器这个工具就不能工作了。
用法介绍:
pt-online-schema-change [OPTIONS] DSN
options可以自行查看help,DNS为你要操作的数据库和表。
这里有两个参数需要介绍一下:
–dry-run  这个参数不建立触发器,不拷贝数据,也不会替换原表。只是创建和更改新表。
–execute  这个参数的作用和前面工作原理的介绍的一样,会建立触发器,来保证最新变更的数据会影响至新表。注意:如果不加这个参数,这个工具会在执行一些检查后退出。这一举措是为了让使用这充分了解了这个工具的原理,同时阅读了官方文档。
使用示例:
在线更改表的的引擎,这个尤其在整理innodb表的时候非常有用,示例如下:

pt-online-schema-change --user=root --password=zhang@123 --host=localhost --lock-wait-time=120 --alter="ENGINE=InnoDB" D=test,t=oss_pvinfo2 --execute

从下面的日志中可以看出它的执行过程:

Altering `test`.`oss_pvinfo2`...Creating new table...Created new table test._oss_pvinfo2_new OK.Altering new table...Altered `test`.`_oss_pvinfo2_new` OK.Creating triggers...Created triggers OK.Copying approximately 995696 rows...Copied rows OK.Swapping tables...Swapped original and new tables OK.Dropping old table...Dropped old table `test`.`_oss_pvinfo2_old` OK.Dropping triggers...Dropped triggers OK.Successfully altered `test`.`oss_pvinfo2`.

在来一个范例,大表添加字段的,语句如下:

pt-online-schema-change --user=root --password=zhang@123 --host=localhost --lock-wait-time=120 --alter="ADD COLUMN domain_id INT" D=test,t=oss_pvinfo2 --execute

3. pt-query-advisor
功能介绍:
根据一些规则分析查询语句,对可能的问题提出建议,这些评判规则大家可以看一下官网的链接:http://www.percona.com/doc/percona-toolkit/2.1/pt-query-advisor.html,这里就不详细列举了。那些查询语句可以来自慢查询文件、general日志文件或者使用pt-query-digest截获的查询语句。目前这个版本有bug,当日志文件非常大的时候会需要很长时间甚至进入死循环。
用法介绍:

pt-query-advisor /path/to/slow-query.logpt-query-advisor --type genlog mysql.logpt-query-digest --type tcpdump.txt --print --no-report | pt-query-advisor

 
使用示例:
分析一个语句的例子:

pt-query-advisor --query "select * from aaa"

分析general log中的查询语句的例子:

pt-query-advisor /data/dbdata/general.log

分析慢查询中的查询语句的例子:

pt-query-advisor /data/dbdata/localhost-slow.log

 
4.  pt-show-grants
功能介绍:
规范化和打印mysql权限,让你在复制、比较mysql权限以及进行版本控制的时候更有效率!
用法介绍:
pt-show-grants [OPTION…] [DSN]
选项自行用help查看,DSN选项也请查看help,选项区分大小写。
使用示例:
查看指定mysql的所有用户权限:

pt-show-grants --host='localhost' --user='root' --password='zhang@123'

查看执行数据库的权限:

pt-show-grants --host='localhost' --user='root' --password='zhang@123' --database='hostsops'

查看每个用户权限生成revoke收回权限的语句:

pt-show-grants --host='localhost' --user='root' --password='zhang@123' --revoke

 
5.  pt-upgrade
功能介绍:
在多台服务器上执行查询,并比较有什么不同!这在升级服务器的时候非常有用,可以先安装并导数据到新的服务器上,然后使用这个工具跑一下sql看看有什么不同,可以找出不同版本之间的差异。
用法介绍:
pt-upgrade [OPTION…] DSN [DSN…] [FILE]
比较文件中每一个查询语句在两个主机上执行的结果,并检查在每个服务器上执行的结果、错误和警告。
使用示例:
只查看某个sql在两个服务器的运行结果范例:

pt-upgrade h='localhost' h=192.168.3.92 --user=root --password=zhang@123 --query="select * from user_data.collect_data limit 5"

查看文件中的对应sql在两个服务器的运行结果范例:

pt-upgrade h='localhost' h=192.168.3.92 --user=root --password=zhang@123 aaa.sql

查看慢查询中的对应的查询SQL在两个服务器的运行结果范例:

pt-upgrade h='localhost' h=192.168.3.92 --user=root --password=zhang@123 slow.log
pt-upgrade h=192.168.3.91 h=192.168.3.92 --user=root --password=zhang@123 --query="select * from user_data.collect_data" --compare query_times

SyntaxHighlighter.highlight();

关于MySQL外键的简单学习教程

在MySQL中,InnoDB引擎类型的表支持了外键约束。
外键的使用条件:
1.两个表必须是InnoDB表,MyISAM表暂时不支持外键(据说以后的版本有可能支持,但至少目前不支持);
2.外键列必须建立了索引,MySQL 4.1.2以后的版本在建立外键时会自动创建索引,但如果在较早的版本则需要显示建立;
3.外键关系的两个表的列必须是数据类型相似,也就是可以相互转换类型的列,比如int和tinyint可以,而int和char则不可以;
外键的好处:可以使得两张表关联,保证数据的一致性和实现一些级联操作;
外键的定义语法:
代码如下:

[CONSTRAINT symbol] FOREIGN KEY [id] (index_col_name, …) REFERENCES tbl_name (index_col_name, …) [ON DELETE {RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT}] [ON UPDATE {RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT}] 

该语法可以在 CREATE TABLE 和 ALTER TABLE 时使用,如果不指定CONSTRAINT symbol,MYSQL会自动生成一个名字。
ON DELETE、ON UPDATE表示事件触发限制,可设参数:

  • RESTRICT(限制外表中的外键改动)
  • CASCADE(跟随外键改动)
  • SET NULL(设空值)
  • SET DEFAULT(设默认值)
  • NO ACTION(无动作,默认的)

如果子表试图创建一个在父表中不存在的外键值,InnoDB会拒绝任何INSERT或UPDATE操作。如果父表试图UPDATE或者DELETE任何子表中存在或匹配的外键值,最终动作取决于外键约束定义中的ON UPDATE和ON DELETE选项。InnoDB支持5种不同的动作,如果没有指定ON DELETE或者ON UPDATE,默认的动作为RESTRICT:

  1. CASCADE: 从父表中删除或更新对应的行,同时自动的删除或更新自表中匹配的行。ON DELETE CANSCADE和ON UPDATE CANSCADE都被InnoDB所支持。

  2. SET NULL: 从父表中删除或更新对应的行,同时将子表中的外键列设为空。注意,这些在外键列没有被设为NOT NULL时才有效。ON DELETE SET NULL和ON UPDATE SET SET NULL都被InnoDB所支持。

  3. NO ACTION: InnoDB拒绝删除或者更新父表。

  4. RESTRICT: 拒绝删除或者更新父表。指定RESTRICT(或者NO ACTION)和忽略ON DELETE或者ON UPDATE选项的效果是一样的。

  5. SET DEFAULT: InnoDB目前不支持。

  外键约束使用最多的两种情况无外乎:

  1)父表更新时子表也更新,父表删除时如果子表有匹配的项,删除失败;

  2)父表更新时子表也更新,父表删除时子表匹配的项也删除。

  前一种情况,在外键定义中,我们使用ON UPDATE CASCADE ON DELETE RESTRICT;后一种情况,可以使用ON UPDATE CASCADE ON DELETE CASCADE。

ALTER TABLE tbl_name  ADD [CONSTRAINT [symbol]] FOREIGN KEY  [index_name] (index_col_name, ...)  REFERENCES tbl_name (index_col_name,...)  [ON DELETE reference_option]  [ON UPDATE reference_option]

SyntaxHighlighter.highlight();

快速理解MySQL中主键与外键的实例教程

主键与外键的关系,通俗点儿讲,我现在有一个论坛,有两张表,一张是主贴 thread,一张是回帖 reply

先说说主键,主键是表里面唯一识别记录的字段,一般是帖子id,体现在访问的时候,例如是
thread.php?id=1   表示我要访问的是帖子id是1 的帖子~

再来说说外键,当我们删除某个帖子的时候,需要执行另一个操作,就是删除所有回帖,如果正常情况下,我们需要执行两次delete操作(thread和 reply),这时候如果存在外键,例如,在reply 表里面建立一个指向thread表的主键(id)的外键(这个外键绑的字段,必须是对应帖子的id),并指定响应 delete ,那你在删除 thread 的时候,mysql 自己会帮你把 reply 表中这个帖子的回复都删掉,而不需要你手动再去执行一次reply表的delete操作~

至于两者之间的关系,在刚才的例子中,reply 表的外键,指向的就是 thread 表的主键~~

搞个例子,简单演示一下使用,做dage和xiaodi两个表,大哥表是主键,小弟表是外键:
建表:

CREATE TABLE `dage` ( `id` int(11) NOT NULL auto_increment, `name` varchar(32) default '', PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=latin1;CREATE TABLE `xiaodi` ( `id` int(11) NOT NULL auto_increment, `dage_id` int(11) default NULL, `name` varchar(32) default '', PRIMARY KEY (`id`), KEY `dage_id` (`dage_id`), CONSTRAINT `xiaodi_ibfk_1` FOREIGN KEY (`dage_id`) REFERENCES `dage` (`id`)) ENGINE=InnoDB DEFAULT CHARSET=latin1;

插入个大哥:

mysql> insert into dage(name) values('铜锣湾');
Query OK, 1 row affected (0.01 sec)
mysql> select * from dage;
+----+--------+| id | name  |+----+--------+| 1 | 铜锣湾 |+----+--------+1 row in set (0.00 sec)

插入个小弟:

mysql> insert into xiaodi(dage_id,name) values(1,'铜锣湾_小弟A');
Query OK, 1 row affected (0.02 sec)
mysql> select * from xiaodi;
+----+---------+--------------+| id | dage_id | name     |+----+---------+--------------+| 1 |    1 | 铜锣湾_小弟A |+----+---------+--------------+

把大哥删除:

mysql> delete from dage where id=1;
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`bstar/xiaodi`, CONSTRAINT `xiaodi_ibfk_1` FOREIGN KEY (`dage_id`) REFERENCES `dage` (`id`))

提示:不行呀,有约束的,大哥下面还有小弟,可不能扔下我们不管呀!

插入一个新的小弟:

mysql> insert into xiaodi(dage_id,name) values(2,'旺角_小弟A');   

       

2ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`bstar/xiaodi`, CONSTRAINT `xiaodi_ibfk_1` FOREIGN KEY (`dage_id`) REFERENCES `dage` (`id`))

提示:小子,想造反呀!你还没大哥呢!

把外键约束增加事件触发限制:

mysql> show create table xiaodi;

CONSTRAINT `xiaodi_ibfk_1` FOREIGN KEY (`dage_id`) REFERENCES `dage` (`id`)

mysql> alter table xiaodi drop foreign key xiaodi_ibfk_1;

Query OK, 1 row affected (0.04 sec)Records: 1 Duplicates: 0 Warnings: 
mysql> alter table xiaodi add foreign key(dage_id) references dage(id) on delete cascade on update cascade;
Query OK, 1 row affected (0.04 sec)Records: 1 Duplicates: 0 Warnings: 0

再次试着把大哥删了:

mysql> delete from dage where id=1;
Query OK, 1 row affected (0.01 sec)
mysql> select * from dage;
Empty set (0.01 sec)
mysql> select * from xiaodi;
Empty set (0.00 sec)

SyntaxHighlighter.highlight();

Windows下MySQL日志基本的查看以及导入导出用法教程

MYSQL有不同类型的日志文件(各自存储了不同类型的日志),从它们当中可以查询到MYSQL里都做了些什么,对于MYSQL的管理工作,这些日志文件是不可缺少的。
1.错误日志(The error log):记录了数据库启动、运行以及停止过程中错误信息;
2.ISAM操作日志(The isam log):记录了所有对ISAM表的修改,该日志仅仅用于调试ISAM模式;
3.SQL执行日志(The query log):记录了客户端的连接以及所执行的SQL语句;
4.更新日志(The update log):记录了改变数据的语句,已经不建议使用,由二进制日志替代;
5.二进制日志(The binary log):记录了所有对数据库数据的修改语句;
6.超时日志(The slow log):记录所有执行时间超过最大SQL执行时间(long_query_time)或未使用索引的语句;

如果你是在用mysql的复制、备份功能,那么从服务器还提供了一种叫做relay log的日志文件。

默认情况下所有日志文件会记录在MYSQL的数据目录下,你可以通过强制mysql去关闭并重新打开一个文件进行日志记录,当然系统会自动加后缀(如.00001, .00002),方式有在mysql环境下执行语句 mysql>flush logs; 或者通过mysqladmin管理程序执行 #mysqladmin flush-logs 或 #mysqladmin refresh

这些日志的启动方式可以在mysqld_safe方式启动数据库的时候,后面跟选项参数,也可以在配置文件里配置,推荐采用第二种方式,配置方法很简单,我只配置了三种日志:

[mysqld]log=/var/log/mysqld_common.loglog-error=/var/log/mysqld_err.loglog-bin=/var/log/mysqld_bin.bin

查看
日志的查看很简单,大部分都是文本,直接用vim、less、more之类的工具看就可以了,值得说明的是二进制文件的查看:

1). 首先确定是否开启了二进制文件记录功能

mysql>show variables like 'log_bin';
2). 如果你想知道现在记录二进制数据的文件具体信息,你可以通过下列语句看到现在正在记录哪个文件,以及记录的当前位置:
mysql>show master status;

3). 查看二进制数据需要借助程序mysqlbinlog,看看它支持哪些选项,根据自己需要来使用。

mysql>mysqlbinlog /var/log/mysql/mysql-bin.000040;

查询某个时间范围的可以执行下列语句,如果记录很多可以将结果定向到一个文件里自己慢慢看:-) :

mysql>mysqlbinlog --start-datetime='2008-01-01 00:00:00' --stop-datetime='2008-08-08 00:00:00' /var/log/mysql/mysql-bin.000040 > ./tmp.log

导出
MySQL的数据库导出有很多种,我现在就介绍一下MySQL自带的mysqldump命令导出导入。
注:导出时,按照mysql表编码导出。如果导入时,mysql服务器端的编码不和表一致,导入出错。
1、MySQL导出整个数据库表结构及数据命令:

mysqldump -u用户名 -p密码 dbName>f:/路径+导出SQL的名称 

注:生成.sql文件,可是是多个数据库,多个数据库用逗号分隔。
2、MySQL导出数据库单个表表结构及数据命令:

mysqldump -u用户名 -p密码 数据库名 表名 >f:/路径+导出SQL的名称 

注:多个表可以用逗号分隔。
3、MySQL导出整个数据库表结构命令:

mysqldump -u用户名 -p密码 -d 数据库名>f:/路径+导出SQL的名称 

注:整个数据库表结构,生成.sql文件。
4、MySQL导出数据库单个表结构命令:

mysqldump -u用户名 -p密码 -d 数据库名 表名 >f:/路径+导出SQL的名称 

注:单个表结构,生成.sql文件,可是多张表。多表以空格区分

导入
MySQL的导入:
1)进入cmd
2)

mysql -h localhost -u用户名 -p密码 
mysql -h -localhost -u用户名 -p密码 进入mysql create database test use test source f:/test.sql 

SyntaxHighlighter.highlight();

MySQL中事务概念的简洁学习教程

事务是由一步或几步数据库操作序列组成逻辑执行单元,这系列操作要么全部执行,要么全部放弃执行。程序和事务是两个不同的概念。一般而言:一段程序中可能包含多个事务。

事务具有四个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持续性(Durability)。这四个特性也简称ACID性。

1)原子性:事务是应用中最小的执行单位,就如原子是自然界最小颗粒,具有不可再分的特征一样。事务是应用中不可再分的最小逻辑执行体。

2)一致性:事务执行的结果,必须使数据库从一个一致性状态,变到另一个一致性状态。当数据库中只包含事务成功提交的结果时,数据库处于一致性状态。一致性是通过原子性来保证的。

3)隔离性:各个事务的执行互不干扰,任意一个事务的内部操作对其他并发的事务,都是隔离的。也就是说:并发执行的事务之间不能看到对方的中间状态,并发执行的事务之间不能相互影响。

4)持续性:持续性也称为持久性,指事务一旦提交,对数据所做的任何改变,都要记录到永久存储器中,通常是保存进物理数据库。

在关系型数据库中,事务的隔离性分为四个隔离级别,在解读这四个级别前先介绍几个关于读数据的概念。

1)脏读(Dirty Reads):所谓脏读就是对脏数据(Drity Data)的读取,而脏数据所指的就是未提交的数据。也就是说,一个事务正在对一条记录做修改,在这个事务完成并提交之前,这条数据是处于待定状态的(可能提交也可能回滚),这时,第二个事务来读取这条没有提交的数据,并据此做进一步的处理,就会产生未提交的数据依赖关系。这种现象被称为脏读。

2)不可重复读(Non-Repeatable Reads):一个事务先后读取同一条记录,但两次读取的数据不同,我们称之为不可重复读。也就是说,这个事务在两次读取之间该数据被其它事务所修改。

3)幻读(Phantom Reads):一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为幻读。

 

事务四个隔离级别对比:

1)未提交读(Read Uncommitted):SELECT语句以非锁定方式被执行,所以有可能读到脏数据,隔离级别最低。

2)提交读(Read Committed):只能读取到已经提交的数据。即解决了脏读,但未解决不可重复读。

3)可重复读(Repeated Read):在同一个事务内的查询都是事务开始时刻一致的,InnoDB的默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻读。

4)串行读(Serializable):完全的串行化读,所有SELECT语句都被隐式的转换成SELECT … LOCK IN SHARE MODE,即读取使用表级共享锁,读写相互都会阻塞。隔离级别最高。

隔离级别对比表:

数据库的事务有下列语句组成:

一组DML(Data Manipulate Language,即数据操作语言)经过这组DML修改后数据将保持较好的一致性。

一个DDL(Data Definition Language,即数据定义语言)语句。

一个DCL(Data control Language,即数据控制语言)语句。

       DDL和DCL语句最多只能有一个,因为DDL和DCL语句都会导致事务立即提交。

       当事务所包含的全部数据库操作都成功执行后,应该提交(commit)事务,使这些修改永久生效。

       事务提交有两种方式:显式提交和自动提交。

(1)显式提交:使用commit。

(2)自动提交:执行DDL或DCL,或者程序正常退出。

数据库事务传播级别,指的是事务嵌套时,应该采用什么策略,即在一个事务中调用别的事务,该怎么办

假如有一下两个事务:

ServiceA {       void methodA ()  {       ServiceB . methodB ();     }    }    ServiceB {     void methodB ()  {     }   } 

1 : PROPAGATION_REQUIRED

加入当前正要执行的事务不在另外一个事务里,那么就起一个新的事务

比如说, ServiceB.methodB 的事务级别定义为 PROPAGATION_REQUIRED, 那么由于执行 ServiceA.methodA 的时候,

ServiceA.methodA 已经起了事务,这时调用 ServiceB.methodB , ServiceB.methodB 看到自己已经运行在 ServiceA.methodA

的事务内部,就不再起新的事务。而假如 ServiceA.methodA 运行的时候发现自己没有在事务中,他就会为自己分配一个事务。

这样,在 ServiceA.methodA 或者在 ServiceB.methodB 内的任何地方出现异常,事务都会被回滚。即使 ServiceB.methodB 的事务已经被

提交,但是 ServiceA.methodA 在接下来 fail 要回滚, ServiceB.methodB 也要回滚

2 : PROPAGATION_SUPPORTS

如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行

3 : PROPAGATION_MANDATORY

必须在一个事务中运行。也就是说,他只能被一个父事务调用。否则,他就要抛出异常

4 : PROPAGATION_REQUIRES_NEW

这个就比较绕口了。 比如我们设计 ServiceA.methodA 的事务级别为 PROPAGATION_REQUIRED , ServiceB.methodB 的事务级别为 PROPAGATION_REQUIRES_NEW ,

那么当执行到 ServiceB.methodB 的时候, ServiceA.methodA 所在的事务就会挂起, ServiceB.methodB 会起一个新的事务,等待 ServiceB.methodB 的事务完成以后,

他才继续执行。他与 PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为 ServiceB.methodB 是新起一个事务,那么就是存在

两个不同的事务。如果 ServiceB.methodB 已经提交,那么 ServiceA.methodA 失败回滚, ServiceB.methodB 是不会回滚的。如果 ServiceB.methodB 失败回滚,

如果他抛出的异常被 ServiceA.methodA 捕获, ServiceA.methodA 事务仍然可能提交。

5 : PROPAGATION_NOT_SUPPORTED

当前不支持事务。比如 ServiceA.methodA 的事务级别是 PROPAGATION_REQUIRED ,而 ServiceB.methodB 的事务级别是 PROPAGATION_NOT_SUPPORTED ,

那么当执行到 ServiceB.methodB 时, ServiceA.methodA 的事务挂起,而他以非事务的状态运行完,再继续 ServiceA.methodA 的事务。

6 : PROPAGATION_NEVER

不能在事务中运行。假设 ServiceA.methodA 的事务级别是 PROPAGATION_REQUIRED , 而 ServiceB.methodB 的事务级别是 PROPAGATION_NEVER ,

那么 ServiceB.methodB 就要抛出异常了。

7 : PROPAGATION_NESTED

理解 Nested 的关键是 savepoint 。他与 PROPAGATION_REQUIRES_NEW 的区别是, PROPAGATION_REQUIRES_NEW 另起一个事务,将会与他的父事务相互独立,

SyntaxHighlighter.highlight();

PHP正则表达式 验证电子邮件地址

    我们最经常遇到的验证,就是电子邮件地址验证。网站上常见。各种网页脚本也都常用“正则表达式”(regular expression)对我们输入的电子邮件地址进行验证,判断是否合法。有的还能分解出用户名和域名。现在用PHP语言实现一下电子邮件地址验证程序,用的是PHP正则表达式库。

<?php
    header ( "Content-Type: text/html; charset=UTF-8" );
    $reply = "";
    if ( isset($_POST["email_address"]) )
    {
        $email_address = $_POST["email_address"];
        $pattern = "/^([0-9A-Za-z\\-_\\.]+)@([0-9a-z]+\\.[a-z]{2,3}(\\.[a-z]{2})?)$/i";
        if ( preg_match( $pattern, $email_address ) )
        {
            $reply = "您输入的电子邮件地址合法<br /><br />\n";
            $user_name = preg_replace( $pattern ,"$1", $email_address );
            $domain_name = preg_replace( $pattern ,"$2", $email_address );
            $reply .= "用户名:".$user_name."<br />\n";
            $reply .= "域名:".$domain_name."<br />\n\n";
        }
        else
        {
            $reply = "您输入的电子邮件地址不合法";
        }
    }
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="zh" xml:lang="zh">
<head>
<title>电子邮件地址验证程序</title>
</head>
<body style="text-align: center;">
<h1>电子邮件地址验证程序</h1>
<form action="#" method="post">
请输入电子邮件地址:<input name="email_address" type="text" style="width: 300px;" /><br />
<input type="submit" value="验证电子邮件地址" />
</form>
<?php
    echo $reply;
?>
</body>
</html>

SyntaxHighlighter.highlight();

php实现zip文件解压操作

<? /***********************@file - path to zip file 需要解压的文件的路径*@destination - destination directory for unzipped files 解压之后存放的路径*@需要使用 ZZIPlib library ,请确认该扩展已经开启*/ function unzip_file($file, $destination){ // 实例化对象 $zip = new ZipArchive() ; //打开zip文档,如果打开失败返回提示信息 if ($zip->open($file) !== TRUE) {   die ("Could not open archive"); } //将压缩文件解压到指定的目录下 $zip->extractTo($destination); //关闭zip文档 $zip->close();   echo 'Archive extracted to directory'; } //测试执行 //unzip_file("func.zip","jieya"); ?>

SyntaxHighlighter.highlight();

php中注册器模式类用法实例分析

本文实例讲述了php中注册器模式类用法。分享给大家供大家参考,具体如下:

注册器读写类
Registry.class.php

<?php/**  * 注册器读写类  */class Registry extends ArrayObject{  /**    * Registry实例   *   * @var object    */  private static $_instance = null;  /**   * 取得Registry实例   *    * @note 单件模式   *    * @return object   */  public static function getInstance()  {    if (self::$_instance === null) {      self::$_instance = new self();      echo "new register object!";    }    return self::$_instance;  }  /**   * 保存一项内容到注册表中   *    * @param string $name 索引   * @param mixed $value 数据   *    * @return void   */  public static function set($name, $value)  {    self::getInstance()->offsetSet($name, $value);  }  /**   * 取得注册表中某项内容的值   *    * @param string $name 索引   *    * @return mixed   */  public static function get($name)  {    $instance = self::getInstance();    if (!$instance->offsetExists($name)) {      return null;    }    return $instance->offsetGet($name);  }  /**   * 检查一个索引是否存在    *    * @param string $name 索引   *    * @return boolean   */  public static function isRegistered($name)  {    return self::getInstance()->offsetExists($name);  }  /**   * 删除注册表中的指定项   *    * @param string $name 索引   *    * @return void   */  public static function remove($name)  {    self::getInstance()->offsetUnset($name);  }}

需要注册的类
 
test.class.php

<?phpclass Test{   function hello()   {    echo "hello world";    return;   }} ?>
<?php//引入相关类require_once "Registry.class.php";require_once "test.class.php";//new a object$test=new Test();//$test->hello();//注册对象Registry::set('testclass',$test);//取出对象$t = Registry::get('testclass');//调用对象方法$t->hello();?>

SyntaxHighlighter.highlight();

php实现分页显示

所谓分页显示,也就是将数据库中的结果集人为的分成一段一段的来显示,这里需要两个初始的参数:

每页多少条记录($PageSize)?

当前是第几页($CurrentPageID)?

现在只要再给我一个结果集,我就可以显示某段特定的结果出来。
至于其他的参数,比如:上一页($PReviousPageID)下一页($NextPageID)总页数($numPages)等等,都可以根据之前的知识获得。

以MySQL数据库为例,如果要从表内截取某段内容,sql语句可以用:select * from table limit offset, rows。看看下面一组sql语句,尝试一下发现其中的规率。

前10条记录:select * from table limit 0,10

第11至20条记录:select * from table limit 10,10

第21至30条记录:select * from table limit 20,10

……

这一组sql语句其实就是当$PageSize=10的时候取表内每一页数据的sql语句,我们可以总结出这样一个模板:

select * from table limit ($CurrentPageID - 1) * $PageSize, $PageSize
// 建立数据库连接 $link = mysql_connect("localhost", "mysql_user", "mysql_passWord")     or die("Could not connect: " . mysql_error());  // 获取当前页数 if( isset($_GET['page']) ){   $page = intval( $_GET['page'] ); } else{   $page = 1; }  // 每页数量 $PageSize = 10;  // 获取总数据量 $sql = "select count(*) as amount from table"; $result = mysql_query($sql); $row = mysql_fetch_row($result); $amount = $row['amount'];  // 记算总共有多少页 if( $amount ){   if( $amount < $page_size ){ $page_count = 1; }        //如果总数据量小于$PageSize,那么只有一页   if( $amount % $page_size ){                 //取总数据量除以每页数的余数     $page_count = (int)($amount / $page_size) + 1;      //如果有余数,则页数等于总数据量除以每页数的结果取整再加一   }else{     $page_count = $amount / $page_size;           //如果没有余数,则页数等于总数据量除以每页数的结果   } } else{   $page_count = 0; } // 翻页链接 $page_string = ''; if( $page == 1 ){   $page_string .= '第一页|上一页|'; } else{   $page_string .= '第一页|.($page-1).'>上一页|'; }  if( ($page == $page_count) || ($page_count == 0) ){   $page_string .= '下一页|尾页'; } else{   $page_string .= '.($page+1).'>下一页|.$page_count.'>尾页'; } // 获取数据,以二维数组格式返回结果 if( $amount ){   $sql = "select * from table order by id desc limit ". ($page-1)*$page_size .", $page_size";   $result = mysql_query($sql);   while ( $row = mysql_fetch_row($result) ){     $rowset[] = $row;   } }else{   $rowset = array(); } // 没有包含显示结果的代码,那不在讨论范围,只要用foreach就可以很简单的用得到的二维数组来显示结果 ?>

SyntaxHighlighter.highlight();

PHP翻页跳转功能实现方法

我们都知道用php+mysql在web 页实现数据库资料全部显示是非常简单而有趣的,数据库资料很少的情况下页面显示还是让人满意的,但是当数据库资料非常多的情况下,页面的显示情况将会变的非常糟糕,下面就来介绍一下如何实现当前页面数据资料显示数量及如何实现动态的翻转功能。
这里将介绍两种翻页显示功能的实现
先介绍一下在翻页中用到的数据库语法:

mysql_query("select * from table order by id desc");

这条数据库语句再熟悉不过了,是用来搜索记录并倒序地显示出来,但并不能在翻页功能中起作用,而下面这个扩展了的语法才是实现翻页的核心功能:

mysql_query("select * from table order by id desc limit $start,$limit");

这里的 $start 是数据库搜索的起始行,$limit 是从起始行开始搜索到 $limit 条记录结束,好了,有了这个核心功能后,我们可以开始翻页功能了。

第一种翻页功能:
这里介绍的功能是翻页功能中最简单的一种,只能实现向前翻页和向后翻页,本站的非常新闻和下载中心的翻页功能就是这种。
 先介绍翻页功能实现的思路:

  • 先确定当前页固定显示的数据记录数量,假设为 20 条记录,设定 $limit 的值为 20: $limit=20;
  • 显示数据库记录时,必须是从第一条开始显示,所以这里设置 $start 的初始值为 0:$start=0;
  • 翻页功能的实现依赖 $start 的动态变化,当向后翻页时 $start 规律地加上 $limit:$start+$limit;而向前翻页时 $start 则规律地减去 $limit:$start-$limit;

有了以上的思路后,可以开始设计程序了

page.php:

<? //设置当前页显示的数量(这个数量可任意设置) $limit=20; //初始化数据库搜索起始记录 if (!emptyempty($start)) $start=0; mysql_connect("localhost","",""); mysql_select_db(database); //设置数据库记录总数 $result=mysql_query("select * from table"); $num_max=mysql_numrows($result); $result=mysql_query("select * from table order by id desc limit $start,$limit); $num=mysql_numrows($result); echo "<table><tr><td>翻页功能</td></tr>"; if (!emptyempty($num)) { for ($i=0;$i<$num;$i++) { $val=mysql_result($result,$i,"val"); $val1=mysql_result($result,$i,"val1"); echo "<tr><td>$val</td><td>$val1</td></tr>"; } } echo "<tr><td>"; //设置向前翻页的跳转 $prve=$start-$limit; if ($prve>=0) { echo "<a href=page.php?start=$prve>prve</a>"; } //设置向后翻页的跳转 $next=$start+$limit; if ($next<$num_max) { echo "<a href=page.php?start=$next>next</a>"; } echo "</td></tr></table>"; ?>

一个前翻,后翻功能的程序完成了,但这个功能对处理更多资料显示时,将会显得很累赘.下面将会继续介绍功能更强大,更加复杂的翻页功能–循环翻页(我一直都这么叫,因为找不到更合适的叫法).:)
前面介绍了简单的翻页功能实现,下面介绍的翻页功能更加强大,更加复杂,本站的非常论坛和非常文章就是使用这个循环翻页功能的.
循环翻页是前翻后翻加上数字共同实现的,具体的表现形式为:
页: prve <<1 2 3 4 ……. 20 >> next
里面的数字表示各当前的页面,前翻 prve 和后翻 next 已不仅仅是当前页的前后翻转,而是更加复杂的数字控制前后翻转。

同以往一样,在进行程序设计之前,先理清一下思路,我建议读者在看完如何实现循环翻页的功能后,能自己动手实践一次,因为这里研究的一些方法和思路可能比较抽象。
首先我们大胆假设数据库里存有超过1000 条的记录,我们希望当前显示 25 条记录,而数字翻转控制为 20,因此就有如下的显示结果:
页: 0 1 2 3 ……… 19 >> next
后翻后的显示结果:
页: prve <<20 27 28 ……. 49 >> next
好,我们来看看其中的规律,一个固定的显示数字 25 ,一个固定的数字控制倍翻 20.我们可以利用这两个数字来实现循环翻页功能;
首先设置固定显示的变量:
$limit=20;

数据库初始变量的设置:
$start=0;

数据库记录总数为:

$num;
页数变量:$page;
一个页数循环显示的程序如下:

<? ... $result=mysql_query("select * from table"); $num=mysql_numrows($result); for ($page=0;$page<($num/$limit);$page++) { echo $page; if ($page>0 && ($page%20)==0) { break; //退出循环 } } ?>

这段代码除了显示数字外,其它功能一概没有实现,因为多了数字控制翻转,所以必须要有几个变量来标记并识别这些控制量,这里用了$s 来标记,这个变量是用来控制数字循环翻页控制的,现在可以看看实现循环翻页的完整代码page.php:

<? $limit=25; if (!emptyempty($start)) $start=0; if (!emptyempty($s)) $s=0; mysql_connect("localhost","",""); mysql_select_db(database); //统计数据库记录总数 $result=mysql_query("select * from table"); $num=mysql_numrows($result); $result=mysql_query("select * from table order by id limit $start,$limit"); $numb=mysql_numrows($result); echo "<table>"; if (!emptyempty($numb)) { for($i=0;$i<$numb;$i++) { $val=mysql_result($result,$i,"val"); $val1=mysql_result($result,$i,"val1"); echo "<tr><td>$val</td><td>$val1</td></tr>"; } } echo "</table>"; //数字循环翻页的控制 echo "<table>"; echo "<tr><td>页:</td>"; //前翻控制 if ($s>20) { if ($s==21) { $st=$s-21; } else { $st=$s-20; } $pstart=$st*$limit; echo "<td><a href=page.php?"; echo "start=$pstart&s=$st>prve</a></td>"; } echo "<td> >></td>"; //设置当前页对应页数无链接功能 $star=$start; //注意循环的初始附值,仔细想想为什么不是 0 for ($page=$s;$page<($num/$limit);$page++) { $start=$page*$limit; echo "<td>"; if($page!=$star/$limit) { echo "<a href=page.php?"; echo "start=$start&s=$s>"; } echo $page; if($page!=$star/$limit) { echo "</a>"; } echo "</td>"; //控制数字页面限制显示功能,控制只显示 20 页 if ($page>0 && ($page%20)==0) { if ($s==0) { $s=$s+21; } else { $s=$s+20; } $start=$start+$limit; if ((($num/$limit)-1)>$page) { echo "<td> <<</td><td><a href'page.php?"; echo "start=$start&s=$s>next</a></td>"; } //注意跳出循环的控制 break; } } echo "</tr></table>"; ?>

SyntaxHighlighter.highlight();

PHP扩展开发教程(总结)

PHP是一种解释型的语言,对于用户而言,我们精心的控制内存意味着easier prototyping和更少的崩溃!当我们深入到内核之后,所有的安全防线都已经被越过,最终还是要依赖于真正有责任心的软件工程师来保证系统的稳定运行。

1、线程安全宏定义

在TSRM/TSRM.h文件中有如下定义

#define TSRMLS_FETCH()       void ***tsrm_ls = (void ***) ts_resource_ex(0, NULL)
#define TSRMLS_FETCH_FROM_CTX(ctx) void ***tsrm_ls = (void ***) ctx
#define TSRMLS_SET_CTX(ctx)   ctx = (void ***) tsrm_ls
#define TSRMG(id, type, element)   (((type) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(id)])->element)
#define TSRMLS_D   void ***tsrm_ls
#define TSRMLS_DC  , TSRMLS_D
#define TSRMLS_C   tsrm_ls
#define TSRMLS_CC  , TSRMLS_C

在ext/xsl/php_xsl.h有这么一段话

/* In every utility function you add that needs to use variables.                                                                   
   in php_xsl_globals, call TSRM_FETCH(); after declaring other.
   variables used by that function, or better yet, pass in TSRMLS_CC
   after the last function argument and declare your utility function
   with TSRMLS_DC after the last declared argument.  Always refer to
   the globals in your function as XSL_G(variable).  You are.
   encouraged to rename these macros something shorter, see
   examples in any other php module directory.
*/

1.在方法定义时加上TSRMLS_D(如果方法没有参数用这个)或者TSRMLS_DC(有1个以上的参数)

2.在方法调用时用TSRMLS_C(如果方法没有参数用这个)或者TSRMLS_CC(有1个以上的参数)

应该可以这样理解

第一个后缀字母D表示定义,即D=Define,第一个后缀字母C表示调用,即C=Call,而第二个后缀字母C是不是表示逗号呢? C=Comma (逗号)

TSRMLS_D就是定义了,所以是  void ***tsrm_ls

TSRMLS_DC是带逗号的定义,所以是 , void ***tsrm_ls

TSRMLS_C是调用,即tsrm_ls

TSRMLS_CC是调用并带逗号,即 ,tsrm_ls

所以一个是形参、一个是实参

可以这样使用

int php_myext_action(int action_id, char *message TSRMLS_DC);
php_myext_action(42, “The meaning of life” TSRMLS_CC);

一般推荐使用tsrm_ls指针定义的方式来保证线程安全

TSRMLS_FETCH调用需要一定的处理时间。这在单次迭代中并不明显,但是随着你的线程数增多,随着你调用TSRMLS_FETCH()的点的增多,你的扩展就会显现出这个瓶颈。因此,请谨慎的使用它。 注意:为了和c++编译器兼容,请确保将TSRMLS_FETCH()和所有变量定义放在给定块作用域的顶部(任何其他语句之前)。因为TSRMLS_FETCH()宏自身有多种不同的解析方式,因此最好将它作为变量定义的最后一行

2、PHP的生命周期

PHP的最多的两种运行模式是WEB模式、CLI模式,无论哪种模式,PHP工作原理都是一样的,作为一种SAPI运行。

1、当我们在终端敲入php这个命令的时候,它使用的是CLI。

它就像一个web服务器一样来支持php完成这个请求,请求完成后再重新把控制权交给终端。

2、当使用Apache作为宿主时,当一个请求到来时,PHP会来支持完成这个请求

PHP_MINIT_FUNCTION  初始化module时运行
PHP_MSHUTDOWN_FUNCTION  当module被卸载时运行
PHP_RINIT_FUNCTION  当一个REQUEST请求初始化时运行
PHP_RSHUTDOWN_FUNCTION  当一个REQUEST请求结束时运行
PHP_MINFO_FUNCTION  这个是设置phpinfo中这个模块的信息
PHP_GINIT_FUNCTION  初始化全局变量时
PHP_GSHUTDOWN_FUNCTION  释放全局变量时

比如PHP_GINIT_FUNCTION

PHP_GINIT_FUNCTION(test){  /** 初始化全局变量 */}//对应的C代码void zm_globals_ctor_test (zend_test_globals *test_globals TSRMLS_DC){  /** 初始化全局变量 */}//在线程退出时,需要将之前自己申请的资源释放时,可以使用 PHP_GSHUTDOWN_FUNCTION来注册析构函数。PHP_GSHUTDOWN_FUNCTION(test){  /** 清除全局变量 */}//对应的C代码void zm_globals_dtor_test (zend_test_globals *test_globals TSRMLS_DC){  /** 清除全局变量 */}

这里有一段代码,可以测试一下

int minit_time;PHP_MINIT_FUNCTION(test){  minit_time = time(NULL);  return SUCCESS;}PHP_MSHUTDOWN_FUNCTION(test){  FILE *fp=fopen("mshutdown.txt","a+");  fprintf(fp,"%ld/n",time(NULL));  fclose(fp);  return SUCCESS;}int rinit_time;PHP_RINIT_FUNCTION(test){  rinit_time = time(NULL);  return SUCCESS;}PHP_RSHUTDOWN_FUNCTION(test){  FILE *fp=fopen("rshutdown.txt","a+");  fprintf(fp,"%ld/n",time(NULL));  fclose(fp);  return SUCCESS;}PHP_MINFO_FUNCTION(test){  php_info_print_table_start();  php_info_print_table_header(, "module info", "enabled");  php_info_print_table_end();  /* Remove comments if you have entries in php.ini  DISPLAY_INI_ENTRIES();  */}PHP_FUNCTION(test){  php_printf("%d",time_of_minit);  php_printf("%d",time_of_rinit);  return;}

3、段错误调试

Linux下的C程序常常会因为内存访问错误等原因造成segment fault(段错误)此时如果系统core dump功能是打开的,那么将会有内存映像转储到硬盘上来,之后可以用gdb对core文件进行分析,还原系统发生段错误时刻的堆栈情况。这对于我们发现程序bug很有帮助。
使用ulimit -a可以查看系统core文件的大小限制;使用ulimit -c [kbytes]可以设置系统允许生成的core文件大小。

ulimit -c 0 不产生core文件
ulimit -c 100 设置core文件最大为100k
ulimit -c unlimited 不限制core文件大小

步骤:

1、当发生段错误时,我们查看ulimit -a (core file size (blocks, -c) 0)并没有文件,
2、设置 :ulimit -c unlimited 不限制core文件大小
3、运行程序 ,发生段错误时会自动记录在core中 (php -f WorkWithArray.php)
4、ls -al core.* 在那个文件下(-rw——- 1 leconte leconte 139264 01-06 22:3 1 core.2065)
5、使用gdb 运行程序和段错误记录的文件。(gdb ./test core.2065)
6、会提哪行有错。

很多系统默认的core文件大小都是0,我们可以通过在shell的启动脚本/etc/bashrc或者~/.bashrc等地方来加入 ulimit -c 命令来指定core文件大小,从而确保core文件能够生成。
除此之外,还可以在/proc/sys/kernel/core_pattern里设置core文件的文件名模板,详情请看core的官方man手册。

4、常见的变量操作宏

CG    -> Complier Global      编译时信息,包括函数表等(zend_globals_macros.h:32)
EG    -> Executor Global      执行时信息(zend_globals_macros.h:43)
PG    -> PHP Core Global      主要存储php.ini中的信息
SG    -> SAPI Global          SAPI信息

1、SG  针对SAPI信息 在main/SAPI.h文件中

typedef struct _sapi_globals_struct {  void *server_context;  sapi_request_info request_info;  sapi_headers_struct sapi_headers;  int read_post_bytes;  unsigned char headers_sent;  struct stat global_stat;  char *default_mimetype;  char *default_charset;  HashTable *rfc1867_uploaded_files;  long post_max_size;  int options;  zend_bool sapi_started;  double global_request_time;  HashTable known_post_content_types;  zval *callback_func;  zend_fcall_info_cache fci_cache;  zend_bool callback_run;} sapi_globals_struct;

看一下SG的定义

BEGIN_EXTERN_C()
#ifdef ZTS
# define SG(v) TSRMG(sapi_globals_id, sapi_globals_struct *, v)
SAPI_API extern int sapi_globals_id;
#else
# define SG(v) (sapi_globals.v)
extern SAPI_API sapi_globals_struct sapi_globals;
#endif
SAPI_API void sapi_startup(sapi_module_struct *sf);
SAPI_API void sapi_shutdown(void);
SAPI_API void sapi_activate(TSRMLS_D);
SAPI_API void sapi_deactivate(TSRMLS_D);
SAPI_API void sapi_initialize_empty_request(TSRMLS_D);
END_EXTERN_C()

成员都在sapi_globals_struct这里了

那么我么可以这样调用

SG(default_mimetype)
SG(request_info).request_uri

可以感受一下这么一段代码

static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC){  char buf[SAPI_CGI_MAX_HEADER_LENGTH];  sapi_header_struct *h;  zend_llist_position pos;  long rfc2616_headers = 0;  if(SG(request_info).no_headers == 1) {    return SAPI_HEADER_SENT_SUCCESSFULLY;  }  if (SG(sapi_headers).http_response_code != 200) {    int len;    len = sprintf(buf, "Status: %d/r/n", SG(sapi_headers).http_response_code);    PHPWRITE_H(buf, len);  }  if (SG(sapi_headers).send_default_content_type) {    char *hd;    hd = sapi_get_default_content_type(TSRMLS_C);    PHPWRITE_H("Content-type:", sizeof("Content-type: ")-1);    PHPWRITE_H(hd, strlen(hd));    PHPWRITE_H("/r/n", 2);    efree(hd);  }  h = zend_llist_get_first_ex(&sapi_headers->headers, &pos);  while (h) {    PHPWRITE_H(h->header, h->header_len);    PHPWRITE_H("/r/n", 2);    h = zend_llist_get_next_ex(&sapi_headers->headers, &pos);  }  PHPWRITE_H("/r/n", 2);  return SAPI_HEADER_SENT_SUCCESSFULLY;}

 2、EG  Executor Globals

EG获取的是struct _zend_execution_globals结构体中的数据

struct _zend_execution_globals { ... HashTable symbol_table;  /* 全局作用域,如果没有进入函数内部,全局=活动 */ HashTable *active_symbol_table; /* 活动作用域,当前作用域 */ ...}

通常,使用EG(symbol_table)获取的是全局作用域中的符号表,使用EG(active_symbol_table)获取的是当前作用域下的符号表

例如 来定义$foo = ‘bar’

zval *fooval;
 
MAKE_STD_ZVAL(fooval);
ZVAL_STRING(fooval, “bar”, 1);
ZEND_SET_SYMBOL(EG(active_symbol_table), “foo”, fooval);

或者从符号表中查找$foo

zval **fooval;
if(zend_hash_find(&EG(symbol_table), “foo”, sizeof(“foo”), (void **)&fooval) == SUCCESS) {
    RETURN_STRINGL(Z_STRVAL_PP(fooval), Z_STRLEN_PP(fooval));
} else {
    RETURN_FALSE;
}

上面的代码中,EG(active_symbol_table) == &EG(symbol_table)

3、CG() 用来访问核心全局变量。(zend/zend_globals_macros.h)

4、PG() PHP全局变量。我们知道php.ini会映射一个或者多个PHP全局结构。(main/php_globals.h)

5、FG() 文件全局变量。大多数文件I/O或相关的全局变量的数据流都塞进标准扩展出口结构。(ext/standard/file.h)

5、获取变量的类型和值

#define Z_TYPE(zval)        (zval).type
#define Z_TYPE_P(zval_p)    Z_TYPE(*zval_p)
#define Z_TYPE_PP(zval_pp)  Z_TYPE(**zval_pp)

比如获取一个变量的类型

void describe_zval(zval *foo){  if ( Z_TYPE_P(foo) == IS_NULL )  {    php_printf("这个变量的数据类型是: NULL");  }  else  {    php_printf("这个变量的数据类型不是NULL,这种数据类型对应的数字是: %d", Z_TYPE_P(foo));  }}

有这么几种类型

#define IS_NULL     0
#define IS_LONG     1
#define IS_DOUBLE   2
#define IS_BOOL     3
#define IS_ARRAY    4
#define IS_OBJECT   5
#define IS_STRING   6
#define IS_RESOURCE 7
#define IS_CONSTANT 8
#define IS_CONSTANT_ARRAY   9
#define IS_CALLABLE 10

php_printf()函数是内核对printf()函数的一层封装,我们可以像使用printf()函数那样使用它,以一个P结尾的宏的参数大多是*zval型变量。 此外获取变量类型的宏还有两个,分别是Z_TYPE和Z_TYPE_PP,前者的参数是zval型,而后者的参数则是**zval

比如gettype函数的实现

//开始定义php语言中的函数gettypePHP_FUNCTION(gettype){  //arg间接指向调用gettype函数时所传递的参数。是一个zval**结构  //所以我们要对他使用__PP后缀的宏。  zval **arg;  //这个if的操作主要是让arg指向参数~  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {    return;  }  //调用Z_TYPE_PP宏来获取arg指向zval的类型。  //然后是一个switch结构,RETVAL_STRING宏代表这gettype函数返回的字符串类型的值  switch (Z_TYPE_PP(arg)) {    case IS_NULL:      RETVAL_STRING("NULL", 1);      break;    case IS_BOOL:      RETVAL_STRING("boolean", 1);      break;    case IS_LONG:      RETVAL_STRING("integer", 1);      break;    case IS_DOUBLE:      RETVAL_STRING("double", 1);      break;    case IS_STRING:      RETVAL_STRING("string", 1);      break;    case IS_ARRAY:      RETVAL_STRING("array", 1);      break;    case IS_OBJECT:      RETVAL_STRING("object", 1);      break;    case IS_RESOURCE:      {        char *type_name;        type_name = zend_rsrc_list_get_rsrc_type(Z_LVAL_PP(arg) TSRMLS_CC);        if (type_name) {          RETVAL_STRING("resource", 1);          break;        }      }    default:      RETVAL_STRING("unknown type", 1);  }}

获取变量的值,有这么多宏来获取

 

Long

Boolean

Double

String value

String length

Z_LVAL( )
Z_BVAL( )
Z_DVAL( )
Z_STRVAL( )
Z_STRLEN( )
Z_LVAL_P( )
Z_BVAL_P( )
Z_DVAL_P( )
Z_STRVAL_P( )
Z_STRLEN_P( )
Z_LVAL_PP( )
Z_BVAL_PP( )
Z_DVAL_PP( )
Z_STRVAL_PP( )
Z_STRLEN_PP( )

HashTable

Object

Object properties

Object class entry

Resource value

Z_ARRVAL( )
Z_OBJ( )
Z_OBJPROP( )
Z_OBJCE( )
Z_RESVAL( )
Z_ARRVAL_P( )
Z_OBJ_P( )
Z_OBJPROP_P( )
Z_OBJCE_P( )
Z_RESVAL_P( )
Z_ARRVAL_PP( )
Z_OBJ_PP( )
Z_OBJPROP_PP( )
Z_OBJCE_PP( )
Z_RESVAL_PP( )

rot13函数的实现

PHP_FUNCTION(rot13){ zval **arg; char *ch, cap; int i;   if (ZEND_NUM_ARGS( ) != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) {   WRONG_PARAM_COUNT; } *return_value = **arg; zval_copy_ctor(return_value); convert_to_string(return_value);   for(i=0, ch=return_value->value.str.val;   i<return_value->value.str.len; i++, ch++) {    cap = *ch & 32;    *ch &= ~cap;    *ch = ((*ch>='A') && (*ch<='Z') ? ((*ch-'A'+13) % 26 + 'A') : *ch) | cap;  }}

要获取变量的值,也应该使用Zend定义的宏进行访问。对于简单的标量数据类型、Boolean,long,double, 使用Z_BVAL, Z_LVAL, Z_DVAL

void display_values(zval boolzv, zval *longpzv, zval **doubleppzv){ if (Z_TYPE(boolzv) == IS_BOOL) {  php_printf("The value of the boolean is : %s/n", Z_BVAL(boolzv) ? "true" : "false"); } if(Z_TYPE_P(longpzv) == IS_LONG) {  php_printf("The value of the long is: %ld/n", Z_LVAL_P(longpzv)); } if(Z_TYPE_PP(doubleppzv) == IS_DOUBLE) {  php_printf("The value of the double is : %f/n", Z_DVAL_PP(doubleppzv)); }}

对于字符串类型,因为它含有两个字段char * (Z_STRVAL) 和 int (Z_STRLEN),因此需要用两个宏来进行取值,因为需要二进制安全的输出这个字符串

void display_string(zval *zstr){ if (Z_TYPE_P(zstr) != IS_STRING) {  php_printf("The wronng datatype was passed!/n");  return ; } PHPWRITE(Z_STRVAL_P(zstr), Z_STRLEN_P(zstr));}

因为数组在zval中是以HashTable形式存在的,因此使用Z_ARRVAL()进行访问

void display_zval(zval *value){  switch (Z_TYPE_P(value)) {    case IS_NULL:      /* 如果是NULL,则不输出任何东西 */      break;     case IS_BOOL:      /* 如果是bool类型,并且true,则输出1,否则什么也不干 */      if (Z_BVAL_P(value)) {        php_printf("1");      }      break;    case IS_LONG:      /* 如果是long整型,则输出数字形式 */      php_printf("%ld", Z_LVAL_P(value));      break;    case IS_DOUBLE:      /* 如果是double型,则输出浮点数 */      php_printf("%f", Z_DVAL_P(value));      break;    case IS_STRING:      /* 如果是string型,则二进制安全的输出这个字符串 */      PHPWRITE(Z_STRVAL_P(value), Z_STRLEN_P(value));      break;    case IS_RESOURCE:      /* 如果是资源,则输出Resource #10 格式的东东 */      php_printf("Resource #%ld", Z_RESVAL_P(value));      break;    case IS_ARRAY:      /* 如果是Array,则输出Array5个字母! */      php_printf("Array");      break;    case IS_OBJECT:      php_printf("Object");      break;    default:      /* Should never happen in practice,       * but it's dangerous to make assumptions       */       php_printf("Unknown");       break;  }} 

一些类型转换函数

ZEND_API void convert_to_long(zval *op);
ZEND_API void convert_to_double(zval *op);
ZEND_API void convert_to_null(zval *op);
ZEND_API void convert_to_boolean(zval *op);
ZEND_API void convert_to_array(zval *op);
ZEND_API void convert_to_object(zval *op);
ZEND_API void _convert_to_string(zval *op ZEND_FILE_LINE_DC);

6、常量的实例化

我们可以这样实例化

PHP_MINIT_FUNCTION(consts) //模块初始化时定义常量{  REGISTER_LONG_CONSTANT("CONSTS_MEANING_OF_LIFE", 42, CONST_CS | CONST_PERSISTENT);  REGISTER_DOUBLE_CONSTANT("CONSTS_PI", 3.1415926, CONST_PERSISTENT);  REGISTER_STRING_CONSTANT("CONSTS_NAME", "leon", CONST_CS|CONST_PERSISTENT);}PHP_RINIT_FUNCTION(consts) //每次请求时定义常量{  char buffer[40];  srand((int)time(NULL));  snprintf(buffer, sizeof(buffer), "%d", rand());  REGISTER_STRING_CONSTANT("CONSTS_RAND", estrdup(buffer), CONST_CS);  return SUCCESS;}

常见的宏

/*注册LONG类型常量*/
#define REGISTER_LONG_CONSTANT(name, lval, flags)  zend_register_long_constant((name), sizeof(name), (lval), (flags), module_number TSRMLS_CC)

 /*注册double类型常量*/
#define REGISTER_DOUBLE_CONSTANT(name, dval, flags)  zend_register_double_constant((name), sizeof(name), (dval), (flags), module_number TSRMLS_CC)

/*注册STRING类型常量*/
#define REGISTER_STRING_CONSTANT(name, str, flags)  zend_register_string_constant((name), sizeof(name), (str), (flags), module_number TSRMLS_CC)

/*注册STRING类型常量*/
#define REGISTER_STRINGL_CONSTANT(name, str, len, flags)  zend_register_stringl_constant((name), sizeof(name), (str), (len), (flags), module_number TSRMLS_CC)

7、全局变量

#php-fpm 生成 POST|GET|COOKIE|SERVER|ENV|REQUEST|FILES全局变量的流程php_cgi_startup() -> php_module_startup() -> php_startup_auto_globals() -> 保存变量到symbol_table符号表php_cgi_startup()在 fpm/fpm/fpm_main.c中定义php_module_startup() 在main/main.c中定义php_startup_auto_globals() 在main/php_variables.h中定义zend_hash_update(&EG(symbol_table), "_GET", sizeof("_GET") + 1, &vars, sizeof(zval *), NULL);/* 读取$_SERVER变量 */static PHP_FUNCTION(print_server_vars) {  zval **val;  if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **)&val) == SUCCESS) {    RETURN_ZVAL(*val, 1, 0);  }else{   RETURN_FALSE;  }}/* 读取$_SERVER[$name] */ZEND_BEGIN_ARG_INFO(print_server_var_arginfo, 0)  ZEND_ARG_INFO(0, "name")ZEND_END_ARG_INFO()static PHP_FUNCTION(print_server_var) {  char *name;  int name_len;  zval **val;  HashTable *ht_vars = NULL;  HashPosition pos;  zval **ret_val;  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &name, &name_len) == FAILURE) {    RETURN_NULL();  }  if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **)&val) == SUCCESS) {    ht_vars = Z_ARRVAL_PP(val);    //此处需传入大于name长度+1的值,因为字符串值后面需要'/0'    if (zend_hash_find(ht_vars, name, name_len+1, (void **)&ret_val) == SUCCESS) {       RETURN_STRING(Z_STRVAL_PP(ret_val), 0);    }else{      RETURN_NULL();    }  }else{    RETURN_NULL();  }}

8、包装第三方库

配置(config.m4)

SEARCH_PATH="/usr/local /usr"   #lib搜索的目录SEARCH_FOR="/include/curl/curl.h" #lib头文件的路径if test -r $PHP_LIBS/$SEARCH_FOR; then  LIBS_DIR=$PHP_LIBSelse # search default path list  AC_MSG_CHECKING([for libs files in default path])  for i in $SEARCH_PATH ; do    if test -r $i/$SEARCH_FOR; then      LIBS_DIR=$i        #搜索到的lib的路径      AC_MSG_RESULT(found in $i)    fi  donefi/*验证lib是否存在*/if test -z "$LIBS_DIR"; then  AC_MSG_RESULT([not found])  AC_MSG_ERROR([Please reinstall the libs distribution])fi/*编译的时候添加lib的include目录, -I/usr/include*/PHP_ADD_INCLUDE($LIBS_DIR/include)LIBNAME=curl      #lib名称 LIBSYMBOL=curl_version #lib的一个函数,用来PHP_CHECK_LIBRARY验证lib/*验证lib*/PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, [  PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $LIBS_DIR/$PHP_LIBDIR, LIBS_SHARED_LIBADD) #编译的时候链接lib, -llibcurl  AC_DEFINE(HAVE_LIBSLIB,1,[ ])],[  AC_MSG_ERROR([wrong libs lib version or lib not found])],[  -L$LIBS_DIR/$PHP_LIBDIR -lm]) PHP_SUBST(LIBS_SHARED_LIBADD)

9、用于返回的宏

//这些宏都定义在Zend/zend_API.h文件里
#define RETVAL_RESOURCE(l)              ZVAL_RESOURCE(return_value, l)
#define RETVAL_BOOL(b)                  ZVAL_BOOL(return_value, b)
#define RETVAL_NULL()                   ZVAL_NULL(return_value)
#define RETVAL_LONG(l)                  ZVAL_LONG(return_value, l)
#define RETVAL_DOUBLE(d)                ZVAL_DOUBLE(return_value, d)
#define RETVAL_STRING(s, duplicate)         ZVAL_STRING(return_value, s, duplicate)
#define RETVAL_STRINGL(s, l, duplicate)     ZVAL_STRINGL(return_value, s, l, duplicate)
#define RETVAL_EMPTY_STRING()           ZVAL_EMPTY_STRING(return_value)
#define RETVAL_ZVAL(zv, copy, dtor)     ZVAL_ZVAL(return_value, zv, copy, dtor)
#define RETVAL_FALSE                    ZVAL_BOOL(return_value, 0)
#define RETVAL_TRUE                     ZVAL_BOOL(return_value, 1)
#define RETURN_RESOURCE(l)              { RETVAL_RESOURCE(l); return; }
#define RETURN_BOOL(b)                  { RETVAL_BOOL(b); return; }
#define RETURN_NULL()                   { RETVAL_NULL(); return;}
#define RETURN_LONG(l)                  { RETVAL_LONG(l); return; }
#define RETURN_DOUBLE(d)                { RETVAL_DOUBLE(d); return; }
#define RETURN_STRING(s, duplicate)     { RETVAL_STRING(s, duplicate); return; }
#define RETURN_STRINGL(s, l, duplicate) { RETVAL_STRINGL(s, l, duplicate); return; }
#define RETURN_EMPTY_STRING()           { RETVAL_EMPTY_STRING(); return; }
#define RETURN_ZVAL(zv, copy, dtor)     { RETVAL_ZVAL(zv, copy, dtor); return; }
#define RETURN_FALSE                    { RETVAL_FALSE; return; }
#define RETURN_TRUE                     { RETVAL_TRUE; return; }

其实,除了这些标量类型,还有很多php语言中的复合类型我们需要在函数中返回,如数组和对象,我们可以通过RETVAL_ZVAL与RETURN_ZVAL来操作它们

10、hashTable的遍历函数

//基于long key的操作函数
zval *v3;
MAKE_STD_ZVAL(v3);
ZVAL_STRING(v3, “value3”, 1);
zend_hash_index_update(names, 0, &v3, sizeof(zval *), NULL);//按数字索引键更新HashTable元素的值
zval **v4;
zend_hash_index_find(names, 1, &v4); //按数字索引获取HashTable元素的值
php_printf(“v4 : “);
PHPWRITE(Z_STRVAL_PP(v4), Z_STRLEN_PP(v4));
php_printf(“/n”);
ulong idx;
idx = zend_hash_index_exists(names, 10);//按数字索引查找HashTable,如果找到返回 1, 反之则返回 0
zend_hash_index_del(names, 2);    //按数字索引删除HashTable元素
//hashTable的遍历函数
zend_hash_internal_pointer_reset(names); //初始化hash指针
zend_hash_internal_pointer_reset_ex(names, &pos);//初始化hash指针,并付值给pos
zend_hash_get_current_data(names, (void**) &val); //获取当前hash存储值,data should be cast to void**, ie: (void**) &data
zend_hash_get_current_data_ex(names, (void**) &val, &pos) == SUCCESS; //获取当前hash存储值
zend_hash_get_current_key(names, &key, &klen, &index, 0) == HASH_KEY_IS_LONG
zend_hash_get_current_key_ex(names, &key, &klen, &index, 0, &pos) == HASH_KEY_IS_LONG; //读取hashtable当前的KEY,返回值会有两种 HASH_KEY_IS_LONG | HASH_KEY_IS_STRING ,分别对应array(“value”),array(“key”=>”value”)两种hashtable
zend_hash_move_forward(names);
zend_hash_move_forward_ex(names, &pos); //hash指针移至下一位
//HashTable长度
php_printf(“%*carray(%d) {/n”, depth * 2, ‘ ‘, zend_hash_num_elements(Z_ARRVAL_P(zv))

一个简单的函数

function hello_array_strings($arr) {  if (!is_array($arr)) return NULL;  printf("The array passed contains %d elements ", count($arr));  foreach($arr as $data) {    if (is_string($data)) echo "$data ";  }}
PHP_FUNCTION(hello_array_strings){  zval *arr, **data;  HashTable *arr_hash;  HashPosition pointer;  int array_count;  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr) == FAILURE) {    RETURN_NULL();  }  arr_hash = Z_ARRVAL_P(arr);  array_count = zend_hash_num_elements(arr_hash);  php_printf("The array passed contains %d elements ", array_count);  for(zend_hash_internal_pointer_reset_ex(arr_hash, &pointer); zend_hash_get_current_data_ex(arr_hash, (void**) &data, &pointer) == SUCCESS; zend_hash_move_forward_ex(arr_hash, &pointer)) {    if (Z_TYPE_PP(data) == IS_STRING) {      PHPWRITE(Z_STRVAL_PP(data), Z_STRLEN_PP(data));      php_printf(" ");    }  }  RETURN_TRUE;}

SyntaxHighlighter.highlight();