notadd 初始化所有modules

notadd\vendor\notadd\framework\src\Module\ModuleManager.php

function getModules

/**
 * Modules of list.
 *
 * @return \Illuminate\Support\Collection
 */
public function getModules()
{
    if ($this->modules->isEmpty()) {
        if ($this->files->isDirectory($this->getModulePath())) {
            collect($this->files->directories($this->getModulePath()))->each(function ($directory) {
                if ($this->files->exists($file = $directory . DIRECTORY_SEPARATOR . 'composer.json')) {
                    $package = new Collection(json_decode($this->files->get($file), true));
                    $identification = Arr::get($package, 'name');
                    $type = Arr::get($package, 'type');
                    if ($type == 'notadd-module' && $identification) {
                        $provider = '';
                        if ($entries = data_get($package, 'autoload.psr-4')) {
                            foreach ($entries as $namespace => $entry) {
                                $provider = $namespace . 'ModuleServiceProvider';
                            }
                        }
                        if ($this->files->exists($autoload = $directory . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php')) {
                            $this->files->requireOnce($autoload);
                        }
                        $authors = Arr::get($package, 'authors');
                        $description = Arr::get($package, 'description');
                        if (class_exists($provider)) {
                            $module = new Module($identification);
                            $module->setAuthor($authors);
                            $module->setDescription($description);
                            $module->setDirectory($directory);
                            $module->setEnabled($this->container->isInstalled() ? $this->container->make('setting')->get('module.' . $identification . '.enabled', false) : false);
                            $module->setInstalled($this->container->isInstalled() ? $this->container->make('setting')->get('module.' . $identification . '.installed', false) : false);
                            $module->setEntry($provider);
                            if (method_exists($provider, 'alias')) {
                                $module->setAlias(call_user_func([$provider, 'alias']));
                            } else {
                                $module->setAlias([$identification]);
                            }
                            method_exists($provider, 'description') && $module->setDescription(call_user_func([$provider, 'description']));
                            method_exists($provider, 'name') && $module->setName(call_user_func([$provider, 'name']));
                            method_exists($provider, 'script') && $module->setScript(call_user_func([$provider, 'script']));
                            method_exists($provider, 'stylesheet') && $module->setStylesheet(call_user_func([$provider, 'stylesheet']));
                            method_exists($provider, 'version') && $module->setVersion(call_user_func([$provider, 'version']));
                            $this->modules->put($identification, $module);
                        } else {
                            $this->unloaded->put($identification, [
                                'authors'        => $authors,
                                'description'    => $description,
                                'directory'      => $directory,
                                'identification' => $identification,
                                'provider'       => $provider,
                            ]);
                        }
                    }
                }
            });
        }
    }

    return $this->modules;
}

这个function 获取到所有的module

我想一定是系统初始化的时候初始化了每一个module 所以才能注册到module的route

notadd 查找/admin路由绑定的地方

一直想找notadd的后台时如何创建路由的,结果一直没有找到

今天总算是让我找到了

一下记录一下步骤

获取所有路由列表

php notadd route:list > D:route.txt

image.png

发现是在Notadd\Administration\Controllers\AdminController这个文件里定义的 

我打开notadd\modules\administration\src\ModuleServiceProvider.php

看到ModuleServiceProvider的boot function是这样的

/**
 * Boot service provider.
 *
 * @throws \Illuminate\Contracts\Container\BindingResolutionException
 */
public function boot()
{
    $administrator = new Administrator($this->app['events'], $this->app['router']);
    $administrator->registerPath('admin');
    $administrator->registerHandler(AdminController::class . '@handle');
    $this->administration->setAdministrator($administrator);
    $this->app->make(Dispatcher::class)->subscribe(CsrfTokenRegister::class);
    $this->app->make(Dispatcher::class)->subscribe(PermissionGroupRegister::class);
    $this->app->make(Dispatcher::class)->subscribe(PermissionModuleRegister::class);
    $this->app->make(Dispatcher::class)->subscribe(PermissionRegister::class);
    $this->app->make(Dispatcher::class)->subscribe(PermissionTypeRegister::class);
    $this->app->make(Dispatcher::class)->subscribe(RouteRegister::class);
    $this->loadTranslationsFrom(realpath(__DIR__ . '/../resources/translations'), 'administration');
    $this->loadViewsFrom(realpath(__DIR__ . '/../resources/views'), 'admin');
    $this->publishes([
        realpath(__DIR__ . '/../resources/mixes/administration/dist/assets/admin') => public_path('assets/admin'),
        realpath(__DIR__ . '/../resources/mixes/neditor')                          => public_path('assets/neditor'),
    ], 'public');
}

重点是这几行

image.png

我不知道registerPath和registerHandler是做什么用的,那么我向上追溯到了notadd\vendor\notadd\framework\src\Administration\Abstracts\Administrator.php

/**
 * Init administrator.
 *
 * @throws \InvalidArgumentException
 */
final public function init()
{
    if (is_null($this->path) || is_null($this->handler)) {
        throw new InvalidArgumentException('Handler or Path must be Setted!');
    }
    $this->router->group(['middleware' => 'web'], function () {
        $this->router->get($this->path, $this->handler);
    });
}

/**
 * Register administration handler.
 *
 * @param $handler
 */
public function registerHandler($handler)
{
    $this->handler = $handler;
}

/**
 * Register administration route path.
 *
 * @param string $path
 */
public function registerPath($path)
{
    $this->path = $path;
}

发现了这些代码,好吧 原来这个路由是在这里去绑定的······ 怪不得我开始怎么找也找不到呢

所以是吧/admin  绑定到了notadd\modules\administration\src\Controllers\AdminController.php 的 handle function

/**
 * Return index content.
 *
 * @param \Notadd\Foundation\Extension\ExtensionManager $extension
 * @param \Notadd\Foundation\Module\ModuleManager       $module
 *
 * @return \Illuminate\Contracts\View\View
 */
public function handle(ExtensionManager $extension, ModuleManager $module)
{
    $this->share('extensions', $extension->getEnabledExtensions());
    $this->share('modules', $module->getEnabledModules());
    $this->share('translations', json_encode($this->translator->fetch('zh-cn')));
    return $this->view('admin::layout');
}

所以在这些操作之前,一定有一个地方初始化了所有的module

我想找到这个地方

Laravel 事件

事件类通常被保存在 app/Events 目录下,而它们的处理程序则被保存在 app/Handlers/Events 目录下。

leokim\app\Events\LeokimTestEvent.php

<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class LeokimTestEvent
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('channel-name');
    }
}

