zsh进阶配置 - 显示 hostname

你可以查看我在github上的开源项目 KeluLinuxKit 获得完整源码。

手上有好几台服务器,配置都是相似的,也使用zsh。每次窗口稍微一多,就不知道是在哪个服务器上了,每次hostname也是麻烦。寻思着还是想办法在zsh上显示hostname吧。然后找到了一个超强的 .zshrc 配置。原文在这:My Extravagant Zsh Prompt

我稍微做了一点点调整,将它加到 .zshrc 里去,最后是这样子的。

效果图如下:

源码如下:

function collapse_pwd {
    echo $(pwd | sed -e "s,^$HOME,~,")
}

function prompt_char {
    git branch >/dev/null 2>/dev/null && echo '±' && return
    hg root >/dev/null 2>/dev/null && echo '☿' && return
    echo 'YUKI.N > '
}

function battery_charge {
    echo `$BAT_CHARGE` 2>/dev/null
}

function virtualenv_info {
    [ $VIRTUAL_ENV ] && echo '('`basename $VIRTUAL_ENV`') '
}

function hg_prompt_info {
    hg prompt --angle-brackets "\
< on %{$fg[magenta]%}<branch>%{$reset_color%}>\
< at %{$fg[yellow]%}<tags|%{$reset_color%}, %{$fg[yellow]%}>%{$reset_color%}>\
%{$fg[green]%}<status|modified|unknown><update>%{$reset_color%}<
patches: <patches|join( → )|pre_applied(%{$fg[yellow]%})|post_applied(%{$reset_color%})|pre_unapplied(%{$fg_bold[black]%})|post_unapplied(%{$reset_color%})>>" 2>/dev/null
}

PROMPT='
%{$fg[magenta]%}%n%{$reset_color%} at %{$fg[yellow]%}%m%{$reset_color%} in %{$fg_bold[green]%}$(collapse_pwd)%{$reset_color%}$(hg_prompt_info)$(git_prompt_info)
$(virtualenv_info)$(prompt_char) '

RPROMPT='$(battery_charge)'

ZSH_THEME_GIT_PROMPT_PREFIX=" on %{$fg[magenta]%}"
ZSH_THEME_GIT_PROMPT_SUFFIX="%{$reset_color%}"
ZSH_THEME_GIT_PROMPT_DIRTY="%{$fg[green]%}!"
ZSH_THEME_GIT_PROMPT_UNTRACKED="%{$fg[green]%}?"
ZSH_THEME_GIT_PROMPT_CLEAN=""

参考资料


Laravel 资源推荐

使用 Laravel 进行开发也有两年的时间了。整理了一下平时常用的资源。

首先不得不说这个 Github 项目,整理了来自Laravel生态系统的精选资源,包括书签、包、教程、视频以及其它诸多很酷的资源。原项目有4000+的star,folk过来做了本土化。 非常的全面。基本上所有的项目我也看了一遍,发现下面几个项目最实用。记录一下。

插件包

项目案例

参考资料


php is_a 函数

这几天写了个测试用例,发现一个点记录下。Eloquent 在取出一系列数据后是个集合collection。所以在判断的时候应该判断是否为collection,而不是数组。即:

 is_a($item, 'Illuminate\Database\Eloquent\Collection')

先前没有考虑清楚,使用is_array判断。然而collection是个object而非array。

具体代码如下:

/**
 * 删除所有子关系最后删除自己
 *
 * @return bool
 * @throws \Exception
 */
public function del()
{
    $className = get_class($this);
    foreach ($className::$hasModels as $relate) {
        $relateModel = $this->$relate;
        if (is_array($relateModel) || is_a($relateModel, 'Illuminate\Database\Eloquent\Collection')) {
            foreach ($this->$relate as $item) {
                $item->delete();
            }
        } else {
            $relateModel->delete();
        }
    }

    $this->delete();
    return true;
}

事实上 instanceof 也可以实现这样的效果:

if($relateModel instanceof 'Illuminate\Database\Eloquent\Collection') {

}

参考资料


Laravel 小技巧

本文节选自50分钟学会Laravel 50个小技巧,有修改。针对Laravel 5.1 版本。

一共分为8篇

