[PHP] 介紹 htmlentities() 函式

htmlentities()  函式的一個主要用意,就是在將使用者輸入的資料,在傳送出去時,將它編碼成安全的字串,以免被惡意值入不必要的程式,也就是俗稱的 Sanitize Input (HTML 的部份)。

例如以下範例程式:

<?php
$input = "<p><script>alert('這是警告訊息!');</script></p>";
echo htmlentities($input);
echo "\n";
echo htmlentities($input, ENT_QUOTES, 'UTF-8');
echo "\n";

在後端會取得如下的資料:

htmlentities() 的輸出結果

有沒有發現第一行跟第二行不一樣,在於單引號在第一行並沒有被正確的編碼;
要使用第二行的方式才行。

原因是 htmlentities() 在預設上,是不會將單引號編碼的,而要加上第二個參數 ENT_QUOTES 才行。
在 PHP 的官網有說明 htmlentities(),第二個參數預設是 ENT_COMPAT,只會將雙引號編碼,而單引號不會。

所以以後在傳送有 html 的資料的時候,務必使用:
htmlentities($input, ENT_QUOTES, ‘UTF-8’);

[Laravel] laravel filesystem 使用 public 的 storage

在 config/filesystems.php 檔案中,將:

‘default’ => ‘local’,
改成
‘default’ => ‘public‘,

代表將會把上傳的圖片,存到 storage/app/public 資料夾中。

然後要再執行以下指令來建立symbolic link:

# cd ~/path/project_folder
# ln -s ../storage/app/public ./public/storage

就可以在網頁中使用以下語法,來取得路徑:

<img src=”{{ asset(‘storage/file.jpg’) }}”>

[Laravel] ClearCompiledCommand.php

在執行 $ composer update 時,若遇到以下錯誤訊息:

[RuntimeException]                                                           
  Error Output: PHP Fatal error:  Call to undefined method Illuminate\Foundation\Application::getCachedCompilePath() in /.../path/.../vendor/laravel/framework/src/Illuminate/Foundation/Console/ClearCompiledCommand.php on line 28

vendor/compiled.php 檔案移除,再重新執行 $ composer update 即可。

[Laravel] use php artisan generate

參考:Laravel-4-Generators

// 建立tags資料表,欄位有name,型別用string代表VARCHAR
$ php artisan generate:migration create_tags_table --fields="name:string"

// 將資料表s_deposit更換成另一個名稱。使用php artisan migrate:make單純建立一個migrate檔
$ php artisan migrate:make rename_s_deposit_table

// 建立lessons與tags的多對多的關聯資料表
$ php artisan generate:pivot lessons tags

// 執行尚未執行的migrate檔
$ php artisan migrate

// 替tags資料表建立seed檔:準備建立假資料
$ php artisan generate:seed tags

// 重新執行所有的migrate檔及seed檔
$ php artisan migrate:refresh --seed

// 建立產生資料表的migrate檔
$ php artisan generate:migration create_app_version_table

// 建立controller檔
$ php artisan controller:make TagsController

// 建立一個將deposit欄位新增至users資料表的migration檔
$ php artisan generate:migration add_deposit_to_users_table

[Laravel] Basic Authentication

1. update app/controllers/LessonsController.php

<?php
use Acme\Transformers\LessonTransformer;

class LessonsController extends ApiController {
    
    /**
     * @var My\Transformers\LessonTransformer
     */
    protected $lessonTransformer;
    
    function __construct(LessonTransformer $lessonTransformer)
    {
        $this->lessonTransformer = $lessonTransformer;
        
        $this->beforeFilter('auth.basic', ['on' => 'post']);
        // or use this for multi http verb to auth.basic
        // $this->beforeFilter('auth.basic', array('on' => array('post','get')) );
    }
    
    
  /**
   * Display a listing of the resource.
   *
   * @return Response
   */
  public function index()
  {
    // 1. All is bad
    // 2. No way to attach meta data
    // 3. Linking db structure to the API output
    // 4. No way to signal headers/response codes
    
    //return Lesson::all();
  
    $lessons = Lesson::all();
    //return Response::json([
      //'data' => $lessons->toArray()
    //], 200);
    //return Response::json([
      //'data' => $this->transformCollection($lessons)
    //], 200);
    //return Response::json([
      //'data' => $this->lessonTransformer->transformCollection($lessons->all())
    //], 200);
    return $this->respond([
      'data' => $this->lessonTransformer->transformCollection($lessons->all())
    ]);
  }