1.创建事件

php artisan make:event LeokimTestEvent

2.创建事件handle

leokim\app\Handlers\Events\LeokimTestEventHandler.php

<?php
namespace App\Handlers\Events;
use App\Events\LeokimTestEvent;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldBeQueued;

class LeokimTestEventHandler{
    public function __construct()
    {
    }

    public function handle(LeokimTestEvent $event)
    {
        echo '<br>事件触发测试';
    }
}

3.在EventServiceProvider中注册

leokim\app\Providers\EventServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Support\Facades\Event;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider
{
    /**
     * The event listener mappings for the application.
     *
     * @var array
     */
    protected $listen = [
        'App\Events\Event' => [
            'App\Listeners\EventListener',
        ],
        'App\Events\LeokimTestEvent' => [
            'App\Handlers\Events\LeokimTestEventHandler',
        ],
    ];

    /**
     * Register any events for your application.
     *
     * @return void
     */
    public function boot()
    {
        parent::boot();

        //
    }
}

4.在controller中触发

<?php

namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Events\LeokimTestEvent;

class LeokimController extends Controller
{
    protected $leo;

    function __construct($leo)
    {
        $this->leo = $leo;
    }

    function index(){
        echo '123';
        event(new LeokimTestEvent());
    }

    function test($id){
        return $this->leo.$id;
    }
}

Laravel Contracts 学习笔记

Laravel 的 Contracts 是一组定义了框架核心服务的接口。说白了就是一组接口。使用它就是为了降低耦合性。