Model 篇

  1. model自动检验字段合法性

     class User extends Eloquent
     {
         public static $autoValidate = true;
         protected static $rules = [
             'email' => 'required|between:6,255|email|unique:backend_users',
             'login' => 'required|between:2,255|unique:backend_users',
             'password' => 'required:create|between:4,255|confirmed',
             'password_confirmation' => 'required_with:password|between:4,255'
         ];
        
         protected static function boot()
         {
             parent::boot();
             // You can also replace this with static::creating or static::updating
             static::saving(function ($model) {
                 if($model::$autoValidate) {
        
                     return $model->validate();
                 }
             });
         }
        
         public function validate() { }
     }
    
  2. 用trait创建通用方法

     trait CrudTrait
     {
         /**
          * 创建实例
          *
          * @author kelvinblood <admin@kelu.org>
          * @param array $array
          * @return mixed
          */
         public static function createInstance(Array $array)
         {
             $className = get_called_class();
             $newClass = new $className();
             foreach ($array as $key => $value) {
                 $newClass->$key = $value;
             }
             $newClass->save();
    
             return $className::find($newClass->uuid);
         }
     }
    
  3. uuid作为主键

      use Ramsey\Uuid\Uuid;
         
      trait UUIDModel
      {
          public $incrementing = false;
          protected static function boot()
          {
              parent::boot();
              static::creating(function ($model) {
                  $key = $model->getKeyName();
                  if(empty($model->{$key})) {
                      $model->{$key} = (string)$model->generateNewId();
                  }
              });
          }
    
          public function generateNewUuid()
          {
              return Uuid::uuid4();
          }
      }
    
  4. 简单的加减数字

    这样就可以省去读取、赋值和 save 操作了。

     $customer = Customer::find($customer_id);
     $loyalty_points = $customer->loyalty_points + 50;
     $customer->update(['loyalty_points' => $loyalty_points]);
        
     // adds one loyalty point
        
     Customer::find($customer_id)->increment('loyalty_points', 50);
     // subtracts one loyalty point
        
     Customer::find($customer_id)->decrement('loyalty_points', 50);
    
  5. 为model添加多余字段

    嗯,这个方法算是取巧,也很实用。

     function getFullNameAttribute()
     {
         return $this->first_name . ' ' . $this->last_name;
     }
    
  6. 保存model时返回关系

     public function store()
     {
         $post = new Post;
         $post->fill(Input::all());
         $post->user_id = Auth::user()->user_id;
         $post->user;
         return $post->save();
      }
    
  7. 同时新建model和migration.

      php artisan make:model Books -m
    