  /**
   * Show the form for creating a new resource.
   *
   * @return Response
   */
  public function create()
  {
    //
  }


  /**
   * Store a newly created resource in storage.
   *
   * @return Response
   */
  public function store()
  {
    if( ! Input::get('title') or ! Input::get('body') ){
        return $this->setStatusCode(422)
                    ->respondWithError('Parameters failed validation for a lesson.');
    }
    
    Lesson::create(Input::all());
    
    return $this->respondCreated('Lesson successfully created.');
  }


  /**
   * Display the specified resource.
   *
   * @param  int  $id
   * @return Response
   */
  public function show($id)
  {
    $lesson = Lesson::find($id);
    if( ! $lesson)
    {
        return $this->respondNotFound('Lesson does not exist.');
        
      //return Response::json([
        //'error' => [
          //'message' => 'Lesson does not exist'
        //]
      //], 404);
    }
    
    return $this->respond([
        'data' => $this->lessonTransformer->transform($lesson)
        //'data' => $this->transform($lesson->toArray())
    ]);
  }


  /**
   * Show the form for editing the specified resource.
   *
   * @param  int  $id
   * @return Response
   */
  public function edit($id)
  {
    //
  }


  /**
   * Update the specified resource in storage.
   *
   * @param  int  $id
   * @return Response
   */
  public function update($id)
  {
    //
  }


  /**
   * Remove the specified resource from storage.
   *
   * @param  int  $id
   * @return Response
   */
  public function destroy($id)
  {
    //
  }


}

2. update app/controllers/ApiController.php

<?php

use Illuminate\Http\Response as IlluminateResponse;

class ApiController extends BaseController{
    
    /**
     * getStatusCode function.
     * 
     * @access public
     * @return void
     */
    public function getStatusCode()
    {
        return $this->statusCode;
    }
    
    /**
     * setStatusCode function.
     * 
     * @access public
     * @param mixed $statusCode
     * @return void
     */
    public function setStatusCode($statusCode)
    {
        $this->statusCode = $statusCode;
        
        return $this;
    }
    
    /**
     * respondNotFound function.
     * 
     * @access public
     * @param string $message (default: 'Not Found!')
     * @return void
     */
    public function respondNotFound($message = 'Not Found!')
    {
        return $this->setStatusCode(IlluminateResponse::HTTP_NOT_FOUND)->respondWithError($message);
    }
    
    /**
     * respondInternalError function.
     * 
     * @access public
     * @param string $message (default: 'Internal Error!')
     * @return void
     */
    public function respondInternalError($message = 'Internal Error!')
    {
        return $this->setStatusCode(IlluminateResponse::HTTP_INTERNAL_SERVER_ERROR)->respondWithError($message);
    }
    
    /**
     * respond function.
     * 
     * @access public
     * @param mixed $data
     * @param mixed $headers (default: [])
     * @return void
     */
    public function respond($data, $headers = [])
    {
        return Response::json($data, $this->getStatusCode(), $headers);
    }
    
    public function respondWithError($message)
    {
        return $this->respond([
            'error' => [
                'message' => $message,
                'status_code' => $this->getStatusCode()
            ]
        ]);
    }
    
    /**
     * respondCreated function.
     * 
     * @access public
     * @param mixed $message
     * @return void
     */
    public function respondCreated($message)
    {
        return $this->setStatusCode(IlluminateResponse::HTTP_CREATED)->respond([
            'message' =>  $message
        ]);
    }
    
}

[Laravel] Refactoring

1. add app/controllers/ApiController.php

<?php
class ApiController extends BaseController{
    protected $statusCode = 200;
    
