해리의 데브로그

The PHP Practitioner 08 - Security and Configuration

|

Laracasts - The PHP Practitioner 강의를 듣고 정리한 포스팅 입니다.

1. Config.php

database/Connection.php 에는 현재 DSN, 아이디, 비밀번호가 전부 노출되어있음. 보안상의 문제가 발생할 수 있으므로, 이러한 데이터들을 따로 일괄적으로 관리하는 것이 더 적합함. => config.php 생성

  • options 은 PDO 객체를 생성할 때 지정할 수 있는 옵션으로, 에러가 발생 시, 어떠한 에러가 발생하는지를 알기 위해서는 PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION 를 사용
<?php

return [
    'database' => [
        'name' => 'mytodo',
        'username' => 'root',
        'password' => '111111',
        'connection' => 'mysql:host=localhost',
        'options' => [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
        ]
    ]
];

2. config.php 호출

bootrap.php 에서 config.php 를 호출한 후, 데이터를 Connection::make 의 인자로 넘겨준다.

<?php

$config = require 'config.php';
require 'database/Connection.php';
require 'database/QueryBuilder.php';


return new QueryBuilder(
    Connection::make($config['database'])
);

Connection.php 에서 PDO 객체의 파라미터로, 넘어온 연관배열의 값을 입력해준다.

<?php

class Connection
{
    public static function make($config)
    {
        try {
            return new PDO(
                $config['connection'].';dbname='.$config['name'],
                $config['username'],
                $config['password'],
                $config['options']
            );
        } catch (PDOException $e) {
            die($e->getMessage());
        }
    }
}

The PHP Practitioner 07 - PDO (2)

|

Laracasts - The PHP Practitioner 강의를 듣고 정리한 포스팅 입니다.

1. PDO Refactoring

앞 포스팅 코드를 기반으로 refactoring을 진행할 수 있음. 데이터베이스에 연결 & 쿼리 실행 + 데이터 갖고오기의 코드를 database라는 디렉토리 내에 각각 세분화 시킬 수 있음.

  • Connection.php - 데이터 베이스 연결
  • QueryBuilder.php - 쿼리 실행 + 데이터 갖고오기

Static method는 인스턴스를 생성하지 않고도 전역으로 접근할 수 있게 함. :: 는 static method임을 지칭함

// Connection.php
class Connection
{
    # way to make a method accessible globally without requiring instance
    public static function make()
    {
        try {
            return new PDO('mysql:host=localhost;dbname=mytodo;', 'root', '111111');
        } catch (PDOException $e) {
            die($e->getMessage());
        }
    }
}

// general method
$connection = new Connection();
$connection->make();

// static method
$Connection::make();

2. QueryBuilder.php

PDO 객체를 외부로부터 받아 생성자 메서드인 __construct의 인자로 할당시킴. 모든 데이터를 가지고오는 selectAll 함수를 QueryBuilder 의 메서드로 생성

// QueryBuilder.php
<?php

class QueryBuilder
{
    protected $pdo;

    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    }

    public function selectAll($table)
    {
        $statement = $this->pdo->prepare("select * from {$table}");
        $statement->execute();
        return $statement->fetchAll(PDO::FETCH_CLASS);
    }
}

3. Bootstrap.php

Connection.php & QueryBuilder.php는 데이터베이스를 다루는 코드로 index.php에서 해당 파일들을 require 로 호출하는 것은 선호되는 모습은 아님.

따라서 behind the scence 을 관장하는 bootstrap.php 파일을 생성하여 Connection.php와 QueryBuilder.php를 연결시키는 로직을 작성

<?php

# bootstrap: responsible for behind the scene
require 'database/Connection.php';
require 'database/QueryBuilder.php';

// option 1.
//$pdo = Connection::make();
//$query = new QueryBuilder($pdo);

// option 2.
//$query = new QueryBuilder(Connection::make());

// option 3.
return new QueryBuilder(
    Connection::make()
);