即便如此,是不是也有同学会搞不清楚Contracts在lavarel体系中的到底在一个什么样的位置?下面上一张自制的图,也许有地方不对,但是初学的同学可以通过它大概的,简单的解决下心中的困惑。

20170425175642777.jpg

通过这张图我们可以看到,当写好自定义的Contract接口及其实现类后,在ServiceProvider中绑定,此时服务容器已经登记上这个Contract了。之后就可以在要用到它的地方,经过服务容器解析直接使用了。

下面就详细写一下怎么具体的使用:

第一步,写一个Contract接口:

<?php

namespace App\Contracts;

interface LeokimContract
{
    public function test($id);
}


第二步,写上面Contract的实现类:

<?php

use App\Contracts\LeokimContract;

class LeokimService implements LeokimContract
{
    function test($id){
        return "ID is :".$id;
    }
}

第三步,写一个自定义的ServiceProvider:

<?php

namespace App\Providers;

use App\Http\Controllers\LeokimController;
use Illuminate\Support\ServiceProvider;

class LeokimProviders extends ServiceProvider
{
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }

    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        //简单绑定
//        $this->app->bind(LeokimController::class, function (){
//            return new LeokimController('LeoKim-Test!! ');
//        });

        //给Contracts一个别名
//        $this->app->bind('LeokimContract','APP\Contracts\LeokimContract');

        //绑定接口到实现
        $this->app->bind('App\Contracts\LeokimContract','App\Services\LeokimService');
    }
}

这里起别名的作用,是为了在使用的时候方便,不需要写完整的命名空间;绑定的作用是为了使用Contracts时,服务容器能够有线索找到它的实现类,从而解析出来。

第四步,在config\app.PHP中注册这个服务提供者:

在providers中加入这行代码即可:

 App\Providers\HelloServiceProvider::class,

第五步,可以使用了:

<?php

namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

use App\Contracts\LeokimContract;

class LeokimController extends Controller
{
    protected $leo;

//    function __construct($leo)
//    {
//        $this->leo = $leo;
//    }

    function __construct(LeokimContract $leo)
    {
        $this->leo = $leo;
    }

//    function index(){
//        echo '123';
//    }

    function test($id){
        return $this->leo->test($id);
    }
}
  1. 在构造方法中,将Contract接口引入(注入)这里参数中‘LeokimContract’就是刚才起的别名。

  2. 因为刚才已经在服务提供者中绑定了Contract和其实现方法,所以这里能够通过LeokimContract这个Contract,解析并使用其实现类中的方法test().

使用免费的ssl证书

小程序ajax请求都需要https 没办法 我只有自己弄一下

我这里要介绍的是另外一个 acme.sh 这个是用 Shell 脚本编写的,安装更容易,Let's Encrypt 那个 certbot 工具需要安装一大堆系统库以及 python 库,Python 的 pip 在国内还会有墙的问题…

安装acme.sh

curl https://get.acme.sh | sh

然后重新载入一下

source ~/.bashrc

现在就可以执行acme.sh命令了

acme.sh --issue -d ssl.leokim.cn -w /data/wwwroot/default/ssl

这个以后就生成秘钥了

放到指定文件夹下 

用阿里云创建虚拟主机的程序跑就行了

2.png