    /**
     * getStatusCode function.
     * 
     * @access public
     * @return void
     */
    public function getStatusCode()
    {
        return $this--->statusCode;
    }
    
    /**
     * setStatusCode function.
     * 
     * @access public
     * @param mixed $statusCode
     * @return void
     */
    public function setStatusCode($statusCode)
    {
        $this->statusCode = $statusCode;
        
        return $this;
    }
    
    /**
     * respondNotFound function.
     * 
     * @access public
     * @param string $message (default: 'Not Found!')
     * @return void
     */
    public function respondNotFound($message = 'Not Found!')
    {
        return $this->setStatusCode(404)->respondWithError($message);
    }
    
    /**
     * respondInternalError function.
     * 
     * @access public
     * @param string $message (default: 'Internal Error!')
     * @return void
     */
    public function respondInternalError($message = 'Internal Error!')
    {
        return $this->setStatusCode(500)->respondWithError($message);
    }
    
    /**
     * respond function.
     * 
     * @access public
     * @param mixed $data
     * @param mixed $headers (default: [])
     * @return void
     */
    public function respond($data, $headers = [])
    {
        return Response::json($data, $this->getStatusCode(), $headers);
    }
    
    public function respondWithError($message)
    {
        return $this->respond([
            'error' => [
                'message' => $message,
                'status_code' => $this->getStatusCode()
            ]
        ]);
    }
}

2. update app/controllers/LessonsController.php

<?php
use Acme\Transformers\LessonTransformer;

class LessonsController extends ApiController {
    
    /**
     * @var My\Transformers\LessonTransformer
     */
    protected $lessonTransformer;
    
    function __construct(LessonTransformer $lessonTransformer)
    {
        $this->lessonTransformer = $lessonTransformer;
    }
    
    
  /**
   * Display a listing of the resource.
   *
   * @return Response
   */
  public function index()
  {
    // 1. All is bad
    // 2. No way to attach meta data
    // 3. Linking db structure to the API output
    // 4. No way to signal headers/response codes
    
    //return Lesson::all();
  
    $lessons = Lesson::all();
    //return Response::json([
      //'data' => $lessons->toArray()
    //], 200);
    //return Response::json([
      //'data' => $this->transformCollection($lessons)
    //], 200);
    //return Response::json([
      //'data' => $this->lessonTransformer->transformCollection($lessons->all())
    //], 200);
    return $this->respond([
      'data' => $this->lessonTransformer->transformCollection($lessons->all())
    ]);
  }


  /**
   * Show the form for creating a new resource.
   *
   * @return Response
   */
  public function create()
  {
    //
  }


  /**
   * Store a newly created resource in storage.
   *
   * @return Response
   */
  public function store()
  {
    //
  }


  /**
   * Display the specified resource.
   *
   * @param  int  $id
   * @return Response
   */
  public function show($id)
  {
    $lesson = Lesson::find($id);
    if( ! $lesson)
    {
        return $this->respondNotFound('Lesson does not exist.');
        
      //return Response::json([
        //'error' => [
          //'message' => 'Lesson does not exist'
        //]
      //], 404);
    }
    
    return $this->respond([
        'data' => $this->lessonTransformer->transform($lesson)
        //'data' => $this->transform($lesson->toArray())
    ]);
  }


  /**
   * Show the form for editing the specified resource.
   *
   * @param  int  $id
   * @return Response
   */
  public function edit($id)
  {
    //
  }


  /**
   * Update the specified resource in storage.
   *
   * @param  int  $id
   * @return Response
   */
  public function update($id)
  {
    //
  }


  /**
   * Remove the specified resource from storage.
   *
   * @param  int  $id
   * @return Response
   */
  public function destroy($id)
  {
    //
  }


}

[Laravel] Extraction

1. ex: make a file app/Acme/Transformers/Transformer.php

<?php namespace Acme\Transformers;
    
abstract class Transformer{
    public function transformCollection(array $items)
    {
        return array_map([$this, 'transform'], $items);
    }
    