4. index.php

  • bootstrap.php
    • Connection::make : 스태틱 메서드인 make 가 호출되며 make 는 PDO 객체를 반환함.
    • bootstrap.php는 make 로 부터 반환된 PDO 객체를 바탕으로 QueryBuilder 인스턴스 객체를 반환하여 반환 값을 $query 변수에 저장
  • $query (QueryBuilder 인스턴스 객체가 저장된 변수) 에서 모든 데이터를 갖고오는 메서드 selectAll 을 호출하며, 이때 테이블명에 해당하는 todos 를 파라미터로 넘김.
  • 해당 로직의 값을 $tasks 에 저장시키며, 렌더링된 값이 index.view.php 를 통해 표시됨.
<?php

$query = require 'bootstrap.php';

$tasks = $query->selectAll('todos');

require 'index.view.php';

The PHP Practitioner 06 - PDO (1)

|

Laracasts - The PHP Practitioner 강의를 듣고 정리한 포스팅 입니다.

1. PDO

PHP Database Object의 약자로, 데이터베이스에 접근하는 공통 API를 제공하는 것을 목적으로 만들어 짐. Prepared statement를 제공하므로 SQL Injection 방어에 사용될 수 있음.

2. PDO 조작

PDO를 통해 데이터베이스에 연결하기

  • new PDO(DSN-Data Source Name, 아이디, 비밀번호)
  • 데이터베이스 연결에 실패할 경우 PDOException 을 발생시키도록 try-catch 로 예외처리가능
try {
    $pdo = new PDO('mysql:host=localhost;dbname=mytodo;', 'root', '111111');
} catch (PDOException $e) {
    die($e->getMessage());
}

데이터베이스 접근 & 조회하기: 쿼리문 작성(prepare) - 실행(execute) - 가져오기(fetch)

# SQL 실행
$statement = $pdo->prepare('select * from todos'); # SQL문 작성
$statement ->execute(); #실행

# 데이터 갖고오기
var_dump($statement->fetchAll()); #모든 데이터 갖고오기
# fetch result override: 결과를 오브젝트(제너릭 클래스의 인스턴스)로 fetch해옴 
var_dump($statement->fetchAll(PDO::FETCH_OBJ));
# 응용
$results = $statement->fetchAll(PDO::FETCH_OBJ);
var_dump($results[0]->description);

결과를 커스텀 클래스의 인스턴스에 저장하여 호출도 가능함.

  • PDO::FETCH_CLASS 사용
#index.php
require 'Task.php';

$tasks = $statement->fetchAll(PDO::FETCH_CLASS, 'Task');

var_dump($tasks[0]->foobar());

#Task.php
<?php

class Task
{
    public $description;
    public $completed;

    public function foobar()
    {
        return 'foobar';
    }

}

3. 최종 리팩토링

동작하는 부분을 함수로 만들어 function.php으로 옮긴 후 require 로 index.php에서 호출하여 사용

# index.php
<?php

require 'Task.php';
require 'function.php';

$pdo = connectToDb();
$tasks = fetchAllTasks($pdo);

require 'index.view.php';

#function.php

<?php

function connectToDb()
{
    try {
        return new PDO('mysql:host=localhost;dbname=mytodo;', 'root', '111111');
    } catch (PDOException $e) {
        die($e->getMessage());
    }
}

function fetchAllTasks($pdo)
{
    $statement = $pdo->prepare('select * from todos');
    $statement ->execute();

    //$tasks = $statement->fetchAll(PDO::FETCH_OBJ);
    return $statement->fetchAll(PDO::FETCH_CLASS, 'Task');
}

The PHP Practitioner 05 - Class

|

Laracasts - The PHP Practitioner 강의를 듣고 정리한 포스팅 입니다.

1. Class의 기본 구성

property와 method로 구성되어있음.

  • 생성자 메서드: __construct : Automatically triggered on instantiation
  • $this : 인스턴스 객체
  • $this->프로퍼티명 = 변수 : 객체의 프로퍼티에 변수(데이터) 할당
<?php

class Task {

    // property
    protected $description;
    protected $completed = false;