B3T8QJA1WQQDOW[FPJR0JLS.png

image.png

http://www.07net01.com/2017/01/1769454.html

======================================

昨天那个用不了 浏览器报不安全 然后今天我重新操作了一下

git clone https://github.com/letsencrypt/letsencrypt 
cd letsencrypt
./letsencrypt-auto certonly --manual -d ssl.leokim.cn

//证书在下面文件夹里
cd /etc/letsencrypt/live/ssl.leokim.cn/


//然后修改httpd配置文件 填好证书路径就好了
<VirtualHost *:443>
    ServerAdmin admin@linuxeye.com
    DocumentRoot "/data/wwwroot/default/ssl"
    ServerName ssl.leokim.cn

    SSLEngine on
    SSLCertificateFile "/etc/letsencrypt/archive/ssl.leokim.cn/fullchain1.pem"
    SSLCertificateKeyFile "/etc/letsencrypt/archive/ssl.leokim.cn/privkey1.pem"
    ErrorLog "/data/wwwlogs/ssl.leokim.cn_error_apache.log"
    CustomLog "/data/wwwlogs/ssl.leokim.cn_apache.log" common
<Directory "/data/wwwroot/default/ssl">
    SetOutputFilter DEFLATE
    Options FollowSymLinks ExecCGI
    Require all granted
    AllowOverride All
    Order allow,deny
    Allow from all
    DirectoryIndex index.html index.php
</Directory>
</VirtualHost>

使用Let‘s Encrypt为网站添加HTTPS

计算得出非连续数字分段

<?php

$array = array(10010,10011,10012,10013,10015,10016,10026,10027,10028,10029);

$range = get_num_range($array);

print_r($range);

function get_num_range($arr){

    $range = array();

    $i = 1;

    $tmp_min = $tmp_max = '';

    foreach($arr as $no){

        //如果没有此range的最小值 第一个就是最小值

        if(empty($tmp_min)){

            $tmp_min = $no;

        }

        //下一个值不存在计算出最大值

        $next_no = $no+1;

        if(!in_array($next_no,$arr) && !empty($next_no)){

            $tmp_max = $no;

            $range[] = array($tmp_min, $tmp_max);

            $tmp_min = '';

        }

    }

    return $range;

}

?>

MySQL 事务,锁

ACID

  • Atomic同一个事务里,要么都提交,要么都回滚

  • Consistency即在事务开始之前和事务结束之后,数据库的完整性约束没有被破坏

  • Isolation 并发事务间的数据是彼此隔离的

  • Durabiliy 事务提交后,所有结果务必被持久化

支持事务:InnoDB,NDBCluster, TokuDB

不支持事务:MyISAM,MEMORY

  1. 隔离性通过锁实现

  2. 原子性,一致性,持久性通过数据库的redo和undo来完成

撤销日志: UNDO LOG (现在正在活跃的事务,但还没有提交,或者还没有完成check point)

重做日志:REDO LOG (已经提交的事务)

REDO 和 UNDO 在做数据库恢复的时候的作用是相反的,

RODO:在做数据库恢复的时候,如果有事务还没有真正的提交,那么需要前滚(扫描RODO LOG检查哪些事务已经提交了,但还没有持久化到数据文件【table spacs, data table spage】里的,再把这些重做一次事务【再重新提交一次】)

UNDO: 正好是相反 找到还没有提交的事务 但是再undo log里需要进行一次回滚,就是把已经提交的事务回滚掉

在做数据库恢复的时候要做这样的一个过程

autocommit = 0|1

设置事务自动 开启|关闭

显示事务 启动 | 关闭

  1. 以START TRANSACTION/BEGIN开始事务

  2. 以COMMIT/ROLLBACK TRANSACTION结束事务

隐性事务提交

主要是DDL,DCL会引发事务的隐性提交

SQL四种语言:DDL,DML,DCL,TCL

MySQL 多列索引的生效规则

mysql中 myisam,innodb默认使用的是 Btree索引,至于btree的数据结构是怎样的都不重要,
只需要知道结果,既然是索引那这个数据结构最后是排好序;就像新华字典他的目录就是按照a,b,c..这样排好序的;
所以你在找东西的时候才快,比如你找 “中” 这个字的解释,你肯定就会定位到目录的 z 开头部分;

组合索引可以这样理解,比如(a,b,c),abc都是排好序的,在任意一段a的下面b都是排好序的,任何一段b下面c都是排好序的;

713671-20170223142607007-29450957.png

组合索引的生效原则是  从前往后依次使用生效,如果中间某个索引没有使用,那么断点前面的索引部分起作用,断点后面的索引没有起作用;
比如

where a=3 and b=45 and c=5 .... 这种三个索引顺序使用中间没有断点,全部发挥作用;
where a=3 and c=5... 这种情况下b就是断点,a发挥了效果,c没有效果
where b=3 and c=4... 这种情况下a就是断点,在a后面的索引都没有发挥作用,这种写法联合索引没有发挥任何效果;
where b=45 and a=3 and c=5 .... 这个跟第一个一样,全部发挥作用,abc只要用上了就行,跟写的顺序无关

(a,b,c) 三个列上加了联合索引(是联合索引 不是在每个列上单独加索引)

还需注意,  (a,b,c)多列索引和 (a,c,b)是不一样的,看上面的图也看得出来关系顺序是不一样的;
分析几个实际例子来加强理解;
分析句子中使用的索引情况

select * from mytable where a=3 and b=5 and c=4;
abc三个索引都在where条件里面用到了,而且都发挥了作用

select * from mytable where  c=4 and b=6 and a=3;
这条语句列出来只想说明 mysql没有那么笨,where里面的条件顺序在查询之前会被mysql自动优化,效果跟上一句一样

select * from mytable where a=3 and c=7;
a用到索引,b没有用,所以c是没有用到索引效果的

select * from mytable where a=3 and b>7 and c=3;
a用到了,b也用到了,c没有用到,这个地方b是范围值,也算断点,只不过自身用到了索引

select * from mytable where b=3 and c=4;
因为a索引没有使用,所以这里 bc都没有用上索引效果

select * from mytable where a>4 and b=7 and c=9;
a用到了  b没有使用,c没有使用

select * from mytable where a=3 order by b;
a用到了索引,b在结果排序中也用到了索引的效果,前面说了,a下面任意一段的b是排好序的

select * from mytable where a=3 order by c;
a用到了索引,但是这个地方c没有发挥排序效果,因为中间断点了,使用 explain 可以看到 filesort

select * from mytable where b=3 order by a;
b没有用到索引,排序中a也没有发挥索引效果

MySQL 索引使用笔记

用voucher表来做单表测试

  1. 创建voucher_id为主键

  2. 创建voucher_idx(voucher_id, prefix, voucher_no)

    image.png

当我查主键的时候毫无疑问 肯定是走主键索引的

image.png

当我只查找主键字段的时候 Extra显示“Using index”不回表直接使用到覆盖索引

image.png

可以看到 当查询条件里有主键的时候 查询会直接用主键查询 可能是因为主键是唯一索引 直接就定位到是最简方法

image.png

下面不用主键来做条件, 直接用voucher_idx里的字段来查找

可以看到type变成了ref 不再是有主键时的const

ref:是一种索引访问(有时也叫索引查找),它返回所有匹配某个单个值的行。然而,它可能会找到多个符合条件的行,因此它是查找和扫描的混合体…

可以看到找到了45310条记录,测试的voucher总数是319814  45310/319814≈0.15 不到30%所以还是能使用索引的

image.png

但是这就不对了 prefix是‘A10’的占了76% 还是能用到索引 是不是说明“一个值超过30%就无法使用索引”的描述? 可能是新版功能加强了吧

image.png

这个确实能证实,不是从索引左侧的列搜索用不到索引,这条语句扫描了319814行

image.png

下面这个语句 其实说明了不按索引顺序 也是能用到索引的 应该是索引优化器把where里的索引列又排了下

image.png

查了一下优化器执行的代码 好像也没有对索引重排 看来还是service层做了优化让我们能使用到索引?

image.png

我又创建了一个索引voucher_idx2,我以为搜索条件是voucher_no, prefix和 prefix,voucher_no的时候会不一样,结果都用到了voucher_idx2

image.png

image.png

image.png

这可以说明当查询条件和搜索条件都在索引内时,不按索引顺序也是能用到索引的,但是扫描了全表

image.png

image.png

但是查询列或者搜索列里有不在索引列里的列的时候 是会扫描全表的

image.png

image.png

那我把所有列都拿进来 是不是都能用到索引?? 

image.png 

可以看到,其实是走了索引,但是也是扫了全表,效率应该相当差, 需要用数据量很大的库来试一下,如果不按索引顺序来跑的效率

image.png