    public abstract function transform($item);
}

2. ex: make a file app/Acme/Transformers/LessonTransformer.php

 $lesson['title'],
            'body' => $lesson['body'],
            'active' => (boolean) $lesson['some_bool']
        ];
    }
}

3. update composer.json: add app/Acme to classmap

"autoload": {
		"classmap": [
			"app/commands",
			"app/controllers",
			"app/models",
			"app/database/migrations",
			"app/database/seeds",
			"app/tests/TestCase.php",
			
			"app/Acme"
		]
	},

4. update app/controllers/LessonsController.php

<?php
use Acme\Transformers\LessonTransformer;

class LessonsController extends \BaseController {
    
    /**
     * @var My\Transformers\LessonTransformer
     */
    protected $lessonTransformer;
    
    function __construct(LessonTransformer $lessonTransformer)
    {
        $this->lessonTransformer = $lessonTransformer;
    }

public function index()
  {
    // 1. All is bad
    // 2. No way to attach meta data
    // 3. Linking db structure to the API output
    // 4. No way to signal headers/response codes
    
    //return Lesson::all();
  
    $lessons = Lesson::all();
    //return Response::json([
      //'data' => $lessons->toArray()
    //], 200);
    //return Response::json([
      //'data' => $this->transformCollection($lessons)
    //], 200);
    return Response::json([
      'data' => $this->lessonTransformer->transformCollection($lessons->all())
    ], 200);
  }

public function show($id)
  {
    $lesson = Lesson::find($id);
    if( ! $lesson)
    {
      return Response::json([
        'error' => [
          'message' => 'Lesson does not exist'
        ]
      ], 404);
    }
    
    return Response::json([
        'data' => $this->lessonTransformer->transform($lesson)
        //'data' => $this->transform($lesson->toArray())
    ], 200);
  }

}

[Laravel] Transformations

1. update app/controllers/LessonsController.php

public function index()
{
  // 1. All is bad
  // 2. No way to attach meta data
  // 3. Linking db structure to the API output
  // 4. No way to signal headers/response codes
    
  //return Lesson::all();
  
  $lessons = Lesson::all();
  //return Response::json([
    //'data' => $lessons->toArray()
  //], 200);
  return Response::json([
    'data' => $this->transformCollection($lessons);
  ], 200);
}

public function show($id)
{
  $lesson = Lesson::find($id);
  if( ! $lesson)
  {
    return Response::json([
      'error' => [
        'message' => 'Lesson does not exist'
      ]
    ], 404);
  }
    
  return Response::json([
    'data' => $this->transform($lesson->toArray())
  ], 200);
}


public function transformCollection($lessons)
{
  return array_map([$this, 'transform'], $lessons->toArray());
}
  
public function transform($lesson)
{
    return [
        'title' => $lesson['title'],
        'body' => $lesson['body'],
        'active' => (boolean) $lesson['some_bool']
    ];
}

[Laravel] Responses and Codes

1. update app/controllers/LessonsController.php
check http status code

public function index()
{
  // 1. All is bad
  // 2. No way to attach meta data
  // 3. Linking db structure to the API output
  // 4. No way to signal headers/response codes
    
  //return Lesson::all();
  
  $lessons = Lesson::all();
  return Response::json([
    'data' => $lessons->toArray()
  ], 200);  //200 also can use 404(not found)
}

2. use curl to test

$ curl -i http://localhost:8000/api/v1/lessons
// or you can use "python -mjson.tool" to show more human readable
$ curl http://localhost:8000/api/v1/lessons | python -mjson.tool

Screen Shot 2014-05-17 at 8.31.50 AM

3. update show function to test specific id
try: http://localhost:8000/api/v1/lessons/5 or try: http://localhost:8000/api/v1/lessons/50

public function show($id)
{
  $lesson = Lesson::find($id);
  if( ! $lesson)
  {
    return Response::json([
      'error' => [
        'message' => 'Lesson does not exist'
      ]
    ], 404);
  }
    
  return Response::json([
    'data' => $lesson->toArray()
  ], 200);
}