PHP初体验
PHP初体验
提笔写初体验总不知道从何说起,直接聊PHP中的函数、PHP网络技术、数据库操作、PHP模板等感觉又不是初体验。最后还是决定从PHP的面向对象、PHP的魔术方法、PHP的反射、PHP中的异常和错误这4个方面简单介绍一下。
PHP面向对象的“形”与“本”
这里我们就不给面向对象下定义了,不过我们还是要说一下类和对象的。类是对象的抽象组织,对象是类的具体存在。接下来我们就拿PHP为例,来探讨一下对象的“形”与“本”的问题。
在PHP中,每个类的定义都是以关键字class开头,后面是类名和一对花括号,括号中包含类成员和方法的定义。如下是一个简单类的定义:
class Person {
public $name;
private $age;
private $sex;
public static $information = "I come from the earth";
public function __construct($name="zhangsan", $age=23, $sex="male") {
$this->sex = $sex;
$this->age = $age;
$this->name = $name;
} public function sayHello(){
echo "My name is $this->name and I am $this->age years old. I am a $this->sex.\r\n";
echo self::$information;
echo "<br>";
}
}
$person = new Person();
$person->name = 'Lisi';
$person->sayHello();
echo serialize($person); //输出结果如下:
//My name is Lisi and I am 23 years old. I am a male. I come from the earth
//O:6:"Person":3:{s:4:"name";s:4:"Lisi";s:11:"Personage";i:23;s:11:"Personsex";s:4:"male";}
Person类
当把类对象序列化输出时,可以看出类对象在存储时类似于数组的形式。那么类对象与数组从本质上又有什么区别与联系呢?接下来从对象“本”来分析一下PHP对对象的底层实现。如下是PHP源码中对变量的定义:
#变量定义
typedef struct _zval_struct zeal; //存储变量的类型 struct _zval_struct {
/* Variable information */
zvalue_value value; /* 存储变量的值 */
zend_uint refcount; /* 变量引用数 */
zend_uchar type; /* 变量类型 */
zend_uchar is_ref; /* 变量是否被引用 */
}; typedef union _zvalue_value {
long lval; /* 长整型 */
double dval; /* double型 */
struct {
char *val;
int len;
} str; /* String型 */
HashTable *ht; /* array型 */
zend_object_value obj; /* 对象型 */
} zvalue_value; #对象底层实现
typedef struct _zend_object {
zend_class_entry *ce; //类入口,存储该对象的类结构,相当于类指针
HashTable *properties; //对象属性组成的HashTable
HashTable *guards; /* 阻止递归调用 */
} zend_object; typedef struct _zend_guard {
zend_bool in_get;
zend_bool in_set;
zend_bool in_unset;
zend_bool in_isset;
zend_bool dummy; /* sizeof(zend_guard) must not be equal to sizeof(void*) */
} zend_guard;
//由于zend_class_entry源码较多且复杂,因此此处省略。
PHP源码中变量、对象的定义
通过上面的代码我们也对PHP如何存储对象有了初步的认识,那对象与数组又是什么关系呢?通过PHP的源码可得,zvalue_object结构中有一个HashTable的类型,它就是存储数组的。PHP对象的结构体中不仅有HashTable(用于存储类对象特有的属性),而且还有对象所属类的入口等,如下是PHP对象的组成:
其中PHP源码中zend_class_entry结构体中存储的就是类的指针,该结构体中包含类常量、静态属性、标准方法、魔术方法、自定义方法等。而属性数组存储的是类对象的属性。接下来我们还是以如上的Person类为例,谈一谈对象与数组:
class Person {
public $name;
private $age;
private $sex;
public static $information = "I come from the earth";
public function __construct($name="zhangsan", $age=23, $sex="male") {
$this->sex = $sex;
$this->age = $age;
$this->name = $name;
}
}
$person = new Person();
$person_arr = array("name"=>"zhangsan", "age"=>"23", "sex"=>"male");
echo serialize($person); echo "<br>"; //输出对象序列化结果并换行符
echo serialize($person_arr); echo "<br>"; //输出数组序列化结果并换行
$object = (object)$person_arr; //将数组转化为对象
echo serialize($object); //输出数组序转化为对象的序列化结果 /*输出结果如下:
O:6:"Person":3:{s:4:"name";s:8:"zhangsan";s:11:"Personage";i:23;s:11:"Personsex";s:4:"male";}
a:3:{s:4:"name";s:8:"zhangsan";s:3:"age";s:2:"23";s:3:"sex";s:4:"male";}
O:8:"stdClass":3:{s:4:"name";s:8:"zhangsan";s:3:"age";s:2:"23";s:3:"sex";s:4:"male";} 结果解释如下:
O代表的是对象,a代表的是数组,
O后的数字(6和8)代表该对象所属类名的长度,紧挨在数字后面的就是类名
类名后面的数字为类对象属性的个数,如上有3个,分别为name,age,sex
大括号中的为对象或数组的属性名和属性值的键值对,其中s代表字符串
最后需要说明的是当把数组转化为对象时,因为没有与数组转换成对象对应的类,因此PHP中一个称为"孤儿"的类stdClass类就会收留这个对象
*/
PHP中的魔术方法
可能细心的你在对象组成的那张图中看到了魔术方法,但是上一节中并没有对zend_class_entry中的内容做任何介绍。那么什么又是魔术方法呢?魔术方法就是以两个下划线“__”开头、具有一些特殊作用的方法。其实如上的Person类中,我们不经意间就使用了魔术方法__construct(),这个魔术方法就是构造方法。用于在创建类对象时对属性进行赋值。接下来我们将介绍几个常见的魔术方法让大家对魔术方法有个初步了解。
- __sleep():该魔术方法是在执行serialize()方法完成前被调用,该方法可以用来关闭对象可能具有的任何数据库连接、加密需要序列化对象属性的值、指定仅序列化对象的某些属性等等。
- __wakeup():该魔术方法是在执行unserialize()方法完成前被调用,该方法可以用来建立对象可能的数据库连接、解密序列化对象属性的值等等。
class Person {
public $name;
private $age;
private $sex;
public static $information = "I come from the earth";
public function __construct($name="zhangsan", $age=23, $sex="male") {
$this->sex = $sex;
$this->age = $age;
$this->name = $name;
}
public function __sleep() {
// TODO: Implement __sleep() method
return array("name","age"); //该方法指定仅序列化对象的name和age属性
}
public function __wakeup() {
// TODO: Implement __wakeup() method.
var_dump($this->name); //打印输出对象的name属性的值
var_dump($this->sex); //打印输出对象的sex属性的值,由于sex没有被序列化,因此输出null
}
}
$serResult = serialize(new Person()); //序列化一个Person类对象,该方法完成前先调用Person类的__sleep()
echo $serResult; echo "<br>"; //输出对象序列化结果并换行符
$unSerResult = unserialize($serResult); //将序列化结果反序列化,该方法完成前调用Person类的__wakeup方法
echo $unSerResult->name; echo "<br>"; //由反序列化得到的Person类对象调用对象的name属性
echo var_dump($unSerResult); echo "<br>"; //输出反序列化得到的Person类对象 /*输出结果如下:
O:6:"Person":2:{s:4:"name";s:8:"zhangsan";s:11:"Personage";i:23;}
string(8) "zhangsan" NULL zhangsan
object(Person)#1 (3) { ["name"]=> string(8) "zhangsan" ["age":"Person":private]=> int(23) ["sex":"Person":private]=> NULL }
*/__sleep()和__wakeup()方法
- __construct():类的构造方法,类对象在创建时会首先调用该方法,因此该方法中可以做一些类对象创建前的初始化工作。
- __destruct():类的析构方法,当类对象在销毁时会调用该方法。
class Person {
public $name;
private $age;
private $sex;
public static $information = "I come from the earth";
public function __construct($name="zhangsan", $age=23, $sex="male") {
$this->sex = $sex;
$this->age = $age;
$this->name = $name;
}
public function __destruct() {
// TODO: Implement __destruct() method.
echo "The object will be destructed";
}
} $person = new Person();
unset($person); //unset()方法释放$person对象
echo $person->name; //该行试图打印$person对象的name属性值,由于$person对象已经被销毁了,因此该行会报错
/*
输出结果如下:
The object will be destructed
Notice: Undefined variable: person in /Users/zhangshilei/PhpstormProjects/untitled/demo/Person.php on line 19 Notice: Trying to get property of non-object in /Users/zhangshilei/PhpstormProjects/untitled/demo/Person.php on line 19
*/PHP构造函数和析构函数
- __get()和__set()方法,这两个方法主要是实现了可以在类的外部通过对象直接访问类的私有属性,还可以增加类中没有定义的属性,如给$person对象增加marriage属性,只需$person->marriage="married"即可。
class Person {
public $name;
private $age;
private $sex;
public static $information = "I come from the earth";
public function __construct($name="zhangsan", $age=23, $sex="male") {
$this->sex = $sex;
$this->age = $age;
$this->name = $name;
}
public function __get($field) {
// TODO: Implement __get() method.
echo "the get method is executed<br>";
return $this->$field;
}
public function __set($key, $value) {
// TODO: Implement __set() method.
echo "The set method is executed<br>";
$this->$key = $value;
}
} $person = new Person();
$person->name="Lisi";
echo $person->name; echo "<br>";
$person->age = 25; //age作为$person的私有属性,如果没有__set()方法此句会报错
echo $person->age; //没有__get()方法,此句会报错,如果__get()方法中没有return语句,该句没有返回值 /*输出结果如下:
Lisi
The set method is executed
the get method is executed
25
结果解释如下:
不知道大家注意到了没有,设置公有属性name的值没有调用__set()方法,读取公有属性name的值没有调用__get()方法
只有在设置和读取私有属性age的值才调用了__get()和__set()方法
*/__get()和__set()方法
- __call()和__callStatic():当类对象调用的方法不存在而且类中定义了__call()方法时,则会自动调用类的__call(),当类调用静态方法不存在而且类中定义了方法__callStatic()时,则会自动调用__callStatic()方法。
class Person {
public $name;
private $age;
private $sex;
public static $information = "I come from the earth";
public function __construct($name="zhangsan", $age=23, $sex="male") {
$this->sex = $sex;
$this->age = $age;
$this->name = $name;
}
public function __call($name, $arguments) {
// TODO: Implement __call() method.
echo "被调用方法的名称为:$name<br>";
echo "传入该方法的参数为:", var_dump($arguments),"<br>";
}
public static function __callStatic($name, $arguments) {
// TODO: Implement __callStatic() method.
echo "被调用静态方法的名称为:$name<br>";
echo "传入该方法的参数为:", var_dump($arguments),"<br>";
}
} $person = new Person();
$person->shopping("clothes","fruit","snack");
Person::travel("Beijing","Pair","London","Prussia"); /*
输出结果为:
被调用方法的名称为:shopping
传入该方法的参数为:array(3) { [0]=> string(7) "clothes" [1]=> string(5) "fruit" [2]=> string(5) "snack" }
被调用静态方法的名称为:travel
传入该方法的参数为:array(4) { [0]=> string(7) "Beijing" [1]=> string(4) "Pair" [2]=> string(6) "London" [3]=> string(7) "Prussia" }
*/__call()和__callStatic()方法
- __toString()方法,该方法主要用于格式化打印一个对象,该方法的设计原型来源于Java。只有类中实现了__toString()方法才可以通过echo直接打印该对象。
class Person {
public $name;
private $age;
private $sex;
public static $information = "I come from the earth";
public function __construct($name="zhangsan", $age=23, $sex="male") {
$this->sex = $sex;
$this->age = $age;
$this->name = $name;
}
public function __toString() {
// TODO: Implement __toString() method.
return "My name is $this->name.<br> I am $this->age years old and I am a $this->sex.";
}
} $person = new Person();
echo $person;
/*
输出结果如下:
My name is zhangsan.
I am 23 years old and I am a male.
*/__toString()
关于PHP的魔术方法我们就简单介绍到这里,由上我们可以看出从构造方法上,PHP相比于还稍有欠缺,但PHP中有__set()和__get(),使得动态增加对象的属性字段变得更加方便,而对于Java来说要实现类似的效果,就不得不借助反射API或直接修改编译后字节码的方式实现了。Java中有反射机制,那么PHP中呢?接下来让我们来看一看PHP中的反射实现。
PHP中的反射机制
反射,直观的理解就是根据到达地找到出发地和来源。比如给出一个对象就可以找到对象所属的类、拥有哪些方法。反射可以在PHP运行状态中,扩展分析PHP程序,导出或提取出关于类、方法、属性、参数等的详细信息,这种动态获取信息以及动态调用对象方法的功能称为反射API。
class Person {
public $name;
private $age;
private $sex;
public static $information = "I come from the earth";
public function __construct($name="zhangsan", $age=23, $sex="male") {
$this->sex = $sex;
$this->age = $age;
$this->name = $name;
}
public function __sleep() {
// TODO: Implement __sleep() method
return array("name","age"); //该方法指定仅序列化对象的name和age属性
}
public function __wakeup() {
// TODO: Implement __wakeup() method.
var_dump($this->name); //打印输出对象的name属性的值
var_dump($this->sex); //打印输出对象的sex属性的值,由于sex没有被序列化,因此输出null
}
public function __destruct() {
// TODO: Implement __destruct() method.
echo "The object will be destructed";
}
public function __get($field) {
// TODO: Implement __get() method.
echo "the get method is executed<br>";
return $this->$field;
}
public function __set($key, $value) {
// TODO: Implement __set() method.
echo "The set method is executed<br>";
$this->$key = $value;
}
public function __call($name, $arguments) {
// TODO: Implement __call() method.
echo "被调用方法的名称为:$name<br>";
echo "传入该方法的参数为:", var_dump($arguments),"<br>";
}
public static function __callStatic($name, $arguments) {
// TODO: Implement __callStatic() method.
echo "被调用方法的名称为:$name<br>";
echo "传入该方法的参数为:", var_dump($arguments),"<br>";
}
public function __toString() {
// TODO: Implement __toString() method.
return "My name is $this->name.<br> I am $this->age years old and I am a $this->sex.";
}
public function sayHello($other){
echo "My name is $this->name, nice to meet you $other";
}
} $person = new Person();
$reflect = new ReflectionObject($person);
$props = $reflect->getProperties(); //获取对象的属性列表(所有属性)
foreach ($props as $prop){ //打印类中属性列表
echo $prop->getName(),"\r";
}
echo "<br>";
$methods = $reflect->getMethods(); //获取类的方法列表
foreach ($methods as $method){
echo $method->getName(),"\r";
}
echo "<br>";
//返回对象属性的关联数组
var_dump(get_object_vars($person)); echo "<br>";
//返回类的公有属性的关联数组
var_dump(get_class_vars(get_class($person))); echo "<br>";
//获取类的方法名组成的数组
var_dump(get_class_methods(get_class($person))); /*输出结果如下:
name age sex information
__construct __sleep __wakeup __destruct __get __set __call __callStatic __toString sayHello
array(1) { ["name"]=> string(8) "zhangsan" }
array(2) { ["name"]=> NULL ["information"]=> string(21) "I come from the earth" }
array(10) { [0]=> string(11) "__construct" [1]=> string(7) "__sleep" [2]=> string(8) "__wakeup" [3]=> string(10) "__destruct" [4]=> string(5) "__get" [5]=> string(5) "__set" [6]=> string(6) "__call" [7]=> string(12) "__callStatic" [8]=> string(10) "__toString" [9]=> string(8) "sayHello" }
*/
PHP中的反射应用
如上代码中介绍的是通过对象获取类的方法和属性字段,而反射不仅仅可以用于类和对象,还可以用于函数、扩展模块、异常等。既然反射可以探知类的内部结构,那么就可以利用反射机制实现插件的功能,也可以利用反射机制实现动态代理。接下来举个简单的例子看看如何通过反射机制实现动态代理。
class Person {
public $name;
private $age;
private $sex;
public static $information = "I come from the earth";
public function __construct($name="zhangsan", $age=23, $sex="male") {
$this->sex = $sex;
$this->age = $age;
$this->name = $name;
}
public function sayHello($other){
echo "My name is $this->name, nice to meet you ".implode("",$other)."<br>";
}
}
class Dynamicproxy{
private $target;
public function __construct($className) { //为动态代理传入类名称,则自动生成类对象
$this->target = new $className();
}
public function __call($name, $arguments) {
// TODO: Implement __call() method.
$reflect = new ReflectionObject($this->target);
if($method = $reflect->getMethod($name)){ //获取名称为$name的类方法
if($method->isPublic() && !$method->isAbstract()){
echo "Before the method ".$method->getName()." we should do something<br>";
$method->invoke($this->target,$arguments);
echo "After the method ".$method->getName()." we should do something<br>";
}
}
}
}
$objProxy = new Dynamicproxy("Person");
$objProxy->sayHello("Janny"); /*
输出结果如下:
Before the method sayHello we should do something
My name is zhangsan, nice to meet you Janny
After the method sayHello we should do something
*/
PHP反射实现动态代理
如上的代码中真正实现sayHello()动作的是Person类中的sayHello()方法,而Dynamicproxy仅是一个代理类,其中并没有定义sayHello()方法,而是通过__call()方法动态调用类Person的sayHello()方法。在DynamicProxy类中可以做sayHello()方法的前后拦截,并且可以动态的改变类中的方法和属性。很多时候,善用反射可以保持代码的优雅和简洁,但反射也会破坏类的封装性,因为反射可以使本不应该暴露的方法或属性被强制暴露了出来。
PHP中的异常和错误
在语言级别通常有许多错误处理模式,但这些模式往往建立在约定俗称的基础上,也就是错误都是可预知的。不同的语言对异常和错误的定义也是不一样的,在PHP中,遇到任何自身错误都会触发一个错误,而不是抛出异常。也就是说PHP一旦遇到非正常代码,通常都会触发错误,而不是抛出异常。因此如果想使用异常处理不可预料的问题,是办不到的。比如,想在文件不存在或数据库无法建立连接时触发异常,是不可行的。PHP会把这些作为错误抛出,而不是作为异常捕获。还是回到PHP的错误处理上,PHP中的错误级别大致分为以下几类:
- 最高级别的错误是语法解析错误 prase error。该错误属于语法检查阶段错误,这会导致PHP代码无法通过语法检查。
- 次之的错误是fetal error。该类错误会导致PHP流程终止,其后的代码无法继续执行。
- warning是警告级别的错误,在语法中出现很不恰当的情况才会报此错误,如参数不匹配、除数为0等。这种错误会导致不可预期的结果。
- notice是通知级别的错误,这种错误是在如变量使用前未定义、数组索引是字符时没有加引号等情况。
- 最低级别的错误是deprecated的错误,表示不推荐,不建议。如在PHP5中使用ereg系列的正则匹配函数就会报此类错误,该错误是由于使用了不推荐、过时的函数或语法造成的,不影响PHP正常流程。
function showError(){
$date = '2017-06-05';
if(ereg("([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})",$date,$regs)){ //deprecated级别错误
echo $regs[3].$regs[2].$regs[1];
}else{
echo "Invalid date format: $date";
}
if($value > 5){ //notice级别的错误
echo "It is amazing, the variable $value is not init",PHP_EOL;
}
$a = array('o'=>2,4,6,8);
echo $a[o]; //notice级别的错误
$sum = array_sum($a,3); //warning级别的错误,传入参数不正确
echo fun(); //fetal error
echo "the code is after fetal error"; //该句不执行
//echo "prase error:",$55;
}
showError();PHP中的Error
接下来我们看一看针对上边介绍的各个级别的错误PHP是如何处理的。PHP中提供了set_error_handler()函数来处理错误,当然该函数也不是可以托管所有种类的错误,如E_ERROR、E_PARSE、E_CORE_ERROR等错误,这些错误会以原始的方式显示。当然也可以通过restore_error_handler()取消接管:
function DivisionError($errno, $errmsg, $errfile, $errline){
// 获取当前错误时间
$dt = date("Y-m-d H:i:s (T)");
$errortype = array (
E_ERROR => 'Error',
E_WARNING => 'Warning',
E_PARSE => 'Parsing Error',
E_NOTICE => 'Notice',
E_CORE_ERROR => 'Core Error',
E_CORE_WARNING => 'Core Warning',
E_COMPILE_ERROR => 'Compile Error',
E_COMPILE_WARNING => 'Compile Warning',
E_USER_ERROR => 'User Error',
E_USER_WARNING => 'User Warning',
E_USER_NOTICE => 'User Notice',
E_STRICT => 'Runtime Notice',
E_RECOVERABLE_ERROR => 'Catchable Fatal Error'
); $user_errors = array(E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE);
$err = "错误时间".$dt."<br>";
$err .= "错误等级".$errno ."<br>";
$err .= "错误等级名称" . $errortype[$errno] . "<br>";
$err .= "错误消息" . $errmsg . "<br>";
$err .= "错误发生所在文件" . $errfile . "<br>";
$err .= "错误所在行" . $errline . "<br>";
echo $err;
}
set_error_handler("DivisionError"); //当抛出错误时,直接交给DivisionError处理 function showError(){
$date = '2017-06-05';
if(ereg("([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})",$date,$regs)){ //deprecated级别错误
echo $regs[1].$regs[2].$regs[3];
}else{
echo "Invalid date format: $date";
}
if($value > 5){ //notice级别的错误
echo "It is amazing, the variable $value is not init",PHP_EOL;
}
$a = array('o'=>2,4,6,8);
echo $a[o]; //notice级别的错误
$sum = array_sum($a,3);
echo fun(); //fetal error
echo "the code is after fetal error"; //该句不执行
//echo "prase error:",$55;
}
showError();
PHP错误处理
如上这种“曲折迂回”的处理方式也存在问题:必须依靠程序员自己来掌控对异常的处理,对于异常高发区、敏感区,如果处理不好就会出现业务数据不一致的问题,但是优点就是可以获得程序运行的上下文信息,以进行针对性补救。
对于代码中存在的异常,需要认为的进行抛出,接下来我们通过自定义一个异常类来处理抛出的异常,
class DivisionException extends Exception{ //自定义异常处理类 public function __toString(){ //覆写父类的__toString(),规定输出格式
$errorMessage = '错误发生于文件'.$this->getFile().'第'.$this->getLine().'行<br>'
.'错误原因为'.$this->getMessage();
return $errorMessage;
}
}
class Calculate {
private $num1;
private $num2;
private $operater; public function __construct($num1, $num2, $operater) {
$this->operater = $operater;
$this->num1 = $num1;
$this->num2 = $num2;
} public function getResult() {
try {
if ($this->operater == "+") {
return $this->num1 + $this->num2;
} else if ($this->operater == "-") {
return $this->num1 - $this->num2;
} else if ($this->operater == "*") {
return $this->num1 * $this->num2;
} else {
if($this->num2 == 0){ //如果除数为0则抛出异常
throw new DivisionException("除数不能为0");
}
return $this->num1 / $this->num2;
}
} catch (DivisionException $exception){
echo $exception;
}
}
}
$calculate = new Calculate(10,0,"/");
echo $calculate->getResult();
/**
输出结果如下:
错误发生于文件/Users/zhangshilei/PhpstormProjects/untitled/demo/Person.php第98行
错误原因为除数不能为0
*/
PHP自定义异常处理类
初体验就为大家介绍到这里吧,以后有机会在深入的去了解PHP函数、PHP与网络、PHP与数据库等等的内容吧。
PHP初体验的更多相关文章
- .NET平台开源项目速览(15)文档数据库RavenDB-介绍与初体验
不知不觉,“.NET平台开源项目速览“系列文章已经15篇了,每一篇都非常受欢迎,可能技术水平不高,但足够入门了.虽然工作很忙,但还是会抽空把自己知道的,已经平时遇到的好的开源项目分享出来.今天就给大家 ...
- Xamarin+Prism开发详解四:简单Mac OS 虚拟机安装方法与Visual Studio for Mac 初体验
Mac OS 虚拟机安装方法 最近把自己的电脑升级了一下SSD固态硬盘,总算是有容量安装Mac 虚拟机了!经过心碎的安装探索,尝试了国内外的各种安装方法,最后在youtube上找到了一个好方法. 简单 ...
- Spring之初体验
Spring之初体验 Spring是一个轻量级的Java Web开发框架,以IoC(Inverse of Control 控制反转)和 ...
- Xamarin.iOS开发初体验
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKwAAAA+CAIAAAA5/WfHAAAJrklEQVR4nO2c/VdTRxrH+wfdU84pW0
- 【腾讯Bugly干货分享】基于 Webpack & Vue & Vue-Router 的 SPA 初体验
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57d13a57132ff21c38110186 导语 最近这几年的前端圈子,由于 ...
- 【Knockout.js 学习体验之旅】(1)ko初体验
前言 什么,你现在还在看knockout.js?这货都已经落后主流一千年了!赶紧去学Angular.React啊,再不赶紧的话,他们也要变out了哦.身旁的90后小伙伴,嘴里还塞着山东的狗不理大蒜包, ...
- 在同一个硬盘上安装多个 Linux 发行版及 Fedora 21 、Fedora 22 初体验
在同一个硬盘上安装多个 Linux 发行版 以前对多个 Linux 发行版的折腾主要是在虚拟机上完成.我的桌面电脑性能比较强大,玩玩虚拟机没啥问题,但是笔记本电脑就不行了.要在我的笔记本电脑上折腾多个 ...
- 百度EChart3初体验
由于项目需要在首页搞一个订单数量的走势图,经过多方查找,体验,感觉ECharts不错,封装的很细,我们只需要看自己需要那种类型的图表,搞定好自己的json数据就OK.至于说如何体现出来,官网的教程很详 ...
- Python导出Excel为Lua/Json/Xml实例教程(二):xlrd初体验
Python导出Excel为Lua/Json/Xml实例教程(二):xlrd初体验 相关链接: Python导出Excel为Lua/Json/Xml实例教程(一):初识Python Python导出E ...
- Docker初体验
## Docker初体验 安装 因为我用的是mac,所以安装很简单,下载dmg下来之后拖拽安装即可完成. 需要注意的就是由于之前的docker是基于linux开发,不支持mac,所以就出现了docke ...
随机推荐
- lua 模块
lua 模块 概述 lua 模块类似于封装库 将相应功能封装为一个模块, 可以按照面向对象中的类定义去理解和使用 使用 模块文件示例程序 mod = {} mod.constant = "模 ...
- php 启动过程 - reqeust RSHUTDOWN 过程
php 启动过程 - reqeust RSHUTDOWN 过程 概述 request RSHUTDOWN 过程在请求结束后调用 调用触发 同 request RINIT 过程一样, 先是用 apach ...
- Google Code Jam 2016 Round 1B Problem C. Technobabble
题目链接:https://code.google.com/codejam/contest/11254486/dashboard#s=p2 大意是教授的学生每个人在纸条上写一个自己的topic,每个to ...
- iOS·官方文档译文框架源码注解
导语
- java接收数据接口
1.数据接收接口: 这个可以考虑最简单的Servlet方法,而且效率较高: import java.io.PrintWriter;import java.text.SimpleDateFormat;i ...
- 使用Spigot搭建MineCraft服务器
MineCraft 这种游戏还是人多了好玩,以前的服务器放在同学的Windows电脑上,每次我们想玩的时候就让让去打开,但是总是有一些原因,想玩的时候服务器没开着,不想玩的时候服务器空开着费电.昨天一 ...
- php头像上传预览
php头像上传带预览: 说道上传图片,大家并不陌生,不过,在以后开发的项目中,可能并不会让你使用提交刷新页面式的上传图片,比如上传头像,按照常理,肯定是在相册选择照片之后,确认上传,而肯定不会通过fo ...
- VMWare下ubuntu无法全屏的问题解决
遇到的情况: 在VMWare中,安装ubuntu 最新版操作系统(16.04).运行该系统,发现ubuntu系统在虚拟机中,只能居中显示,全屏也只能占一半显示屏幕.怎么看,怎么不舒服. 分析问题: 一 ...
- javascript原生方法实现extend
var extend = (function () { for(var p in {toString:null}){ //检查当前浏览器是否支持forin循环去遍历出一个不可枚举的属性,比如toStr ...
- Linux下Tomcat进行远程调试
1.更改tomcat远程调试端口(可以使用默认端口不更改) 打开目录下的catalina.sh文件,找到JPDA_ADDRESS=”8000”,8000代表远程调试端口,可以更改成其他没有被占用的端口 ...