public 类的内部外部和子类都能适用
protected 类的内部和子类使用
private 只能在类的内部使用,不能被继承
__construct 构造函数 : 类会在每次创建新对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作。
__destruct 析构函数: 析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。
class MyDestructableClass {
public $name;
function __construct() {
print "In constructor\n";
$this->name = "MyDestructableClass";
}
function __destruct() {
print "Destroying " . $this->name . "\n";
}
}
$obj = new MyDestructableClass();
public __call ( string $name , array $arguments ) : mixed
public static __callStatic ( string $name , array $arguments ) : mixed
在对象中调用一个不可访问方法时,__call() 会被调用。
在静态上下文中调用一个不可访问方法时,__callStatic() 会被调用。
class MyCallClass {
static $nums = 0;
function __call( $name , $arguments ){
var_dump($name , $arguments);
}
private function stark(){
echo 'this is stark';
}
private static function myStatic(){
echo 'this is myStatic';
}
public static function __callStatic($method, $arguments) {
echo '我是要调用的不存在的静态方法名: ', $method, '
';
echo '以下是通过__callStatic方法显示的参数', '
';
var_dump($arguments);
}
}
$obj = new MyCallClass();
$obj->sex('zhangyu','30','男');
$obj->stark('stark');
echo MyCallClass::$nums;
MyCallClass::home();
MyCallClass::myStatic();
?>
public __set ( string $name , mixed $value ) : void
public __get ( string $name ) : mixed
public __isset ( string $name ) : bool
public __unset ( string $name ) : void
在给不可访问属性赋值时,__set() 会被调用。
读取不可访问属性的值时,__get() 会被调用。
当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用。
当对不可访问属性调用 unset() 时,__unset() 会被调用。
属性重载只能在对象中进行。在静态方法中,这些魔术方法将不会被调用。
class PropertyTest {
/** 被重载的数据保存在此 */
private $data = array();
/** 重载不能被用在已经定义的属性 */
public $declared = 1;
/** 只有从类外部访问这个属性时,重载才会发生 */
private $hidden = 2;
public function __set($name, $value)
{
echo "Setting '$name' to '$value'\n";
$this->data[$name] = $value;
}
public function __get($name)
{
echo "Getting '$name'\n";
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
}
$trace = debug_backtrace();
trigger_error(
'Undefined property via __get(): ' . $name .
' in ' . $trace[0]['file'] .
' on line ' . $trace[0]['line'],
E_USER_NOTICE);
return null;
}
/** PHP 5.1.0之后版本 */
public function __isset($name)
{
echo "Is '$name' set?\n";
return isset($this->data[$name]);
}
/** PHP 5.1.0之后版本 */
public function __unset($name)
{
echo "Unsetting '$name'\n";
unset($this->data[$name]);
}
/** 非魔术方法 */
public function getHidden()
{
return $this->hidden;
}
}
echo "\n"
;
$obj = new PropertyTest;
$obj->a = 1;
echo $obj->a . "\n\n";
var_dump(isset($obj->a));
unset($obj->a);
var_dump(isset($obj->a));
echo "\n";
echo $obj->declared . "\n\n";
echo "Let's experiment with the private property named 'hidden':\n";
echo "Privates are visible inside the class, so __get() not used...\n";
echo $obj->getHidden() . "\n";
echo "Privates not visible outside of class, so __get() is used...\n";
echo $obj->hidden . "\n";
?>
public __toString ( void ) : string
__toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo $obj; 应该显示些什么。此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误。
php方法中不区分大小写。
class TestClass
{
public $foo;
public function __construct($foo)
{
$this->foo = $foo;
}
public function __toString() {
return $this->foo;
}
}
$class = new TestClass('Hello');
echo $class;
__invoke ([ $... ] ) : mixed
当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。
class CallableClass
{
function __invoke($x) {
var_dump($x);
}
}
$obj = new CallableClass;
$obj(5);
var_dump(is_callable($obj));
问题:PHP框架有哪些?你用过哪些?各自的优缺点是什么?
Yaf框架:使用PHP扩展的形式写的一个PHP框架,也就是以C语言为底层编写的,性能上要比PHP代码写的框架快一个数量级。
优点:执行效率高、轻量级框架、可扩展性强
缺点:高版本兼容性差,底层代码可读性差,需要安装扩展、功能单一,开发需要编写大量的插件
Yii2框架:是一款优秀的通用Web后端框架,结构简单优雅,实用功能丰富、扩展性强、性能高是它突出的特点。
–
一. 请求周期
1.注册类文件自动加载器:Laravel通过composer进行依赖管理,并在bootstrap/autoload.php中注册了Composer Auto Loader (PSR-4),应用中类的命名空间将被映射到类文件实际路径,不再需要开发者手动导入各种类文件,而由自动加载器自行导入。因此,Laravel允许你在应用中定义的类可以自由放置在Composer Auto Loader能自动加载的任何目录下,但大多数时候还是建议放置在app目录下或app的某个子目录下。
2.创建服务容器:从 bootstrap/app.php 文件中取得 Laravel 应用实例 $app(服务容器)。
3.创建 HTTP / Console 内核:传入的请求在HTTP / Console 内核中进行预处理。HTTP 内核继承自 Illuminate\Foundation\Http\Kernel 类,其中注入了 $app 和 $router 两个实例,内核是完成应用引导、请求处理(包括通过Router转发请求等)的场所。HTTP Kernel 定义了一个 bootstrappers 数组,配置了环境变量加载、应用配置加载、错误处理,以及其他在请求被处理前需要完成的工作。
4.载入服务提供者至容器:在内核引导启动的过程中最重要的动作之一就是载入服务提供者到你的 $app (即所有的服务提供者都要挂载到服务容器下去执行),服务提供者负责引导启动框架的全部各种组件,例如数据库、队列、验证器以及路由组件。因为这些组件引导和配置了框架的各种功能,所以服务提供者是整个 Laravel 启动过程中最为重要的部分,所有的服务提供者都配置在 config/app.php 文件中的 providers 数组中。首先,所有提供者的 register 方法会被调用;一旦所有提供者注册完成,接下来,boot 方法将会被调用。
5.分发请求:一旦应用完成引导和所有服务提供者都注册完成,Request 将会移交给Router进行分发。在通过Router转发请求时,所有请求都必须先经过全局HTTP中间件栈的处理,再调度到Router并获得其回调,然后执行该回调。
6.发送响应并结束:由Response发送响应,然后由内核发出terminate,包括调用可终止的中间件(定义了terminate方法的全局HTTP中间件和路由中间件)、 $app 服务容器终止。
二. 服务容器和服务提供者
服务容器是 Laravel 管理类依赖和运行依赖注入的有力工具,在类中可通过 $this->app 来访问容器,在类之外通过 $app 来访问容器;服务提供者是 Laravel 应用程序引导启动的中心,关系到服务提供者自身、事件监听器、路由的启动运行。因为应用程序中注册的路由通过RouteServiceProvider实例来加载,而事件监听器在EventServiceProvider类中进行注册。在新创建的应用中,AppServiceProvider 文件中方法实现都是空的,这个提供者是你添加应用专属的引导和服务的最佳位置,当然,对于大型应用你可能希望创建几个服务提供者,每个都具有粒度更精细的引导。服务提供者在 config/app.php 配置文件中的providers数组中进行注册。
namespace App\Providers;
use Riak\Connection;
use Illuminate\Support\ServiceProvider;
class RiakServiceProvider extends ServiceProvider
{
/**
* 在容器中注册绑定
*
* @return void
*/
public function register()
{
$this->app->singleton(Connection::class, function ($app) {
return new Connection(config('riak'));
});
}
}
三. 依赖注入
Laravel 实现依赖注入方式有两种:自动注入和主动注册。自动注入通过参数类型提示由服务容器自动注入实现;主动注册则需开发人员通过绑定机制来实现,即绑定服务提供者或类。
1.绑定服务提供者或类:这种方式对依赖注入的实现可以非常灵活多样
use Illuminate\Support\Facades\Storage;
use App\Http\Controllers\PhotoController;
use App\Http\Controllers\VideoController;
use Illuminate\Contracts\Filesystem\Filesystem;
$this->app->when(PhotoController::class)
->needs(Filesystem::class)
->give(function () {
return Storage::disk('local');
});
$this->app->when(VideoController::class)
->needs(Filesystem::class)
->give(function () {
return Storage::disk('s3');
});
2.参数类型声明:通过对类的构造器参数类型、类的方法参数类型、闭包的参数类型给出提示来实现。
namespace App\Http\Controllers;
use App\Users\Repository as UserRepository;
class UserController extends Controller
{
/**
* user repository 实例。
*/
protected $users;
/**
* 控制器构造方法。
*
* @param UserRepository $users
* @return void
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
}
/**
* 储存一个新用户。
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
$name = $request->input('name');
//
}
}
3.路由参数依赖:下边的示例使用 Illuminate\Http\Request 类型提示的同时还获取到路由参数id
你的路由可能是这样定义的:
Route::put('user/{id}', 'UserController@update');
而控制器对路由参数id的依赖却可能是这样实现的:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class UserController extends Controller
{
/**
* 更新指定的用户。
*
* @param Request $request
* @param string $id
* @return Response
*/
public function update(Request $request, $id)
{
//
}
}
工厂方法或者类生成对象,而不是在代码里直接new。
1.简单工厂模式:把对象的创建和使用的过程分开,这样既减少了代码的重复使用,也方便维护。
2.工厂方法模式:主要用于限制类的公用方法。
3.抽象工厂模式
4.静态工厂方法
使某个类的对象仅允许创建一个。
class Singleton{
//私有属性,用于保存实例
private static $instance;
//构造方法私有化,防止外部创建实例
private function __construct(){}
//公有属性,用于测试
public $a;
//公有方法,用于获取实例
public static function getInstance(){
//判断实例有无创建,没有的话创建实例并返回,有的话直接返回
if(!(self::$instance instanceof self)){
self::$instance = new self();
}
return self::$instance;
}
//克隆方法私有化,防止复制实例
private function __clone(){}
}
全局共享和交换对象。
//注册树
class Register{
protected static $objects;
public static function set($alias,$object){
self::$objects[$alias]=$object;
}
public static function get($alias){
return self::$objects[$alias];
}
public static function _unset($alias){
unset(self::$objects[$alias]);
}
}
Register::set('rand',RandFactory::factory());
$object=Register::get('rand');
print_r($object);
适配器模式,可以将截然不同的函数接口封装成统一的Api
冒泡排序只会操作相邻的两个数据。每次冒泡操作都会对相邻的两个元素进行比较,看是否满足大小关系要求。如果不满足就让它俩互换。一次冒泡会让至少一个元素移动到它应该在的位置,重复 n 次,就完成了 n 个数据的排序工作。
// 定义一个随机的数组
$a = array(1,2,3,3,4,8,9 );
// 第一层可以理解为从数组中键为0开始循环到最后一个
for ($i = 0; $i < count($a) ; $i++) {
// 第二层为从$i+1的地方循环到数组最后
for ($j = $i+1; $j < count($a); $j++) {
// 比较数组中两个相邻值的大小
if ($a[$i] > $a[$j]) {
$tem = $a[$i]; // 这里临时变量,存贮$i的值
$a[$i] = $a[$j]; // 第一次更换位置
$a[$j] = $tem; // 完成位置互换
}
}
}
我们将数组中的数据分为两个区间,已排序区间和未排序区间。初始已排序区间只有一个元素,就是数组的第一个元素。插入算法的核心思想是取未排序区间中的元素,在已排序区间中找到合适的插入位置将其插入,并保证已排序区间数据一直有序。重复这个过程,直到未排序区间中元素为空,算法结束。
function insertSort($arr) {
$len=count($arr);
//这个元素 就是从第二个元素开始,到最后一个元素都是这个需要排序的元素
for($i=1;$i<$len; $i++) {
$tmp = $arr[$i];
//内层循环控制,比较并插入
for($j=$i-1;$j>=0;$j--) {
if($tmp < $arr[$j]) {
//发现插入的元素要小,交换位置,将后边的元素与前面的元素互换
$arr[$j+1] = $arr[$j];
$arr[$j] = $tmp;
} else {
//如果碰到不需要移动的元素,由于是已经排序好是数组,则前面的就不需要再次比较了。
break;
}
}
}
return $arr;
}
快排使用的也是分治思想,区别于归并排序是因为它会设置一个pivot(分区点),我们遍历 p 到 r 之间的数据,将小于 pivot 的放到左边,将大于 pivot 的放到右边,将 pivot 放到中间。经过这一步骤之后,数组 p 到 r 之间的数据就被分成了三个部分,前面 p 到 q-1 之间都是小于 pivot 的,中间是 pivot,后面的 q+1 到 r 之间是大于 pivot 的。有点像数据结构中的二叉树,左小右大,有序排列。
/**
* @param $array
* @return array 要排序的数组
*/
function quick_sort($array)
{
// 判断是否需要运行,因下面已拿出一个中间值,这里<=1
if (count($array) <= 1) {
return $array;
}
$middle = $array[0]; // 中间值
$left = array(); // 接收小于中间值
$right = array();// 接收大于中间值
// 循环比较
for ($i=1; $i < count($array); $i++) {
if ($middle < $array[$i]) {
// 大于中间值
$right[] = $array[$i];
} else {
// 小于中间值
$left[] = $array[$i];
}
}
// 递归排序划分好的2边
$left = quick_sort($left);
$right = quick_sort($right);
// 合并排序后的数据,别忘了合并中间值
return array_merge($left, array($middle), $right);
}