Eloquent ORM 篇

  1. 有条件的关联关系

     class myModel extents Model
     {
          public function category()
          {
          return $this->belongsTo('myCategoryModel', 'categories_id')->where('users_id', Auth::user()->id);
          }
     }
    
  2. 可排序的关联关系

     class Category extends Model
      {
          public function products()
          {
              return $this->hasMany('App\Product')->orderBy('name');
          }
      }        
    
  3. 仅显示关联关系存在的数据

      class Category extends Model
      {
          public function products()
          {
              return $this->hasMany('App\Product');
          }
      }
         
      public function getIndex()
      {
          $categories = Category::with('products')->has('products')->get();
          return view('categories.index', compact('categories'));
      }
    
  4. Where的语法表达式

    真是够奇葩的,呵呵ε=(・д・`*)ハァ…

     $products = Product::where('category', '=', 3)->get();
     $products = Product::where('category', 3)->get();
     $products = Product::whereCategory(3)->get();        
    
  5. Query Builder 的 havingRaw 方法

     SELECT *, COUNT(*) FROM products GROUP BY category_id HAVING count(*) > 1;
        
     DB::table('products')
         ->select('*', DB::raw('COUNT(*) as products_count'))
         ->groupBy('category_id')
         ->having('products_count', '>', 1)
         ->get();
     Product::groupBy('category_id')->havingRaw('COUNT(*) > 1')->get();
    
  6. 日期筛选

     $q->whereDate('created_at', date('Y-m-d'));
     $q->whereDay('created_at', date('d'));
     $q->whereMonth('created_at', date('m'));
     $q->whereYear('created_at', date('Y'));
    
  7. 随机取数据

     $questions = Question::orderByRaw('RAND()')->take(10)->get();        
    
  8. 只取特定字段lists

    $employees = Employee::where('branch_id', 9)->get()->lists('full_name', 'id');
    

Blade 篇

  1. 获取数组首尾的元素

     //hide all but the first item
     @foreach ($menu as $item)
     <div @if ($item != reset($menu)) class="hidden" @endif>
     <h2>\{\{ $item->title }}</h2>
          </div>
     @endforeach
    
      //apply css to last item only
      @foreach ($menu as $item)
     <div @if ($item == end($menu)) class="no_margin" @endif> 
     <h2>\{\{ $item->title }}</h2>
      </div>
     @endforeach
    
  2. 自定义错误页面

     <?php 
     namespace App\Exceptions; 
     use Exception; 
     use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; 
     use Symfony\Component\Debug\ExceptionHandler as SymfonyDisplayer; 
     class Handler extends ExceptionHandler { 
         protected function convertExceptionToResponse(Exception $e) {
             $debug = config('app.debug', false); 
             if($debug) { 
                 return (new SymfonyDisplayer($debug))->createResponse($e);
             }
        
             return response()->view('errors.default', ['exception' => $e], 500);
         }
     }
    

集合篇

  1. 集合筛选

     $customers = Customer::all();
     $us_customers = $customers->filter(function ($customer) {
         return $customer->country == 'United States';
     });
        
     $non_uk_customers = $customers->reject(function ($customer) {
         return $customer->country == 'United Kingdom';
     });
    
  2. find,where

     $collection = App\Person::find([1, 2, 3]);
     $collection = App\Person::all();
     $programmers = $collection->where('type', 'programmer');
     $critic = $collection->where('type', 'critic');
     $engineer = $collection->where('type', 'engineer');
    
  3. closures排序

     $collection = collect([
         ['name' => 'Desk'],
         ['name' => 'Chair'],
         ['name' => 'Bookcase']
     ]);
    
     $sorted = $collection->sortBy(function ($product, $key) {
         return array_search($product['name'], [1 => 'Bookcase', 2 => 'Desk', 3 => 'Chair']);
     });
    
  4. 提取数组的值作为键

     $library = $books->keyBy('title');
        
     [
         'Lean Startup' => ['title' => 'Lean Startup', 'price' => 10],
         'The One Thing' => ['title' => 'The One Thing', 'price' => 15],
         'Laravel: Code Bright' => ['title' => 'Laravel: Code Bright', 'price' => 20],
         'The 4-Hour Work Week' => ['title' => 'The 4-Hour Work Week', 'price' => 5],
     ]
    
  5. collection unions

    没明白。

     // the point is to actually combine results from different models
     $programmers = \App\Person::where('type', 'programmer')->get();
     $critic = \App\Person::where('type', 'critic')->get();
     $engineer = \App\Person::where('type', 'engineer')->get();
    
     $collection = new Collection;
    
     $all = $collection->merge($programmers)->merge($critic)->merge($engineer);
    
  6. collection lookaheads

    没明白。

     $collection = collect([1 => 11, 5 => 13, 12 => 14, 21 => 15])->getCachingIterator();
     foreach ($collection as $key => $value) {
         dump($collection->current() . ':' . $collection->getInnerIterator()->current());
     }
    

中间件/服务篇

  1. 共享cookies

     // app/Http/Middleware/EncryptCookies.php
     protected $except = [
         'shared_cookie'
        
     ];
        
     Cookie::queue('shared_cookie', 'my_shared_value', 10080, null, '.example.com');
    
  2. 有条件的Provider

     // app/Providers/AppServiceProvider.php
     public function register()
     {
         $this->app->bind('Illuminate\Contracts\Auth\Registrar', 'App\Services\Registrar');
        
         if($this->app->environment('production')) {
             $this->app->register('App\Providers\ProductionErrorHandlerServiceProvider');
         } else {
             $this->app->register('App\Providers\VerboseErrorHandlerServiceProvider');
         }
     }
    

路由篇

  1. 嵌套路由

     Route::group(['prefix' => 'account', 'as' => 'account.'], function () {
        
         Route::get('login', ['as' => 'login', 'uses' => 'AccountController@getLogin']);
         Route::get('register', ['as' => 'register', 'uses' => 'AccountController@getRegister']);
        
         Route::group(['middleware' => 'auth'], function () {
             Route::get('edit', ['as' => 'edit', 'uses' => 'AccountController@getEdit']);
         });
        
     });
        
     <a href="\{\{ route('account.login') }}">Login</a>`
     <a href="\{\{ route('account.register') }}">Register</a>`
     <a href="\{\{ route('account.edit') }}">Edit Account</a>`
    
  2. 匹配所有路由

     // app/Http/routes.php
        
     Route::group(['middleware' => 'auth'], function () {
         Route::get('{view}', function ($view) {
             try {
                 return view($view);
             } catch (\Exception $e) {
                 abort(404);
             }
         })->where('view', '.*');
     });
    

其他篇

  1. Model支持多语言

     // database/migrations/create_articles_table.php
     public function up()
     {
       Schema::create('articles', function (Blueprint $table) {
           $table->increments('id');
           $table->boolean('online');
           $table->timestamps();
       });
     }
    
     //database/migrations/create_articles_table.php
     public function up()
     {
       $table->increments('id');
       $table->integer('article_id')->unsigned();
       $table->string('locale')->index();
       $table->string('name');
       $table->text('text');
       $table->unique(['article_id', 'locale']);
       $table->foreign('article_id')->references('id')->on('articles')->onDelete('cascade');
    
     }
    
     // app/Article.php
     class Article extends Model
     {
       use \Dimsav\Translatable\Translatable;
       public $translatedAttributes = ['name', 'text'];
     }
    
     // app/ArticleTranslation.php
     class ArticleTranslation extends Model
     {
       public $timestamps = false;
     }
    
     // app/http/routes.php
    
     Route::get('{locale}', function ($locale) {
       app()->setLocale($locale);
       $article = Article::first();
       return view('article')->with(compact('article'));
     });
    
     // resources/views/article.blade.php
    
     <h1>\{\{ $article->name }}</h1>
    
     \{\{ $article->text }}        
    
  2. 定时清理日志

     $schedule->call(function () {
         Storage::delete($logfile);
     })->weekly();
    
  3. pipeling

     $result = (new Illuminate\Pipeline\Pipeline($container)
         ->send($something)
         ->through('ClassOne', 'ClassTwo', 'ClassThree')
         ->then(function ($something) {
             return 'foo';
         });
    

测试篇

  1. 环境变量

     // phpunit.xml
        
     <php>
         <env name="APP_ENV" value="testing"/>
         <env name="CACHE_DRIVER" value="array"/>
         <env name="SESSION_DRIVER" value="array"/>
         <env name="QUEUE_DRIVER" value="sync"/>
         <env name="DB_DATABASE" value=":memory:"/>
         <env name="DB_CONNECTION" value="sqlite"/>
         <env name="TWILIO_FROM_NUMBER" value="+15005550006"/>
     </php>
        
        
     //	.env.test – add to .gitignore
     TWILIO_ACCOUNT_SID = fillmein
     TWILIO_ACCOUNT_TOKEN = fillmein
        
        
     //	access directly from your tests using helper function
     env('TWILIO_ACCOUNT_TOKEN');
        
        
      // tests/TestCase.php 
      <?php 
      class TestCase extends Illuminate\Foundation\Testing\TestCase { 
         
      /** 
      * The base URL to use while testing the application. 
      * 
      * @var string 
      */ 
      protected $baseUrl = 'http://localhost'; 
      /** 
      * Creates the application. 
      * 
      * @return \Illuminate\Foundation\Application 
      */ 
      public function createApplication() { 
          $app = require __DIR__ . '/../bootstrap/app.php';
          if(file_exists(dirname(__DIR__) . '/.env.test')) {
             Dotenv::load(dirname(__DIR__), '.env.test'); 
          } 
             
          $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
                     
          return $app;
      }   }
    
  2. 自动测试

     // gulpfile.js
    
      var elixir = require('laravel-elixir');
    
      mix.phpUnit();
    
      $ gulp tdd
    

参考资料


几个 IntelliJ IDEA 小技巧

Idea 可谓我的主力IDE,吃饭的家伙。感觉 Idea 太大了没办法面面俱到,下面就列一些常用容易遗忘的技巧。

关于 IDEA

IDEA 全称 IntelliJ IDEA,JetBrains 公司旗下产品。IntelliJ在业界被公认为最好的java开发工具之一,尤其在智能代码助手、代码自动提示、重构、J2EE支持、Ant、JUnit、CVS整合、代码审查、 创新的GUI设计等方面的功能可以说是超常的。另外通过插件,Idea 对 PHP 和前端开发也有强力的支持。个人感觉 IntelliJ IDEA 是目前所有 IDE 中最具备沉浸式的 IDE。

JetBrains 公司下的其他产品包括:

  • PhpStorm 主要用于开发 PHP
  • RubyMine 主要用于开发 Ruby
  • PyCharm 主要用于开发 Python
  • AppCode 主要用于开发 Objective-C / Swift
  • CLion 主要用于开发 C / C++
  • WebStorm 主要用于开发 JavaScript、HTML5、CSS3 等前端技术
  • 0xDBE 主要用于开发 SQL
  • Android Studio 主要用于开发 Android(Google 基本 IntelliJ IDEA 社区版进行迭代所以也姑且算上)

常用快捷键

还有不少是与 Vim 重合的快捷键,就不列出来了。作为开发者,Vim 和 Emacs 少是会一个吧。


PHPDoc 入门

概况

这篇文章的目标是了解 PHPDoc 的使用,文末附上了两个开源项目作为参考。关于 PHPDoc 的安装办法可以查看上一篇文章

DocBlocks 以注释的形式出现,但又不仅仅是注释。它是一份行内的文档,可以让你回想起这一块代码是用来干什么的,phpdoc也根据它来生成文档。

DocBlock使用范围包括

  • 函数(function)
  • 常量
  • 接口
  • trait
  • 类内常量
  • 类属性
  • 类方法(method)
  • include require

一个标准的 DocBlock 像这样子:

 <?php
 /**
  * 这是关于这个function的总结。
  *
  * 这是关于function的 *详细描述* 。
  * 可以使用markdown样式
  *
  * @param 这是输入标签。
  *    下面的return是返回值标签。
  *
  * @return void
  */
  function myFunction($myArgument)
  {
  }

运行

$ phpdoc -d path/to/my/project -f path/to/an/additional/file -t path/to/my/output/folder

运行phpdoc时,需要指定扫描的文件夹或者文件,或者事先在配置文件 phpdoc.xml 中声明。 如果没有指定导出文件夹,则默认导出到当前目录下的output。

配置文件 phpdoc.xml 一般放在项目目录下。全局配置文件 phpdoc.dist.xml 在 phpdoc 的安装目录下,也可以修改。

模板

模板文件在 PHPDoc 的安装目录的 data\templates 目录下。

$ phpdoc -d "./src" -t "./docs/api" --template="checkstyle"  // 使用一个模板
$ phpdoc -d "./src" -t "./docs/api" --template="checkstyle,clean" // 使用多个模板
$ phpdoc -d "./src" -t "./docs/api" --template="data/templates/my_template" // 使用自定义模板

我们可以使用 XSL 或者 Twig 模版引擎来自定义模板。phpDoc 提供了很多便利的方法。希望自定义折腾的可以去试试。默认的模板clean我并不喜欢,我选了颜值最高的responsive模板。样式如下:

 phpdoc --template="responsive" -d ./app -t ./docs

与IntelliJ IDEA结合

大部分人写代码还是需要IDE的。可以帮忙做诸如自动补全、预编译报警等等很多事情,减少问题,加快开发进度。我常用的是 JetBrains 厂的 IntelliJ Idea。 PHPDoc 和 IDEA 的结合还是蛮可以的。从 PHPDoc 的官网上就可以看出来, JeyBrains 还是他们的赞助商。

一些简单的使用方法可以在 Idea 官网找到。最常用的就是在方法或者类名之前输入 /** 后按回车键,自动将常用的 Tag 给写好了。

除了写代码时的便利,导出也可以借由 IDEA 完成。

菜单栏 Run -> Edit Configurations,增加php脚本配置。在 File 栏目填写 phpdoc 的路径,例如我的 C:\my_pp\php\php-5.5.30-nts-Win32-VC11-x64\phpdoc,在 Arguments 栏目填写运行变量,例如我的 --template="responsive" -d "C:\Workspace\wechat.kelu.org\app" -t "C:\Workspace\wechat.kelu.org\docs"

Docblocks详解

Docblocks 使用 Tag 的形式来标记。

标记 用途 描述
@abstract   抽象类的变量和方法
@access public, private or protected 文档的访问、使用权限. @access private 表明这个文档是被保护的。
@author 张三 zhangsan@163.com 文档作者
@copyright 名称 时间 文档版权信息
@deprecated version 文档中被废除的方法
@deprec   同 @deprecated
@example /path/to/example 文档的外部保存的示例文件的位置。
@exception   文档中方法抛出的异常,也可参照 @throws.
@global 类型:$globalvarname 文档中的全局变量及有关的方法和函数
@ignore   忽略文档中指定的关键字
@internal   开发团队内部信息
@link URL 类似于license 但还可以通过link找到文档中的更多个详细的信息
@name 变量别名 为某个变量指定别名
@magic   phpdoc.de compatibility
@package 封装包的名称 一组相关类、函数封装的包名称
@param 如 [$username] 用户名 变量含义注释
@return 如 返回bool 函数返回结果描述,一般不用在void(空返回结果的)的函数中
@see 如 Class Login() 文件关联的任何元素(全局变量,包括,页面,类,函数,定义,方法,变量)。
@since version 记录什么时候对文档的哪些部分进行了更改
@static   记录静态类、方法
@staticvar   在类、函数中使用的静态变量
@subpackage   子版本
@throws   某一方法抛出的异常
@todo   表示文件未完成或者要完善的地方
@var type 文档中的变量及其类型
@version   文档、类、函数的版本信息

实例参考

下面参考一下两个开源项目的 DocBlocks。

  1. October

     /**
      * Administrator user model
      *
      * @package october\backend
      * @author Alexey Bobkov, Samuel Georges
      */
     class User extends UserBase
     {
         /**
          * @var string The database table used by the model.
          */
         protected $table = 'backend_users';
    
         /**
          * Validation rules
          */
         public $rules = [
             'email' => 'required|between:6,255|email|unique:backend_users',
             'login' => 'required|between:2,255|unique:backend_users',
             'password' => 'required:create|between:4,255|confirmed',
             'password_confirmation' => 'required_with:password|between:4,255'
         ];
    
         /**
          * Relations
          */
         public $belongsToMany = [
             'groups' => ['Backend\Models\UserGroup', 'table' => 'backend_users_groups']
         ];
    
         public $attachOne = [
             'avatar' => ['System\Models\File']
         ];
    
         /**
          * Purge attributes from data set.
          */
         protected $purgeable = ['password_confirmation', 'send_invite'];
    
         /**
          * @var string Login attribute
          */
         public static $loginAttribute = 'login';
    
         /**
          * @return string Returns the user's full name.
          */
         public function getFullNameAttribute()
         {
             return trim($this->first_name . ' ' . $this->last_name);
         }
    
         /**
          * Gets a code for when the user is persisted to a cookie or session which identifies the user.
          * @return string
          */
         public function getPersistCode()
         {
             // Option A: @todo config
             // return parent::getPersistCode();
    
             // Option B:
             if (!$this->persist_code) {
                 return parent::getPersistCode();
             }
    
             return $this->persist_code;
         }
         ......
     }
    
  2. BootstrapCMS

    这个项目的开发者也是 laravel 开发组成员。

     /*
      * This file is part of Bootstrap CMS.
      *
      * (c) Graham Campbell <graham@alt-three.com>
      *
      * For the full copyright and license information, please view the LICENSE
      * file that was distributed with this source code.
      */
    
     namespace GrahamCampbell\BootstrapCMS\Models\Relations;
    
     /**
      * This is the has many comments trait.
      *
      * @author Graham Campbell <graham@alt-three.com>
      */
     trait HasManyCommentsTrait
     {
         /**
          * Get the comment relation.
          *
          * @return \Illuminate\Database\Eloquent\Relations\HasOneOrMany
          */
         public function comments()
         {
             return $this->hasMany('GrahamCampbell\BootstrapCMS\Models\Comment');
         }
    
         /**
          * Delete all comments.
          *
          * @return void
          */
         public function deleteComments()
         {
             foreach ($this->comments()->get(['id']) as $comment) {
                 $comment->delete();
             }
         }
     }
    

参考资料