    // method
    public function __construct($description)
    {
        // Automatically triggered on instantiation

        // current instance(object)에
        // description(property)에 $description을 할당
        $this->description = $description;
    }

    public function complete()
    {
        $this->completed = true;
    }

    public function isComplete()
    {
        return $this->completed;
    }
}

2. 클래스 호출

객체 생성 및 프로퍼티 할당. 배열로 여러개의 인스턴스를 한꺼번에 생성할 수 도 있음.

$task = new Task('Go to the store'); // a new task object
$task-> complete(); // complete the task
var_dump($task-> isComplete());
//var_dump($task);

$tasks = [
    new Task('Go to the store'),
    new Task('Finish my screencast'),
    new Task('Clean my room')
];

var_dump($tasks);

3. 응용

HTML과 결합하여 사용할 경우

  • 프로퍼티를 protected로 정의 할 경우에는 외부로의 호출이 불가능함. => 프로퍼티를 public으로 변경
<ul>
  <?php foreach ($tasks as $task) : ?>
  <li><?= $task->description; ?></li>
  <?php endforeach ?>
</ul>

complete된 task는 <strike></strike> 옵션 적용

$tasks = [
    new Task('Go to the  store'),
    new Task('Finish my screencast'),
    new Task('Clean my room')
];

$tasks[0]->complete();
<ul>
  <?php foreach ($tasks as $task) : ?>
  <li>
    <?php if ($task->completed) : ?>
    	<strike><?= $task->description ?></strike>
    <?php else: ?>
    	<?= $task->description; ?>
    <?php endif; ?>
  </li>
  <?php endforeach ?>
</ul>

The PHP Practitioner 04 - Database(MySQL)

|

Laracasts - The PHP Practitioner 강의를 듣고 정리한 포스팅 입니다.

1. MySQL 기본 설정

설치 및 기본 셋팅 관련 정보는 링크 참조

#MySQl 설치
brew install mysql

#MySQL 기본 환경 설정
mysql_secure_installation 

#서버 실행
brew servcies start mysql
mysql.server start #background에서 실행시킬 필요가 없는 경우

#MySQL 접속
mysql -u root -p

#서버 종료
brew servcies stop mysql
mysql.server stop 

2. MySQL 관련 기초 코드

mysql> show databases;  #database list
mysql> create database mytodo; #create database
Query OK, 1 row affected (0.00 sec)

mysql> use mytodo # move to specific database
Database changed

# create schema
mysql> create table todos (
    -> description text,
    -> completed boolean
    -> );

# schema list
mysql> show tables;
+------------------+
| Tables_in_mytodo |
+------------------+
| todos            |
+------------------+
1 row in set (0.00 sec)

# schema detail
mysql> describe todos;
+-------------+------------+------+-----+---------+-------+
| Field       | Type       | Null | Key | Default | Extra |
+-------------+------------+------+-----+---------+-------+
| description | text       | YES  |     | NULL    |       |
| completed   | tinyint(1) | YES  |     | NULL    |       |
+-------------+------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

# delete schema
mysql> drop table todos;
Query OK, 0 rows affected (0.01 sec)

# Add NOT NULL options
mysql> create table todos (
    -> description text NOT NULL,
    -> completed boolean NOT NULL
    -> );
    
# Add id - PK, Auto_increment
mysql> create table todos (
    -> id integer PRIMARY KEY AUTO_INCREMENT,
    -> description text NOT NULL,
    -> completed boolean NOT NULL
    -> );

mysql> describe todos;
+-------------+------------+------+-----+---------+----------------+
| Field       | Type       | Null | Key | Default | Extra          |
+-------------+------------+------+-----+---------+----------------+
| id          | int(11)    | NO   | PRI | NULL    | auto_increment |
| description | text       | NO   |     | NULL    |                |
| completed   | tinyint(1) | NO   |     | NULL    |                |
+-------------+------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

# Table CRUD...
mysql> insert into todos (description, completed) values ('Go to the store', false);
mysql> select * from todos