# README

## 주의사항

* 모든 내용은 공식 문서인 <https://www.php-fig.org/psr/>의 내용이 더 우선시 됩니다.
* PHP Standards Recommendations 한글 번역본입니다.
* 개인이 학습용도로 번역한 내용이니 일부 오역이 있을 수 있습니다.
* 일부 내용은 한글로 번역 후 어색하지 않기 위해 의역을 하였습니다.

## 번역 가이드

* Class, Method, Properties 같은 개발자라면 번역하면 어색한 문장은 원문을 그대로 사용합니다


# PSR-1-basic-coding-standard

이 섹션은 공유되는 PHP 코드 간의 높은 수준의 기술적 상호 호환성을 보장하는 데 필요한 표준 코딩 사항으로 간주되어야 하는 것을 포함합니다.

이 문서에서 핵심이 되는 단어는 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", "OPTIONAL" 입니다. 이것은 [RFC 2119](http://www.ietf.org/rfc/rfc2119.txt)에 설명 된대로 해석해야 합니다.

> 역자주: 위의 키워드는 아래의 번역문에 괄호안에 표시하였습니다

## 1. 개요

* 반드시(MUST) `<?php` 와 `<?=` 태그만을 사용해야 합니다.
* 반드시(MUST) BOM이 없는 UTF-8로만 PHP코드를 작성해야 합니다.
* 각 파일은 무언가를 선언하거나 (클래스, 함수, 상수 등) *또는* 사이드이펙트의 원인이 되는(예 : 출력 생성, .ini 설정 변경 등) 형태로 작성해야하지만(SHOULD) 둘 모두를 수행하면 안됩니다(SHOULD NOT).
* 네임스페이스와 클래스는 반드시(MUST) "autoloading"에 관한 PSR: \[[PSR-0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md), [PSR-4](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md)]을 따라 작성해야 합니다.
* Class 는 반드시(MUST) `StudlyCaps`에 따라 작성해야합니다.

  > 역자주: StudlyCaps 라는것은 단어의 첫글자가 대문자인 규칙을 말합니다. ex: DefaultClass
* Class의 상수(constants)는 반드시(MUST) 전부 대문자와 밑줄`_`만으로 작성해야합니다.
* Method는 반드시(MUST) `camelCase`로 작성해야합니다.

## 2. 파일

### 2.1. PHP Tags

PHP 코드는 반드시(MUST) 긴 `<?php ?>` 태그나 짧은 `<?= ?>` 태그를 사용해야합니다 그외의 태그를 사용해서는 안됩니다 (MUST NOT).

### 2.2. Character Encoding

PHP 코드는 반드시(MUST) BOM이 없는 UTF-8 문자열만 사용해야합니다

### 2.3. 사이드이펙트

각 파일은 새로운 무언가 (클래스, 함수, 상수 등)를 선언하고 다른 사이드이펙트를 일으키지 않거나, 사이드이펙트가 있는 로직를 실행하지만(SHOULD) 둘 다 수행하면 안됩니다(SHOULD NOT).

"사이드이펙트"는 클래스, 함수, 상수 등을 선언하는 것과 직접적으로 관련이없는 논리를 파일에 단순히 포함하는 것부터 실행하는 것 전부를 의미합니다.

> 역자주: 일반적으로 프로그래밍에서 사용되는 사이드이펙트의 의미와 이 문장에서 사용되는 사이드이펙트의 의미는 약간의 차이가 있습니다

"사이드이펙트"에는 Output 생성, 명시적인 "require"또는 "include"의 사용, 외부 서비스 연결, ini 설정 변경, 오류 또는 예외 발생, 전역 변수 또는 정적 변수 변경, 파일의 읽기 또는 쓰기 등이 있습니다.

다음은 선언과 사이드이펙트가 모두 포함 된 파일의 예입니다.

예제:

```php
<?php
// side effect: change ini settings
ini_set('error_reporting', E_ALL);

// side effect: loads a file
include "file.php";

// side effect: generates output
echo "<html>\n";

// declaration
function foo()
{
    // function body
}
```

다음 예제는 사이드이펙트가 없이 작성된 파일입니다.

예제:

```php
<?php
// declaration
function foo()
{
    // function body
}

// conditional declaration is *not* a side effect
if (! function_exists('bar')) {
    function bar()
    {
        // function body
    }
}
```

## 3. Namespace 와 Class

네임 스페이스와 Class는 반드시(MUST) \[자동 로딩 (autoloading)] PSR \[[PSR-0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md), [PSR-4](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md)]을 따라야합니다.

이것은 클래스가 각각의 개별 파일에 작성되어있으며, 공급자(vendor)의 이름으로 시작하는 네임스페이스를 사용했다는 것을 의미합니다.

클래스 이름은 반드시(MUST) `StudlyCaps` 규칙으로 작성되어야 합니다.

PHP 5.3 및 이후 버전 용으로 작성된 코드는 정식 네임 스페이스를 사용해야합니다.

예제:

```php
<?php
// PHP 5.3 and later:
namespace Vendor\Model;

class Foo
{
}
```

5.2.x 용으로 작성된 코드에서 네임 스페이스와 유사한 규칙을 사용하기 위해 클래스 이름에 `Vendor_` 접두어를 붙입니다(SHOULD).

```php
<?php
// PHP 5.2.x and earlier:
class Vendor_Model_Foo
{
}
```

## 4. Class의 상수(Constants), Properties, Methods

"Class"라는 용어는 모든 Class, Interface 및 Trait을 의미합니다.

### 4.1. 상수(Constants)

클래스 상수는 모두 대문자 또는 밑줄`_`로 선언해야합니다 (MUST).

예제:

```php
<?php
namespace Vendor\Model;

class Foo
{
    const VERSION = '1.0';
    const DATE_APPROVED = '2012-06-01';
}
```

### 4.2. Properties

이 가이드는 의도적으로 `$StudlyCaps` , `$camelCase` 또는 `$under_score` 같은 형태로 property 이름을 권장하는 것을 피하고 있습니다.

Properties는 어떤 규칙이 사용 되든 상관없지만 합리적인 범위 내에서 일관되게 적용되어야 합니다(SHOULD). 이 범위는 공급자 수준(vendor-level), 패키지 수준(package-level), Class 수준(class-level) 또는 Method(method-level) 수준 일 수 있습니다.

### 4.3. Methods

Method의 이름은 반드시(MUST) `camelCase`로 작성해야 합니다.


# PSR-3-logger-interface

이 문서는 로깅 라이브러리에 대한 공통 인터페이스를 설명합니다.

가장 중요한 목표는 라이브러리가 `Psr\Log\LoggerInterface` 객체를 받아서 간단하고 보편적인 방법으로 로그를 쓸 수있게하는 것입니다. 커스텀 할 필요가 있는 프레임워크와 CMS는 자체적인 목적을 위해 인터페이스를 확장 할 수 있지만 이 문서와 호환 가능해야합니다 (SHOULD). 이렇게하면 프로그램에서 사용하는 타사 라이브러리가 프로그램의 중앙 집중식 로그에 쓸 수 있습니다.

> 역자주: 타 벤더의 로그를 중앙집중형으로 모아서 남길 수 있다는 의미

이 문서에서 핵심이 되는 단어는 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", "OPTIONAL" 입니다. 이것은 [RFC 2119](http://tools.ietf.org/html/rfc2119)에 설명 된대로 해석해야 합니다.

> 역자주: 위의 키워드는 아래의 번역문에 괄호안에 표시하였습니다

이 문서에서 `구현자(implementor)` 라는 단어는 로그 관련 라이브러리 또는 프레임워크에서 `LoggerInterface` 를 구현하는 누군가로 해석되어야합니다. 로거 사용자는 `사용자(user)`라고합니다.

## 1. 명세서

### 1.1 기본

* `LoggerInterface`는 로그를 8 개의 [RFC 5424](http://tools.ietf.org/html/rfc5424) 레벨 (debug, info, notice, warning, error, critical, alert, emergency)에 맞게 쓸 수 있는 8 가지 method를 제공해야 합니다.
* 9 번째 method 인`log`는 첫 번째 인자로 로그 수준(level)을 입력받습니다. 로그 수준 상수 중 하나를 사용하여 이 메서드를 호출하면 수준별 메서드를 호출 할 때와 동일한 결과를 가져야합니다. 알지 못하는 수준(level)이거나 이 스펙에 정의되지 않은 수준(level)로 이 메소드를 호출하면 반드시 `Psr\Log\InvalidArgumentException` 을 던져야합니다 (MUST). 사용자는 현재 구현이 이를 지원하는지 모른 채 사용자 지정 수준(level)을 사용해서는 안됩니다(SHOULD NOT).

### 1.2 메세지

* 모든 method는 문자열을 메시지로 받거나 `__toString ()` method를 가진 객체를 받아들입니다. 구현자는 전달 된 객체를 특별한 형태로 처리 할 수도있다 (MAY). 그렇지 않은 경우, 구현자는 그것을 문자열로 변환해야한다 (MUST).
* 메시지는 구현자가 문맥 배열의 값으로 대체 할 수 있는(MAY) Placeholder를 포함 할 수있습니다(MAY). Placeholder 이름은 컨텍스트 배열의 키와 일치해야합니다. Placeholder 이름은 하나의 여는 중괄호 `{` 와 하나의 닫는 중괄호 `}`로 구분해야합니다. 구분 기호와 Placeholder 이름 사이에 공백이 없어야합니다 (MUST NOT). Placeholder 이름은 `A-Z`, `a-z`, `0-9`, 밑줄 `_` 및 마침표 `.` 만으로 구성되어야합니다 (SHOULD). 다른 문자의 사용은 Placeholder 사양(specification)의 향후 수정을 위해 예약됩니다. 구현자는 Placeholder를 사용하여 다양한 이스케이프 전략을 구현하고 표시를 위해 로그를 변환 할 수 있습니다 (MAY). 사용자는 데이터가 표시 될 컨텍스트를 알 수 없으므로 Placeholder 값을 미리 이스케이프해서는 안됩니다.

  다음은 참조용으로만 제공되는 Placeholder 보간법의 구현 예입니다.

  ```php
  <?php

  /**
   * Interpolates context values into the message placeholders.
   */
  function interpolate($message, array $context = array())
  {
      // build a replacement array with braces around the context keys
      $replace = array();
      foreach ($context as $key => $val) {
          // check that the value can be casted to string
          if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) {
              $replace['{' . $key . '}'] = $val;
          }
      }

      // interpolate replacement values into the message and return
      return strtr($message, $replace);
  }

  // a message with brace-delimited placeholder names
  $message = "User {username} created";

  // a context array of placeholder names => replacement values
  $context = array('username' => 'bolivar');

  // echoes "User bolivar created"
  echo interpolate($message, $context);
  ```

### 1.3 문맥(Context)

* 모든 메소드는 배열을 컨텍스트 데이터로 허용합니다. 이것은 문자열에 잘 맞지 않는 불필요한 정보를 보유하기위한 것입니다. 배열은 무엇이든 포함 할 수 있습니다. 구현자는 가능한 한 많은 관용으로 컨텍스트 데이터를 처리해야합니다 (MUST). 문맥에서 주어진 값은 예외를 던지거나 PHP error, warning 또는 notice를 발생시켜서는 안됩니다(MUST NOT).
* `Exception` 객체가 컨텍스트 데이터에 전달되면, 그것은 `'exception'` 키에 있어야합니다 (MUST). 로깅 예외(exceptios)는 일반적인 패턴이며, 이로 인해 구현자가 로그 백엔드가 지원할 때 예외에서 스택 추적을 추출 할 수 있습니다. 구현자들은 무엇이든 포함 할 수 있기 때문에`'exception'` 키가 실제로 그것을 사용하기 전에 실제로 `Exception` 인지를 반드시 확인해야합니다 (MUST).

### 1.4 헬퍼 클래스와 인터페이스

* `Psr\Log\AbstractLogger` Class를 확장하고 일반적인 `log` 메소드를 구현함으로써 `LoggerInterface`를 매우 쉽게 구현할 수있게합니다. 다른 8 가지 method는 메시지와 컨텍스트를 전달하는 것입니다.
* 마찬가지로 `Psr\Log\LoggerTrait` 만 사용하면 일반적으로 `log` 메소드를 구현해야합니다. 이 trait은 인터페이스를 구현하지 않으므로 이 경우에는 여전히 `LoggerInterface`를 구현해야합니다.
* `Psr\Log\NullLogger`는 인터페이스와 함께 제공됩니다. 로거가 제공되지 않으면 폴백 (back-back) "블랙홀"구현을 제공하기 위해 인터페이스 사용자가 이를 사용할 수 있습니다 (MAY). 그러나 컨텍스트 데이터 작성 비용면에서 조건부 로깅이 더 나은 접근 방법 일 수 있습니다.

  > 역자주: 아무것도 저장하지 않는 로거를 사용하는 것 보다 경우에 따라 로거를 호출 하도록 하는 편이 더 효율적
* `Psr\Log\LoggerAwareInterface` 는 `setLogger(LoggerInterface $logger)` method만을 포함하고 있으며, 임의의 인스턴스를 로거로 자동으로 연결하기 위해 프레임워크에서 사용할 수 있습니다.
* `Psr\Log\LoggerAwareTrait` trait은 모든 클래스에서 쉽게 동일한 인터페이스를 구현하는 데 사용할 수 있습니다. `$this->logger`에 접근 할 수 있습니다.
* `Psr\Log\LogLevel` 클래스는 8 개의 로그 레벨에 대한 상수를 가지고 있습니다.

## 2. Package

설명한 인터페이스와 클래스는 물론 관련 예외 클래스 및 구현을 확인하는 테스트가 [psr/log](https://packagist.org/packages/psr/log) 패키지의 일부로 제공됩니다.

## 3. `Psr\Log\LoggerInterface`

```php
<?php

namespace Psr\Log;

/**
 * Describes a logger instance.
 *
 * The message MUST be a string or object implementing __toString().
 *
 * The message MAY contain placeholders in the form: {foo} where foo
 * will be replaced by the context data in key "foo".
 *
 * The context array can contain arbitrary data, the only assumption that
 * can be made by implementors is that if an Exception instance is given
 * to produce a stack trace, it MUST be in a key named "exception".
 *
 * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
 * for the full interface specification.
 */
interface LoggerInterface
{
    /**
     * System is unusable.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function emergency($message, array $context = array());

    /**
     * Action must be taken immediately.
     *
     * Example: Entire website down, database unavailable, etc. This should
     * trigger the SMS alerts and wake you up.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function alert($message, array $context = array());

    /**
     * Critical conditions.
     *
     * Example: Application component unavailable, unexpected exception.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function critical($message, array $context = array());

    /**
     * Runtime errors that do not require immediate action but should typically
     * be logged and monitored.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function error($message, array $context = array());

    /**
     * Exceptional occurrences that are not errors.
     *
     * Example: Use of deprecated APIs, poor use of an API, undesirable things
     * that are not necessarily wrong.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function warning($message, array $context = array());

    /**
     * Normal but significant events.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function notice($message, array $context = array());

    /**
     * Interesting events.
     *
     * Example: User logs in, SQL logs.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function info($message, array $context = array());

    /**
     * Detailed debug information.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function debug($message, array $context = array());

    /**
     * Logs with an arbitrary level.
     *
     * @param mixed $level
     * @param string $message
     * @param array $context
     * @return void
     */
    public function log($level, $message, array $context = array());
}
```

## 4. `Psr\Log\LoggerAwareInterface`

```php
<?php

namespace Psr\Log;

/**
 * Describes a logger-aware instance.
 */
interface LoggerAwareInterface
{
    /**
     * Sets a logger instance on the object.
     *
     * @param LoggerInterface $logger
     * @return void
     */
    public function setLogger(LoggerInterface $logger);
}
```

## 5. `Psr\Log\LogLevel`

```php
<?php

namespace Psr\Log;

/**
 * Describes log levels.
 */
class LogLevel
{
    const EMERGENCY = 'emergency';
    const ALERT     = 'alert';
    const CRITICAL  = 'critical';
    const ERROR     = 'error';
    const WARNING   = 'warning';
    const NOTICE    = 'notice';
    const INFO      = 'info';
    const DEBUG     = 'debug';
}
```


# PSR-4-autoloader

이 단어들 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", "OPTIONAL" 은 [RFC 2119](http://tools.ietf.org/html/rfc2119)에 설명 된대로 해석해야 합니다.

> 역자주: 위의 키워드는 아래의 번역문에 괄호안에 표시하였습니다

## 1. 개요

이 PSR은 Class [autoloading](http://php.net/autoload) 에서 파일 경로에 대한 사양(specification)을 설명합니다. 이 PSR은 완벽하게 상호 운용이 가능하며 [PSR-0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md)을 포함한 다른 오토로딩 사양과 함께 사용할 수 있습니다. 또한 이 PSR은 사양에 따라 오토로드되는 파일을 저장할 위치를 설명합니다.

## 2. 명세서

1. "Class"라는 용어는 Class, Interface, Trait 등의 기타 유사한 구조를 말합니다.
2. 정규화 된 클래스 이름의 형식은 다음과 같습니다.

   ```
    \<NamespaceName>(\<SubNamespaceNames>)*\<ClassName>
   ```

   1. 정규화 된 Class 이름은 "공급자 네임스페이스"라고도하는 최상위 네임스페이스를 가져야만 합니다(MUST).
   2. 정규화 된 Class 이름은 하나 이상의 하위 네임스페이스를 가질 수 있습니다 (MAY).
   3. 정규화된 Class 이름은 마지막 클래스 이름(terminating class name)을 가져야합니다 (MUST).

      > 역자주: 좀 더 좋은 의미의 표현을 찾지 못하였습니다
   4. 밑줄은 정규화 된 클래스 이름의 어느 부분에도 특별한 의미가 없습니다.
   5. 정규화 된 클래스 이름의 알파벳 문자는 대소문자의 조합 일 수 있습니다 (MAY).
   6. 모든 클래스 이름은 대소문자를 구분하여 참조해야합니다 (MUST).
3. 정규화 된 클래스 이름에 해당하는 파일을 로드 할 때 ...
   1. 정규화 된 클래스 이름에서 최초의 네임스페이스 구분 기호를 제외하고 연속된 하나 이상의 상위 네임스페이스와 하위 네임스페이스로 구성된 ( "네임스페이스 접두사")는 적어도 하나의 "기본 디렉토리"에 대응합니다.

      > 역자주: 표현을 더 다듬어야 할 듯 하지만 네임스페이스의 조합은 하나의 디렉토리와 매치가 된다는 의미로 이해하시면 될 것 같습니다
   2. "네임스페이스 접두사" 다음의 이어지는 하위 네임스페이스 이름은 "기본 디렉토리" 내의 하위 디렉토리에 해당하며 네임스페이스 구분 기호는 디렉토리 구분 기호를 나타냅니다. 하위 디렉토리 이름은 하위 네임스페이스 이름의 대소문자와 일치해야합니다.
   3. 마지막에 존재하는 Class 이름은 `.php` 로 끝나는 파일 이름과 같아야 합니다.

      파일 이름은 Class 이름의 대소문자와 일치해야합니다(MUST).

      > 역자주: \Vendor\Packages\Class => Class.php
4. 오토로더 구현체는 예외(exception)를 throw해서는 안되며(MUST NOT), 모든 레벨의 오류를 발생해서는 안되며(MUST NOT), 값을 반환해서는 안됩니다 (SHOULD NOT).

## 3. 예제

아래의 표는 주어진 정규화 된 Class 이름, 네임스페이스 접두사 및 기본 디렉토리에 해당하는 파일 경로를 보여줍니다.

| 정규화된 Class 이름                 | Namespace 접두사   | 기본 디렉토리                | 결과 파일 경로                                  |
| ----------------------------- | --------------- | ---------------------- | ----------------------------------------- |
| \Acme\Log\Writer\File\_Writer | Acme\Log\Writer | ./acme-log-writer/lib/ | ./acme-log-writer/lib/File\_Writer.php    |
| \Aura\Web\Response\Status     | Aura\Web        | /path/to/aura-web/src/ | /path/to/aura-web/src/Response/Status.php |
| \Symfony\Core\Request         | Symfony\Core    | ./vendor/Symfony/Core/ | ./vendor/Symfony/Core/Request.php         |
| \Zend\Acl                     | Zend            | /usr/includes/Zend/    | /usr/includes/Zend/Acl.php                |

명세를 따르는 Autoloader의 구현 예제는 [examples file](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader-examples.md)을 참조하십시오. 예제 구현체는 명세(specification)의 일부로 간주되어서는 안되며(MUST NOT) 언제든지 변경 될 수 있습니다.


# PSR-4-autoloader-examples

다음 예는 PSR-4에 호환하는 코드입니다.

> 역자주: 사실상 오토로더는 컴포저로 대동단결 되었기 때문에 직접 구현하시기 보다는 컴포저를 사용하시는 것을 추천합니다

## Closure 예제

```php
<?php
/**
 * An example of a project-specific implementation.
 *
 * After registering this autoload function with SPL, the following line
 * would cause the function to attempt to load the \Foo\Bar\Baz\Qux class
 * from /path/to/project/src/Baz/Qux.php:
 *
 *      new \Foo\Bar\Baz\Qux;
 *
 * @param string $class The fully-qualified class name.
 * @return void
 */
spl_autoload_register(function ($class) {

    // project-specific namespace prefix
    $prefix = 'Foo\\Bar\\';

    // base directory for the namespace prefix
    $base_dir = __DIR__ . '/src/';

    // does the class use the namespace prefix?
    $len = strlen($prefix);
    if (strncmp($prefix, $class, $len) !== 0) {
        // no, move to the next registered autoloader
        return;
    }

    // get the relative class name
    $relative_class = substr($class, $len);

    // replace the namespace prefix with the base directory, replace namespace
    // separators with directory separators in the relative class name, append
    // with .php
    $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';

    // if the file exists, require it
    if (file_exists($file)) {
        require $file;
    }
});
```

## Class 예제

다음은 네임스페이스를 활용하여 여러 클래스를 처리하기위한 클래스 구현의 예제입니다.

```php
<?php
namespace Example;

/**
 * An example of a general-purpose implementation that includes the optional
 * functionality of allowing multiple base directories for a single namespace
 * prefix.
 *
 * Given a foo-bar package of classes in the file system at the following
 * paths ...
 *
 *     /path/to/packages/foo-bar/
 *         src/
 *             Baz.php             # Foo\Bar\Baz
 *             Qux/
 *                 Quux.php        # Foo\Bar\Qux\Quux
 *         tests/
 *             BazTest.php         # Foo\Bar\BazTest
 *             Qux/
 *                 QuuxTest.php    # Foo\Bar\Qux\QuuxTest
 *
 * ... add the path to the class files for the \Foo\Bar\ namespace prefix
 * as follows:
 *
 *      <?php
 *      // instantiate the loader
 *      $loader = new \Example\Psr4AutoloaderClass;
 *
 *      // register the autoloader
 *      $loader->register();
 *
 *      // register the base directories for the namespace prefix
 *      $loader->addNamespace('Foo\Bar', '/path/to/packages/foo-bar/src');
 *      $loader->addNamespace('Foo\Bar', '/path/to/packages/foo-bar/tests');
 *
 * The following line would cause the autoloader to attempt to load the
 * \Foo\Bar\Qux\Quux class from /path/to/packages/foo-bar/src/Qux/Quux.php:
 *
 *      <?php
 *      new \Foo\Bar\Qux\Quux;
 *
 * The following line would cause the autoloader to attempt to load the
 * \Foo\Bar\Qux\QuuxTest class from /path/to/packages/foo-bar/tests/Qux/QuuxTest.php:
 *
 *      <?php
 *      new \Foo\Bar\Qux\QuuxTest;
 */
class Psr4AutoloaderClass
{
    /**
     * An associative array where the key is a namespace prefix and the value
     * is an array of base directories for classes in that namespace.
     *
     * @var array
     */
    protected $prefixes = array();

    /**
     * Register loader with SPL autoloader stack.
     *
     * @return void
     */
    public function register()
    {
        spl_autoload_register(array($this, 'loadClass'));
    }

    /**
     * Adds a base directory for a namespace prefix.
     *
     * @param string $prefix The namespace prefix.
     * @param string $base_dir A base directory for class files in the
     * namespace.
     * @param bool $prepend If true, prepend the base directory to the stack
     * instead of appending it; this causes it to be searched first rather
     * than last.
     * @return void
     */
    public function addNamespace($prefix, $base_dir, $prepend = false)
    {
        // normalize namespace prefix
        $prefix = trim($prefix, '\\') . '\\';

        // normalize the base directory with a trailing separator
        $base_dir = rtrim($base_dir, DIRECTORY_SEPARATOR) . '/';

        // initialize the namespace prefix array
        if (isset($this->prefixes[$prefix]) === false) {
            $this->prefixes[$prefix] = array();
        }

        // retain the base directory for the namespace prefix
        if ($prepend) {
            array_unshift($this->prefixes[$prefix], $base_dir);
        } else {
            array_push($this->prefixes[$prefix], $base_dir);
        }
    }

    /**
     * Loads the class file for a given class name.
     *
     * @param string $class The fully-qualified class name.
     * @return mixed The mapped file name on success, or boolean false on
     * failure.
     */
    public function loadClass($class)
    {
        // the current namespace prefix
        $prefix = $class;

        // work backwards through the namespace names of the fully-qualified
        // class name to find a mapped file name
        while (false !== $pos = strrpos($prefix, '\\')) {

            // retain the trailing namespace separator in the prefix
            $prefix = substr($class, 0, $pos + 1);

            // the rest is the relative class name
            $relative_class = substr($class, $pos + 1);

            // try to load a mapped file for the prefix and relative class
            $mapped_file = $this->loadMappedFile($prefix, $relative_class);
            if ($mapped_file) {
                return $mapped_file;
            }

            // remove the trailing namespace separator for the next iteration
            // of strrpos()
            $prefix = rtrim($prefix, '\\');
        }

        // never found a mapped file
        return false;
    }

    /**
     * Load the mapped file for a namespace prefix and relative class.
     *
     * @param string $prefix The namespace prefix.
     * @param string $relative_class The relative class name.
     * @return mixed Boolean false if no mapped file can be loaded, or the
     * name of the mapped file that was loaded.
     */
    protected function loadMappedFile($prefix, $relative_class)
    {
        // are there any base directories for this namespace prefix?
        if (isset($this->prefixes[$prefix]) === false) {
            return false;
        }

        // look through base directories for this namespace prefix
        foreach ($this->prefixes[$prefix] as $base_dir) {

            // replace the namespace prefix with the base directory,
            // replace namespace separators with directory separators
            // in the relative class name, append with .php
            $file = $base_dir
                  . str_replace('\\', '/', $relative_class)
                  . '.php';

            // if the mapped file exists, require it
            if ($this->requireFile($file)) {
                // yes, we're done
                return $file;
            }
        }

        // never found it
        return false;
    }

    /**
     * If a file exists, require it from the file system.
     *
     * @param string $file The file to require.
     * @return bool True if the file exists, false if not.
     */
    protected function requireFile($file)
    {
        if (file_exists($file)) {
            require $file;
            return true;
        }
        return false;
    }
}
```

### Unit Tests

다음 예제는 위의 클래스 로더를 단위 테스트하는 방법의 한 가지입니다.

```php
<?php
namespace Example\Tests;

class MockPsr4AutoloaderClass extends Psr4AutoloaderClass
{
    protected $files = array();

    public function setFiles(array $files)
    {
        $this->files = $files;
    }

    protected function requireFile($file)
    {
        return in_array($file, $this->files);
    }
}

class Psr4AutoloaderClassTest extends \PHPUnit_Framework_TestCase
{
    protected $loader;

    protected function setUp()
    {
        $this->loader = new MockPsr4AutoloaderClass;

        $this->loader->setFiles(array(
            '/vendor/foo.bar/src/ClassName.php',
            '/vendor/foo.bar/src/DoomClassName.php',
            '/vendor/foo.bar/tests/ClassNameTest.php',
            '/vendor/foo.bardoom/src/ClassName.php',
            '/vendor/foo.bar.baz.dib/src/ClassName.php',
            '/vendor/foo.bar.baz.dib.zim.gir/src/ClassName.php',
        ));

        $this->loader->addNamespace(
            'Foo\Bar',
            '/vendor/foo.bar/src'
        );

        $this->loader->addNamespace(
            'Foo\Bar',
            '/vendor/foo.bar/tests'
        );

        $this->loader->addNamespace(
            'Foo\BarDoom',
            '/vendor/foo.bardoom/src'
        );

        $this->loader->addNamespace(
            'Foo\Bar\Baz\Dib',
            '/vendor/foo.bar.baz.dib/src'
        );

        $this->loader->addNamespace(
            'Foo\Bar\Baz\Dib\Zim\Gir',
            '/vendor/foo.bar.baz.dib.zim.gir/src'
        );
    }

    public function testExistingFile()
    {
        $actual = $this->loader->loadClass('Foo\Bar\ClassName');
        $expect = '/vendor/foo.bar/src/ClassName.php';
        $this->assertSame($expect, $actual);

        $actual = $this->loader->loadClass('Foo\Bar\ClassNameTest');
        $expect = '/vendor/foo.bar/tests/ClassNameTest.php';
        $this->assertSame($expect, $actual);
    }

    public function testMissingFile()
    {
        $actual = $this->loader->loadClass('No_Vendor\No_Package\NoClass');
        $this->assertFalse($actual);
    }

    public function testDeepFile()
    {
        $actual = $this->loader->loadClass('Foo\Bar\Baz\Dib\Zim\Gir\ClassName');
        $expect = '/vendor/foo.bar.baz.dib.zim.gir/src/ClassName.php';
        $this->assertSame($expect, $actual);
    }

    public function testConfusion()
    {
        $actual = $this->loader->loadClass('Foo\Bar\DoomClassName');
        $expect = '/vendor/foo.bar/src/DoomClassName.php';
        $this->assertSame($expect, $actual);

        $actual = $this->loader->loadClass('Foo\BarDoom\ClassName');
        $expect = '/vendor/foo.bardoom/src/ClassName.php';
        $this->assertSame($expect, $actual);
    }
}
```


# PSR-6-cache

캐싱은 모든 프로젝트의 성능을 향상시키는 일반적인 방법이며, 많은 프레임워크 및 라이브러리는 캐싱 라이브러리를 기본 기능 중 하나로 만들게 됩니다. 이로 인해 많은 라이브러리가 다양한 수준의 기능을 갖춘 자체 캐싱 라이브러리를 사용하게 되었습니다. 이러한 상황으로 인해 개발자는 필요한 기능을 제공 할 수도 있고 제공하지 못할 수도 있는 여러 시스템을 배워야합니다. 또한 라이브러리를 캐싱하는 개발자는 제한된 수의 프레임워크 만 지원하거나 많은 수의 어댑터 클래스를 만드는 것 중에서 선택해야합니다.

캐싱 시스템을 위한 공통 인터페이스는 이러한 문제를 해결할 것 입니다. 라이브러리 및 프레임워크 개발자는 의도한대로 동작하는 캐싱 시스템에 의존 할 수 있습니다. 캐싱 시스템의 개발자는 전체 어댑터 모음보다는 단일 인터페이스 세트 만 구현하면됩니다.

이 문서에서 핵심이 되는 단어는 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", "OPTIONAL" 입니다. 이것은 [RFC 2119](http://tools.ietf.org/html/rfc2119)에 설명 된대로 해석해야 합니다.

> 역자주: 위의 키워드는 아래의 번역문에 괄호안에 표시하였습니다

## 목표

이 PSR의 목표는 개발자가 빈틈없는 캐시 라이브러리를 만들 수 있도록 하여 맞춤 개발이 필요없이 기존 프레임 워크와 시스템에 통합 될 수 있도록 하는 것입니다.

## 정의

* **호출 라이브러리(Calling Library)** - 실제로 캐시 서비스가 필요한 라이브러리 또는 코드. 이 라이브러리는이 표준의 인터페이스를 구현하는 캐싱 서비스를 활용할 것이며, 그렇지 않으면 이러한 캐싱 서비스의 구현에 대한 알지 못합니다.

  > 역자주: 인터페이스를 구현한 캐싱 서비스를 사용만하고 구현을 하지 않는다.
* **구현 라이브러리(Implementing Library)** - 이 라이브러리는 모든 호출 라이브러리에 캐싱 서비스를 제공하기 위해이 표준을 구현합니다. 구현 라이브러리는 `Cache\CacheItemPoolInterface` 및 `Cache\CacheItemInterface` 인터페이스를 구현하는 클래스를 제공해야합니다. 라이브러리 구현은 전체 초 단위로 아래에 설명 된 최소 TTL 기능을 지원해야만 합니다 (MUST).
* **TTL** - TTL (Time To Live)은 해당 항목이 저장하고 보관하는 것으로 간주되는 시간입니다. TTL은 일반적으로 초 단위의 시간을 나타내는 정수 또는 DateInterval 개체로 정의됩니다.
* **만료(Expiration)** - 항목이 만료로 설정되는 실제 시간입니다. 이것은 일반적으로 개체가 저장된 시간에 TTL을 추가하여 계산됩니다. 이것은 일반적으로 개체가 저장된 시간에 TTL을 추가하여 계산되지만 DateTime 개체로 명시 적으로 설정할 수도 있습니다. 1:30:00에 저장된 300 초 TTL을 가진 항목의 만료 시간은 1:35:00입니다. 구현 라이브러리는 요청한 만료 시간 전에 항목을 만료시킬 수도 있지만(MAY) 만료 시간에 도달하면 만료 된 것으로 간주해야합니다(MUST). 호출 라이브러리가 항목을 저장요청을 하지만 만료 시간을 지정하지 않거나 만료 시간 또는 TTL을 null로 지정되어 있다면 구현 라이브러리는 설정한 기본 기간을 사용할 수 있습니다 (MAY). 기본 기간이 설정되지 않은 경우 구현 라이브러리는 해당 항목을 영원히 캐시하는 요청으로 해석하거나 기본 구현이 지원하는 동안 저장하는 것으로 해석해야합니다(MUST).
* **Key** - 캐시 된 항목을 고유하게 식별하는 적어도 하나의 문자로 구성된 문자열입니다. 구현 라이브러리는`A-Z`,`a-z`,`0-9`,`_` 및`.` 문자로 구성된 키를 UTF-8 인코딩과 길이가 64 문자 이하의 순서로 지원해야합니다 (MUST). 구현 라이브러리은 추가 문자와 인코딩 또는 더 긴 길이를 지원할 수 있지만 최소한 그 최소값을 지원해야합니다(MAY). 라이브러리는 적절하게 키 문자열을 이스케이프 처리해야하지만 원래의 수정되지 않은 키 문자열을 반환 할 수 있어야합니다(MUST). 다음 문자는 미래의 확장을 위해 예약되어 있으며, 구현 라이브러리에서 지원해서는 안됩니다(MUST NOT) :`{} () / \ @ :`
* **Hit** - 호출 라이브러리가 키로 Item을 요청하고 해당 키에 대해 일치하는 값이 발견되고 그 값이 만료되지 않았으며 값이 다른 이유로 무효하지 않은 경우 캐시 히트(Hit)가 발생합니다. 호출 라이브러리는 모든 `get()` 호출에 대해 `isHit()`를 확인해야합니다 (SHOULD).
* **Miss** - 캐시 미스는 캐시 히트와 반대입니다. 호출 라이브러리가 키로 항목을 요청하고 해당 키에 해당 값이 없거나 값이 발견되었지만 만료되었거나 값이 다른 이유로 유효하지 않은 경우 캐시 미스가 발생합니다. 만료 된 값은 항상 캐시 미스로 간주되어야합니다 (MUST).
* **지연됨(Deferred)** - 지연된 캐시 저장은 캐시 항목이 풀에 의해 즉시 유지되지 않을 수 있음을 나타냅니다. 풀 객체는 일부 저장소 엔진에서 지원하는 대량 세트 연산을 이용하기 위해 지연된 캐시 항목을 유지하는 것을 지연시킬 수 있습니다. 풀은 지연 캐시 항목이 결국 유지되고 데이터가 손실되지 않도록 보장해야하며 호출 라이브러리가 지속되도록 요청하기 전에이를 유지해야합니다. 호출 라이브러리가 `commit()` 메소드를 호출 할 때 모든 미해결 지연 항목을 유지해야합니다 (MUST). 구현 라이브러리는 객체 소멸자, `save()`, timeout 또는 max-items check 또는 다른 적절한 논리에서 모두 지속되는 것과 같이 지연된 항목을 언제 유지할 것인지를 결정하는 데 적합한 논리를 사용할 수 있습니다 (MAY). 지연된 캐시 항목에 대한 요청은 지연되었지만 아직 지속되지 않은 항목을 반환해야합니다 (MUST).

## Data

구현 라이브러리는 다음을 포함하여 모든 직렬화 가능한 PHP 데이터 유형을 지원해야합니다.

* **Strings** - PHP 호환 인코딩의 임의의 크기를 갖는 문자열.
* **Integers** - PHP가 지원하는 모든 크기의 정수 (최대 64 비트).
* **Floats** - 부호가 있는(signed) 모든 부동소수점 값.
* **Boolean** - True 와 False.
* **Null** - 진짜 Null 값. `역자주: 진짜라는 표현을 하는 이유는 PSR-16의 Null과는 다른 의미라서 입니다`
* **Arrays** - 인덱스된 임의의 깊이를 가진 연관 및 다차원 배열
* **Object** - `$o == unserialize(serialize($o))` 와 같이 무손실 직렬화 및 직렬화 해제를 지원하는 객체. 객체는 PHP의 Serializable 인터페이스, 적절한 경우 `__sleep()` 또는 `__wakeup()` 매직 메서드 또는 유사한 언어 기능을 활용할 수 있습니다(MAY).

구현 라이브러리에 전달 된 모든 데이터는 전달 된 그대로 정확하게 반환되어야합니다(MUST). 여기에는 가변 유형이 포함됩니다. 즉, 만약 (int) 5가 저장된 값이었던 경우 (string) 5를 반환하는 것은 오류입니다. 구현 라이브러리는 PHP의 `serialize()`/`unserialize()` 함수를 내부적으로 사용할 수 있지만 꼭 그렇게 할 필요는 없습니다(MAY). 이들과의 호환성은 수용 가능한 객체 값의 기준선으로 사용됩니다.

어떠한 이유로든 저장된 정확한 값을 반환 할 수없는 경우 구현 라이브러리는 데이터가 손상되지 않게 캐시 미스로 응답해야합니다(MUST).

## Key Concepts

### Pool

풀은 캐싱 시스템의 항목 모음을 나타냅니다. 풀은 포함 된 모든 항목의 논리적 저장소입니다. 캐시 가능한 모든 항목은 풀에서 항목 객체로 검색되고 캐시 된 객체의 전체 유니버스와의 모든 상호 작용은 풀을 통해 발생합니다.

### Items

Item은 풀 내의 단일 키 / 값 쌍을 나타냅니다. 키는 Item에 대한 주요 고유 식별자이며 변경할 수 없어야합니다 (MUST). 언제든지 값을 변경할 수 있습니다 (MAY).

## Error handling

캐싱은 종종 애플리케이션 성능의 중요한 부분이지만 애플리케이션 기능의 중요한 부분이되어서는 안됩니다. 따라서 캐시 시스템의 오류로 인해 프로그램이 실패(failure)하지 않아야합니다 (SHOULD NOT). 이런 이유로, 구현 라이브러리는 인터페이스에 의해 정의 된 것과 다른 예외를 던져서는 안되며, 기본 데이터 저장소에 의해 발생된 된 오류나 예외를 버리고 문제를 일으키지 않아야합니다 (SHOULD).

구현 라이브러리는 그러한 오류를 기록하거나 적절한 경우 이를 관리자에게 보고해야합니다 (SHOULD).

호출 라이브러리가 하나 이상의 항목을 삭제하도록 요청하거나 풀을 지우도록 요청한 경우 지정된 키가 존재하지 않으면 오류 상태로 간주해서는 안됩니다(MUST NOT). 다음의 조건(키가 존재하지 않거나 풀이 비어 있음)도 동일하며 오류 조건은 없습니다.

## Interfaces

### CacheItemInterface

`CacheItemInterface`는 캐시 시스템 내부의 항목을 정의합니다. 각 Item 객체는 구현 시스템에 따라 설정 될 수 있는 특정 키와 연결되어야하며(MUST) 일반적으로 `Cache\CacheItemPoolInterface` 객체에 의해 전달됩니다.

`Cache\CacheItemInterface` 객체는 캐시 항목의 저장 및 검색을 캡슐화합니다. 각 `Cache\CacheItemInterface`는 `Cache\CacheItemPoolInterface` 오브젝트에 의해 생성되며, 필요한 모든 설정을 담당 할 뿐만 아니라 오브젝트를 고유 키와 연관시킵니다. `Cache\CacheItemInterface` 객체는 이 문서의 데이터 섹션에 정의 된 모든 유형의 PHP 값을 저장하고 검색 할 수 있어야 합니다.

호출 라이브러리는 Item 객체 자체를 인스턴스화해서는 안됩니다. `getItem()` 메소드를 통해 Pool 객체에서만 요청할 수 있습니다. 라이브러리 호출은 하나의 구현 라이브러리에 의해 생성 된 항목이 다른 구현 라이브러리의 풀과 호환 가능하다고 가정해서는 안됩니다 (SHOULD NOT).

```php
<?php

namespace Psr\Cache;

/**
 * CacheItemInterface defines an interface for interacting with objects inside a cache.
 */
interface CacheItemInterface
{
    /**
     * Returns the key for the current cache item.
     *
     * The key is loaded by the Implementing Library, but should be available to
     * the higher level callers when needed.
     *
     * @return string
     *   The key string for this cache item.
     */
    public function getKey();

    /**
     * Retrieves the value of the item from the cache associated with this object's key.
     *
     * The value returned must be identical to the value originally stored by set().
     *
     * If isHit() returns false, this method MUST return null. Note that null
     * is a legitimate cached value, so the isHit() method SHOULD be used to
     * differentiate between "null value was found" and "no value was found."
     *
     * @return mixed
     *   The value corresponding to this cache item's key, or null if not found.
     */
    public function get();

    /**
     * Confirms if the cache item lookup resulted in a cache hit.
     *
     * Note: This method MUST NOT have a race condition between calling isHit()
     * and calling get().
     *
     * @return bool
     *   True if the request resulted in a cache hit. False otherwise.
     */
    public function isHit();

    /**
     * Sets the value represented by this cache item.
     *
     * The $value argument may be any item that can be serialized by PHP,
     * although the method of serialization is left up to the Implementing
     * Library.
     *
     * @param mixed $value
     *   The serializable value to be stored.
     *
     * @return static
     *   The invoked object.
     */
    public function set($value);

    /**
     * Sets the expiration time for this cache item.
     *
     * @param \DateTimeInterface|null $expiration
     *   The point in time after which the item MUST be considered expired.
     *   If null is passed explicitly, a default value MAY be used. If none is set,
     *   the value should be stored permanently or for as long as the
     *   implementation allows.
     *
     * @return static
     *   The called object.
     */
    public function expiresAt($expiration);

    /**
     * Sets the expiration time for this cache item.
     *
     * @param int|\DateInterval|null $time
     *   The period of time from the present after which the item MUST be considered
     *   expired. An integer parameter is understood to be the time in seconds until
     *   expiration. If null is passed explicitly, a default value MAY be used.
     *   If none is set, the value should be stored permanently or for as long as the
     *   implementation allows.
     *
     * @return static
     *   The called object.
     */
    public function expiresAfter($time);

}
```

### CacheItemPoolInterface

`Cache\CacheItemPoolInterface`의 기본 목적은 호출 라이브러리에서 키를 승인하고 연관된 `Cache\CacheItemInterface` 오브젝트를 리턴하는 것입니다. 또한 전체 캐시 컬렉션과의 상호 작용의 주요 지점이기도합니다. 풀(Pool)의 모든 구성 및 초기화는 구현 라이브러리에 맡깁니다.

```php
<?php

namespace Psr\Cache;

/**
 * CacheItemPoolInterface generates CacheItemInterface objects.
 */
interface CacheItemPoolInterface
{
    /**
     * Returns a Cache Item representing the specified key.
     *
     * This method must always return a CacheItemInterface object, even in case of
     * a cache miss. It MUST NOT return null.
     *
     * @param string $key
     *   The key for which to return the corresponding Cache Item.
     *
     * @throws InvalidArgumentException
     *   If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException
     *   MUST be thrown.
     *
     * @return CacheItemInterface
     *   The corresponding Cache Item.
     */
    public function getItem($key);

    /**
     * Returns a traversable set of cache items.
     *
     * @param string[] $keys
     *   An indexed array of keys of items to retrieve.
     *
     * @throws InvalidArgumentException
     *   If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException
     *   MUST be thrown.
     *
     * @return array|\Traversable
     *   A traversable collection of Cache Items keyed by the cache keys of
     *   each item. A Cache item will be returned for each key, even if that
     *   key is not found. However, if no keys are specified then an empty
     *   traversable MUST be returned instead.
     */
    public function getItems(array $keys = array());

    /**
     * Confirms if the cache contains specified cache item.
     *
     * Note: This method MAY avoid retrieving the cached value for performance reasons.
     * This could result in a race condition with CacheItemInterface::get(). To avoid
     * such situation use CacheItemInterface::isHit() instead.
     *
     * @param string $key
     *   The key for which to check existence.
     *
     * @throws InvalidArgumentException
     *   If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException
     *   MUST be thrown.
     *
     * @return bool
     *   True if item exists in the cache, false otherwise.
     */
    public function hasItem($key);

    /**
     * Deletes all items in the pool.
     *
     * @return bool
     *   True if the pool was successfully cleared. False if there was an error.
     */
    public function clear();

    /**
     * Removes the item from the pool.
     *
     * @param string $key
     *   The key to delete.
     *
     * @throws InvalidArgumentException
     *   If the $key string is not a legal value a \Psr\Cache\InvalidArgumentException
     *   MUST be thrown.
     *
     * @return bool
     *   True if the item was successfully removed. False if there was an error.
     */
    public function deleteItem($key);

    /**
     * Removes multiple items from the pool.
     *
     * @param string[] $keys
     *   An array of keys that should be removed from the pool.
     *
     * @throws InvalidArgumentException
     *   If any of the keys in $keys are not a legal value a \Psr\Cache\InvalidArgumentException
     *   MUST be thrown.
     *
     * @return bool
     *   True if the items were successfully removed. False if there was an error.
     */
    public function deleteItems(array $keys);

    /**
     * Persists a cache item immediately.
     *
     * @param CacheItemInterface $item
     *   The cache item to save.
     *
     * @return bool
     *   True if the item was successfully persisted. False if there was an error.
     */
    public function save(CacheItemInterface $item);

    /**
     * Sets a cache item to be persisted later.
     *
     * @param CacheItemInterface $item
     *   The cache item to save.
     *
     * @return bool
     *   False if the item could not be queued or if a commit was attempted and failed. True otherwise.
     */
    public function saveDeferred(CacheItemInterface $item);

    /**
     * Persists any deferred cache items.
     *
     * @return bool
     *   True if all not-yet-saved items were successfully saved or there were none. False otherwise.
     */
    public function commit();
}
```

### CacheException

이 예외 인터페이스는 캐시 서버에 연결하거나 제공된 잘못된 자격 증명과 같은 *캐시 설정*을 포함하여 치명적인 오류가 발생할 때 사용하기위한 것입니다. 구현 라이브러리에 의해 던져진 예외는 이 인터페이스를 구현해야합니다 (MUST).

```php
<?php

namespace Psr\Cache;

/**
 * Exception interface for all exceptions thrown by an Implementing Library.
 */
interface CacheException
{
}
```

### InvalidArgumentException

```php
<?php

namespace Psr\Cache;

/**
 * Exception interface for invalid cache arguments.
 *
 * Any time an invalid argument is passed into a method it must throw an
 * exception class which implements Psr\Cache\InvalidArgumentException.
 */
interface InvalidArgumentException extends CacheException
{
}
```


# PSR-7-http-message

이 문서는 [RFC 7230](http://tools.ietf.org/html/rfc7230) 및 [RFC 7231](http://tools.ietf.org/html/rfc7231)에 설명한 대로 HTTP 메시지를 처리하는 일반적인 인터페이스 및 [RFC 3986](http://tools.ietf.org/html/rfc3986)에 설명한 대로 HTTP 메시지와 함께 사용할 URI에 관한 내용을 설명합니다

HTTP 메시지는 웹 개발의 기반(foundation)입니다. 웹 브라우저와 cURL과 같은 HTTP 클라이언트는 HTTP 요청 메시지를 작성하여 HTTP 응답 메시지를 제공하는 웹 서버로 전송합니다. 서버 측 코드는 HTTP 요청 메시지를 수신하고 HTTP 응답 메시지를 반환합니다.

HTTP 메시지는 일반적으로 최종 사용자로부터 요청(abstracted)되지만 개발자들은 일반적으로 HTTP API에 요청을 하거나 들어오는 요청을 처리하는 등의 작업을 수행하기 위해 구조화 방법과 이를 액세스하거나 조작하는 방법을 알아야합니다.

모든 HTTP 요청 메시지에는 다음과 같은 특정 형식이 있습니다.

```http
POST /path HTTP/1.1
Host: example.com

foo=bar&baz=bat
```

첫 번째 요청 줄은 "요청 줄(request line)"이며 HTTP 요청 방법(request method), 요청 대상 (일반적으로 URI의 절대값 또는 웹 서버의 경로) 및 HTTP 프로토콜 버전을 순서대로 포함합니다. 그 뒤에는 하나 이상의 HTTP 헤더, 빈 행 및 메시지 본문이옵니다.

HTTP 응답 메시지는 다음과 유사한 구조를 가집니다.

```http
HTTP/1.1 200 OK
Content-Type: text/plain

This is the response body
```

첫 번째 줄은 "상태 줄"이며 HTTP 프로토콜 버전, HTTP 상태 코드 및 사람이 읽을 수 있는 상태 코드 설명 인 "이유 구문"을 순서대로 포함합니다. HTTP 요청 메시지와 마찬가지로 하나 이상의 HTTP 헤더, 빈 행 및 메시지 본문이옵니다.

이 문서에서 설명하는 인터페이스는 HTTP 메시지 및 HTTP 메시지를 구성하는 요소를 추상화 한 것입니다.

이 문서에서 핵심이 되는 단어는 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", "OPTIONAL" 입니다. 이것은 [RFC 2119](http://tools.ietf.org/html/rfc2119)에 설명 된대로 해석해야 합니다.

> 역자주: 위의 키워드는 아래의 번역문에 괄호안에 표시하였습니다

### References

* [RFC 2119](http://tools.ietf.org/html/rfc2119)
* [RFC 3986](http://tools.ietf.org/html/rfc3986)
* [RFC 7230](http://tools.ietf.org/html/rfc7230)
* [RFC 7231](http://tools.ietf.org/html/rfc7231)

## 1. 명세서

### 1.1 Messages

HTTP 메시지는 클라이언트에서 서버로의 요청 또는 서버에서 클라이언트로의 응답을 의미합니다. 이 사양은 HTTP 메시지 `Psr\Http\Message\RequestInterface` 와 `Psr\Http\Message\ResponseInterface` 에 대한 인터페이스를 각각 정의합니다.

`Psr\Http\Message\RequestInterface` 와 `Psr\Http\Message\ResponseInterface` 는 모두 `Psr\Http\Message\MessageInterface`를 상속받습니다(extend). `Psr\Http\Message\MessageInterface` 는 직접 구현 해도 되지만(MAY), 구현자는 `Psr\Http\Message\RequestInterface` 와 `Psr\Http\Message\ResponseInterface` 를 구현해야합니다 (SHOULD).

앞으로 이 인터페이스들을 말 할 때 `Psr\Http\Message` 네임 스페이스는 생략 할 것입니다.

### 1.2 HTTP Headers

#### 대소문자를 구분하지 않는 헤더 필드 이름

HTTP 메시지에는 대소문자를 구분하지 않는 헤더 필드 이름을 포함합니다. `MessageInterface` 를 구현하는 클래스에서 헤더를 대문자와 소문자를 구분하지 않는 이름으로 검색합니다. 예를 들어 `foo` 헤더를 검색하는 것은 `FoO` 헤더를 검색하는 것과 같은 결과를 반환합니다. 비슷하게 `Foo` 헤더를 설정하면 이전에 설정된 `foo` 헤더 값을 덮어 씁니다.

```php
$message = $message->withHeader('foo', 'bar');

echo $message->getHeaderLine('foo');
// Outputs: bar

echo $message->getHeaderLine('FOO');
// Outputs: bar

$message = $message->withHeader('fOO', 'baz');
echo $message->getHeaderLine('foo');
// Outputs: baz
```

헤더는 대소문자의 구분 없이 검색할 수 있지만, `getHeaders()` 로 가져올 경우는 원래의 대소문자가 유지되어야만 합니다 (MUST).

부적합한 HTTP 프로그램은 특정 경우에 따라 달라질 수 있으므로 사용자가 요청이나 응답을 만들 때 HTTP 헤더의 대소문자를 지정할 수 있는 것이 좋습니다.

#### 여러개의 값을 가진 헤더

여러개의 값을 가진 헤더를 수용하면서도 헤더로 문자열 작업을 할 수 있는 편리함을 제공하기 위해, 배열이나 문자열로 `MessageInterface` 의 인스턴스에서 헤더를 검색 할 수 있습니다. `getHeaderLine()` 메서드를 사용하여, 대소문자를 구분하지 않는 헤더의 이름으로 검색한 결과 값을 모두 쉼표로 연결한 문자열을 가져옵니다. 대문자와 소문자를 구분하지 않는 헤더의 모든 헤더 값의 배열을 이름으로 검색하려면 `getHeader()`를 사용하십시오.

```php
$message = $message
    ->withHeader('foo', 'bar')
    ->withAddedHeader('foo', 'baz');

$header = $message->getHeaderLine('foo');
// $header contains: 'bar, baz'

$header = $message->getHeader('foo');
// ['bar', 'baz']
```

참고 : 모든 헤더 값을 쉼표 (예 :`Set-Cookie`)를 사용하여 연결할 수 있는 것은 아닙니다. 이러한 헤더로 작업 할 때 `MessageInterface` 기반의 클래스는 이러한 다중 값 헤더를 검색하기 위해 `getHeader()` 메소드에 의존해야합니다 (SHOULD).

#### Host 헤더

요청에서 `Host` 헤더는 일반적으로 URI의 호스트 구성 부분만 아니라 TCP 연결 할 때 사용하는 호스트를 포함합니다. 그러나 HTTP 사양(specification)에서는 `Host` 헤더가 각각 다른 것을 허용하고 있습니다.

> 역자주: TCP 연결에 사용된 URI와 Http Requeser의 Host Header의 값이 다른 것을 허용합니다

그래서 구현시 `Host` 헤더가 제공되지 않으면 제공된 URI에서 `Host` 헤더를 가져와야 합니다 (MUST).

기본적으로 `RequestInterface::withUri()`은 요청의 `Host` 헤더의 값을 전달 된 `UriInterface` 의 호스트 구성 요소와 일치하는 `Host` 헤더로 대체합니다.

두 번째 인수(`$preserveHost`)에 `true` 를 전달하여 `Host` 헤더의 원래 상태를 보존하도록 선택할 수 있습니다. 이 인수를 `true` 로 설정하면 반환 하는 메시지의 `Host` 헤더를 업데이트하지 않습니다. 단, 메시지에 `Host` 헤더가 포함되어 있지 않으면 예외입니다.

이 테이블은 다양한 초기 요청과 URI에 대해 `$preserveHost` 인자가 `true` 로 설정된 `withUri()` 에 의해 반환 된 요청에 대해 `getHeaderLine('Host')` 가 반환하는 것을 보여줍니다.

| Request Host header[1](#rhh) | Request host component[2](#rhc) | URI host component[3](#uhc) | Result  |
| ---------------------------- | ------------------------------- | --------------------------- | ------- |
| ''                           | ''                              | ''                          | ''      |
| ''                           | foo.com                         | ''                          | foo.com |
| ''                           | foo.com                         | bar.com                     | foo.com |
| foo.com                      | ''                              | bar.com                     | foo.com |
| foo.com                      | bar.com                         | baz.com                     | foo.com |

* 1 변경이 일어나기 전의 `Host` 헤더 값
* 2 변경 전에 요청으로 작성된 URI의 Host 구성 요소.
* 3 `withUri()` 를 통해 주입되는 URI의 Host 구성 요소.

### 1.3 Streams

HTTP 메시지는 시작 줄, 헤더 및 본문으로 구성됩니다. HTTP 메시지 본문은 매우 작거나 매우 클 수 있습니다. 본문의 메시지를 문자열로 변환하려고하면 본문이 완전히 메모리에 저장되어야하기 때문에 의도 한 것보다 많은 메모리를 쉽게 써버릴 수 있습니다. 요청이나 응답의 본문을 메모리에 저장하려고 하면 배제되어 해당 구현을 사용하여 큰 메시지 본문을 처리 할 수 있게됩니다.

> 역자주: 자연스럽게 번역하려면 배제된다는 표현을 다른 것으로 바꾸는게 좋아보이지만 답을 못찾았습니다

`StreamInterface`는 데이터 스트림을 읽거나 쓸 때 구현 세부 사항을 감추기 위해 사용합니다. 문자열이 적절한 메시지 구현이된다면, `php://memory` 와 `php://temp`와 같은 내장 스트림을 사용할 수 있습니다.

`StreamInterface`는 스트림을 효율적으로 읽고, 쓰고, 지나갈 수 있게 해주는 몇가지 메소드를 제공줍니다.

스트림은 `isReadable()`, `isWritable()` 및 `isSeekable()`의 세 가지 메소드를 사용합니다 이 메소드는 스트림 작업자가 스트림이 자신의 요구 사항을 충족하는지 확인하는 데 사용할 수 있습니다.

각 스트림 인스턴스에는 읽기 전용, 쓰기 전용 또는 읽기 / 쓰기가 가능한 다양한 기능이 있습니다. 또한 임의의 임의 액세스 (모든 위치를 앞뒤로 검색) 또는 순차 액세스 (예 : 소켓, 파이프 또는 콜백 기반 스트림의 경우) 만 허용 할 수 있습니다.

마지막으로, `StreamInterface` 는 전체 본문 내용을 즉시 검색하거나 내보내는 것을 단순화하는 `__toString ()` 메소드를 제공합니다.

요청과 응답 인터페이스와 달리,`StreamInterface`는 불변성을 모델링하지 않습니다. 실제 PHP 스트림이 래핑 된 상황에서는 자원과 상호 작용하는 모든 코드가 커서 상태, 내용 등을 포함하여 상태를 변경시킬 수 있으므로 변경이 불가능합니다. 구현시 서버 측 요청 및 클라이언트 측 응답에 읽기 전용 스트림을 사용하는 것이 좋습니다. 사용자는 스트림 인스턴스가 변경 될 수 있으며 메시지 상태를 변경할 수 있다는 사실을 알고 있어야합니다. 의심스럽다면 새 스트림 인스턴스를 만들어 메시지에 첨부하여 상태를 적용합니다.

### 1.4 Request Targets and URIs

RFC 7230에 따라 요청 메시지에는 요청 줄의 두 번째 세그먼트로 "요청 대상"이 포함됩니다. 요청 대상은 다음 형식 중 하나 일 수 있습니다.

* **origin-form** : 경로와 쿼리스트링 (있을 경우)으로 구성됩니다. 이를 보통 상대 URL이라고 합니다. TCP를 통해 전송되는 메시지는 일반적으로 **origin-form**입니다. 스키마(scheme) 및 권한 데이터는 일반적으로 CGI 변수를 통해서만 표현합니다.
* **absolute-form** : ("\[user-info@]호스트\[:port]", 대괄호 안의 항목은 선택 사항), 경로 (있는 경우), 쿼리스트링 (있는 경우) 및 fragment (있는 경우)으로 구성됩니다.

  > 역자주: fragment는 URI의 #(hash) 문자열로 생각하시면 됩니다

  이것은 절대 URI라고도하며 RFC 3986에서 지정하는 유일한 URI 형식입니다. 이 양식은 HTTP 프록시에 요청할 때 일반적으로 사용됩니다.
* **authority-form** : 권한으로만 구성되어있습니다. 이것은 일반적으로 HTTP 클라이언트와 프록시 서버 사이의 연결을 설정하기 위한 CONNECT 요청에만 사용됩니다.
* **asterisk-form** : 이것은 문자열 `*` 로만 구성되며 웹 서버의 일반적인 기능을 결정하기 위해 OPTIONS 메서드와 함께 사용됩니다.

이러한 요청 대상 외에도 요청 대상과는 별도로 '유효한(effective) URL'이 있는 경우가 종종 있습니다. 유효한(effective) URL은 HTTP 메시지 내에서 전송되지 않지만 요청을 하기위한 프로토콜 (http/https), 포트 및 호스트 이름을 결정하는 데 사용됩니다.

유효한(effective) URL은 `UriInterface` 에 의해 표현됩니다. `UriInterface` 는 RFC 3986 (첫번째 사용 사례)에 명시된대로 HTTP와 HTTPS URI를 모델링합니다. 이 인터페이스는 다양한 URI 부분과 상호 작용하기위한 메소드를 제공하여 URI의 반복 구문 분석의 필요성을 없애줍니다. 또한 모델링 된 URI를 문자열 표현으로 변환하기위한 `__toString()` 메소드를 제공합니다.

`getRequestTarget()`으로 request-target을 검색 할 때, 이 메소드는 기본적으로 URI 객체를 사용하고 **origin-form**을 구성하는 데 필요한 모든 구성 요소를 추출합니다. **origin-form**은 가장 일반적인 요청 대상입니다.

최종 사용자가 다른 세 가지 형식 중 하나를 사용하기를 원하거나 사용자가 명시적으로 요청 대상을 덮어쓰고 싶다면 `withRequestTarget()`을 사용하여 이를 처리 할 수 있습니다.

> 역자주: 세가지 형식이란 위에 나오는 네가지 형식 중 **origin-form**을 제외한 세가지를 말합니다

이 메소드를 호출하면 `getUri()`로부터 반환받는 URI에 영향을 주지 않습니다.

예를 들어, 사용자는 아래와 같이 서버에 **asterisk-form** 요청을 할 수 있습니다.

```php
$request = $request
    ->withMethod('OPTIONS')
    ->withRequestTarget('*')
    ->withUri(new Uri('https://example.org/'));
```

이 예제는 최종적으로 다음과 같은 결과를 얻을 수 있습니다.

```http
OPTIONS * HTTP/1.1
```

하지만 HTTP 클라이언트는 유효한(effective) URL (`getUri()`로부터)을 사용하여 프로토콜, 호스트 이름 및 TCP 포트를 결정할 수 있습니다.

HTTP 클라이언트는 반드시 `Uri::getPath()` 와 `Uri::getQuery()` 의 값을 무시해야하며(MUST), 대신 `getRequestTarget()` 에 의해 반환 된 값을 사용해야합니다. 기본값은 이 두 값을 연결한 것입니다.

네 개의 요청 대상 폼 중 하나 이상을 구현하지 않기로 결정한 클라이언트는 여전히 `getRequestTarget()` 을 사용해야한다(MUST). 이들 클라이언트는 지원하지 않는 요청 대상을 거부해야하며(MUST) 반드시 `getUri()` 의 값으로 폴백해서는 안된다(MUST NOT).

`RequestInterface`는 요청 대상을 검색하거나 제공된 요청 대상으로 새로운 인스턴스를 생성하기위한 메소드를 제공합니다. 기본적으로 요청 대상이 인스턴스에서 특별히 구성되지 않으면, `getRequestTarget()`은 구성된 URI의 **origin-form**을 반환할 것입니다. (URI가 구성되지 않으면 "/"). `withRequestTarget($requestTarget)` 은 지정된 요청 대상으로 새로운 인스턴스를 생성하고, 개발자가 다른 3 개의 요청 대상 형식 (absolute-form, authority-form 및 asterisk-form)을 나타내는 요청 메시지를 생성 할 수 있도록합니다. 사용되는 경우 구성된 URI 인스턴스는 여전히 클라이언트에서 사용할 수 있습니다.이 인스턴스는 서버에 대한 연결을 만드는 데 사용될 수 있습니다.

### 1.5 Server-side Requests

`RequestInterface` 는 HTTP 요청 메시지의 일반적인 표현을 제공합니다. 그러나 서버 측 요청의 경우 서버 측 환경의 특성상 추가 처리가 필요합니다. 서버 측 프로세싱은 CGI (Common Gateway Interface)를 고려해야하며 PHP의 SAPI(Server APIs)를 통한 CGI의 추상화 및 확장이 필요합니다. PHP는 다음과 같은 슈퍼 전역 변수를 통해 입력을 정렬하고 단순화했습니다.

* `$ _COOKIE` : 역직렬화된 HTTP 쿠키에 대한 단순화 된 접근을 제공합니다.
* `$ _GET` : 역직렬화된 쿼리스트링 인수에 대한 단순화 된 접근을 제공합니다.
* `$ _POST` : 역직렬화된 HTTP POST를 통해 제출 된 urlencoded 매개 변수에 대해 단순화 된 액세스를 제공합니다. 일반적으로 메시지 본문을 파싱 한 결과로 간주 될 수 있습니다.
* `$ _FILES` : 파일 업로드와 관련된 일련의 메타 데이터를 제공합니다.
* `$ _SERVER` : CGI / SAPI 환경 변수에 대한 액세스를 제공합니다. 일반적으로 요청 메소드, 요청 스킴, 요청 URI 및 헤더가 포함됩니다.

`ServerRequestInterface` 는 `RequestInterface` 를 확장하여 이러한 다양한 슈퍼 전역 변수를 추상화합니다. 이 방법은 사용자가 슈퍼 전역변수에 연결(coupling)하는 것을 줄이고 요청 사용자가 테스트하는 것을 권장하고 도와줍니다. 서버 요청은 "속성(attributes)"이라는 추가 속성 하나를 제공하여 소비자가 응용 프로그램 관련 규칙 (예 : 경로(path) 일치, scheme 일치, 호스트 일치 등)에 대한 인트로스펙트(introspect), 분해(decompose) 및 일치(match) 기능을 제공합니다. 따라서 서버 요청은 여러 요청자간에 메시징을 제공 할 수도 있습니다.

### 1.6 Uploaded files

`ServerRequestInterface` 는 각 가지(leaf)가 `UploadedFileInterface` 인스턴스로 정규화 된 구조로 업로드 파일 트리를 검색하는 메소드를 제공합니다.

As an example, if you have a form that submits an array of files — e.g., the input name "files", submitting `files[0]` and `files[1]` — PHP will represent this as: `$_FILES` 슈퍼 전역변수는 파일 입력 배열을 다룰 때 잘 알려진 문제점을 가지고 있습니다. 예를 들어, 파일의 배열 (예 : 입력 이름이 "files"인 경우, `files[0]` 및 `files[1]`)을 제출하는 폼이 있는 경우 PHP는 다음과 같이 표현합니다.

```php
array(
    'files' => array(
        'name' => array(
            0 => 'file0.txt',
            1 => 'file1.html',
        ),
        'type' => array(
            0 => 'text/plain',
            1 => 'text/html',
        ),
        /* etc. */
    ),
)
```

아래와 같이 예상되는 것 대신 말이죠

```php
array(
    'files' => array(
        0 => array(
            'name' => 'file0.txt',
            'type' => 'text/plain',
            /* etc. */
        ),
        1 => array(
            'name' => 'file1.html',
            'type' => 'text/html',
            /* etc. */
        ),
    ),
)
```

결과적으로 사용자는 이 언어 구현 세부 사항을 알아야하고 주어진 업로드에 대한 데이터를 수집하기위한 코드를 작성해야합니다.

또한 아래와 같은 파일 업로드를 했을 때 `$ _FILES` 가 비어있는 경우가 있습니다

* HTTP method가 `POST` 아닐 때.
* 유닛 테스팅 중일 때.
* [ReactPHP](http://reactphp.org)와 같은 SAPI가 아닌 환경에서 작동 할 때.

이 경우 데이터를 다르게 시드해야합니다. 예를 들면 다음과 같습니다.

* 프로세스가 메시지 본문을 구문 분석하여 파일 업로드를 발견 할 수 있습니다. 이 경우 구현은 파일 업로드를 파일 시스템에 쓰지 *않고* 대신 메모리, I/O 및 저장소 오버 헤드를 줄이기 위해 스트림으로 래핑하도록 선택할 수 있습니다.
* 단위 테스트 시나리오에서 개발자는 다른 시나리오의 유효성을 검사하고 검증하기 위해 파일 업로드 메타 데이터를 스텁(stub) 또는 모의(mock) 할 수 있어야합니다.

`getUploadedFiles ()`는 사용자를 위해 정규화 된 구조를 제공합니다. 구현은 다음을 기대 합니다.

* 주어진 파일 업로드에 대한 모든 정보를 모아서 `Psr\Http\Message\UploadedFileInterface` 인스턴스를 채웁니다.
* 전송된 트리 구조를 재작성합니다. 각 리프는 트리의 주어진 위치에 대한 적절한 `Psr\Http\Message\UploadedFileInterface` 인스턴스입니다.

참조 된 트리 구조는 파일이 제출 된 명명 구조를 모방해야합니다.

가장 단순한 예를 들어서, 이것은 다음과 같이 제출 된 단일 명명 된 양식 요소(form element) 일 수 있습니다.

```html
<input type="file" name="avatar" />
```

이 경우, `$_FILES`의 구조는 다음과 같습니다

```php
array(
    'avatar' => array(
        'tmp_name' => 'phpUxcOty',
        'name' => 'my-avatar.png',
        'size' => 90996,
        'type' => 'image/png',
        'error' => 0,
    ),
)
```

`getUploadedFiles()` 에 의해 반환된 정규화된 양식은 다음과 같습니다

```php
array(
    'avatar' => /* UploadedFileInterface instance */
)
```

이름에 배열 표기법을 사용하는 입력의 경우

```html
<input type="file" name="my-form[details][avatar]" />
```

`$_FILES`은 다음과 같이 보입니다

```php
array (
    'my-form' => array (
        'name' => array (
            'details' => array (
                'avatar' => 'my-avatar.png',
            ),
        ),
        'type' => array (
            'details' => array (
                'avatar' => 'image/png',
            ),
        ),
        'tmp_name' => array (
            'details' => array (
                'avatar' => 'phpmFLrzD',
            ),
        ),
        'error' => array (
            'details' => array (
                'avatar' => 0,
            ),
        ),
        'size' => array (
            'details' => array (
                'avatar' => 90996,
            ),
        ),
    ),
)
```

그리고 `getUploadedFiles()` 에 의해 반환 된 트리는 다음과 같아야합니다

```php
array(
    'my-form' => array(
        'details' => array(
            'avatar' => /* UploadedFileInterface instance */
        ),
    ),
)
```

경우에 따라 다음과 같이 파일 배열을 지정할 수 있습니다.

```html
Upload an avatar: <input type="file" name="my-form[details][avatars][]" />
Upload an avatar: <input type="file" name="my-form[details][avatars][]" />
```

(예를 들어 자바스크립트를 사용해 추가 파일 업로드 인풋을 생성하여 한 번에 여러 파일을 업로드 할 수 있습니다.)

이 경우, 스펙 구현은 주어진 인덱스에있는 파일과 관련된 모든 정보를 집계해야합니다. 왜냐하면 `$_FILES` 는 다음과 같은 경우 정상적인 구조에서 벗어났기 때문입니다

```php
array (
    'my-form' => array (
        'name' => array (
            'details' => array (
                'avatar' => array (
                    0 => 'my-avatar.png',
                    1 => 'my-avatar2.png',
                    2 => 'my-avatar3.png',
                ),
            ),
        ),
        'type' => array (
            'details' => array (
                'avatar' => array (
                    0 => 'image/png',
                    1 => 'image/png',
                    2 => 'image/png',
                ),
            ),
        ),
        'tmp_name' => array (
            'details' => array (
                'avatar' => array (
                    0 => 'phpmFLrzD',
                    1 => 'phpV2pBil',
                    2 => 'php8RUG8v',
                ),
            ),
        ),
        'error' => array (
            'details' => array (
                'avatar' => array (
                    0 => 0,
                    1 => 0,
                    2 => 0,
                ),
            ),
        ),
        'size' => array (
            'details' => array (
                'avatar' => array (
                    0 => 90996,
                    1 => 90996,
                    3 => 90996,
                ),
            ),
        ),
    ),
)
```

위의 `$_FILES` 배열은 `getUploadedFiles()` 에 의해 반환 될 경우 다음 구조체에 해당합니다

```php
array(
    'my-form' => array(
        'details' => array(
            'avatars' => array(
                0 => /* UploadedFileInterface instance */,
                1 => /* UploadedFileInterface instance */,
                2 => /* UploadedFileInterface instance */,
            ),
        ),
    ),
)
```

사용자는 다음을 사용하여 중첩 배열의 인덱스 `1`에 접근합니다.

```php
$request->getUploadedFiles()['my-form']['details']['avatars'][1];
```

업로드 된 파일 데이터는 파생물이므로 ( `$_FILES` 또는 요청 본문에서 파생 된), mutator 메소드 인 `withUploadedFiles()` 또한 인터페이스에 존재하므로 다른 프로세스에 대한 정규화를 위임 할 수 있습니다.

원래 예제의 경우 사용은 다음과 유사합니다.

```php
$file0 = $request->getUploadedFiles()['files'][0];
$file1 = $request->getUploadedFiles()['files'][1];

printf(
    "Received the files %s and %s",
    $file0->getClientFilename(),
    $file1->getClientFilename()
);

// "Received the files file0.txt and file1.html"
```

이 제안은 또한 구현이 비 SAPI 환경에서 작동 할 수 있음을 보장(recognizes)합니다. 따라서 `UploadedFileInterface` 는 환경에 관계없이 작동이 작동 할 수 있도록 보장하는 메소드를 제공합니다. 특히

* `moveTo($targetPath)` 는 임시 업로드 파일에 직접 `move_uploaded_file()`을 호출하는 안전하고 권장되는 대안으로 제공됩니다. 구현체는 환경에 따라 사용할 올바른 작업을 감지합니다.
* `getStream()` 은 `StreamInterface` 인스턴스를 리턴합니다. 비 SAPI 환경에서 개별 업로드 파일을 직접 파일 대신에 `php://temp` 스트림으로 파싱하는 것이 제안되었습니다. 이 경우 업로드 파일이 없습니다. `getStream()`은 환경에 관계없이 작동하도록 보장됩니다.

예를 들면 다음과 같습니다.

```
// Move a file to an upload directory
$filename = sprintf(
    '%s.%s',
    create_uuid(),
    pathinfo($file0->getClientFilename(), PATHINFO_EXTENSION)
);
$file0->moveTo(DATA_DIR . '/' . $filename);

// Stream a file to Amazon S3.
// Assume $s3wrapper is a PHP stream that will write to S3, and that
// Psr7StreamWrapper is a class that will decorate a StreamInterface as a PHP
// StreamWrapper.
$stream = new Psr7StreamWrapper($file1->getStream());
stream_copy_to_stream($stream, $s3wrapper);
```

## 2. Package

설명 된 인터페이스와 클래스는 [psr/http-message](https://packagist.org/packages/psr/http-message) 패키지의 일부로 제공됩니다.

## 3. Interfaces

### 3.1 `Psr\Http\Message\MessageInterface`

```php
<?php
namespace Psr\Http\Message;

/**
 * HTTP messages consist of requests from a client to a server and responses
 * from a server to a client. This interface defines the methods common to
 * each.
 *
 * Messages are considered immutable; all methods that might change state MUST
 * be implemented such that they retain the internal state of the current
 * message and return an instance that contains the changed state.
 *
 * @see http://www.ietf.org/rfc/rfc7230.txt
 * @see http://www.ietf.org/rfc/rfc7231.txt
 */
interface MessageInterface
{
    /**
     * Retrieves the HTTP protocol version as a string.
     *
     * The string MUST contain only the HTTP version number (e.g., "1.1", "1.0").
     *
     * @return string HTTP protocol version.
     */
    public function getProtocolVersion();

    /**
     * Return an instance with the specified HTTP protocol version.
     *
     * The version string MUST contain only the HTTP version number (e.g.,
     * "1.1", "1.0").
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * new protocol version.
     *
     * @param string $version HTTP protocol version
     * @return static
     */
    public function withProtocolVersion($version);

    /**
     * Retrieves all message header values.
     *
     * The keys represent the header name as it will be sent over the wire, and
     * each value is an array of strings associated with the header.
     *
     *     // Represent the headers as a string
     *     foreach ($message->getHeaders() as $name => $values) {
     *         echo $name . ': ' . implode(', ', $values);
     *     }
     *
     *     // Emit headers iteratively:
     *     foreach ($message->getHeaders() as $name => $values) {
     *         foreach ($values as $value) {
     *             header(sprintf('%s: %s', $name, $value), false);
     *         }
     *     }
     *
     * While header names are not case-sensitive, getHeaders() will preserve the
     * exact case in which headers were originally specified.
     *
     * @return string[][] Returns an associative array of the message's headers.
     *     Each key MUST be a header name, and each value MUST be an array of
     *     strings for that header.
     */
    public function getHeaders();

    /**
     * Checks if a header exists by the given case-insensitive name.
     *
     * @param string $name Case-insensitive header field name.
     * @return bool Returns true if any header names match the given header
     *     name using a case-insensitive string comparison. Returns false if
     *     no matching header name is found in the message.
     */
    public function hasHeader($name);

    /**
     * Retrieves a message header value by the given case-insensitive name.
     *
     * This method returns an array of all the header values of the given
     * case-insensitive header name.
     *
     * If the header does not appear in the message, this method MUST return an
     * empty array.
     *
     * @param string $name Case-insensitive header field name.
     * @return string[] An array of string values as provided for the given
     *    header. If the header does not appear in the message, this method MUST
     *    return an empty array.
     */
    public function getHeader($name);

    /**
     * Retrieves a comma-separated string of the values for a single header.
     *
     * This method returns all of the header values of the given
     * case-insensitive header name as a string concatenated together using
     * a comma.
     *
     * NOTE: Not all header values may be appropriately represented using
     * comma concatenation. For such headers, use getHeader() instead
     * and supply your own delimiter when concatenating.
     *
     * If the header does not appear in the message, this method MUST return
     * an empty string.
     *
     * @param string $name Case-insensitive header field name.
     * @return string A string of values as provided for the given header
     *    concatenated together using a comma. If the header does not appear in
     *    the message, this method MUST return an empty string.
     */
    public function getHeaderLine($name);

    /**
     * Return an instance with the provided value replacing the specified header.
     *
     * While header names are case-insensitive, the casing of the header will
     * be preserved by this function, and returned from getHeaders().
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * new and/or updated header and value.
     *
     * @param string $name Case-insensitive header field name.
     * @param string|string[] $value Header value(s).
     * @return static
     * @throws \InvalidArgumentException for invalid header names or values.
     */
    public function withHeader($name, $value);

    /**
     * Return an instance with the specified header appended with the given value.
     *
     * Existing values for the specified header will be maintained. The new
     * value(s) will be appended to the existing list. If the header did not
     * exist previously, it will be added.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * new header and/or value.
     *
     * @param string $name Case-insensitive header field name to add.
     * @param string|string[] $value Header value(s).
     * @return static
     * @throws \InvalidArgumentException for invalid header names.
     * @throws \InvalidArgumentException for invalid header values.
     */
    public function withAddedHeader($name, $value);

    /**
     * Return an instance without the specified header.
     *
     * Header resolution MUST be done without case-sensitivity.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that removes
     * the named header.
     *
     * @param string $name Case-insensitive header field name to remove.
     * @return static
     */
    public function withoutHeader($name);

    /**
     * Gets the body of the message.
     *
     * @return StreamInterface Returns the body as a stream.
     */
    public function getBody();

    /**
     * Return an instance with the specified message body.
     *
     * The body MUST be a StreamInterface object.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return a new instance that has the
     * new body stream.
     *
     * @param StreamInterface $body Body.
     * @return static
     * @throws \InvalidArgumentException When the body is not valid.
     */
    public function withBody(StreamInterface $body);
}
```

### 3.2 `Psr\Http\Message\RequestInterface`

```php
<?php
namespace Psr\Http\Message;

/**
 * Representation of an outgoing, client-side request.
 *
 * Per the HTTP specification, this interface includes properties for
 * each of the following:
 *
 * - Protocol version
 * - HTTP method
 * - URI
 * - Headers
 * - Message body
 *
 * During construction, implementations MUST attempt to set the Host header from
 * a provided URI if no Host header is provided.
 *
 * Requests are considered immutable; all methods that might change state MUST
 * be implemented such that they retain the internal state of the current
 * message and return an instance that contains the changed state.
 */
interface RequestInterface extends MessageInterface
{
    /**
     * Retrieves the message's request target.
     *
     * Retrieves the message's request-target either as it will appear (for
     * clients), as it appeared at request (for servers), or as it was
     * specified for the instance (see withRequestTarget()).
     *
     * In most cases, this will be the origin-form of the composed URI,
     * unless a value was provided to the concrete implementation (see
     * withRequestTarget() below).
     *
     * If no URI is available, and no request-target has been specifically
     * provided, this method MUST return the string "/".
     *
     * @return string
     */
    public function getRequestTarget();

    /**
     * Return an instance with the specific request-target.
     *
     * If the request needs a non-origin-form request-target — e.g., for
     * specifying an absolute-form, authority-form, or asterisk-form —
     * this method may be used to create an instance with the specified
     * request-target, verbatim.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * changed request target.
     *
     * @see http://tools.ietf.org/html/rfc7230#section-5.3 (for the various
     *     request-target forms allowed in request messages)
     * @param mixed $requestTarget
     * @return static
     */
    public function withRequestTarget($requestTarget);

    /**
     * Retrieves the HTTP method of the request.
     *
     * @return string Returns the request method.
     */
    public function getMethod();

    /**
     * Return an instance with the provided HTTP method.
     *
     * While HTTP method names are typically all uppercase characters, HTTP
     * method names are case-sensitive and thus implementations SHOULD NOT
     * modify the given string.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * changed request method.
     *
     * @param string $method Case-sensitive method.
     * @return static
     * @throws \InvalidArgumentException for invalid HTTP methods.
     */
    public function withMethod($method);

    /**
     * Retrieves the URI instance.
     *
     * This method MUST return a UriInterface instance.
     *
     * @see http://tools.ietf.org/html/rfc3986#section-4.3
     * @return UriInterface Returns a UriInterface instance
     *     representing the URI of the request.
     */
    public function getUri();

    /**
     * Returns an instance with the provided URI.
     *
     * This method MUST update the Host header of the returned request by
     * default if the URI contains a host component. If the URI does not
     * contain a host component, any pre-existing Host header MUST be carried
     * over to the returned request.
     *
     * You can opt-in to preserving the original state of the Host header by
     * setting `$preserveHost` to `true`. When `$preserveHost` is set to
     * `true`, this method interacts with the Host header in the following ways:
     *
     * - If the Host header is missing or empty, and the new URI contains
     *   a host component, this method MUST update the Host header in the returned
     *   request.
     * - If the Host header is missing or empty, and the new URI does not contain a
     *   host component, this method MUST NOT update the Host header in the returned
     *   request.
     * - If a Host header is present and non-empty, this method MUST NOT update
     *   the Host header in the returned request.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * new UriInterface instance.
     *
     * @see http://tools.ietf.org/html/rfc3986#section-4.3
     * @param UriInterface $uri New request URI to use.
     * @param bool $preserveHost Preserve the original state of the Host header.
     * @return static
     */
    public function withUri(UriInterface $uri, $preserveHost = false);
}
```

#### 3.2.1 `Psr\Http\Message\ServerRequestInterface`

```php
<?php
namespace Psr\Http\Message;

/**
 * Representation of an incoming, server-side HTTP request.
 *
 * Per the HTTP specification, this interface includes properties for
 * each of the following:
 *
 * - Protocol version
 * - HTTP method
 * - URI
 * - Headers
 * - Message body
 *
 * Additionally, it encapsulates all data as it has arrived at the
 * application from the CGI and/or PHP environment, including:
 *
 * - The values represented in $_SERVER.
 * - Any cookies provided (generally via $_COOKIE)
 * - Query string arguments (generally via $_GET, or as parsed via parse_str())
 * - Upload files, if any (as represented by $_FILES)
 * - Deserialized body parameters (generally from $_POST)
 *
 * $_SERVER values MUST be treated as immutable, as they represent application
 * state at the time of request; as such, no methods are provided to allow
 * modification of those values. The other values provide such methods, as they
 * can be restored from $_SERVER or the request body, and may need treatment
 * during the application (e.g., body parameters may be deserialized based on
 * content type).
 *
 * Additionally, this interface recognizes the utility of introspecting a
 * request to derive and match additional parameters (e.g., via URI path
 * matching, decrypting cookie values, deserializing non-form-encoded body
 * content, matching authorization headers to users, etc). These parameters
 * are stored in an "attributes" property.
 *
 * Requests are considered immutable; all methods that might change state MUST
 * be implemented such that they retain the internal state of the current
 * message and return an instance that contains the changed state.
 */
interface ServerRequestInterface extends RequestInterface
{
    /**
     * Retrieve server parameters.
     *
     * Retrieves data related to the incoming request environment,
     * typically derived from PHP's $_SERVER superglobal. The data IS NOT
     * REQUIRED to originate from $_SERVER.
     *
     * @return array
     */
    public function getServerParams();

    /**
     * Retrieve cookies.
     *
     * Retrieves cookies sent by the client to the server.
     *
     * The data MUST be compatible with the structure of the $_COOKIE
     * superglobal.
     *
     * @return array
     */
    public function getCookieParams();

    /**
     * Return an instance with the specified cookies.
     *
     * The data IS NOT REQUIRED to come from the $_COOKIE superglobal, but MUST
     * be compatible with the structure of $_COOKIE. Typically, this data will
     * be injected at instantiation.
     *
     * This method MUST NOT update the related Cookie header of the request
     * instance, nor related values in the server params.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * updated cookie values.
     *
     * @param array $cookies Array of key/value pairs representing cookies.
     * @return static
     */
    public function withCookieParams(array $cookies);

    /**
     * Retrieve query string arguments.
     *
     * Retrieves the deserialized query string arguments, if any.
     *
     * Note: the query params might not be in sync with the URI or server
     * params. If you need to ensure you are only getting the original
     * values, you may need to parse the query string from `getUri()->getQuery()`
     * or from the `QUERY_STRING` server param.
     *
     * @return array
     */
    public function getQueryParams();

    /**
     * Return an instance with the specified query string arguments.
     *
     * These values SHOULD remain immutable over the course of the incoming
     * request. They MAY be injected during instantiation, such as from PHP's
     * $_GET superglobal, or MAY be derived from some other value such as the
     * URI. In cases where the arguments are parsed from the URI, the data
     * MUST be compatible with what PHP's parse_str() would return for
     * purposes of how duplicate query parameters are handled, and how nested
     * sets are handled.
     *
     * Setting query string arguments MUST NOT change the URI stored by the
     * request, nor the values in the server params.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * updated query string arguments.
     *
     * @param array $query Array of query string arguments, typically from
     *     $_GET.
     * @return static
     */
    public function withQueryParams(array $query);

    /**
     * Retrieve normalized file upload data.
     *
     * This method returns upload metadata in a normalized tree, with each leaf
     * an instance of Psr\Http\Message\UploadedFileInterface.
     *
     * These values MAY be prepared from $_FILES or the message body during
     * instantiation, or MAY be injected via withUploadedFiles().
     *
     * @return array An array tree of UploadedFileInterface instances; an empty
     *     array MUST be returned if no data is present.
     */
    public function getUploadedFiles();

    /**
     * Create a new instance with the specified uploaded files.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * updated body parameters.
     *
     * @param array $uploadedFiles An array tree of UploadedFileInterface instances.
     * @return static
     * @throws \InvalidArgumentException if an invalid structure is provided.
     */
    public function withUploadedFiles(array $uploadedFiles);

    /**
     * Retrieve any parameters provided in the request body.
     *
     * If the request Content-Type is either application/x-www-form-urlencoded
     * or multipart/form-data, and the request method is POST, this method MUST
     * return the contents of $_POST.
     *
     * Otherwise, this method may return any results of deserializing
     * the request body content; as parsing returns structured content, the
     * potential types MUST be arrays or objects only. A null value indicates
     * the absence of body content.
     *
     * @return null|array|object The deserialized body parameters, if any.
     *     These will typically be an array or object.
     */
    public function getParsedBody();

    /**
     * Return an instance with the specified body parameters.
     *
     * These MAY be injected during instantiation.
     *
     * If the request Content-Type is either application/x-www-form-urlencoded
     * or multipart/form-data, and the request method is POST, use this method
     * ONLY to inject the contents of $_POST.
     *
     * The data IS NOT REQUIRED to come from $_POST, but MUST be the results of
     * deserializing the request body content. Deserialization/parsing returns
     * structured data, and, as such, this method ONLY accepts arrays or objects,
     * or a null value if nothing was available to parse.
     *
     * As an example, if content negotiation determines that the request data
     * is a JSON payload, this method could be used to create a request
     * instance with the deserialized parameters.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * updated body parameters.
     *
     * @param null|array|object $data The deserialized body data. This will
     *     typically be in an array or object.
     * @return static
     * @throws \InvalidArgumentException if an unsupported argument type is
     *     provided.
     */
    public function withParsedBody($data);

    /**
     * Retrieve attributes derived from the request.
     *
     * The request "attributes" may be used to allow injection of any
     * parameters derived from the request: e.g., the results of path
     * match operations; the results of decrypting cookies; the results of
     * deserializing non-form-encoded message bodies; etc. Attributes
     * will be application and request specific, and CAN be mutable.
     *
     * @return mixed[] Attributes derived from the request.
     */
    public function getAttributes();

    /**
     * Retrieve a single derived request attribute.
     *
     * Retrieves a single derived request attribute as described in
     * getAttributes(). If the attribute has not been previously set, returns
     * the default value as provided.
     *
     * This method obviates the need for a hasAttribute() method, as it allows
     * specifying a default value to return if the attribute is not found.
     *
     * @see getAttributes()
     * @param string $name The attribute name.
     * @param mixed $default Default value to return if the attribute does not exist.
     * @return mixed
     */
    public function getAttribute($name, $default = null);

    /**
     * Return an instance with the specified derived request attribute.
     *
     * This method allows setting a single derived request attribute as
     * described in getAttributes().
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * updated attribute.
     *
     * @see getAttributes()
     * @param string $name The attribute name.
     * @param mixed $value The value of the attribute.
     * @return static
     */
    public function withAttribute($name, $value);

    /**
     * Return an instance that removes the specified derived request attribute.
     *
     * This method allows removing a single derived request attribute as
     * described in getAttributes().
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that removes
     * the attribute.
     *
     * @see getAttributes()
     * @param string $name The attribute name.
     * @return static
     */
    public function withoutAttribute($name);
}
```

### 3.3 `Psr\Http\Message\ResponseInterface`

```php
<?php
namespace Psr\Http\Message;

/**
 * Representation of an outgoing, server-side response.
 *
 * Per the HTTP specification, this interface includes properties for
 * each of the following:
 *
 * - Protocol version
 * - Status code and reason phrase
 * - Headers
 * - Message body
 *
 * Responses are considered immutable; all methods that might change state MUST
 * be implemented such that they retain the internal state of the current
 * message and return an instance that contains the changed state.
 */
interface ResponseInterface extends MessageInterface
{
    /**
     * Gets the response status code.
     *
     * The status code is a 3-digit integer result code of the server's attempt
     * to understand and satisfy the request.
     *
     * @return int Status code.
     */
    public function getStatusCode();

    /**
     * Return an instance with the specified status code and, optionally, reason phrase.
     *
     * If no reason phrase is specified, implementations MAY choose to default
     * to the RFC 7231 or IANA recommended reason phrase for the response's
     * status code.
     *
     * This method MUST be implemented in such a way as to retain the
     * immutability of the message, and MUST return an instance that has the
     * updated status and reason phrase.
     *
     * @see http://tools.ietf.org/html/rfc7231#section-6
     * @see http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
     * @param int $code The 3-digit integer result code to set.
     * @param string $reasonPhrase The reason phrase to use with the
     *     provided status code; if none is provided, implementations MAY
     *     use the defaults as suggested in the HTTP specification.
     * @return static
     * @throws \InvalidArgumentException For invalid status code arguments.
     */
    public function withStatus($code, $reasonPhrase = '');

    /**
     * Gets the response reason phrase associated with the status code.
     *
     * Because a reason phrase is not a required element in a response
     * status line, the reason phrase value MAY be empty. Implementations MAY
     * choose to return the default RFC 7231 recommended reason phrase (or those
     * listed in the IANA HTTP Status Code Registry) for the response's
     * status code.
     *
     * @see http://tools.ietf.org/html/rfc7231#section-6
     * @see http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
     * @return string Reason phrase; must return an empty string if none present.
     */
    public function getReasonPhrase();
}
```

### 3.4 `Psr\Http\Message\StreamInterface`

```php
<?php
namespace Psr\Http\Message;

/**
 * Describes a data stream.
 *
 * Typically, an instance will wrap a PHP stream; this interface provides
 * a wrapper around the most common operations, including serialization of
 * the entire stream to a string.
 */
interface StreamInterface
{
    /**
     * Reads all data from the stream into a string, from the beginning to end.
     *
     * This method MUST attempt to seek to the beginning of the stream before
     * reading data and read the stream until the end is reached.
     *
     * Warning: This could attempt to load a large amount of data into memory.
     *
     * This method MUST NOT raise an exception in order to conform with PHP's
     * string casting operations.
     *
     * @see http://php.net/manual/en/language.oop5.magic.php#object.tostring
     * @return string
     */
    public function __toString();

    /**
     * Closes the stream and any underlying resources.
     *
     * @return void
     */
    public function close();

    /**
     * Separates any underlying resources from the stream.
     *
     * After the stream has been detached, the stream is in an unusable state.
     *
     * @return resource|null Underlying PHP stream, if any
     */
    public function detach();

    /**
     * Get the size of the stream if known.
     *
     * @return int|null Returns the size in bytes if known, or null if unknown.
     */
    public function getSize();

    /**
     * Returns the current position of the file read/write pointer
     *
     * @return int Position of the file pointer
     * @throws \RuntimeException on error.
     */
    public function tell();

    /**
     * Returns true if the stream is at the end of the stream.
     *
     * @return bool
     */
    public function eof();

    /**
     * Returns whether or not the stream is seekable.
     *
     * @return bool
     */
    public function isSeekable();

    /**
     * Seek to a position in the stream.
     *
     * @see http://www.php.net/manual/en/function.fseek.php
     * @param int $offset Stream offset
     * @param int $whence Specifies how the cursor position will be calculated
     *     based on the seek offset. Valid values are identical to the built-in
     *     PHP $whence values for `fseek()`.  SEEK_SET: Set position equal to
     *     offset bytes SEEK_CUR: Set position to current location plus offset
     *     SEEK_END: Set position to end-of-stream plus offset.
     * @throws \RuntimeException on failure.
     */
    public function seek($offset, $whence = SEEK_SET);

    /**
     * Seek to the beginning of the stream.
     *
     * If the stream is not seekable, this method will raise an exception;
     * otherwise, it will perform a seek(0).
     *
     * @see seek()
     * @see http://www.php.net/manual/en/function.fseek.php
     * @throws \RuntimeException on failure.
     */
    public function rewind();

    /**
     * Returns whether or not the stream is writable.
     *
     * @return bool
     */
    public function isWritable();

    /**
     * Write data to the stream.
     *
     * @param string $string The string that is to be written.
     * @return int Returns the number of bytes written to the stream.
     * @throws \RuntimeException on failure.
     */
    public function write($string);

    /**
     * Returns whether or not the stream is readable.
     *
     * @return bool
     */
    public function isReadable();

    /**
     * Read data from the stream.
     *
     * @param int $length Read up to $length bytes from the object and return
     *     them. Fewer than $length bytes may be returned if underlying stream
     *     call returns fewer bytes.
     * @return string Returns the data read from the stream, or an empty string
     *     if no bytes are available.
     * @throws \RuntimeException if an error occurs.
     */
    public function read($length);

    /**
     * Returns the remaining contents in a string
     *
     * @return string
     * @throws \RuntimeException if unable to read.
     * @throws \RuntimeException if error occurs while reading.
     */
    public function getContents();

    /**
     * Get stream metadata as an associative array or retrieve a specific key.
     *
     * The keys returned are identical to the keys returned from PHP's
     * stream_get_meta_data() function.
     *
     * @see http://php.net/manual/en/function.stream-get-meta-data.php
     * @param string $key Specific metadata to retrieve.
     * @return array|mixed|null Returns an associative array if no key is
     *     provided. Returns a specific key value if a key is provided and the
     *     value is found, or null if the key is not found.
     */
    public function getMetadata($key = null);
}
```

### 3.5 `Psr\Http\Message\UriInterface`

```php
<?php
namespace Psr\Http\Message;

/**
 * Value object representing a URI.
 *
 * This interface is meant to represent URIs according to RFC 3986 and to
 * provide methods for most common operations. Additional functionality for
 * working with URIs can be provided on top of the interface or externally.
 * Its primary use is for HTTP requests, but may also be used in other
 * contexts.
 *
 * Instances of this interface are considered immutable; all methods that
 * might change state MUST be implemented such that they retain the internal
 * state of the current instance and return an instance that contains the
 * changed state.
 *
 * Typically the Host header will also be present in the request message.
 * For server-side requests, the scheme will typically be discoverable in the
 * server parameters.
 *
 * @see http://tools.ietf.org/html/rfc3986 (the URI specification)
 */
interface UriInterface
{
    /**
     * Retrieve the scheme component of the URI.
     *
     * If no scheme is present, this method MUST return an empty string.
     *
     * The value returned MUST be normalized to lowercase, per RFC 3986
     * Section 3.1.
     *
     * The trailing ":" character is not part of the scheme and MUST NOT be
     * added.
     *
     * @see https://tools.ietf.org/html/rfc3986#section-3.1
     * @return string The URI scheme.
     */
    public function getScheme();

    /**
     * Retrieve the authority component of the URI.
     *
     * If no authority information is present, this method MUST return an empty
     * string.
     *
     * The authority syntax of the URI is:
     *
     * <pre>
     * [user-info@]host[:port]
     * </pre>
     *
     * If the port component is not set or is the standard port for the current
     * scheme, it SHOULD NOT be included.
     *
     * @see https://tools.ietf.org/html/rfc3986#section-3.2
     * @return string The URI authority, in "[user-info@]host[:port]" format.
     */
    public function getAuthority();

    /**
     * Retrieve the user information component of the URI.
     *
     * If no user information is present, this method MUST return an empty
     * string.
     *
     * If a user is present in the URI, this will return that value;
     * additionally, if the password is also present, it will be appended to the
     * user value, with a colon (":") separating the values.
     *
     * The trailing "@" character is not part of the user information and MUST
     * NOT be added.
     *
     * @return string The URI user information, in "username[:password]" format.
     */
    public function getUserInfo();

    /**
     * Retrieve the host component of the URI.
     *
     * If no host is present, this method MUST return an empty string.
     *
     * The value returned MUST be normalized to lowercase, per RFC 3986
     * Section 3.2.2.
     *
     * @see http://tools.ietf.org/html/rfc3986#section-3.2.2
     * @return string The URI host.
     */
    public function getHost();

    /**
     * Retrieve the port component of the URI.
     *
     * If a port is present, and it is non-standard for the current scheme,
     * this method MUST return it as an integer. If the port is the standard port
     * used with the current scheme, this method SHOULD return null.
     *
     * If no port is present, and no scheme is present, this method MUST return
     * a null value.
     *
     * If no port is present, but a scheme is present, this method MAY return
     * the standard port for that scheme, but SHOULD return null.
     *
     * @return null|int The URI port.
     */
    public function getPort();

    /**
     * Retrieve the path component of the URI.
     *
     * The path can either be empty or absolute (starting with a slash) or
     * rootless (not starting with a slash). Implementations MUST support all
     * three syntaxes.
     *
     * Normally, the empty path "" and absolute path "/" are considered equal as
     * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically
     * do this normalization because in contexts with a trimmed base path, e.g.
     * the front controller, this difference becomes significant. It's the task
     * of the user to handle both "" and "/".
     *
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
     * any characters. To determine what characters to encode, please refer to
     * RFC 3986, Sections 2 and 3.3.
     *
     * As an example, if the value should include a slash ("/") not intended as
     * delimiter between path segments, that value MUST be passed in encoded
     * form (e.g., "%2F") to the instance.
     *
     * @see https://tools.ietf.org/html/rfc3986#section-2
     * @see https://tools.ietf.org/html/rfc3986#section-3.3
     * @return string The URI path.
     */
    public function getPath();

    /**
     * Retrieve the query string of the URI.
     *
     * If no query string is present, this method MUST return an empty string.
     *
     * The leading "?" character is not part of the query and MUST NOT be
     * added.
     *
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
     * any characters. To determine what characters to encode, please refer to
     * RFC 3986, Sections 2 and 3.4.
     *
     * As an example, if a value in a key/value pair of the query string should
     * include an ampersand ("&") not intended as a delimiter between values,
     * that value MUST be passed in encoded form (e.g., "%26") to the instance.
     *
     * @see https://tools.ietf.org/html/rfc3986#section-2
     * @see https://tools.ietf.org/html/rfc3986#section-3.4
     * @return string The URI query string.
     */
    public function getQuery();

    /**
     * Retrieve the fragment component of the URI.
     *
     * If no fragment is present, this method MUST return an empty string.
     *
     * The leading "#" character is not part of the fragment and MUST NOT be
     * added.
     *
     * The value returned MUST be percent-encoded, but MUST NOT double-encode
     * any characters. To determine what characters to encode, please refer to
     * RFC 3986, Sections 2 and 3.5.
     *
     * @see https://tools.ietf.org/html/rfc3986#section-2
     * @see https://tools.ietf.org/html/rfc3986#section-3.5
     * @return string The URI fragment.
     */
    public function getFragment();

    /**
     * Return an instance with the specified scheme.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified scheme.
     *
     * Implementations MUST support the schemes "http" and "https" case
     * insensitively, and MAY accommodate other schemes if required.
     *
     * An empty scheme is equivalent to removing the scheme.
     *
     * @param string $scheme The scheme to use with the new instance.
     * @return static A new instance with the specified scheme.
     * @throws \InvalidArgumentException for invalid schemes.
     * @throws \InvalidArgumentException for unsupported schemes.
     */
    public function withScheme($scheme);

    /**
     * Return an instance with the specified user information.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified user information.
     *
     * Password is optional, but the user information MUST include the
     * user; an empty string for the user is equivalent to removing user
     * information.
     *
     * @param string $user The user name to use for authority.
     * @param null|string $password The password associated with $user.
     * @return static A new instance with the specified user information.
     */
    public function withUserInfo($user, $password = null);

    /**
     * Return an instance with the specified host.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified host.
     *
     * An empty host value is equivalent to removing the host.
     *
     * @param string $host The hostname to use with the new instance.
     * @return static A new instance with the specified host.
     * @throws \InvalidArgumentException for invalid hostnames.
     */
    public function withHost($host);

    /**
     * Return an instance with the specified port.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified port.
     *
     * Implementations MUST raise an exception for ports outside the
     * established TCP and UDP port ranges.
     *
     * A null value provided for the port is equivalent to removing the port
     * information.
     *
     * @param null|int $port The port to use with the new instance; a null value
     *     removes the port information.
     * @return static A new instance with the specified port.
     * @throws \InvalidArgumentException for invalid ports.
     */
    public function withPort($port);

    /**
     * Return an instance with the specified path.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified path.
     *
     * The path can either be empty or absolute (starting with a slash) or
     * rootless (not starting with a slash). Implementations MUST support all
     * three syntaxes.
     *
     * If an HTTP path is intended to be host-relative rather than path-relative
     * then it must begin with a slash ("/"). HTTP paths not starting with a slash
     * are assumed to be relative to some base path known to the application or
     * consumer.
     *
     * Users can provide both encoded and decoded path characters.
     * Implementations ensure the correct encoding as outlined in getPath().
     *
     * @param string $path The path to use with the new instance.
     * @return static A new instance with the specified path.
     * @throws \InvalidArgumentException for invalid paths.
     */
    public function withPath($path);

    /**
     * Return an instance with the specified query string.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified query string.
     *
     * Users can provide both encoded and decoded query characters.
     * Implementations ensure the correct encoding as outlined in getQuery().
     *
     * An empty query string value is equivalent to removing the query string.
     *
     * @param string $query The query string to use with the new instance.
     * @return static A new instance with the specified query string.
     * @throws \InvalidArgumentException for invalid query strings.
     */
    public function withQuery($query);

    /**
     * Return an instance with the specified URI fragment.
     *
     * This method MUST retain the state of the current instance, and return
     * an instance that contains the specified URI fragment.
     *
     * Users can provide both encoded and decoded fragment characters.
     * Implementations ensure the correct encoding as outlined in getFragment().
     *
     * An empty fragment value is equivalent to removing the fragment.
     *
     * @param string $fragment The fragment to use with the new instance.
     * @return static A new instance with the specified fragment.
     */
    public function withFragment($fragment);

    /**
     * Return the string representation as a URI reference.
     *
     * Depending on which components of the URI are present, the resulting
     * string is either a full URI or relative reference according to RFC 3986,
     * Section 4.1. The method concatenates the various components of the URI,
     * using the appropriate delimiters:
     *
     * - If a scheme is present, it MUST be suffixed by ":".
     * - If an authority is present, it MUST be prefixed by "//".
     * - The path can be concatenated without delimiters. But there are two
     *   cases where the path has to be adjusted to make the URI reference
     *   valid as PHP does not allow to throw an exception in __toString():
     *     - If the path is rootless and an authority is present, the path MUST
     *       be prefixed by "/".
     *     - If the path is starting with more than one "/" and no authority is
     *       present, the starting slashes MUST be reduced to one.
     * - If a query is present, it MUST be prefixed by "?".
     * - If a fragment is present, it MUST be prefixed by "#".
     *
     * @see http://tools.ietf.org/html/rfc3986#section-4.1
     * @return string
     */
    public function __toString();
}
```

### 3.6 `Psr\Http\Message\UploadedFileInterface`

```php
<?php
namespace Psr\Http\Message;

/**
 * Value object representing a file uploaded through an HTTP request.
 *
 * Instances of this interface are considered immutable; all methods that
 * might change state MUST be implemented such that they retain the internal
 * state of the current instance and return an instance that contains the
 * changed state.
 */
interface UploadedFileInterface
{
    /**
     * Retrieve a stream representing the uploaded file.
     *
     * This method MUST return a StreamInterface instance, representing the
     * uploaded file. The purpose of this method is to allow utilizing native PHP
     * stream functionality to manipulate the file upload, such as
     * stream_copy_to_stream() (though the result will need to be decorated in a
     * native PHP stream wrapper to work with such functions).
     *
     * If the moveTo() method has been called previously, this method MUST raise
     * an exception.
     *
     * @return StreamInterface Stream representation of the uploaded file.
     * @throws \RuntimeException in cases when no stream is available.
     * @throws \RuntimeException in cases when no stream can be created.
     */
    public function getStream();

    /**
     * Move the uploaded file to a new location.
     *
     * Use this method as an alternative to move_uploaded_file(). This method is
     * guaranteed to work in both SAPI and non-SAPI environments.
     * Implementations must determine which environment they are in, and use the
     * appropriate method (move_uploaded_file(), rename(), or a stream
     * operation) to perform the operation.
     *
     * $targetPath may be an absolute path, or a relative path. If it is a
     * relative path, resolution should be the same as used by PHP's rename()
     * function.
     *
     * The original file or stream MUST be removed on completion.
     *
     * If this method is called more than once, any subsequent calls MUST raise
     * an exception.
     *
     * When used in an SAPI environment where $_FILES is populated, when writing
     * files via moveTo(), is_uploaded_file() and move_uploaded_file() SHOULD be
     * used to ensure permissions and upload status are verified correctly.
     *
     * If you wish to move to a stream, use getStream(), as SAPI operations
     * cannot guarantee writing to stream destinations.
     *
     * @see http://php.net/is_uploaded_file
     * @see http://php.net/move_uploaded_file
     * @param string $targetPath Path to which to move the uploaded file.
     * @throws \InvalidArgumentException if the $targetPath specified is invalid.
     * @throws \RuntimeException on any error during the move operation.
     * @throws \RuntimeException on the second or subsequent call to the method.
     */
    public function moveTo($targetPath);

    /**
     * Retrieve the file size.
     *
     * Implementations SHOULD return the value stored in the "size" key of
     * the file in the $_FILES array if available, as PHP calculates this based
     * on the actual size transmitted.
     *
     * @return int|null The file size in bytes or null if unknown.
     */
    public function getSize();

    /**
     * Retrieve the error associated with the uploaded file.
     *
     * The return value MUST be one of PHP's UPLOAD_ERR_XXX constants.
     *
     * If the file was uploaded successfully, this method MUST return
     * UPLOAD_ERR_OK.
     *
     * Implementations SHOULD return the value stored in the "error" key of
     * the file in the $_FILES array.
     *
     * @see http://php.net/manual/en/features.file-upload.errors.php
     * @return int One of PHP's UPLOAD_ERR_XXX constants.
     */
    public function getError();

    /**
     * Retrieve the filename sent by the client.
     *
     * Do not trust the value returned by this method. A client could send
     * a malicious filename with the intention to corrupt or hack your
     * application.
     *
     * Implementations SHOULD return the value stored in the "name" key of
     * the file in the $_FILES array.
     *
     * @return string|null The filename sent by the client or null if none
     *     was provided.
     */
    public function getClientFilename();

    /**
     * Retrieve the media type sent by the client.
     *
     * Do not trust the value returned by this method. A client could send
     * a malicious media type with the intention to corrupt or hack your
     * application.
     *
     * Implementations SHOULD return the value stored in the "type" key of
     * the file in the $_FILES array.
     *
     * @return string|null The media type sent by the client or null if none
     *     was provided.
     */
    public function getClientMediaType();
}
```


# PSR-11-container

이 문서는 의존성 주입 컨테이너에 대한 공통 인터페이스를 설명합니다.

`ContainerInterface`에 의해 설정된 목표는 프레임 워크와 라이브러리가 컨테이너를 사용하여 객체와 매개 변수 (이 문서의 나머지 부분에서 *항목*이라고 함)를 얻는 방법을 표준화하는 것입니다.

이 문서에서 핵심이 되는 단어는 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", "OPTIONAL" 입니다. 이것은 [RFC 2119](http://tools.ietf.org/html/rfc2119)에 설명 된대로 해석해야 합니다.

> 역자주: 위의 키워드는 아래의 번역문에 괄호안에 표시하였습니다

이 문서에서 `implementor` 라는 단어는 의존성 삽입 관련 라이브러리나 프레임워크에서 `ContainerInterface`를 구현하는 누군가로 해석되어야합니다. 의존성 주입 컨테이너 (DIC)의 사용자는 `사용자`라고합니다.

## 1. 명세서

### 1.1 기본

#### 1.1.1 엔트리 식별자

엔트리 식별자는 컨테이너 내의 항목을 고유하게 식별하는 적어도 하나의 문자로 구성된 PHP에서 유효한 문자열입니다. 엔트리 식별자는 불투명 한 문자열이므로 호출자는 문자열의 구조가 의미를 전달한다고 가정해서는 안됩니다(SHOULD NOT).

#### 1.1.2 컨테이너에서 읽어오기

* `Psr\Container\ContainerInterface`는 `get`과 `has` 두가지 메서드를 제공합니다.
* `get` 은 하나의 필수 매개 변수를 가집니다. 엔트리 식별자는 반드시 문자열이어야 합니다 (MUST). `get` 은 어떤 값이든 (하나의 *mixed* 값) 반환 할 수 있고 식별자가 컨테이너에 알려지지 않은 경우에는 `NotFoundExceptionInterface` 예외를 던집니다. 동일한 식별자를 가진 `get` 에 대한 두 번의 연속적인 호출은 동일한 값을 반환해야합니다 (SHOULD). 그러나 `implementor` 디자인 또는 `user` 설정에 따라 다른 값들이 반환 될 수 있습니다. 그래서 `user`는 2번의 연속적인 호출에서 같은 값을 얻는 것에 의존하지 않아야합니다 (SHOULD NOT).
* `has` 는 하나의 고유 한 매개 변수를 가집니다. 엔트리 식별자는 반드시 문자열이어야 합니다(MUST). `has` 는 엔트리 식별자가 컨테이너에 알려지면 `true` 를 리턴하고 그렇지 않으면 `false` 를 리턴해야합니다 (MUST). `has($id)` 가 false 를 반환하면 `get($id)` 는 `NotFoundExceptionInterface` 예외를 던져야합니다.

### 1.2 Exceptions

컨테이너에 의해 직접 던져진 예외는 [`Psr\Container\ContainerExceptionInterface`](#container-exception)을 상속해야한다(SHOULD).

존재하지 않는 id를 가진 `get` 메서드에 대한 호출은 [`Psr\Container\NotFoundExceptionInterface`](#not-found-exception) 예외를 던져야 한다(MUST).

### 1.3 권장 사용법

사용자는 객체가 *자신의 의존성*을 검색 할 수 있도록 컨테이너에 객체를 넘겨서는 안됩니다(SHOULD NOT). 즉, 컨테이너는 [Service Locator](https://en.wikipedia.org/wiki/Service_locator_pattern)로 사용됩니다. 이런 행동은 일반적으로 방해가 됩니다

자세한 내용은 META 문서의 섹션 4를 참조하십시오.

## 2. Package

설명한 인터페이스와 클래스 및 예외 사항은 [psr/container](https://packagist.org/packages/psr/container) 패키지의 일부로 제공됩니다.

PSR 컨테이너 구현체를 제공하는 패키지는 `psr/container-implementation` 을 제공한다고 선언해야합니다.

구현이 필요한 프로젝트에는 `psr/container-implementation`의 `1.0.0` 을 요구합니다.

## 3. Interfaces

### 3.1. `Psr\Container\ContainerInterface`

```php
<?php
namespace Psr\Container;

/**
 * Describes the interface of a container that exposes methods to read its entries.
 */
interface ContainerInterface
{
    /**
     * Finds an entry of the container by its identifier and returns it.
     *
     * @param string $id Identifier of the entry to look for.
     *
     * @throws NotFoundExceptionInterface  No entry was found for **this** identifier.
     * @throws ContainerExceptionInterface Error while retrieving the entry.
     *
     * @return mixed Entry.
     */
    public function get($id);

    /**
     * Returns true if the container can return an entry for the given identifier.
     * Returns false otherwise.
     *
     * `has($id)` returning true does not mean that `get($id)` will not throw an exception.
     * It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`.
     *
     * @param string $id Identifier of the entry to look for.
     *
     * @return bool
     */
    public function has($id);
}
```

### 3.2. `Psr\Container\ContainerExceptionInterface`

```php
<?php
namespace Psr\Container;

/**
 * Base interface representing a generic exception in a container.
 */
interface ContainerExceptionInterface
{
}
```

### 3.3. `Psr\Container\NotFoundExceptionInterface`

```php
<?php
namespace Psr\Container;

/**
 * No entry was found in the container.
 */
interface NotFoundExceptionInterface extends ContainerExceptionInterface
{
}
```


# PSR-12-extended-coding-style-guide

이 문서에서 핵심이 되는 단어는 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", "OPTIONAL" 입니다. 이것은 [RFC 2119](http://tools.ietf.org/html/rfc2119)에 설명 된대로 해석해야 합니다.

> 역자주: 위의 키워드는 아래의 번역문에 괄호안에 표시하였습니다

## 개요

이 규칙은 코딩 스타일 가이드인 [PSR-2](http://www.php-fig.org/psr/psr-2/)를 확장 및 대체하며, 기본 코딩 표준 인 [PSR-1](http://www.php-fig.org/psr/psr-1/)을 준수해야합니다.

[PSR-2](http://www.php-fig.org/psr/psr-2/)와 마찬가지로 이 사양의 목적은 다른 작성자의 코드를 읽을 때 이해하기 어려운 것을 줄이는 것입니다. 이것은 PHP 코드의 형식을 지정하는 방법에 대한 규칙과 기대 사항을 공유하여 열거합니다. 이 PSR은 코딩 스타일 도구가 구현할 수 있는 설정 방법을 제공하기 위해 노력하고, 프로젝트에서 이 PSR의 준수를 선언함을 통해 개발자는 서로 다른 프로젝트와 쉽게 연동 할 수 있습니다. 다양한 저자가 여러 프로젝트에서 협업을 할 때, 모든 프로젝트에서 한가지의 가이드라인을 사용하는 것이 도움이 됩니다. 따라서 이 가이드로 얻는 혜택은 규칙 자체가 아니라 이러한 규칙을 공유하는 것을 통해 얻을 수 있습니다.

[PSR-2](http://www.php-fig.org/psr/psr-2/)는 2012 년에 승인되었으며 그 이후로 PHP에 많은 발전이 있었으므로 코딩 스타일 지침에 영향을 미쳤습니다. [PSR-2](http://www.php-fig.org/psr/psr-2/)는 문서 작성 당시 존재했던 PHP 기능은 대부분 포함하고 있지만, 새로운 기능들은 매우 다양하게 해석 될 수 있습니다. 따라서 이 PSR은 새로운 기능을 사용하여 보다 현대적인 관점에서 PSR-2의 내용을 명확하게 하고 PSR-2에 관한 오류의 개정본을 만듭니다.

### 이전 버전

이 문서 전체의 내용 중, 프로젝트에서 사용하는 PHP 버전에 없는 지침은 무시할 수 있습니다 (MAY).

### 예제

이 예제는 아래의 규칙 중 일부를 간략하게 설명합니다.

```php
<?php

declare(strict_types=1);

namespace Vendor\Package;

use Vendor\Package\{ClassA as A, ClassB, ClassC as C};
use Vendor\Package\SomeNamespace\ClassD as D;

use function Vendor\Package\{functionA, functionB, functionC};

use const Vendor\Package\{ConstantA, ConstantB, ConstantC};

class Foo extends Bar implements FooInterface
{
    public function sampleFunction(int $a, int $b = null): array
    {
        if ($a === $b) {
            bar();
        } elseif ($a > $b) {
            $foo->bar($arg1);
        } else {
            BazClass::bar($arg2, $arg3);
        }
    }

    final public static function bar()
    {
        // method body
    }
}
```

## 2. 일반

### 2.1 기본 코딩 표준

코드는 [PSR-1](http://www.php-fig.org/psr/psr-1/)에 요약 된 모든 규칙을 따라야합니다.

PSR-1의 '*StudlyCaps*'라는 용어는 *PascalCase*로 해석해야하며(MUST) 첫 단어가 첫 문자를 포함하여 대문자로 표시해야합니다.

### 2.2 Files

모든 PHP 파일은 Unix LF (linefeed) 줄만 사용해야합니다 (MUST).

모든 PHP 파일은 단일 LF로 끝나는 비어 있지 않은 라인으로 끝나야합니다 (MUST).

PHP code만 존재하는 파일에서는 닫는 `?>`태그를 생략해야합니다 (MUST).

### 2.3 Lines

라인 길이에 엄격한 제한이 있어서는 안됩니다(MUST NOT).

한줄에 들어가는 글자수에 대한 가벼운 제한은 120자여야 합니다. (MUST).

한줄은 80자를 넘지 않아야합니다 (SHOULD NOT). 그보다 긴 줄은 각각 80자 이하의 여러 줄으로 나눠야합니다 (SHOULD).

HEREDOC/NOWDOC 구문을 제외하고 줄의 끝에 공백이 있으면 안됩니다(MUST NOT).

명시적으로 금지 된 경우를 제외하고 가독성을 높이고 관련 코드 블록을 표시하기 위해 빈 줄을 추가 할 수 있습니다 (MAY).

한 줄에 하나 이상의 문장이 있어서는 안됩니다 (MUST NOT).

### 2.4 들여쓰기

코드는 4개의 스페이스로 들여쓰기를 사용해야만하며(MUST), 탭을 사용하지 않아야만 합니다 (MUST NOT).

### 2.5 Keyword와 Type

모든 PHP 예약 키워드와 타입 [\[1\]](http://php.net/manual/en/reserved.keywords.php)[\[2\]](http://php.net/manual/en/reserved.other-reserved-words.php)은 소문자 여야만합니다 (MUST).

향후 PHP 버전에 추가되는 모든 새로운 유형과 키워드는 반드시 소문자 여야만합니다 (MUST).

반드시 `boolean` 대신 `bool`, `integer` 대신 `int` 등 짧은 형태의 타입 키워드를 사용해야합니다 (MUST).

## 3. Declare 선언문, 네임 스페이스 및 Import 선언문

PHP 파일의 머릿글은 여러 블록으로 구성 될 수 있습니다. 이미 존재한다면, 아래의 각 블록은 하나의 빈 행으로 분리해야하며 빈 행을 포함해서는 안됩니다 (MUST NOT). 관련이 없는 블록은 생략 할 수 있지만(MUST) 각 블록은 아래에 나열된 순서여야 합니다.

1. `<?php`로 태그 열기.
2. 파일 수준의 docblock.
3. 하나 이상의 declare 선언문.
4. 파일의 네임 스페이스 선언.
5. 하나 이상의 클래스 기반 `use` import 문.
6. 하나 이상의 함수 기반 `use` import 문.
7. 하나 이상의 상수 기반 `use` import 문.
8. 파일의 나머지 코드.

파일에 HTML과 PHP가 섞여 있다면 위 섹션 중 하나라도 여전히 사용할 수 있습니다. 그럴땐 파일의 최상단에는 declare 문이 있어야 하며 (MUST), 나머지 부분은 닫는 PHP 태그와 HTML과 PHP가 섞인 코드가 있어야합니다.

열기 태그 `<?php`가 파일의 첫 번째 줄에 있을 때, PHP 열기 및 닫기 태그 외부에 마크업을 포함하는 파일이 아니라면 다른 문장이 없는 자체 라인 상에 있어야 합니다(MUST).

> 역자주: 설명이 어려우니 그냥 아래에 나오는 예제를 참고하세요

Import 문은 항상 정규화된 형식이어야 하므로 백 슬래시로 시작해서는 안됩니다 (MUST).

다음은 모든 블록의 전체 목록의 예제를 보여줍니다.

```php
<?php

/**
 * This file contains an example of coding styles.
 */

declare(strict_types=1);

namespace Vendor\Package;

use Vendor\Package\{ClassA as A, ClassB, ClassC as C};
use Vendor\Package\SomeNamespace\ClassD as D;
use Vendor\Package\AnotherNamespace\ClassE as E;

use function Vendor\Package\{functionA, functionB, functionC};
use function Another\Vendor\functionD;

use const Vendor\Package\{CONSTANT_A, CONSTANT_B, CONSTANT_C};
use const Another\Vendor\CONSTANT_D;

/**
 * FooBar is an example class.
 */
class FooBar
{
    // ... additional PHP code ...
}

```

두 단계 이상의 복합 네임 스페이스는 사용해서는 안됩니다 (MUST NOT). 그러므로 허용하는 최대 복합 단계는 다음과 같습니다.

```php
<?php

use Vendor\Package\SomeNamespace\{
    SubnamespaceOne\ClassA,
    SubnamespaceOne\ClassB,
    SubnamespaceTwo\ClassY,
    ClassZ,
};
```

그리고 다음은 허용되지 않습니다(MUST NOT).

```php
<?php

use Vendor\Package\SomeNamespace\{
    SubnamespaceOne\AnotherNamespace\ClassA,
    SubnamespaceOne\ClassB,
    ClassZ,
};
```

PHP를 열고 닫는 태그 외부에서 마크 업을 포함하는 파일에 엄격한 유형을 선언하고자 할 때, 선언문은 파일의 첫 번째 줄에 있어야하며 (MUST), 여는 PHP 태그와 strict types 선언과 닫는 태그를 포함해야합니다.

예 :

```php
<?php declare(strict_types=1) ?>
<html>
<body>
    <?php
        // ... additional PHP code ...
    ?>
</body>
</html>
```

Declare 문은 공백을 포함하지 않아야하며(MUST) `declare(strict_types=1)` (선택 항목 인 세미콜론 종결자 포함)이어야합니다(MUST).

블록 선언문은 허용되며 반드시 아래와 같은 형식이여야합니다(MUST). 중괄호와 간격의 위치 참고 :

```php
declare(ticks=1) {
    // some code
}
```

## 4. 클래스, 프로퍼티, 메소드

"클래스"라는 용어는 모든 클래스, 인터페이스 및 특성-trait을 나타냅니다.

닫는 중괄호는 같은 줄의 주석이나 명령문 다음에 와서는 안됩니다(MUST NOT).

새 클래스를 인스턴스화 할 때 생성자에 전달 된 인수가 없는 경우에도 항상 괄호가 있어야합니다(MUST).

> 역자주 : new Foo; 같은걸 하지 말라는 의미

```php
new Foo();
```

### 4.1 확장과 구현

`extends`와 `implements` 키워드는 클래스 이름과 같은 줄에 선언해야합니다 (MUST).

해당 클래스의 여는 중괄호는 바로 다음 줄에 있어야만 합니다(MUST). 클래스의 닫는 중괄호는 본문 뒤의 다음 줄로 가야만 합니다(MUST).

여는 중괄호는 반드시(MUST) 바로 다음 줄에 있어야하며 중간에 빈 줄이 있어서는 안됩니다.

닫는 중괄호는 반드시(MUST) 바로 다음 줄에 있어야하며 빈 줄이 앞에 있어서는 안됩니다(MUST NOT).

```php
<?php

namespace Vendor\Package;

use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

class ClassName extends ParentClass implements \ArrayAccess, \Countable
{
    // constants, properties, methods
}
```

`implements`의 목록과 인터페이스의 경우, `extends`는 여러 줄에 걸쳐 나뉘어 질 수 있으며(MAY), 각 줄은 한 번의 들여쓰기를 합니다. 그렇게 할 때 목록의 첫 번째 항목은 다음 줄에 있어야하며 한 줄에 하나의 인터페이스만 있어야합니다.

```php
<?php

namespace Vendor\Package;

use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

class ClassName extends ParentClass implements
    \ArrayAccess,
    \Countable,
    \Serializable
{
    // constants, properties, methods
}
```

### 4.2 특성-trait 사용

특성-trait을 구현하기 위해 클래스 내에서 사용되는 `use` 키워드는 클래스를 여는 중괄호 다음 줄에 선언해야합니다 (MUST).

```php
<?php

namespace Vendor\Package;

use Vendor\Package\FirstTrait;

class ClassName
{
    use FirstTrait;
}
```

클래스로 가져온 각각의 특성-trait은 반드시 한 줄에 하나씩 포함해야하며(MUST), 각 포함내역은 반드시 고유한 `use` import 문을 가져야합니다 (MUST).

```php
<?php

namespace Vendor\Package;

use Vendor\Package\FirstTrait;
use Vendor\Package\SecondTrait;
use Vendor\Package\ThirdTrait;

class ClassName
{
    use FirstTrait;
    use SecondTrait;
    use ThirdTrait;
}
```

클래스에 `use` import 문 다음에 아무것도 없는 경우, 클래스를 닫는 중괄호는 마지막 `use` import 문 다음에 있어야합니다 (MUST).

```php
<?php

namespace Vendor\Package;

use Vendor\Package\FirstTrait;

class ClassName
{
    use FirstTrait;
}
```

그렇지 않으면 마지막 `use` import 문 다음에 빈 줄이 있어야합니다(MUST).

```php
<?php

namespace Vendor\Package;

use Vendor\Package\FirstTrait;

class ClassName
{
    use FirstTrait;

    private $property;
}
```

`insteadof` 와 `as`연산자를 사용할 때는 들여쓰기, 간격 및 새 줄을 반드시(MUST) 다음과 같이 사용해야합니다.

```php
<?php

class Talker
{
    use A, B, C {
        B::smallTalk insteadof A;
        A::bigTalk insteadof C;
        C::mediumTalk as FooBar;
    }
}
```

### 4.3 속성-property 과 상수-constant

모든 속성-property에서 가시성을 반드시(MUST) 선언해야합니다.

> 역자주: 가시성은 다른 언어에서 접근제어자 등으로 부르는 public, protected, private 를 의미합니다

프로젝트의 PHP 최소 버전이 상수의 가시성 (PHP 7.1.0 이상)을 지원하는 경우 모든 상수에 대한 가시성을 반드시(MUST) 선언해야합니다.

`var` 키워드는 속성-property를 선언하는데 사용해서는 안됩니다 (MUST NOT).

선언문마다 하나 이상의 속성-property이 선언해서는 안됩니다(MUST NOT).

protected나 private 가시성을 나타내기 위해 속성 이름 앞에 하나의 밑줄을 사용해서는 안됩니다 (MUST NOT). 즉, 밑줄 접두사는 명시적인 의미가 없습니다.

형식 선언과 속성-property 이름 사이에 공백이 있어야합니다(MUST).

속성-property 선언은 다음과 같아야 합니다.

```php
<?php

namespace Vendor\Package;

class ClassName
{
    public $foo = null;
    public static int $bar = 0;
}
```

### 4.4 메소드와 함수

모든 메소드에서 가시성을 선언해야합니다 (MUST).

메소드 이름은 protected나 private 가시성을 나타내기 위해 하나의 밑줄로 접두어를 붙여서는 안됩니다 (MUST NOT). 즉, 밑줄 접두사는 명시적인 의미가 없습니다.

메서드와 함수 이름은 메서드 이름 다음에 공백으로 선언해서는 안됩니다 (MUST NOT). 여는 중괄호는 반드시(MUST) 같은 줄에 있어야하며 닫는 중괄호는 반드시(MUST) 그 다음 줄에 있어야합니다. 여는 괄호 뒤에 공백이 있으면 안되며(MUST NOT) 닫는 괄호 앞에 공백이 있어서는 안됩니다(MUST NOT).

메소드 선언은 다음과 같습니다. 괄호, 쉼표, 공백 및 중괄호의 배치에 유의하십시오.

```php
<?php

namespace Vendor\Package;

class ClassName
{
    public function fooBarBaz($arg1, &$arg2, $arg3 = [], &...$arg4)
    {
        // method body
    }
}
```

함수 선언은 다음과 같습니다. 괄호, 쉼표, 공백 및 중괄호의 배치에 유의하십시오.

```php
<?php

function fooBarBaz($arg1, &$arg2, $arg3 = [], &...$arg4)
{
    // function body
}
```

### 4.5 메서드 및 함수 인수

인수 목록에는 각 쉼표 앞에 공백이 있으면 안되며(MUST NOT) 각 쉼표 뒤에 하나의 공백이 있어야합니다(MUST).

기본값을 가진 메소드 및 함수 인수는 가변 길이 인수 목록의 앞이면서 인수 목록의 끝에 있어야합니다(MUST).

```php
<?php

namespace Vendor\Package;

class ClassName
{
    public function foo(int $arg1, &$arg2, $arg3 = [], &...$arg4)
    {
        // method body
    }
}
```

인수 목록은 여러 줄에 걸쳐 나뉘어 질 수 있으며(MAY), 각 줄은 한 번 들여 쓰일 수 있습니다. 그렇게 할 때 목록의 첫 번째 항목은 다음 줄에 있어야하며(MUST) 한 줄에 하나의 인수 만 있어야합니다(MUST).

인수 목록이 여러 줄에 걸쳐 분할되어 있으면 닫는 괄호와 여는 중괄호는 공백을 한개 사용하여 같은 줄에 함께 있어야합니다 (MUST).

```php
<?php

namespace Vendor\Package;

class ClassName
{
    public function aVeryLongMethodName(
        ClassTypeHint $arg1,
        &$arg2,
        array $arg3 = []
    ) {
        // method body
    }
}
```

반환형식(return type) 선언이 있으면 콜론 뒤에 공백이 하나 있어야하며(MUST) 그 다음에 형식 선언이옵니다. 콜론과 반환형식 선언은 두 문자 사이에 공백이 없는 인수 목록을 닫는 괄호와 같은 줄에 있어야합니다(MUST).

```php
<?php

declare(strict_types=1);

namespace Vendor\Package;

class ReturnTypeVariations
{
    public function functionName(int $arg1, $arg2): string
    {
        return 'foo';
    }

    public function anotherFunction(
        string $foo,
        string $bar,
        int $baz
    ): string {
        return 'foo';
    }
}
```

nullable type 선언에서는 물음표와 type 사이에 공백이 없어야합니다(MUST NOT).

```php
<?php

declare(strict_types=1);

namespace Vendor\Package;

class ReturnTypeVariations
{
    public function functionName(?string $arg1, ?int &$arg2): ?string
    {
        return 'foo';
    }
}
```

인수 앞에 참조 연산자 `&`를 사용하면 앞의 예시와 같이 뒤에 공백이 있어서는 안됩니다(MUST NOT).

가변 3 도트 연산자와 인자 이름 사이에는 공백이 없어야합니다(MUST NOT).

```php
public function process(string $algorithm, ...$parts)
{
    // processing
}
```

참조 연산자와 가변 3 도트 연산자를 결합 할 때 두 연산자 사이에 공백이 없어야합니다(MUST NOT).

```php
public function process(string $algorithm, &...$parts)
{
    // processing
}
```

### 4.6 `abstract`, `final`, 과 `static`

`abstract`와 `final` 선언이 있을 경우, 가시성 선언 앞에 와야합니다 (MUST).

`static` 선언이 있을 경우, 가시성 선언 뒤에 와야합니다(MUST).

```php
<?php

namespace Vendor\Package;

abstract class ClassName
{
    protected static $foo;

    abstract protected function zim();

    final public static function bar()
    {
        // method body
    }
}
```

### 4.7 메서드와 함수 호출

메서드나 함수를 호출 할 때 메서드나 함수 이름과 여는 괄호 사이에 공백이 없어야합니다(MUST NOT). 여는 괄호 뒤에 공백이 있으면 안되며(MUST NOT) 닫는 괄호 앞에 공백이 있어서는 안됩니다(MUST NOT). 인수 목록에는 각 쉼표 앞에 공백이 있으면 안되며(MUST NOT) 각 쉼표 뒤에 하나의 공백이 있어야합니다(MUST).

```php
<?php

bar();
bar(...['foo','bar']);
$foo->bar($arg1);
Foo::bar($arg2, $arg3);
```

인수 목록은 여러 줄에 걸쳐 나뉘어 질 수 있으며(MAY), 각 줄은 한 번 들여쓰기를 합니다. 그렇게 할 때 목록의 첫 번째 항목은 다음 줄에 있어야하며(MUST) 한 줄에 하나의 인수 만 있어야합니다(MUST). (익명함수나 배열의 경우처럼) 여러 줄로 분할되는 단일 인수는 인수 목록 자체를 분할하는 이것에는 해당하지 않습니다.

```php
<?php

$foo->bar(
    $longArgument,
    $longerArgument,
    $muchLongerArgument
);
```

```php
<?php

somefunction($foo, $bar, [
  // ...
], $baz);

$app->get('/hello/{name}', function ($name) use ($app) {
    return 'Hello ' . $app->escape($name);
});
```

## 5. 제어구조

제어 구조의 일반적인 스타일 규칙은 다음과 같습니다.

* 제어 구조 키워드 다음에 하나의 공백이 있어야합니다(MUST).
* 여는 괄호 뒤에 공백이 있으면 안됩니다(MUST NOT).
* 닫는 괄호 앞에 공백이 없어야 합니다(MUST NOT).
* 닫는 괄호와 여는 중괄호 사이에 하나의 공백이 있어야 합니다(MUST).
* 본문은 한 번 들여쓰기해야합니다(MUST).
* 닫는 중괄호는 몸체 뒤의 다음 줄에 있어야합니다(MUST).

각 구조의 본문은 중괄호로 묶어야합니다(MUST). 이것은 구조가 어떻게 보이는지 표준화하고 새로운 라인을 본문에 추가 할 때 오류가 발생할 가능성을 줄입니다.

### 5.1 `if`, `elseif`, `else`

`if` 구조는 다음과 같습니다. 괄호, 공백 및 중괄호의 배치에 유의하십시오. `else`와 `elseif`는 이전 본문의 닫는 중괄호와 같은 줄에 있습니다.

```php
<?php

if ($expr1) {
    // if body
} elseif ($expr2) {
    // elseif body
} else {
    // else body;
}
```

`else if` 키워드 대신 `elseif` 키워드를 사용하여 모든 제어 키워드가 하나의 단어처럼 보이도록 해야 합니다 (SHOULD).

> 역자주: phpstorm과 같은 ide에서 else if로 사용하고 코드 자동정렬을 할 경우 else { if(){} } 같은 형태로 분할 해버리기도 합니다. 이때는 설정을 수정할 수도 있지만 저는 elseif 로 사용하시는 것을 권장합니다

괄호 안의 표현은 여러 줄에 걸쳐 나뉘어 질 수 있으며(MAY), 그 다음 줄은 적어도 한 번 들여 쓰일 수 있습니다. 그렇게 할 때 첫 번째 조건은 반드시(MUST) 다음 줄에 있어야합니다. 닫는 괄호와 여는 중괄호는 한개 공백과 함께 한 줄에 함께 있어야합니다 (MUST). 조건 사이의 부울-boolean 연산자는 항상 줄의 시작 또는 끝에 있어야하며 둘 다를 혼합해서는 안됩니다(MUST).

```php
<?php

if (
    $expr1
    && $expr2
) {
    // if body
} elseif (
    $expr3
    && $expr4
) {
    // elseif body
}
```

### 5.2 `switch`, `case`

`switch` 구조체는 다음과 같습니다. 괄호, 공백 및 중괄호의 배치에 유의하십시오. `case` 문은 `switch`에서 한 번 들여쓰기해야하며(MUST) `break` 키워드 (또는 다른 종료 키워드)는 `case` 본문과 같은 수준에서 들여쓰기해야합니다 (MUST). 비어 있지 않은 `case` 본문에서 다음 case를 의도적으로 실행시킬 경우(fall-through) `// no break`와 같은 주석이 있어야합니다(MUST).

```php
<?php

switch ($expr) {
    case 0:
        echo 'First case, with a break';
        break;
    case 1:
        echo 'Second case, which falls through';
        // no break `역자주: 다음case를 실행시키기 위해 의도적으로 break를 넣지 않은 경우 반드시 추가해야하는 주석`
    case 2:
    case 3:
    case 4:
        echo 'Third case, return instead of break';
        return;
    default:
        echo 'Default case';
        break;
}
```

괄호 안의 표현은 여러 행에 걸쳐 나뉘어 질 수 있으며(MAY), 그 다음 행은 적어도 한 번 들여쓰기를 합니다. 그럴 경우 첫 번째 조건은 반드시 다음 줄에 있어야 합니다(MUST). 닫는 괄호와 여는 중괄호는 하나의 공백과 함께 한 줄에 함께 있어야 합니다 (MUST). 조건 사이의 부울-boolean 연산자는 항상 줄의 시작 또는 끝에 있어야하며 둘 다를 혼합해서는 안됩니다(MUST).

```php
<?php

switch (
    $expr1
    && $expr2
) {
    // structure body
}
```

### 5.3 `while`, `do while`

`while` 문은 다음과 같습니다. 괄호, 공백 및 중괄호의 배치에 유의하십시오.

```php
<?php

while ($expr) {
    // structure body
}
```

괄호 안의 표현은 여러 행에 걸쳐 나뉘어 질 수 있으며(MAY), 그 다음 행은 적어도 한 번 들여쓰기를 합니다. 그럴 경우 첫 번째 조건은 반드시 다음 줄에 있어야 합니다(MUST). 닫는 괄호와 여는 중괄호는 하나의 공백과 함께 한 줄에 함께 있어야합니다 (MUST). 조건 사이의 부울-boolean 연산자는 항상 줄의 시작 또는 끝에 있어야하며 둘 다를 혼합해서는 안됩니다(MUST).

```php
<?php

while (
    $expr1
    && $expr2
) {
    // structure body
}
```

비슷하게, `do while` 문은 다음과 같이 보입니다. 괄호, 공백 및 중괄호의 배치에 유의하십시오.

```php
<?php

do {
    // structure body;
} while ($expr);
```

괄호 안의 표현은 여러 행에 걸쳐 나뉘어 질 수 있으며(MAY), 그 다음 행은 적어도 한 번 들여쓰기를 합니다. 그럴 경우 첫 번째 조건은 반드시 다음 줄에 있어야 합니다(MUST). 조건들 사이의 부울 연산자는 언제나 둘의 혼합이 아닌 라인의 시작 또는 끝 부분에 있어야합니다 (MUST).

```php
<?php

do {
    // structure body;
} while (
    $expr1
    && $expr2
);
```

### 5.4 `for`

`for` 문은 다음과 같이 보입니다. 괄호, 공백 및 중괄호의 배치에 유의하십시오.

```php
<?php

for ($i = 0; $i < 10; $i++) {
    // for body
}
```

괄호 안의 표현은 여러 행에 걸쳐 나뉘어 질 수 있으며(MAY), 그 다음 행은 적어도 한 번 들여쓰기를 합니다. 그럴 경우 첫 번째 조건은 반드시 다음 줄에 있어야 합니다(MUST). 닫는 괄호와 여는 중괄호는 하나의 공백과 함께 한 줄에 함께 있어야합니다 (MUST).

```php
<?php

for (
    $i = 0;
    $i < 10;
    $i++
) {
    // for body
}
```

### 5.5 `foreach`

`foreach` 문은 다음과 같이 보입니다. 괄호, 공백 및 중괄호의 배치에 유의하십시오.

```php
<?php

foreach ($iterable as $key => $value) {
    // foreach body
}
```

### 5.6 `try`, `catch`, `finally`

`try-catch-finally` 블록은 다음과 같이 보입니다. 괄호, 공백 및 중괄호의 배치에 유의하십시오.

```php
<?php

try {
    // try body
} catch (FirstThrowableType $e) {
    // catch body
} catch (OtherThrowableType | AnotherThrowableType $e) {
    // catch body
} finally {
    // finally body
}
```

## 6. 연산자

연산자의 스타일 규칙은 arity (피연산자 수)별로 그룹화됩니다.

연산자 주변에 공백이 허용되며, 가독성을 위해 여러 공백이 있을 수 있습니다(MAY).

여기에 설명되지 않은 모든 연산자는 정의되지 않은 상태로 남아 있습니다.

### 6.1. 단항 연산자

증가/감소 연산자, 논리 not 연산자, 변환 및 부정 연산자는 연산자와 피연산자 사이에 공백이 없어야합니다(MUST NOT).

```php
$value++;
--$value;
$a = !$b;
$c = +$d;
$e = -$f;
```

타입 캐스팅 연산자는 괄호 안에 공백이 없어야합니다(MUST NOT).

```php
$intValue = (int) $input;
```

### 6.2. 이진 연산자

모든 이진 [arithmetic](http://php.net/manual/en/language.operators.arithmetic.php), [comparison](http://php.net/manual/en/language.operators.comparison.php), [assignment](http://php.net/manual/en/language.operators.assignment.php), [bitwise](http://php.net/manual/en/language.operators.bitwise.php), [logical](http://php.net/manual/en/language.operators.logical.php), \[문자열-string]\[] 및 [type](http://php.net/manual/en/language.operators.type.php) 연산자의 앞뒤에는 최소한 하나 이상의 공백이 있어야 합니다(MUST).

```php
if ($a === $b) {
    $foo = $bar ?? $a ?? $b;
} elseif ($a > $b) {
    $foo = $a + $b * $c;
} elseif ($a <=> $c) {
    $foo += $bar % $a & $b ** $c;
}
```

### 6.3. 삼항-Ternary 연산자

삼항 연산자라고도하는 조건부 연산자 `?` 및 `:`문자 앞뒤에는 하나 이상의 공백이 와야합니다(MUST).

```php
$variable = $foo ? 'foo' : 'bar';
```

조건부 연산자의 중간 피연산자가 생략되면 연산자는 다른 이진 \[비교-comparison]\[] 연산자와 동일한 스타일 규칙을 따라야합니다(MUST).

```php
$variable = $foo ?: 'bar';
```

## 7. 클로저-Closures

클로저는 `function` 키워드 뒤의 공백과 `use` 키워드 앞뒤에 공백으로 선언해야합니다 (MUST).

여는 중괄호는 반드시(MUST) 같은 줄에 있어야하며 닫는 중괄호는 반드시(MUST) 그 다음 줄에 있어야합니다.

인수 목록이나 변수 목록의 여는 괄호 다음에 공백이 있으면 안되며(MUST NOT), 인수 목록이나 변수 목록의 닫는 괄호 앞에 공백이 있으면 안됩니다(MUST NOT).

인수 목록과 변수 목록에는 각 쉼표 앞에 공백이 있어서는 안되며(MUST NOT) 각 쉼표 뒤에 하나의 공백이 있어야합니다(MUST).

기본값을 가진 클로저 인수는 인수 목록의 끝에 와야합니다 (MUST).

반환유형(return type)이 있는 경우 일반 함수 및 메소드와 동일한 규칙을 따라야 하며 `use` 키워드가 존재한다면, 콜론은 두 문자 사이에 공백없이 `use` 리스트의 닫는 괄호에 따라와야합니다 (MUST).

클로저 선언은 다음과 같습니다. 괄호, 쉼표, 공백 및 중괄호의 배치에 유의하십시오.

```php
<?php

$closureWithArgs = function ($arg1, $arg2) {
    // body
};

$closureWithArgsAndVars = function ($arg1, $arg2) use ($var1, $var2) {
    // body
};

$closureWithArgsVarsAndReturn = function ($arg1, $arg2) use ($var1, $var2): bool {
    // body
};
```

인수 목록과 변수 목록은 여러 행에 걸쳐 나뉘어 질 수 있으며(MAY), 각 행은 한 번 들여쓰기됩니다.

그럴경우 목록의 첫 번째 항목은 다음 줄에 있어야하며(MUST), 한 줄에 하나의 인수 또는 변수 만 있어야합니다(MUST).

마지막 리스트 (인수 또는 변수의 여부)가 여러 줄로 나뉘어 질 때 닫는 괄호와 여는 중괄호는 하나의 공백을 사용하여 같은 줄에 함께 있어야합니다 (MUST).

다음은 인수 목록이 있거나 없는 클로저의 예와 여러 줄에 걸쳐있는 변수 목록입니다.

```php
<?php

$longArgs_noVars = function (
    $longArgument,
    $longerArgument,
    $muchLongerArgument
) {
   // body
};

$noArgs_longVars = function () use (
    $longVar1,
    $longerVar2,
    $muchLongerVar3
) {
   // body
};

$longArgs_longVars = function (
    $longArgument,
    $longerArgument,
    $muchLongerArgument
) use (
    $longVar1,
    $longerVar2,
    $muchLongerVar3
) {
   // body
};

$longArgs_shortVars = function (
    $longArgument,
    $longerArgument,
    $muchLongerArgument
) use ($var1) {
   // body
};

$shortArgs_longVars = function ($arg) use (
    $longVar1,
    $longerVar2,
    $muchLongerVar3
) {
   // body
};
```

형식 지정 규칙은 함수 또는 메소드 호출에서 클로저가 직접 인수로 사용될 때도 적용됩니다.

```php
<?php

$foo->bar(
    $arg1,
    function ($arg2) use ($var1) {
        // body
    },
    $arg3
);
```

## 8. 익명 클래스

익명 클래스는 위 섹션의 클로저와 동일한 지침과 원칙을 따라야합니다(MUST).

```php
<?php

$instance = new class {};
```

`implements` 인터페이스 목록이 감싸지 않는 한 여는 중괄호는 `class` 키워드와 같은 줄에 있을 수 있습니다(MAY). 인터페이스 목록이 감쌀 경우 중괄호는 마지막 인터페이스 바로 다음 행에 있어야합니다 (MUST).

```php
<?php

// Brace on the same line
$instance = new class extends \Foo implements \HandleableInterface {
    // Class content
};

// Brace on the next line
$instance = new class extends \Foo implements
    \ArrayAccess,
    \Countable,
    \Serializable
{
    // Class content
};
```

```php
$intValue = (int) $input;
```


# PSR-13-links

하이퍼 미디어 링크는 HTML 컨텍스트와 다양한 API 형식 컨텍스트에서 점점 더 중요한 웹의 일부가 되고 있습니다. 그러나 일반적인 하이퍼 미디어 형식은 하나도 없으며 형식 간 링크를 나타내는 일반적인 방법도 없습니다.

> 역자주: 완전히 표준화되지 못했다는 의미

이 스펙은 PHP 개발자에게 사용되는 직렬화 형식과는 별도로 하이퍼 미디어 링크를 표현하는 간단하고 일반적인 방법을 제공하는 것을 목표로 합니다. 그래서 시스템은 하이퍼 미디어 링크를 사용하여 하나 이상의 연결된 포맷으로 응답을 직렬화 할 수 있습니다.

이 문서에서 핵심이 되는 단어는 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", "OPTIONAL" 입니다. 이것은 \[RFC 2119]에 설명 된대로 해석해야 합니다.

> 역자주: 위의 키워드는 아래의 번역문에 괄호안에 표시하였습니다

### References

* [RFC 2119](http://tools.ietf.org/html/rfc2119)
* [RFC 4287](https://tools.ietf.org/html/rfc4287)
* [RFC 5988](https://tools.ietf.org/html/rfc5988)
* [RFC 6570](https://tools.ietf.org/html/rfc6570)
* [IANA Link Relations Registry](http://www.iana.org/assignments/link-relations/link-relations.xhtml)
* [Microformats Relations List](http://microformats.org/wiki/existing-rel-values#HTML5_link_type_extensions)

## 1. 명세서

### 1.1 Basic links

하이퍼 미디어 링크는 최소한 다음으로 구성됩니다.

* 참조되는 대상 자원을 나타내는 URI입니다.
* 대상 자원과 관련된 소스의 관계를 정의합니다.

사용 된 형식에 따라 링크의 다양한 다른 속성이 존재할 수 있습니다. 추가 속성(additional attributes)은 표준화되거나 보편화되지 않았기 때문에 이 표준은 표준화하려고하지 않습니다.

이 명세서는 목정상 다음의 정의가 적용됩니다.

* **Implementing Object** - 이 명세에 정의 된 인터페이스 중 하나를 구현하는 객체.
* **Serializer** - 하나 이상의 Link 객체를 사용하고 정의 된 형식으로 직렬화 된 표현을 생성하는 라이브러리 또는 기타 시스템입니다.

### 1.2 Attributes

모든 링크는 URI와 관계를 넘어 0개 이상의 추가 속성을 포함 할 수있습니다(MAY). 여기에 허용되는 값의 정식 등록된 것이 없으며 값의 유효성은 컨텍스트 및 종종 특정 직렬화 형식에 따라 다릅니다. 일반적으로 지원되는 값에는 'hreflang', 'title'및 'type'이 포함됩니다.

직렬화 포맷에 의해 필요한 경우 링크 객체의 속성을 생략 할 수 있습니다(MAY). 그러나 serializers는 serialization 형식의 정의에 의해 방지되지 않는 한 사용자 확장을 허용하기 위해 가능한 모든 제공된 속성을 인코딩해야합니다 (SHOULD).

일부 속성 (일반적으로 `hreflang`)은 그들의 문맥에 두 번 이상 나타날 수 있습니다. 따라서 속성 값은 단순한 값이 아닌 값의 배열 일 수 있습니다(MAY). Serializers는 직렬화 된 형식 (공백으로 구분 된 목록, 쉼표로 구분 된 목록 등)에 적합한 형식으로 배열을 인코딩 할 수 있습니다 (MAY). 특정 속성이 특정 컨텍스트에서 다중 값을 가질 수 없는 경우, 직렬자는 제공된 첫 번째 값을 사용하고 모든 후속 값을 무시해야합니다 (MUST).

속성 값이 부울 `true`이면, serializers는 적절한 경우 축약형을 사용할 수 있고 직렬화 형식으로 지원할 수 있습니다 (MAY). 예를 들어, HTML은 속성의 존재가 부울 의미를 가질 때 속성이 값을 갖지 못하게합니다. 이 규칙은 속성이 부울 `true`인 경우에만 적용되며 정수 1과 같은 PHP의 "truthy"값은 적용되지 않습니다.

속성 값이 부울 `false` 인 경우, serializer는 속성의 의미를 변경하지 않는 한 속성을 완전히 생략해서는 안됩니다 (SHOULD). 이 규칙은 속성이 부울 `false` 인 경우에만 적용되며, 정수 0과 같은 PHP의 다른 "falsey"값에는 적용되지 않습니다.

### 1.3 Relationships

링크 관계는 문자열로 정의되며 공개적으로 정의 된 관계의 경우 간단한 키워드이고 비공개인 관계의 경우 절대 URI를 사용합니다.

간단한 키워드가 사용되는 경우 [IANA 레지스트리](http://www.iana.org/assignments/link-relations/link-relations.xhtml)의 키워드와 일치해야합니다(SHOULD).

선택적으로 [microformats.org](http://microformats.org/wiki/existing-rel-values) 레지스트리를 사용할 수도 있지만(MAY) 모든 컨텍스트에서 유효하지 않을 수 있습니다.

위의 레지스트리 중 하나 또는 이와 유사한 공개 레지스트리에 정의되지 않은 관계는 "비공개"으로 간주됩니다. 즉 특정 응용 프로그램 또는 유스 케이스에 한정됩니다. 그러한 관계는 반드시 절대 URI를 사용해야 합니다(MUST).

## 1.4 Link Templates

[RFC 6570](https://tools.ietf.org/html/rfc6570)은 URI 템플릿의 형식, 즉 클라이언트에서 제공 한 값으로 채워질 것으로 예상되는 URI의 패턴을 정의합니다. 일부 하이퍼 미디어 형식은 템플릿 링크를 지원하지만 다른 링크는 템플릿이 아니라는 것을 나타내는 특별한 방법이 있을 수 있습니다. URI 템플릿을 지원하지 않는 형식의 Serializer는 템플릿 기반 링크가 발견되면 이를 무시해야 합니다 (MUST).

## 1.5 Evolvable providers

어떤 경우에는 링크 공급자가 추가 링크를 추가해야 할 수도 있습니다. 어떤 경우에는 링크 공급자가 반드시 읽기 전용이며 링크는 런타임시 다른 데이터 소스에서 파생됩니다. 이러한 이유로 수정 가능한 공급자는 선택적으로 구현 될 수 있는 보조 인터페이스입니다.

또한 PSR-7 응답 객체 같은 일부 링크 공급자 객체는 의도적으로 변경할 수 없습니다. 이것은 메소드를 해당 링크에 추가하는 방법은 맞지 않는 다는 것을 의미합니다 따라서 `EvolvableLinkProviderInterface` 의 단일 메소드는 원본과 동일하지만 추가 링크 객체가 포함 된 새로운 객체가 반환되어야 합니다.

## 1.6 Evolvable link objects

링크 오브젝트는 대부분의 경우 값 오브젝트입니다. 따라서 PSR-7 값 객체와 동일한 방식으로 처리하도록 하는 것이 좋은 방식입니다.

> 역자주: 원문은 evolve이지만 의역하였습니다

이러한 이유 때문에, 단일 변경으로 새 객체 인스턴스를 생성하는 메소드인 `EvolvableLinkInterface`가 추가적으로 제공됩니다. 같은 방식이 PSR-7에서 사용되었고, PHP의 copy-on-write 동작 덕분에 여전히 CPU와 메모리가 효율적입니다.

그러나 링크의 templated 된 값은 href 값에 기반하므로 templated 된 값에 대한 확장 가능한 방법은 없습니다.

> 역자주: evolvable지만 좀 더 좋은 단어를 찾지 못하였습니다

이것은 독립적으로 설정하면 안되지만(MUST NOT), href 값이 RFC 6570 링크 템플릿인지에 따라서 결정됩니다.

## 2. Package

설명 한 인터페이스와 클래스는 [psr/link](https://packagist.org/packages/psr/link) 패키지의 일부로 제공됩니다.

## 3. Interfaces

### 3.1 `Psr\Link\LinkInterface`

```php
<?php

namespace Psr\Link;

/**
 * A readable link object.
 */
interface LinkInterface
{
    /**
     * Returns the target of the link.
     *
     * The target link must be one of:
     * - An absolute URI, as defined by RFC 5988.
     * - A relative URI, as defined by RFC 5988. The base of the relative link
     *     is assumed to be known based on context by the client.
     * - A URI template as defined by RFC 6570.
     *
     * If a URI template is returned, isTemplated() MUST return True.
     *
     * @return string
     */
    public function getHref();

    /**
     * Returns whether or not this is a templated link.
     *
     * @return bool
     *   True if this link object is templated, False otherwise.
     */
    public function isTemplated();

    /**
     * Returns the relationship type(s) of the link.
     *
     * This method returns 0 or more relationship types for a link, expressed
     * as an array of strings.
     *
     * @return string[]
     */
    public function getRels();

    /**
     * Returns a list of attributes that describe the target URI.
     *
     * @return array
     *   A key-value list of attributes, where the key is a string and the value
     *  is either a PHP primitive or an array of PHP strings. If no values are
     *  found an empty array MUST be returned.
     */
    public function getAttributes();
}
```

### 3.2 `Psr\Link\EvolvableLinkInterface`

```php
<?php

namespace Psr\Link;

/**
 * An evolvable link value object.
 */
interface EvolvableLinkInterface extends LinkInterface
{
    /**
     * Returns an instance with the specified href.
     *
     * @param string $href
     *   The href value to include. It must be one of:
     *     - An absolute URI, as defined by RFC 5988.
     *     - A relative URI, as defined by RFC 5988. The base of the relative link
     *       is assumed to be known based on context by the client.
     *     - A URI template as defined by RFC 6570.
     *     - An object implementing __toString() that produces one of the above
     *       values.
     *
     * An implementing library SHOULD evaluate a passed object to a string
     * immediately rather than waiting for it to be returned later.
     *
     * @return static
     */
    public function withHref($href);

    /**
     * Returns an instance with the specified relationship included.
     *
     * If the specified rel is already present, this method MUST return
     * normally without errors, but without adding the rel a second time.
     *
     * @param string $rel
     *   The relationship value to add.
     * @return static
     */
    public function withRel($rel);

    /**
     * Returns an instance with the specified relationship excluded.
     *
     * If the specified rel is already not present, this method MUST return
     * normally without errors.
     *
     * @param string $rel
     *   The relationship value to exclude.
     * @return static
     */
    public function withoutRel($rel);

    /**
     * Returns an instance with the specified attribute added.
     *
     * If the specified attribute is already present, it will be overwritten
     * with the new value.
     *
     * @param string $attribute
     *   The attribute to include.
     * @param string $value
     *   The value of the attribute to set.
     * @return static
     */
    public function withAttribute($attribute, $value);

    /**
     * Returns an instance with the specified attribute excluded.
     *
     * If the specified attribute is not present, this method MUST return
     * normally without errors.
     *
     * @param string $attribute
     *   The attribute to remove.
     * @return static
     */
    public function withoutAttribute($attribute);
}
```

#### 3.2 `Psr\Link\LinkProviderInterface`

```php
<?php

namespace Psr\Link;

/**
 * A link provider object.
 */
interface LinkProviderInterface
{
    /**
     * Returns an iterable of LinkInterface objects.
     *
     * The iterable may be an array or any PHP \Traversable object. If no links
     * are available, an empty array or \Traversable MUST be returned.
     *
     * @return LinkInterface[]|\Traversable
     */
    public function getLinks();

    /**
     * Returns an iterable of LinkInterface objects that have a specific relationship.
     *
     * The iterable may be an array or any PHP \Traversable object. If no links
     * with that relationship are available, an empty array or \Traversable MUST be returned.
     *
     * @return LinkInterface[]|\Traversable
     */
    public function getLinksByRel($rel);
}
```

#### 3.3 `Psr\Link\EvolvableLinkProviderInterface`

```php
<?php

namespace Psr\Link;

/**
 * An evolvable link provider value object.
 */
interface EvolvableLinkProviderInterface extends LinkProviderInterface
{
    /**
     * Returns an instance with the specified link included.
     *
     * If the specified link is already present, this method MUST return normally
     * without errors. The link is present if $link is === identical to a link
     * object already in the collection.
     *
     * @param LinkInterface $link
     *   A link object that should be included in this collection.
     * @return static
     */
    public function withLink(LinkInterface $link);

    /**
     * Returns an instance with the specified link removed.
     *
     * If the specified link is not present, this method MUST return normally
     * without errors. The link is present if $link is === identical to a link
     * object already in the collection.
     *
     * @param LinkInterface $link
     *   The link to remove.
     * @return static
     */
    public function withoutLink(LinkInterface $link);
}
```


# PSR-14-event-dispatcher

Event Dispatching은 개발자가 어플리케이션에 로직을 쉽고 일관되게 주입 할 수있게 해주는 일반적이며 잘 테스트 된 메커니즘입니다.

이 PSR의 목표는 라이브러리와 컴포넌트가가 다양한 어플리케이션과 프레임워크간에 보다 자유롭게 재사용 될 수 있도록 Event 기반 확장 및 협업을 위한 공통 메커니즘을 확립하는 것입니다.

이 문서에서 핵심이 되는 단어는 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", "OPTIONAL" 입니다. 이것은 [RFC 2119](http://tools.ietf.org/html/rfc2119)에 설명 된대로 해석해야 합니다.

> 역자주: 위의 키워드는 아래의 번역문에 괄호안에 표시하였습니다

## 목표

Event 발송 및 처리를 위한 공통 인터페이스를 사용하면 개발자가 공통된 방식으로 여러 프레임워크 및 기타 라이브러리와 상호 작용할 수 있는 라이브러리를 만들 수 있습니다.

몇 가지 예 :

* 사용자가 권한이 없을 때 데이터에 저장/액세스를 방지하는 보안 프레임워크.
* 일반적인 전체 페이지 캐싱 시스템.
* 다른 라이브러리를 확장하는 라이브러리. 프레임워크가 둘 다 통합되어 있는지 여부와 관계 없음.
* 어플리케이션 내에서 수행 된 모든 작업을 추적하는 로깅 패키지

## 정의

* **Event** - Event는 *Emitter*가 생성 한 메시지입니다. 임의의 PHP 객체 일 수 있습니다.
* **Listener** - Listener는 Event를 전달할 실행가능한 어떠한 PHP 입니다. 동일한 Event를 0 개 이상의 Listener에 전달할 수 있습니다. Listener는 원할 경우 다른 비동기 동작을 큐에 넣을 수 있습니다 (MAY).
* **Emitter** - Emitter는 Event를 보내고자 하는 임의의 코드입니다. 이것은 "호출 코드"라고도합니다. 이것은 특정 데이터 구조로 표현되지 않고 유스케이스를 가르킵니다.
* **Dispatcher** - Dispatcher는 Emitter에 의해 Event 객체를 전달하는 서비스 객체입니다. Dispatcher는 Event가 관련된 모든 Listener에게 전달되도록 보장하지만, 실행할 Listener를 결정하는 것은 Listener Provider에게 위임해야합니다.(MUST)
* **Listener Provider** - Listener Provider는 주어진 Event와 관련이있는 Listener를 결정할 책임이 있지만 Listener 자신을 호출해서는 안됩니다(MUST NOT). Listener Provider는 0 개 이상의 관련 Listener를 지정할 수 있습니다.

## 이벤트

Event는 Emitter와 해당하는 Listener 간의 통신 단위 역할을 하는 객체입니다.

Emitter에 정보를 다시 돌려주는 Listener를 호출하는 경우 Event 객체는 변경 될 수 있습니다(MAY).

그러나 그러한 양방향 통신이 필요하지 않은 경우 Event는 변경 불가능한 것으로 정의하는 것이 좋습니다(RECOMMENDED). 즉, 뮤 테이터 (mutator) 메소드가 존재하지 않도록 정의합니다.

구현체는 동일한 객체가 모든 Listener에게 반드시 전달된다고 가정해야합니다 (MUST).

Event 객체가 무손실 직렬화 및 비 직렬화를 지원한다는 것이 권장되지만(RECOMMENDED) 필수적이지는 않습니다(NOT REQUIRED). `$event == unserialize(serialize($event))`는 참이어야합니다 (SHOULD). 객체는 PHP의 `Serializable` 인터페이스, `__sleep()` 또는 `__wakeup()` 매직 메소드 또는 적절한 경우 언어(PHP) 내 유사한 기능을 활용 할 수 있습니다 (MAY).

## 멈출수 있는 Event-Stoppable Event

**Stoppable Event**는 더 많은 Listener가 호출되는 것을 방지하는 방법을 추가한, Event의 특별한 케이스입니다. 이것은 `StoppableEventInterface`를 구현하여 표현합니다.

`StoppableEventInterface`를 구현 한 Event는 해당하는 Event가 완료되면 `isPropagationStopped()`로부터 `true`를 리턴해야합니다 (MUST). 그것이 언제인지를 결정하는 것은 클래스를 구현하는 자의 몫입니다. 예를 들어, PSR-7의 `RequestInterface` 객체가 대응하는 `ResponseInterface` 객체와 일치하도록 요청하는 Event는 Listener가 호출 할 `setResponse(ResponseInterface $res)`메소드를 가질 수 있습니다. 이것은 `isPropagationStopped()` 가 `true`를 반환합니다.

## 리스너-Listener

Listener는 실행가능한 어떤 PHP 일 것입니다. Listener는 하나의 매개 변수만을 가져야하며(MUST), 이것은 리스너가 반응해야하는 Event입니다.

Listener는 해당 유즈 케이스와 관련하여 파라메터에 구체적으로 타입 힌트를 입력해야합니다 (SHOULD).

즉, Listener는 인터페이스에 대해 타입 힌트를 입력하여 해당 인터페이스를 구현하는 모든 Event 유형 또는 해당 인터페이스의 특정 구현과 호환 가능하다는 것을 표현합니다.

Listener는 `void`리턴을 가져야하고 (SHOULD), 명시 적으로 리턴하는 타입 힌트를 입력해야합니다 (SHOULD). Dispatcher는 Listener의 리턴 값을 무시해야합니다 (MUST).

Listener는 다른 코드에 행동을 위임 할 수 있습니다(MAY). 이것은 실제 비즈니스 로직을 실행하는 객체 둘러싼 얇은 래퍼(wrapper) 인 Listener가 포함됩니다.

Listener는 cron, 큐 서버 또는 유사한 기술을 사용하는 보조 프로세스를 통해 나중에 처리하기 위해 Event로부터 정보를 큐에 넣을 수 있습니다(MAY). 그러기 위해 Event 객체 자체를 직렬화 할 수있습니다. 그러나 모든 Event 객체가 안전하게 직렬화 될 수 있는 것은 아니므로주의해야합니다. 보조 프로세스는 Event 객체에 대한 모든 변경 사항이 다른 Listener에 전파되지 않는다고 가정해야합니다 (MUST).

## 발송자-Dispatcher

Dispatcher는 `EventDispatcherInterface`를 구현 한 서비스 객체입니다. Listener Provider로부터 전달 된 Event에 대한 Listener를 찾아서 해당 Event와 함께 각 Listener를 호출합니다.

Dispatcher는 :

* ListenerProvider에서 반환 된 순서대로 Listener를 동기적으로 호출해야합니다(MUST).
* Listener를 호출 한 후 전달 된 것과 동일한 Event 객체를 반환해야합니다(MUST).
* 모든 Listener가 실행완료될 때까지 Emitter로 돌아 가지 않아야합니다(MUST NOT).

Dispatcher는 Stoppable Event가 전달되면

* 각 Listener가 호출되기 전에 Event에 대해 반드시 `isPropagationStopped()`를 호출해야합니다 (MUST). 그 메소드가 `true`를 리턴하면, 반드시 Event를 Emitter에게 즉시 반환해야하고 더 이상의 Listener를 호출해서는 안됩니다. 이것은 `isPropagationStopped()`에서 항상 `true`를 반환하는 Event가 Dispatcher에 전달되면 0개의 Listener가 호출됨을 의미합니다.

Dispatcher는 Listener 제공자로부터 리턴 된 Listener가 type-safe하다고 가정해야합니다 (SHOULD). 즉, Dispatcher는 `$listener($event)`호출이 `TypeError`를 생성하지 않아야한다고 가정해야합니다 (SHOULD).

### Error handling

Listener가 던진 예외 또는 오류는 이후의 모든 Listener의 실행을 차단해야합니다(MUST). Listener에 의해 던져진 오류 또는 예외는 Emitter로 전달 되야합니다(MUST).

Dispatcher는 던져진 객체를 잡아서 기록 할 수 있고(MAY), 추가적인 조치가 행동을 하도록 허용하지만 반드시 원래의 Throwable을 다시 던져야합니다 (MUST).

## Listener Provider

Listener Provider는, Listener가 어느 Event에 관련이 있는지, 어떤 Listener가 호출되어야 하는지를 결정하는 서비스 객체입니다. Listener가 무엇을 의미하는지 그리고 Listener가 선택하는 방법에 따라 Listener를 돌려주는 순서를 결정할 수 있습니다. 다음이 포함될 수 있습니다(MAY):

* 구현자가 고정 된 순서로 Event에 Listener를 할당 할 수 있도록 일부 등록 메커니즘을 허용합니다.
* Event 유형 과 구현 된 인터페이스를 기반으로 리플렉션을 통해 해당 Listener 목록을 만듭니다.
* 런타임에 참조 될 수 있는 미리 컴파일 된 Listener의 목록 생성합니다.
* 현재 사용자에게 특정 권한이 있는 경우에만 특정 Listener가 호출되도록 액세스 제어 형식을 구현합니다.
* 엔티티와 같이 Event에 의해 참조되는 객체에서 일부 정보를 추출하고 해당 객체에 대해 미리 정의 된 라이프 사이클 메소드를 호출합니다.
* 임의의 로직를 사용하여 하나 이상의 다른 Listener Provider에게 책임을 위임합니다.

위의 메커니즘이나 다른 메커니즘을 원하는대로 사용할 수 있습니다(MAY).

Listener Provider는 Event의 클래스 이름을 사용하여 Event를 다른 Event와 구별해야합니다 (SHOULD). 또한 Event에 대한 다른 정보를 적절하게 고려할 수 있습니다(MAY).

Listener Provider는 Listener 적용 가능 여부를 결정할 때 부모 유형을 Event의 자체 유형과 동일하게 처리해야합니다(MUST).

다음과 같은 경우 :

```php
class A {}

class B extends A {}

$b = new B();

function listener(A $event): void {};
```

Listener Provider는 리스너의 다른 조건에 의해 타입이 호환되지 않는 한 호환성이 유효하므로, `$b`의 Listener로서 `listener()`를 처리 해야합니다 (MUST).

## 오브젝트 구성-Object composition

Dispatcher는 관련된 Listener를 판별하기 위해 Listener Provider를 구성해야합니다 (SHOULD). Listener Provider가 Dispatcher와는 별개의 오브젝트로 구현되지만 반드시 요구되지는 않는 것이 좋습니다 (RECOMMENDED).

## Interfaces

```php
namespace Psr\EventDispatcher;

/**
 * Defines a dispatcher for events.
 */
interface EventDispatcherInterface
{
    /**
     * Provide all relevant listeners with an event to process.
     *
     * @param object $event
     *   The object to process.
     *
     * @return object
     *   The Event that was passed, now modified by listeners.
     */
    public function dispatch(object $event);
}
```

```php
namespace Psr\EventDispatcher;

/**
 * Mapper from an event to the listeners that are applicable to that event.
 */
interface ListenerProviderInterface
{
    /**
     * @param object $event
     *   An event for which to return the relevant listeners.
     * @return iterable[callable]
     *   An iterable (array, iterator, or generator) of callables.  Each
     *   callable MUST be type-compatible with $event.
     */
    public function getListenersForEvent(object $event) : iterable;
}
```

```php
namespace Psr\EventDispatcher;

/**
 * An Event whose processing may be interrupted when the event has been handled.
 *
 * A Dispatcher implementation MUST check to determine if an Event
 * is marked as stopped after each listener is called.  If it is then it should
 * return immediately without calling any further Listeners.
 */
interface StoppableEventInterface
{
    /**
     * Is propagation stopped?
     *
     * This will typically only be used by the Dispatcher to determine if the
     * previous listener halted propagation.
     *
     * @return bool
     *   True if the Event is complete and no further listeners should be called.
     *   False to continue calling listeners.
     */
    public function isPropagationStopped() : bool;
}
```


# PSR-15-request-handlers

이 문서에서는 [PSR-7](http://www.php-fig.org/psr/psr-7/) 또는 후속 PSR로 설명 된 HTTP 메시지를 사용하는 HTTP 서버 요청 처리기 ("요청 처리기") 및 HTTP 서버 미들웨어 구성 요소 ("미들웨어")에 대한 일반적인 인터페이스에 대해 설명합니다.

HTTP 요청 처리기는 모든 웹 응용 프로그램의 기본 요소입니다. 서버 측 코드는 요청 메시지를 수신하여 처리하고 응답 메시지를 생성합니다. HTTP 미들웨어는 공통 요청 및 응답 처리를 응용 프로그램 계층에서 분리하는 한 방법입니다.

이 문서에서 설명하는 인터페이스는 요청 처리기 및 미들웨어에 대한 추상화입니다.

*참고 : "요청 처리기"와 "미들웨어"에 대한 모든 참조는 **서버 요청** 처리에만 해당됩니다 .*

이 문서에서 핵심이 되는 단어는 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", "OPTIONAL" 입니다. 이것은 [rfc2119](http://tools.ietf.org/html/rfc2119)에 설명 된대로 해석해야 합니다.

> 역자주: 위의 키워드는 아래의 번역문에 괄호안에 표시하였습니다

### References

* [PSR-7](http://www.php-fig.org/psr/psr-7/)
* [RFC 2119](http://tools.ietf.org/html/rfc2119)

## 1. 명세서

### 1.1 Request Handlers

요청 처리기는 PSR-7에 정의 된대로 요청을 처리하고 응답을 생성하는 개별 구성 요소입니다.

요청 처리자가 응답을 생성하지 못하는 경우 요청 처리기가 예외를 던질 수 있습니다 (MAY). 예외 유형이 정의되지 않았습니다.

이 표준을 사용하는 요청 처리기는 다음 인터페이스를 구현해야합니다 (MUST).

* `Psr\Http\Server\RequestHandlerInterface`

### 1.2 Middleware

미들웨어 구성 요소는 들어오는 요청을 처리하고 PSR-7에 정의 된 결과 응답을 생성 할 때 다른 미들웨어 구성 요소와 함께 참여하는 개별 구성 요소입니다.

미들웨어 컴포넌트는 충분한 조건이 만족된다면 요청 처리자에게 위임하지 않고 응답을 생성하고 리턴 할 수 있습니다 (MAY).

이 표준을 사용하는 미들웨어는 다음 인터페이스를 구현해야합니다(MUST).

* `Psr\Http\Server\MiddlewareInterface`

### 1.3 Generating Responses

응답을 생성하는 미들웨어 또는 요청 처리기는 특정 HTTP 메시지 구현에 대한 의존을 방지하기 위해 PSR-7 `ResponseInterface` 의 프로토타입 또는 `ResponseInterface` 인스턴스를 생성 할 수 있는 팩토리를 작성하는 것이 좋습니다(RECOMMENDED).

### 1.4 Handling Exceptions

미들웨어를 사용하는 모든 응용 프로그램에는 예외를 잡아서 응답으로 변환하는 구성 요소가 포함되는 것이 좋습니다(RECOMMENDED). 이 미들웨어는 실행 된 첫 번째 구성 요소 여야하며(SHOULD) 응답이 항상 생성되도록하기 위해 모든 추가 처리를 래핑해야합니다.

## 2. Interfaces

### 2.1 Psr\Http\Server\RequestHandlerInterface

요청 처리기는 다음의 인터페이스를 구현되어야합니다(MUST).

```php
namespace Psr\Http\Server;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

/**
 * Handles a server request and produces a response.
 *
 * An HTTP request handler process an HTTP request in order to produce an
 * HTTP response.
 */
interface RequestHandlerInterface
{
    /**
     * Handles a request and produces a response.
     *
     * May call other collaborating code to generate the response.
     */
    public function handle(ServerRequestInterface $request): ResponseInterface;
}
```

### 2.2 Psr\Http\Server\MiddlewareInterface

미들웨어 구성 요소는 다음 인터페이스에 호환 가능하게 구현해야합니다(MUST).

```php
namespace Psr\Http\Server;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

/**
 * Participant in processing a server request and response.
 *
 * An HTTP middleware component participates in processing an HTTP message:
 * by acting on the request, generating the response, or forwarding the
 * request to a subsequent middleware and possibly acting on its response.
 */
interface MiddlewareInterface
{
    /**
     * Process an incoming server request.
     *
     * Processes an incoming server request in order to produce a response.
     * If unable to produce the response itself, it may delegate to the provided
     * request handler to do so.
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface;
}
```


# PSR-16-simple-cache

## Common Interface for Caching Libraries

이 문서는 캐시 항목과 캐시 드라이버에 대한 간단하면서도 확장 가능한 인터페이스를 설명합니다.

이 문서에서 핵심이 되는 단어는 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", "OPTIONAL" 입니다. 이것은 [RFC 2119](http://tools.ietf.org/html/rfc2119)에 설명 된대로 해석해야 합니다.

> 역자주: 위의 키워드는 아래의 번역문에 괄호안에 표시하였습니다

최종 구현체는 제시된 것보다 더 많은 기능을 가진 객체로 만들 수 있지만(MAY) 반드시 지정한 인터페이스/기능을 먼저 구현해야합니다(MUST).

## 1. 명세서

### 1.1 소개

캐싱은 모든 프로젝트의 성능을 향상시키는 일반적인 방법이며, 캐싱 라이브러리를 많은 프레임 워크 및 라이브러리의 가장 일반적인 기능 중 하나로 만듭니다. 여기서 말하는 상호 운용성은 라이브러리가 자체 캐싱 구현을 중단하고 프레임 워크 또는 다른 캐시 전용 라이브러리에 의해 제공되는 구현에 쉽게 의지 할 수 있음을 의미합니다.

PSR-6은 이미 이 문제를 해결했지만 단순한 유스 케이스에 필요한 이상으로 공식적이고 장황한 방법으로 해결합니다. 이 간단한 접근 방식은 일반적인 경우에 대해 표준화와 간소화 된 인터페이스를 구축하는 것을 목표로합니다. 이것은 PSR-6과 독립적이지만 PSR-6과의 호환성을 가진채 가능한 한 간단하게 만들도록 설계되었습니다.

### 1.2 정의

호출 라이브러리, 구현 라이브러리, TTL, 만료 및 키에 대한 정의는 PSR-6에서 복사 한 것과 동일한 것으로 가정합니다

* **호출 라이브러리(Calling Library)** - 실제로 캐시 서비스가 필요한 라이브러리 또는 코드. 이 라이브러리는이 표준의 인터페이스를 구현하는 캐싱 서비스를 활용할 것이며, 그렇지 않으면 이러한 캐싱 서비스의 구현에 대한 알지 못합니다.

  > 역자주: 인터페이스를 구현한 캐싱 서비스를 사용만하고 구현을 하지 않는다.
* **구현 라이브러리(Implementing Library)** - 이 라이브러리는 모든 호출 라이브러리에 캐싱 서비스를 제공하기 위해이 표준을 구현합니다. 구현 라이브러리는 `Cache\CacheItemPoolInterface` 및 `Cache\CacheItemInterface` 인터페이스를 구현하는 클래스를 제공해야합니다(MUST). 라이브러리 구현은 전체 초 단위로 아래에 설명 된 최소 TTL 기능을 지원해야만 합니다 (MUST).
* **TTL** - TTL (Time To Live)은 해당 항목이 저장하고 보관하는 것으로 간주되는 시간입니다. TTL은 일반적으로 초 단위의 시간을 나타내는 정수 또는 DateInterval 개체로 정의됩니다.
* **만료(Expiration)** - 항목이 만료로 설정되는 실제 시간입니다. 이것은 일반적으로 개체가 저장된 시간에 TTL을 추가하여 계산됩니다. 이것은 일반적으로 개체가 저장된 시간에 TTL을 추가하여 계산되지만 DateTime 개체로 명시 적으로 설정할 수도 있습니다. 1:30:00에 저장된 300 초 TTL을 가진 항목의 만료 시간은 1:35:00입니다. 구현 라이브러리는 요청한 만료 시간 전에 항목을 만료시킬 수도 있지만(MAY) 만료 시간에 도달하면 만료 된 것으로 간주해야합니다(MUST). 호출 라이브러리가 항목을 저장요청을 하지만 만료 시간을 지정하지 않거나 만료 시간 또는 TTL을 null로 지정되어 있다면 구현 라이브러리는 설정한 기본 기간을 사용할 수 있습니다 (MAY). 기본 기간이 설정되지 않은 경우 구현 라이브러리는 해당 항목을 영원히 캐시하는 요청으로 해석하거나 기본 구현이 지원하는 동안 저장하는 것으로 해석해야합니다(MUST).
* **Key** - 캐시 된 항목을 고유하게 식별하는 적어도 하나의 문자로 구성된 문자열입니다. 구현 라이브러리는`A-Z`,`a-z`,`0-9`,`_` 및`.` 문자로 구성된 키를 UTF-8 인코딩과 길이가 64 문자 이하의 순서로 지원해야합니다 (MUST). 구현 라이브러리은 추가 문자와 인코딩 또는 더 긴 길이를 지원할 수 있지만(MAY) 최소한 그 최소값을 지원해야합니다(MUST). 라이브러리는 적절하게 키 문자열을 이스케이프 처리해야하지만 원래의 수정되지 않은 키 문자열을 반환 할 수 있어야합니다(MUST). 다음 문자는 미래의 확장을 위해 예약되어 있으며, 구현 라이브러리에서 지원해서는 안됩니다(MUST NOT) :`{} () / \ @ :`
* **Cache** - `Psr\SimpleCache\CacheInterface` 인터페이스를 구현하는 객체.
* **Cache Misses** - 캐시 미스 (cache miss)는 널(null)을 리턴 할 것이고 따라서 `null`이 하나만 저장된 경우 구별이 불가능합니다. 이것은 PSR-6와 가장 큰 차이입니다.

### 1.3 Cache

구현체는 특정 캐시 항목에 대해 TTL을 지정되지 않은 경우 사용자가 기본 TTL을 지정하는 메커니즘을 제공 할 수 있습니다 (MAY). 사용자 지정 기본값도 제공되지 않으면 구현체은 기본 구현체에서 허용되는 최대 유효 값으로 기본 설정되어야합니다(MUST). 기본 구현체가 TTL을 지원하지 않으면 사용자 지정 TTL을 자동으로 무시해야합니다 (MUST).

### 1.4 Data

라이브러리를 구현할 때 다음을 포함하여 모든 직렬화 가능한 PHP 데이터 유형을 지원해야합니다 (MUST).

* **Strings** - PHP 호환 인코딩의 임의의 크기를 갖는 문자열.
* **Integers** - PHP가 지원하는 모든 크기의 정수 (최대 64 비트).
* **Floats** - 부호가 있는(signed) 모든 부동소수점 값.
* **Booleans** - True 와 False.
* **Null** - null값 (캐시미스와 구별이 되지 않습니다).
* **Arrays** - 인덱스된 임의의 깊이를 가진 연관 및 다차원 배열
* **Objects** - `$o == unserialize(serialize($o))` 와 같이 무손실 직렬화 및 직렬화 해제를 지원하는 객체. 객체는 PHP의 Serializable 인터페이스, 적절한 경우 `__sleep()` 또는 `__wakeup()` 매직 메서드 또는 유사한 언어 기능을 활용할 수 있습니다(MAY).

구현 라이브러리에 전달 된 모든 데이터는 전달 된 그대로 정확하게 반환되어야합니다(MUST). 여기에는 가변 유형이 포함됩니다. 즉, 만약 (int) 5가 저장된 값이었던 경우 (string) 5를 반환하는 것은 오류입니다. 구현 라이브러리는 PHP의 serialize()/unserialize() 함수를 내부적으로 사용할 수 있지만 꼭 그렇게 할 필요는 없습니다(MAY). 이들과의 호환성은 수용 가능한 객체 값의 기준선으로 사용됩니다.

어떠한 이유로든 저장된 정확한 값을 반환 할 수없는 경우 구현 라이브러리는 데이터가 손상되지 않게 캐시 미스로 응답해야합니다(MUST).

## 2. Interfaces

### 2.1 CacheInterface

캐시 인터페이스는 각 캐시 항목의 기본 읽기, 쓰기 및 삭제를 수반하는 캐시 항목 모음에 대한 가장 기본적인 작업을 정의합니다.

또한 한 번에 여러 캐시 항목을 쓰거나, 읽거나 삭제하는 것과 같은 캐시 항목의 여러 세트를 처리하기위한 방법을 제공합니다. 이는 수행 할 캐시 읽기 / 쓰기가 많을 때 유용하며 대기 시간을 대폭 줄이는 캐시 서버에 대한 단일 호출로 작업을 수행 할 수 있습니다.

CacheInterface의 인스턴스는 단일 키 네임 스페이스가있는 캐시 항목의 단일 모음에 해당하며 PSR-6의 "풀"과 동일합니다. 서로 다른 CacheInterface 인스턴스는 동일한 데이터 저장소에 의해 백업 될 수 있지만 (MAY) 반드시 논리적으로 독립적이어야합니다 (MUST).

```php
<?php

namespace Psr\SimpleCache;

interface CacheInterface
{
    /**
     * Fetches a value from the cache.
     *
     * @param string $key     The unique key of this item in the cache.
     * @param mixed  $default Default value to return if the key does not exist.
     *
     * @return mixed The value of the item from the cache, or $default in case of cache miss.
     *
     * @throws \Psr\SimpleCache\InvalidArgumentException
     *   MUST be thrown if the $key string is not a legal value.
     */
    public function get($key, $default = null);

    /**
     * Persists data in the cache, uniquely referenced by a key with an optional expiration TTL time.
     *
     * @param string                 $key   The key of the item to store.
     * @param mixed                  $value The value of the item to store. Must be serializable.
     * @param null|int|\DateInterval $ttl   Optional. The TTL value of this item. If no value is sent and
     *                                      the driver supports TTL then the library may set a default value
     *                                      for it or let the driver take care of that.
     *
     * @return bool True on success and false on failure.
     *
     * @throws \Psr\SimpleCache\InvalidArgumentException
     *   MUST be thrown if the $key string is not a legal value.
     */
    public function set($key, $value, $ttl = null);

    /**
     * Delete an item from the cache by its unique key.
     *
     * @param string $key The unique cache key of the item to delete.
     *
     * @return bool True if the item was successfully removed. False if there was an error.
     *
     * @throws \Psr\SimpleCache\InvalidArgumentException
     *   MUST be thrown if the $key string is not a legal value.
     */
    public function delete($key);

    /**
     * Wipes clean the entire cache's keys.
     *
     * @return bool True on success and false on failure.
     */
    public function clear();

    /**
     * Obtains multiple cache items by their unique keys.
     *
     * @param iterable $keys    A list of keys that can obtained in a single operation.
     * @param mixed    $default Default value to return for keys that do not exist.
     *
     * @return iterable A list of key => value pairs. Cache keys that do not exist or are stale will have $default as value.
     *
     * @throws \Psr\SimpleCache\InvalidArgumentException
     *   MUST be thrown if $keys is neither an array nor a Traversable,
     *   or if any of the $keys are not a legal value.
     */
    public function getMultiple($keys, $default = null);

    /**
     * Persists a set of key => value pairs in the cache, with an optional TTL.
     *
     * @param iterable               $values A list of key => value pairs for a multiple-set operation.
     * @param null|int|\DateInterval $ttl    Optional. The TTL value of this item. If no value is sent and
     *                                       the driver supports TTL then the library may set a default value
     *                                       for it or let the driver take care of that.
     *
     * @return bool True on success and false on failure.
     *
     * @throws \Psr\SimpleCache\InvalidArgumentException
     *   MUST be thrown if $values is neither an array nor a Traversable,
     *   or if any of the $values are not a legal value.
     */
    public function setMultiple($values, $ttl = null);

    /**
     * Deletes multiple cache items in a single operation.
     *
     * @param iterable $keys A list of string-based keys to be deleted.
     *
     * @return bool True if the items were successfully removed. False if there was an error.
     *
     * @throws \Psr\SimpleCache\InvalidArgumentException
     *   MUST be thrown if $keys is neither an array nor a Traversable,
     *   or if any of the $keys are not a legal value.
     */
    public function deleteMultiple($keys);

    /**
     * Determines whether an item is present in the cache.
     *
     * NOTE: It is recommended that has() is only to be used for cache warming type purposes
     * and not to be used within your live applications operations for get/set, as this method
     * is subject to a race condition where your has() will return true and immediately after,
     * another script can remove it, making the state of your app out of date.
     *
     * @param string $key The cache item key.
     *
     * @return bool
     *
     * @throws \Psr\SimpleCache\InvalidArgumentException
     *   MUST be thrown if the $key string is not a legal value.
     */
    public function has($key);
}
```

### 2.2 CacheException

```php

<?php

namespace Psr\SimpleCache;

/**
 * Interface used for all types of exceptions thrown by the implementing library.
 */
interface CacheException
{
}
```

### 2.3 InvalidArgumentException

```php
<?php

namespace Psr\SimpleCache;

/**
 * Exception interface for invalid cache arguments.
 *
 * When an invalid argument is passed it, must throw an exception which implements
 * this interface.
 */
interface InvalidArgumentException extends CacheException
{
}
```


# PSR-17-http-factory

이 문서는 [PSR-7](https://www.php-fig.org/psr/psr-7/)에 호환하는 HTTP 객체를 만드는 팩토리의 공통 표준을 설명합니다.

PSR-7은 HTTP 객체를 만드는 방법에 대한 권장 사항을 포함하지 않았기 때문에 PSR-7의 특정 구현과 관련되지 않은 구성 요소 내에 새로운 HTTP 객체를 생성해야 할 때 어려움을 겪습니다.

이 문서에 설명 된 인터페이스는 PSR-7 객체를 인스턴스화 할 수 있는 방법을 설명합니다.

이 문서에서 핵심이 되는 단어는 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", "OPTIONAL" 입니다. 이것은 [RFC 2119](https://tools.ietf.org/html/rfc2119)에 설명 된대로 해석해야 합니다.

> 역자주: 위의 키워드는 아래의 번역문에 괄호안에 표시하였습니다

## 1. 명세서

HTTP 팩토리는 PSR-7에 정의 된대로 새 HTTP 객체를 만드는 방법입니다. HTTP 팩토리는 패키지가 제공하는 각 객체 유형에 대해 이러한 인터페이스를 구현해야합니다 (MUST).

## 2. Interfaces

다음 인터페이스들은 단일 클래스 내에서 또는 별도의 클래스들 내에서 함께 구현 될 수있습니다 (MAY).

### 2.1 RequestFactoryInterface

클라이언트 요청을 생성하는 기능

```php
namespace Psr\Http\Message;

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\UriInterface;

interface RequestFactoryInterface
{
    /**
     * Create a new request.
     *
     * @param string $method The HTTP method associated with the request.
     * @param UriInterface|string $uri The URI associated with the request. 
     */
    public function createRequest(string $method, $uri): RequestInterface;
}
```

### 2.2 ResponseFactoryInterface

응답을 생성하는 하는 기능

```php
namespace Psr\Http\Message;

use Psr\Http\Message\ResponseInterface;

interface ResponseFactoryInterface
{
    /**
     * Create a new response.
     *
     * @param int $code The HTTP status code. Defaults to 200.
     * @param string $reasonPhrase The reason phrase to associate with the status code
     *     in the generated response. If none is provided, implementations MAY use
     *     the defaults as suggested in the HTTP specification.
     */
    public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface;
}
```

### 2.3 ServerRequestFactoryInterface

서버 요청을 생성하는 기능

```php
namespace Psr\Http\Message;

use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\UriInterface;

interface ServerRequestFactoryInterface
{
    /**
     * Create a new server request.
     *
     * Note that server parameters are taken precisely as given - no parsing/processing
     * of the given values is performed. In particular, no attempt is made to
     * determine the HTTP method or URI, which must be provided explicitly.
     *
     * @param string $method The HTTP method associated with the request.
     * @param UriInterface|string $uri The URI associated with the request. 
     * @param array $serverParams An array of Server API (SAPI) parameters with
     *     which to seed the generated request instance.
     */
    public function createServerRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface;
}
```

### 2.4 StreamFactoryInterface

요청 및 응답을 위한 스트림을 생성하는 기능

```php
namespace Psr\Http\Message;

use Psr\Http\Message\StreamInterface;

interface StreamFactoryInterface
{
    /**
     * Create a new stream from a string.
     *
     * The stream SHOULD be created with a temporary resource.
     *
     * @param string $content String content with which to populate the stream.
     */
    public function createStream(string $content = ''): StreamInterface;

    /**
     * Create a stream from an existing file.
     *
     * The file MUST be opened using the given mode, which may be any mode
     * supported by the `fopen` function.
     *
     * The `$filename` MAY be any string supported by `fopen()`.
     *
     * @param string $filename The filename or stream URI to use as basis of stream.
     * @param string $mode The mode with which to open the underlying filename/stream.
     *
     * @throws \RuntimeException If the file cannot be opened.
     * @throws \InvalidArgumentException If the mode is invalid.
     */
    public function createStreamFromFile(string $filename, string $mode = 'r'): StreamInterface;

    /**
     * Create a new stream from an existing resource.
     *
     * The stream MUST be readable and may be writable.
     *
     * @param resource $resource The PHP resource to use as the basis for the stream.
     */
    public function createStreamFromResource($resource): StreamInterface;
}
```

이 인터페이스의 구현은 문자열에서 리소스를 만들 때 임시 스트림을 사용해야합니다 (SHOULD). 이렇게하는 권장 방법은 다음과 같습니다 (RECOMMENDED).

```php
$resource = fopen('php://temp', 'r+');
```

### 2.5 UploadedFileFactoryInterface

업로드 된 파일의 스트림을 만들 수 있는 기능

```php
namespace Psr\Http\Message;

use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UploadedFileInterface;

interface UploadedFileFactoryInterface
{
    /**
     * Create a new uploaded file.
     *
     * If a size is not provided it will be determined by checking the size of
     * the stream.
     *
     * @link http://php.net/manual/features.file-upload.post-method.php
     * @link http://php.net/manual/features.file-upload.errors.php
     *
     * @param StreamInterface $stream The underlying stream representing the
     *     uploaded file content.
     * @param int $size The size of the file in bytes.
     * @param int $error The PHP file upload error.
     * @param string $clientFilename The filename as provided by the client, if any.
     * @param string $clientMediaType The media type as provided by the client, if any.
     *
     * @throws \InvalidArgumentException If the file resource is not readable.
     */
    public function createUploadedFile(
        StreamInterface $stream,
        int $size = null,
        int $error = \UPLOAD_ERR_OK,
        string $clientFilename = null,
        string $clientMediaType = null
    ): UploadedFileInterface;
}
```

### 2.6 UriFactoryInterface

클라이언트 및 서버 요청에 대한 URI를 생성하는 기능

```php
namespace Psr\Http\Message;

use Psr\Http\Message\UriInterface;

interface UriFactoryInterface
{
    /**
     * Create a new URI.
     *
     * @param string $uri The URI to parse.
     *
     * @throws \InvalidArgumentException If the given URI cannot be parsed.
     */
    public function createUri(string $uri = '') : UriInterface;
}
```


# PSR-18-http-client

이 문서에서는 HTTP 요청을 보내고 HTTP 응답을받는 일반적인 인터페이스에 대해 설명합니다.

이 문서에서 핵심이 되는 단어는 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", "OPTIONAL" 입니다. 이것은 [RFC 2119](http://tools.ietf.org/html/rfc2119)에 설명 된대로 해석해야 합니다.

> 역자주: 위의 키워드는 아래의 번역문에 괄호안에 표시하였습니다

## 목표

이 PSR의 목표는 개발자가 HTTP 클라이언트 구현에서 분리 된 라이브러리를 만들 수있게하는 것입니다. 이렇게하면 종속성 수가 줄어들고 버전 충돌의 가능성이 줄어들기 때문에 라이브러리를 더 많이 재사용 할 수 있습니다.

두 번째 목표는 HTTP 클라이언트가 [Liskov 치환 원칙](https://en.wikipedia.org/wiki/Liskov_substitution_principle)에 따라 대체 될 수 있다는 것입니다. 즉, 요청을 보낼 때 모든 클라이언트가 동일한 방식으로 작동해야합니다.

## 정의

* **클라이언트** - 클라이언트는 PSR-7 호환 HTTP 요청 메시지를 보내고 PSR-7 호환 HTTP 응답 메시지를 호출 라이브러리에 반환하기 위해 이 사양을 구현하는 라이브러리입니다.
* **호출 라이브러리** - 호출 라이브러리는 클라이언트를 사용하는 모든 코드입니다. 이 문서에 존재하는 인터페이스를 구현하지 않지만 이를 구현하는 객체 (클라이언트)를 사용합니다.

## 클라이언트

클라이언트는 `ClientInterface`를 구현 한 객체입니다.

클라이언트는 다음과 같은 것을 할 수 있습니다.(MAY) :

* 제공된 HTTP 요청을 변경하여 보낼지 선택합니다. 예를 들어, 보내는 메시지 본문을 압축 할 수 있습니다.
* 수신 된 HTTP 응답을 호출 라이브러리로 되돌리기 전에 변경할 것인지 선택합니다. 예를 들어 들어오는 메시지 본문의 압축을 풀 수 있습니다.

클라이언트가 HTTP 요청이나 HTTP 응답을 변경하기로 결정한 경우, 객체가 내부적으로 일관성을 유지하는지 확인해야합니다 (MUST). 예를 들어, 클라이언트가 메시지 본문을 압축 해제하기로 선택한 경우, 반드시 `Content-Encoding` 헤더를 제거하고`Content-Length` 헤더를 조정해야합니다.

결과적으로 [PSR-7 객체는 불변이므로](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-7-http-message-meta.md#why-value-objects), 호출 라이브러리는 `ClientInterface::sendRequest()`에 전달 된 객체가 실제로 전송 된 것과 동일한 PHP 객체가 될 것이라고 가정해서는 안됩니다. 예를 들어, 예외에 의해 반환 된 Request 객체는 `sendRequest()`에 전달 된 것과 다른 객체 일 수 있기 때문에 참조 (===)로 비교할 수 없습니다.

클라이언트는 반드시 다음을 지켜야합니다.(MUST) :

* 호출 라이브러리에 반환되는 내용이 상태 코드 200 이상의 유효한 HTTP 응답이 되도록 여러개의 HTTP 1xx 응답 자체를 재구성하십시오

## 오류 처리

클라이언트는 올바른 형식의 HTTP 요청이나 HTTP 응답을 오류 조건으로 취급해서는 안됩니다 (MUST NOT).

예를 들어 400 및 500 범위의 응답 상태 코드는 예외를 발생시키지 않아야하며 정상적으로 호출 라이브러리에 반환되어야합니다 (MUST).

클라이언트가 HTTP 요청을 전혀 보낼 수 없거나 HTTP 응답을 PSR-7 응답 객체로 구문 분석을 할 수없는 경우에만 클라이언트는 `Psr\Http\Client\ClientExceptionInterface` 인스턴스를 던져야합니다.

요청 메시지가 올바른 형식의 HTTP 요청이 아니거나 일부 중요한 정보 (예 : 호스트 또는 메서드)가 없어서 요청을 보낼 수없는 경우, 클라이언트는 `Psr\Http\Client\RequestExceptionInterface`의 인스턴스를 던져야만 합니다.(MUST)

타임 아웃을 포함하여 어떤 종류의 네트워크 장애로 인해 요청을 보낼 수없는 경우, 클라이언트는 `Psr\Http\Client\NetworkExceptionInterface`의 인스턴스를 던져야만 합니다 (MUST).

클라이언트는 위에서 정의된 인터페이스를 적합하게 구현한다면 여기에 정의 된 것보다 더 구체적인 예외 (예 :`TimeOutException` 또는 `HostNotFoundException`)를 던질 수도 있습니다 (MAY).

## Interfaces

### ClientInterface

```php
namespace Psr\Http\Client;

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

interface ClientInterface
{
    /**
     * Sends a PSR-7 request and returns a PSR-7 response.
     *
     * @param RequestInterface $request
     *
     * @return ResponseInterface
     *
     * @throws \Psr\Http\Client\ClientExceptionInterface If an error happens while processing the request.
     */
    public function sendRequest(RequestInterface $request): ResponseInterface;
}
```

### ClientExceptionInterface

```php
namespace Psr\Http\Client;

/**
 * Every HTTP client related exception MUST implement this interface.
 */
interface ClientExceptionInterface extends \Throwable
{
}
```

### RequestExceptionInterface

```php
namespace Psr\Http\Client;

use Psr\Http\Message\RequestInterface;

/**
 * Exception for when a request failed.
 *
 * Examples:
 *      - Request is invalid (e.g. method is missing)
 *      - Runtime request errors (e.g. the body stream is not seekable)
 */
interface RequestExceptionInterface extends ClientExceptionInterface
{
    /**
     * Returns the request.
     *
     * The request object MAY be a different object from the one passed to ClientInterface::sendRequest()
     *
     * @return RequestInterface
     */
    public function getRequest(): RequestInterface;
}
```

### NetworkExceptionInterface

```php
namespace Psr\Http\Client;

use Psr\Http\Message\RequestInterface;

/**
 * Thrown when the request cannot be completed because of network issues.
 *
 * There is no response object as this exception is thrown when no response has been received.
 *
 * Example: the target host name can not be resolved or the connection failed.
 */
interface NetworkExceptionInterface extends ClientExceptionInterface
{
    /**
     * Returns the request.
     *
     * The request object MAY be a different object from the one passed to ClientInterface::sendRequest()
     *
     * @return RequestInterface
     */
    public function getRequest(): RequestInterface;
}
```


# PSR-20-clock

## Common Interface for Accessing the Clock

이 문서는 시스템의 시간을 읽기 위한 간단한 인터페이스에 대해 설명합니다.

이 문서에서 핵심이 되는 단어는 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", "OPTIONAL" 입니다. 이것은 [RFC 2119](http://tools.ietf.org/html/rfc2119)에 설명 된대로 해석해야 합니다.

> 역자주: 위의 키워드는 아래의 번역문에 괄호안에 표시하였습니다

최종 구현은 제안된 것보다 더 많은 기능을 가진 개체로 만들 수도 있지만, 표시한 인터페이스/기능을 먼저 구현해야 합니다.

## 1. 명세서

### 1.1 소개

시간에 접근하기위한 표준 메서드를 만들면, 시점에 기반한 사이드이펙트가 있는 동작을 테스트할 때, 상호 운용성을 가지게 됩니다.

> 역자주: 특정한 시간에만 동작하는 코드를 테스트할 때, 강제로 시간을 바꿔서 해당 코드가 동작하거나 하지 않는 테스트를 하기 좋아진다는 의미입니다

일반적으로 현재 시간을 가져오는 방법으로는 `\time()` 또는 `new \DateTimeImmutable('now')`을 사용하는 것이 포함됩니다. 그러나 이것은 일부 상황에서 현재 시간을 모킹-mocking하는 것을 불가능하게 만듭니다.

> 역자주: 이 메서드들은 강제로 시간을 바꾸는 기능을 지원하지 않습니다. 따라서 시점에 따른 코드의 동작을 테스트 할 수 없습니다

### 1.2 정의

* **Clock** - clock은 현재 시간과 날짜를 읽을 수 있습니다.
* **Timestamp** - Jan 1, 1970 00:00:00 UTC 이후의 초-second로 표시되는 현재 시간

#### 1.3 사용법

**현재 타임스탬프 가져오기**

이 작업은 반환된 `\DateTimeImmutable`에서 `getTimestamp()` 메서드를 사용해서 처리해야 합니다.

```php
$timestamp = $clock->now()->getTimestamp();
```

## 2. Interfaces

### 2.1 ClockInterface

clock 인터페이스는 clock에서 현재 시간과 날짜를 읽는 가장 기본적인 동작을 정의합니다.

이것은 반드시 `\DateTimeImmutable`로 시간을 반환해야 합니다 (MUST).

```php
<?php

namespace Psr\Clock;

interface ClockInterface
{
    /**
     * Returns the current time as a DateTimeImmutable Object
     */
    public function now(): \DateTimeImmutable;

}
```


# PSR-0

> **사용중단** - 2014-10-21을 기준으로 PSR-0은 사용이 중단되었습니다. 새로운 표준으로 [PSR-4](https://github.com/kkame/fig-standards/blob/gitbook/accepted/psr-4-autoloader/README.md)을 사용하시길 권장합니다.

다음은 오토로더 상호 운용성을 위해 반드시 준수해야하는 필수 요구 사항에 대해 설명합니다.

## 필수 요구사항

* 정규화된 네임스페이스 및 클래스는 다음과 같은 구조를 따라야 합니다. `\<Vendor Name>\(<Namespace>\)*<Class Name>`
* 각 네임스페이스의 최상위 네임스페이스에는 `공급자(Vendor)의 이름`을 사용해야 합니다.
* 각 네임스페이스는 원하는 만큼의 하위 네임스페이스를 가질 수 있습니다.
* 각 네임스페이스의 구분기호는 `DIRECTORY_SEPARATOR`로 변환됩니다.
* CLASS 이름에서 각 `_` 문자는 `DIRECTORY_SEPARATOR`로 변환됩니다. `_`는 네임스페이스에서 특별한 의미가 없습니다.
* 규칙에 맞게 작성된 클래스를 파일시스템에서 불러올 때 `.php`가 마지막에 붙습니다.
* 공급자(Vendor) 이름, 네임 스페이스 및 클래스 이름의 알파벳 문자는 소문자와 대문자의 조합으로 구성 될 수 있습니다.

## 예제

* `\Doctrine\Common\IsolatedClassLoader` => `/path/to/project/lib/vendor/Doctrine/Common/IsolatedClassLoader.php`
* `\Symfony\Core\Request` => `/path/to/project/lib/vendor/Symfony/Core/Request.php`
* `\Zend\Acl` => `/path/to/project/lib/vendor/Zend/Acl.php`
* `\Zend\Mail\Message` => `/path/to/project/lib/vendor/Zend/Mail/Message.php`

## 밑줄이 있는 네임스페이스와 클래스의 예제

* `\namespace\package\Class_Name` => `/path/to/project/lib/vendor/namespace/package/Class/Name.php`
* `\namespace\package_name\Class_Name` => `/path/to/project/lib/vendor/namespace/package_name/Class/Name.php`

고통받지 않고 오토로더를 사용하려면 최소한 이런 규약들을 지켜야 합니다. PHP 5.3 이상에서 아래와 같은 샘플 SplClassLoader 구현을 사용하여 이러한 표준을 따르고 있는지 테스트 할 수 있습니다.

## 구현 예제

다음은 위에 제안 된 표준이 자동으로 로드되는 방식을 간단하게 보여주는 예제입니다.

```php
<?php

function autoload($className)
{
    $className = ltrim($className, '\\');
    $fileName  = '';
    $namespace = '';
    if ($lastNsPos = strrpos($className, '\\')) {
        $namespace = substr($className, 0, $lastNsPos);
        $className = substr($className, $lastNsPos + 1);
        $fileName  = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
    }
    $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';

    require $fileName;
}
spl_autoload_register('autoload');
```

## SplClassLoader 구현예제

다음 샘플은 위에 제안 된 오토로더 표준을 따를때 클래스를 자동으로 로드 할 수 있는 SplClassLoader의 구현 예제입니다.

PHP 5.3에서 PSR-0을 따르는 클래스를 로드하는 방법 중 현재 권장되는 방법입니다.

* <http://gist.github.com/221634>


# PSR-2-coding-style-guide

> **사용중단** - 2019-08-10을 기준으로 PSR-2은 사용이 중단되었습니다. 새로운 표준으로 [PSR-12](/accepted/psr-12-extended-coding-style-guide)을 사용하시길 권장합니다.

이 가이드는 기본 코딩 표준인 [PSR-1](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md)을 확장입니다.

이 가이드의 의도는 다른 저자의 코드를 볼 때 가독성을 해치는 것을 줄이는 것입니다. PHP 코드의 형식을 지정하는 방법에 대한 여러가지 규칙과 기대 사항을 나열함으로써 이를 달성합니다.

이 스타일 규칙은 다양한 회원 프로젝트 간의 공통점에서 파생되었습니다. 다양한 저자가 여러 프로젝트에서 공동 작업을 수행 할 때 모든 프로젝트 중에서 한 가지의 가이드을 사용하는 것이 도움이 됩니다. 따라서 이 가이드의 이점은 규칙 자체가 아니라 이러한 규칙을 공유하는 것입니다.

이 문서에서 핵심이 되는 단어는 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", "OPTIONAL" 입니다. 이것은 [RFC 2119](http://www.ietf.org/rfc/rfc2119.txt)에 설명 된대로 해석해야 합니다.

> 역자주: 위의 키워드는 아래의 번역문에 괄호안에 표시하였습니다

## 1. 개요

* 코드는 반드시(MUST) "코딩 스타일 가이드" PSR \[[PSR-1](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md)]을 따라야합니다.
* 코드는 들여쓰기에 반드시(MUST) 탭이 아니라 스페이스 4칸을 사용해야합니다.
* 한줄에 들어가는 문자열의 길이에 엄격한 제한이 있으면 안됩니다. 가벼운 제한은 한줄에 120자입니다 (MUST). 가능하다면 한줄은 80자 이하여야 합니다 (SHOULD).
* `namespace` 선언 다음에 빈 줄이 하나 있어야만 하며(MUST) 그리고 `use` 선언 블록 뒤에 빈 줄이 하나 있어야만 합니다 (MUST).
* 클래스의 여는 중괄호는 다음 줄로 가야만 하며(MUST), 닫는 중괄호는 본문 뒤의 다음 줄로 가야만 합니다(MUST).
* 메서드의 여는 중괄호는 다음 줄로 가야만 하며(MUST), 닫는 중괄호는 본문 뒤의 다음 줄로 가야만 합니다(MUST).
* 모든 속성 및 메서드에서 가시성을 선언해야만 합니다 (MUST). `abstract`와`final`은 가시성 (visibility) 전에 선언되어야만 합니다(MUST). `static`은 가시성 뒤에 선언되어야만 합니다 (MUST).
* 제어문 키워드는 그 뒤에 하나의 공백을 가져야만 합니다 (MUST). 메서드와 함수에서 해서는 안됩니다(MUST NOT).

  > 역자주: if, switch, try, catch, foreach 등
* 제어문를 여는 중괄호는 같은 줄에 있어야만 하며(MUST), 닫는 중괄호는 반드시 본문 뒤의 다음 줄로 가야만 합니다(MUST).
* 제어문를 여는 괄호는 그 뒤에 공백이 없어야만 하며(MUST NOT), 제어문에 대한 닫는 괄호는 전에는 공백이 없어야만 합니다(MUST NOT).

### 1.1. 예제

이 예는 아래의 규칙 중 일부를 간략하게 설명합니다.

```php
<?php
namespace Vendor\Package;

use FooInterface;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

class Foo extends Bar implements FooInterface
{
    public function sampleMethod($a, $b = null)
    {
        if ($a === $b) {
            bar();
        } elseif ($a > $b) {
            $foo->bar($arg1);
        } else {
            BazClass::bar($arg2, $arg3);
        }
    }

    final public static function bar()
    {
        // method body
    }
}
```

## 2. General

### 2.1. Basic Coding Standard

코드는 반드시(MUST) [PSR-1](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md)에 요약 된 모든 규칙을 따라야합니다.

### 2.2. Files

모든 PHP 파일은 Unix LF (linefeed) 줄 끝을 사용해야합니다.

모든 PHP 파일은 하나의 빈 줄을 가진 채로 끝나야합니다.

닫는 `?>` 태그는 PHP만 포함 된 파일일 경우 생략해야합니다 (MUST).

### 2.3. 라인 (Lines)

라인 길이에 엄격한 제한이 있어서는 안됩니다(MUST NOT).

라인 길이에 대한 소프트 제한은 120 자여야 합니다(MUST). 자동화 된 스타일 검사기는 반드시(MUST) 경고해야하지만 소프트 한도에서 오류를 표시해서는 안됩니다(MUST NOT).

라인은 80자를 넘지 않아야합니다 (SHOULD NOT). 그보다 긴 행은 각각 80 문자 이하의 여러 행으로 나눠 져야합니다 (SHOULD).

비어 있지 않은 라인 끝에는 공백 문자가 없어야합니다(MUST NOT).

가독성을 높이고 관련 코드 블록을 나타내기 위해 빈 라인을 추가 할 수 있습니다 (MAY).

한 라인에 하나 이상의 문장이 있어서는 안됩니다(MUST NOT).

### 2.4. 들여쓰기 (Indenting)

코드는 반드시(MUST) 4 스페이스의 들여쓰기를 사용해야하며, 들여쓰기에는 탭을 사용하지 않아야합니다(MUST NOT).

> 공백 만 사용하고 탭과 공백을 섞지 않으면 diff, 패치, 히스토리 및 주석 문제를 피할 수 있습니다. 공백을 사용하면 행간 정렬을 위해 세분화 된 하위 들여쓰기를 쉽게 삽입 할 수 있습니다.

### 2.5. 예약어(Keywords)와 True/False/Null

PHP [예약어](http://php.net/manual/en/reserved.keywords.php)는 소문자여야 합니다.

PHP 상수 `true`,`false` 및 `null`은 반드시(MUST) 소문자여야 합니다.

## 3. 네임 스페이스 및 사용 선언

네임스페이스가 존재할 때 `namespace` 선언 다음에 하나의 빈 라인이 있어야 합니다(MUST).

네임스페이스가 존재할 때, 모든 `use` 선언은 반드시 `namespace` 선언을 따라야 만합니다(MUST).

선언 하나당 하나의 `use` 키워드가 있어야합니다(MUST).

`use` 블록 뒤에 빈 줄이 하나 있어야합니다(MUST).

예제 :

```php
<?php
namespace Vendor\Package;

use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

// ... additional PHP code ...

```

## 4. 클래스, 프로퍼티(Properties) 및 메서드

"클래스"라는 용어는 모든 클래스, 인터페이스 및 trait을 나타냅니다.

### 4.1. 확장 및 구현

`extends`와 `implements` 키워드는 클래스 이름과 같은 라인에서 선언되어야합니다 (MUST).

해당 클래스의 여는 중괄호는 자체 줄에 있어야합니다(MUST). 클래스의 닫는 중괄호는 본문 뒤의 다음 줄로 가야합니다(MUST).

```php
<?php
namespace Vendor\Package;

use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

class ClassName extends ParentClass implements \ArrayAccess, \Countable
{
    // constants, properties, methods
}
```

`implements`의 목록은 여러 줄에 걸쳐 나뉘어 질 수 있습니다. 각 줄은 한 번 들여쓰기를 합니다. 그렇게 할 때 목록의 첫 번째 항목은 다음 줄에 있어야하며 한 줄에 하나의 인터페이스만 있어야합니다.

```php
<?php
namespace Vendor\Package;

use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

class ClassName extends ParentClass implements
    \ArrayAccess,
    \Countable,
    \Serializable
{
    // constants, properties, methods
}
```

### 4.2. 프로퍼티(Properties)

모든 프로퍼티에서 가시성을 반드시 선언해야합니다(MUST).

`var` 키워드는 프로퍼티를 선언하는데 사용되어서는 안됩니다 (MUST NOT).

명령문마다 하나 이상의 프로퍼티가 선언되어서는 안됩니다(MUST NOT).

프로퍼티 이름은 protected 또는 private 가시성을 나타내기 위해 하나의 밑줄이 붙어서는 안됩니다 (SHOULD NOT).

속성 선언은 다음과 같습니다.

```php
<?php
namespace Vendor\Package;

class ClassName
{
    public $foo = null;
}
```

### 4.3. Methods

모든 메서드는 가시성을 선언해야합니다 (MUST).

메서드 이름은 protected 또는 private 가시성을 표현하기 위해 하나의 밑줄로 접두어를 사용해서는 안됩니다 (SHOULD NOT).

메서드 이름은 메서드 이름 다음에 공백으로 선언하면 안됩니다 (MUST NOT). 여는 중괄호는 반드시 자신의 줄에 있어야하며(MUST) 닫는 중괄호는 반드시 그 다음 줄에 있어야합니다(MUST). 여는 괄호 뒤에 공백이 있으면 안되며(MUST NOT) 닫는 괄호 앞에 공백이 있어서는 안됩니다(MUST NOT).

메서드 선언은 다음과 같습니다. 괄호, 쉼표, 공백 및 중괄호의 배치에 유의하십시오.

```php
<?php
namespace Vendor\Package;

class ClassName
{
    public function fooBarBaz($arg1, &$arg2, $arg3 = [])
    {
        // method body
    }
}
```

### 4.4. 메서드 인수(Method Arguments)

인수 목록에는 각 쉼표 앞에 공백이 있으면 안되며(MUST NOT) 각 쉼표 뒤에 하나의 공백이 있어야합니다(MUST).

디폴트 값을 가진 메서드 인수는 인수 목록의 끝에 와야합니다 (MUST).

```php
<?php
namespace Vendor\Package;

class ClassName
{
    public function foo($arg1, &$arg2, $arg3 = [])
    {
        // method body
    }
}
```

인수 목록은 여러 줄에 걸쳐 나뉘어 질 수 있으며(MAY), 각 줄은 한 번 들여 쓰일 수 있습니다. 그렇게 할 때 목록의 첫 번째 항목은 다음 줄에 있어야하며(MUST) 한 줄에 하나의 인수 만 있어야합니다(MUST).

인수 목록이 여러 줄에 걸쳐 분할되어 있으면 닫는 괄호와 여는 중괄호는 한 줄의 공백을 사용하여 각각의 줄에 함께 있어야합니다 (MUST).

```php
<?php
namespace Vendor\Package;

class ClassName
{
    public function aVeryLongMethodName(
        ClassTypeHint $arg1,
        &$arg2,
        array $arg3 = []
    ) {
        // method body
    }
}
```

### 4.5. `abstract`, `final`, and `static`

`abstract`와`final` 선언은 가시성 선언 앞에 와야합니다(MUST).

`static` 선언은 가시성 선언 뒤에 와야합니다(MUST).

```php
<?php
namespace Vendor\Package;

abstract class ClassName
{
    protected static $foo;

    abstract protected function zim();

    final public static function bar()
    {
        // method body
    }
}
```

### 4.6. 메소드(Method) 호출과 함수(Function) 호출

메서드 나 함수 호출을 할 때 메서드 나 함수 이름과 여는 괄호 사이에 공백이 없어야합니다(MUST NOT). 여는 괄호 뒤에 공백이 있으면 안되며(MUST NO) 닫는 괄호 앞에 공백이 있어서는 안됩니다(MUST NOT). 인수 목록에는 각 쉼표 앞에 공백이 있으면 안되며(MUST NOT) 각 쉼표 뒤에 하나의 공백이 있어야합니다(MUST).

```php
<?php
bar();
$foo->bar($arg1);
Foo::bar($arg2, $arg3);
```

인수 목록은 여러 줄에 걸쳐 나뉘어 질 수 있으며(MAY), 각 줄은 한 번 들여 쓰일 수 있습니다. 그렇게 할 때 목록의 첫 번째 항목은 다음 줄에 있어야하며(MUST) 한 줄에 하나의 인수 만 있어야합니다(MUST).

```php
<?php
$foo->bar(
    $longArgument,
    $longerArgument,
    $muchLongerArgument
);
```

## 5. 제어문(Control Structures)

제어문의 일반적인 스타일 규칙은 다음과 같습니다.

* 제어문 키워드 다음에 하나의 공백이 있어야합니다 (MUST).
* 여는 괄호 뒤에 공백이 있으면 안됩니다 (MUST NOT).
* 닫는 괄호 앞에 공백이 없어야합니다 (MUST NOT).
* 닫는 괄호와 여는 중괄호 사이에 하나의 공백이 있어야합니다 (MUST).
* 구조체는 한 번 들여쓰기되어야합니다 (MUST).
* 닫는 중괄호는 제어문의 다음 줄에 있어야합니다 (MUST).

각 제어문는 중괄호로 묶어야합니다 (MUST). 이것은 구조가 어떻게 보이는지를 표준화하고, 새로운 라인이 제어문에 추가 될 때 오류가 발생할 가능성을 줄입니다.

### 5.1. `if`, `elseif`, `else`

`if` 구조는 다음과 같습니다. 괄호, 공백 및 중괄호의 배치에 유의하십시오. `else`와 `elseif`는 이전 제어문의 닫는 중괄호와 같은 줄에 있습니다.

```php
<?php
if ($expr1) {
    // if body
} elseif ($expr2) {
    // elseif body
} else {
    // else body;
}
```

`else if` 키워드 대신 `elseif` 키워드를 사용하여 모든 제어 키워드가 단일 단어처럼 보이도록 해야 합니다 (SHOULD).

### 5.2. `switch`, `case`

`switch` 구조체는 다음과 같습니다. 괄호, 공백 및 중괄호의 배치에 유의하십시오. `case` 문은 `switch`에서 한 번 들여쓰기되어야하며(MUST) `break` 키워드 (또는 다른 종결 키워드)는 `case` 문과 같은 수준에서 들여쓰기되어야합니다 (MUST). 비어 있지 않은 `case` 문에서 의도적으로 다음으로 넘기는 경우 `// no break`와 같은 주석이 있어야합니다(MUST).

```php
<?php
switch ($expr) {
    case 0:
        echo 'First case, with a break';
        break;
    case 1:
        echo 'Second case, which falls through';
        // no break
    case 2:
    case 3:
    case 4:
        echo 'Third case, return instead of break';
        return;
    default:
        echo 'Default case';
        break;
}
```

### 5.3. `while`, `do while`

`while` 문은 다음과 같습니다. 괄호, 공백 및 중괄호의 배치에 유의하십시오.

```php
<?php
while ($expr) {
    // structure body
}
```

비슷한 경우로, do while 문은 다음과 같이 보입니다. 괄호, 공백 및 중괄호의 배치에 유의하십시오.

```php
<?php
do {
    // structure body;
} while ($expr);
```

### 5.4. `for`

`for` 문은 다음과 같이 보입니다. 괄호, 공백 및 중괄호의 배치에 유의하십시오.

```php
<?php
for ($i = 0; $i < 10; $i++) {
    // for body
}
```

### 5.5. `foreach`

`foreach` 문은 다음과 같이 보입니다. 괄호, 공백 및 중괄호의 배치에 유의하십시오.

```php
<?php
foreach ($iterable as $key => $value) {
    // foreach body
}
```

### 5.6. `try`, `catch`

`try catch` 블록은 다음과 같이 보입니다. 괄호, 공백 및 중괄호의 배치에 유의하십시오.

```php
<?php
try {
    // try body
} catch (FirstExceptionType $e) {
    // catch body
} catch (OtherExceptionType $e) {
    // catch body
}
```

## 6. 클로저(Closures)

클로저는 `function` 키워드 뒤의 공백과 `use` 키워드 앞뒤 공백으로 선언해야합니다 (MUST).

여는 중괄호는 반드시 같은 줄에 있어야하며(MUST) 닫는 중괄호는 반드시 그 다음 줄에 있어야합니다(MUST).

인수 목록이나 변수 목록의 여는 괄호 다음에 공백이 있으면 안되며(MUST NOT) 인수 목록이나 변수 목록의 닫는 괄호 앞에 공백이 있으면 안됩니다(MUST NOT).

인수 목록과 변수 목록에는 각 쉼표 앞에 공백이 있어서는 안되며(MUST NOT) 각 쉼표 뒤에 하나의 공백이 있어야합니다(MUST).

기본값을 가진 클로저 인수는 인수 목록의 끝에 와야합니다 (MUST).

클로저 선언은 다음과 같습니다. 괄호, 쉼표, 공백 및 중괄호의 배치에 유의하십시오.

```php
<?php
$closureWithArgs = function ($arg1, $arg2) {
    // body
};

$closureWithArgsAndVars = function ($arg1, $arg2) use ($var1, $var2) {
    // body
};
```

인수 목록과 변수 목록은 여러 행에 걸쳐 나뉘어 질 수 있습니다(MAY). 각 행은 한 번씩 들여쓰기됩니다. 그렇게 할 경우 첫 번째 항목은 다음 줄에 있어야만하며(MUST) 한 줄에 하나의 인수 또는 변수 만 있어야합니다(MUST).

마지막 리스트 (인수 또는 변수의 여부)가 여러 줄로 나뉘어 질 때, 닫는 괄호와 여는 중괄호는 하나의 공백을 사용하여 한줄에 있어야합니다 (MUST).

다음은 인수 목록이 있거나 없는 클로저의 예와 여러 줄에 걸쳐있는 변수 목록입니다.

```php
<?php
$longArgs_noVars = function (
    $longArgument,
    $longerArgument,
    $muchLongerArgument
) {
    // body
};

$noArgs_longVars = function () use (
    $longVar1,
    $longerVar2,
    $muchLongerVar3
) {
    // body
};

$longArgs_longVars = function (
    $longArgument,
    $longerArgument,
    $muchLongerArgument
) use (
    $longVar1,
    $longerVar2,
    $muchLongerVar3
) {
    // body
};

$longArgs_shortVars = function (
    $longArgument,
    $longerArgument,
    $muchLongerArgument
) use ($var1) {
    // body
};

$shortArgs_longVars = function ($arg) use (
    $longVar1,
    $longerVar2,
    $muchLongerVar3
) {
    // body
};
```

형식 지정 규칙은 함수 또는 메소드 호출에서 클로저가 직접 인수로 사용될 때도 적용됩니다.

```php
<?php
$foo->bar(
    $arg1,
    function ($arg2) use ($var1) {
        // body
    },
    $arg3
);
```

## 7. 결론

이 가이드에는 의도적으로 생략 한 스타일과 연습의 많은 요소가 있습니다. 여기에는 다음이 포함되지만 이에 국한되지는 않습니다 :

* 전역 변수 및 전역 상수 선언
* 함수 선언
* 운영자 및 과제
* 라인 간 정렬
* 주석 및 문서 블록
* 클래스 이름 접두사 및 접미사
* 모범 사례

향후 권장 사항은 스타일이나 실습의 요소 또는 기타 요소를 다루기 위해이 가이드를 수정하고 확장 할 수 있습니다.

## 부록 A. 설문조사

이 스타일 가이드를 작성하면서이 그룹은 공통 실천을 결정하기 위해 회원 프로젝트에 대한 설문 조사를 실시했습니다. 조사는 후일을 위해 여기에 유지됩니다.

### A.1. 설문조사 자료

```
url,http://www.horde.org/apps/horde/docs/CODING_STANDARDS,http://pear.php.net/manual/en/standards.php,http://solarphp.com/manual/appendix-standards.style,http://framework.zend.com/manual/en/coding-standard.html,https://symfony.com/doc/2.0/contributing/code/standards.html,http://www.ppi.io/docs/coding-standards.html,https://github.com/ezsystems/ezp-next/wiki/codingstandards,http://book.cakephp.org/2.0/en/contributing/cakephp-coding-conventions.html,https://github.com/UnionOfRAD/lithium/wiki/Spec%3A-Coding,http://drupal.org/coding-standards,http://code.google.com/p/sabredav/,http://area51.phpbb.com/docs/31x/coding-guidelines.html,https://docs.google.com/a/zikula.org/document/edit?authkey=CPCU0Us&hgd=1&id=1fcqb93Sn-hR9c0mkN6m_tyWnmEvoswKBtSc0tKkZmJA,http://www.chisimba.com,n/a,https://github.com/Respect/project-info/blob/master/coding-standards-sample.php,n/a,Object Calisthenics for PHP,http://doc.nette.org/en/coding-standard,http://flow3.typo3.org,https://github.com/propelorm/Propel2/wiki/Coding-Standards,http://developer.joomla.org/coding-standards.html
voting,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,no,no,no,?,yes,no,yes
indent_type,4,4,4,4,4,tab,4,tab,tab,2,4,tab,4,4,4,4,4,4,tab,tab,4,tab
line_length_limit_soft,75,75,75,75,no,85,120,120,80,80,80,no,100,80,80,?,?,120,80,120,no,150
line_length_limit_hard,85,85,85,85,no,no,no,no,100,?,no,no,no,100,100,?,120,120,no,no,no,no
class_names,studly,studly,studly,studly,studly,studly,studly,studly,studly,studly,studly,lower_under,studly,lower,studly,studly,studly,studly,?,studly,studly,studly
class_brace_line,next,next,next,next,next,same,next,same,same,same,same,next,next,next,next,next,next,next,next,same,next,next
constant_names,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper,upper
true_false_null,lower,lower,lower,lower,lower,lower,lower,lower,lower,upper,lower,lower,lower,upper,lower,lower,lower,lower,lower,upper,lower,lower
method_names,camel,camel,camel,camel,camel,camel,camel,camel,camel,camel,camel,lower_under,camel,camel,camel,camel,camel,camel,camel,camel,camel,camel
method_brace_line,next,next,next,next,next,same,next,same,same,same,same,next,next,same,next,next,next,next,next,same,next,next
control_brace_line,same,same,same,same,same,same,next,same,same,same,same,next,same,same,next,same,same,same,same,same,same,next
control_space_after,yes,yes,yes,yes,yes,no,yes,yes,yes,yes,no,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes
always_use_control_braces,yes,yes,yes,yes,yes,yes,no,yes,yes,yes,no,yes,yes,yes,yes,no,yes,yes,yes,yes,yes,yes
else_elseif_line,same,same,same,same,same,same,next,same,same,next,same,next,same,next,next,same,same,same,same,same,same,next
case_break_indent_from_switch,0/1,0/1,0/1,1/2,1/2,1/2,1/2,1/1,1/1,1/2,1/2,1/1,1/2,1/2,1/2,1/2,1/2,1/2,0/1,1/1,1/2,1/2
function_space_after,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no,no
closing_php_tag_required,no,no,no,no,no,no,no,no,yes,no,no,no,no,yes,no,no,no,no,no,yes,no,no
line_endings,LF,LF,LF,LF,LF,LF,LF,LF,?,LF,?,LF,LF,LF,LF,?,,LF,?,LF,LF,LF
static_or_visibility_first,static,?,static,either,either,either,visibility,visibility,visibility,either,static,either,?,visibility,?,?,either,either,visibility,visibility,static,?
control_space_parens,no,no,no,no,no,no,yes,no,no,no,no,no,no,yes,?,no,no,no,no,no,no,no
blank_line_after_php,no,no,no,no,yes,no,no,no,no,yes,yes,no,no,yes,?,yes,yes,no,yes,no,yes,no
class_method_control_brace,next/next/same,next/next/same,next/next/same,next/next/same,next/next/same,same/same/same,next/next/next,same/same/same,same/same/same,same/same/same,same/same/same,next/next/next,next/next/same,next/same/same,next/next/next,next/next/same,next/next/same,next/next/same,next/next/same,same/same/same,next/next/same,next/next/next
```

### A.2. 설문 조사 범례

`indent_type`: 들여쓰기의 유형. `tab`= "탭 사용", `2`또는 `4`= "공백 개수"

`line_length_limit_soft`: "소프트"줄 길이 제한 (문자 수). `?`= 식별 할 수 없거나 응답이 없으면 `아니오`는 제한이 없음을 의미합니다.

`line_length_limit_hard`: "하드"줄 길이 제한 (문자 수). `?`= 식별 할 수 없거나 응답이 없으면 `아니오`는 제한이 없음을 의미합니다.

`class_names`: 클래스 이름 지정 방법. `lower` = 소문자 만, `lower_under` = 밑줄 구분 기호가있는 소문자, `studly` = StudlyCase.

`class_brace_line`: 클래스의 여는 중괄호가 class 키워드와 `같은` 줄에 있거나, 그 `다음` 줄에 있습니까?

`constant_names`: 클래스 상수는 어떻게 명명됩니까? `upper` = 밑줄 구분자가있는 대문자.

`true_false_null`: `true`, `false` 및 `null` 키워드는 모두 `lower`또는 모두 `upper`로 철자가 맞습니까?

`method_names`: 메소드은 어떻게 명명됩니까? `camel` =`camelCase`,`lower_under` = 밑줄 구분 기호가있는 소문자.

`method_brace_line`: 메소드의 여는 중괄호가 메소드 이름과 `같은` 줄 또는 `다음` 줄에 있습니까?

`control_brace_line`: 제어문의 여는 중괄호가 `같은` 줄 또는 `다음` 줄에 있습니까?

`control_space_after`: 제어문 키워드 다음에 공백이 있습니까?

`always_use_control_braces`: 제어문은 항상 중괄호를 사용합니까?

`else_elseif_line`: `else` 나`elseif`를 사용할 때, 이전 닫는 중괄호와 `같은` 줄로 가야합니까, 아니면 `다음` 줄로 가나 요?

`case_break_indent_from_switch`: `switch` 문에서 `case`와 `break`가 몇 번 들여 쓰여졌습니까?

`function_space_after`: 함수 호출은 함수 이름 뒤에 여는 괄호 앞에 공백이 있습니까?

`closing_php_tag_required`: PHP 만 포함 된 파일에서 닫는 `?>`태그가 필요합니까?

`line_endings`: 어떤 유형의 종료 라인이 사용됩니까?

`static_or_visibility_first`: 메서드를 선언 할 때 `static`이 먼저 오는가, 아니면 가시성이 먼저 오는가?

`control_space_parens`: 제어 구조 표현식에서 여는 괄호 뒤에 공백이 있고 닫는 괄호 앞에 공백이 있습니까? `yes` =`if ( $expr )`,`no` =`if ($expr)`입니다.

`blank_line_after_php`: 여는 PHP 태그 뒤에 빈 줄이 있습니까?

`class_method_control_brace`: 클래스, 메서드 및 컨트롤 구조에 대해 여는 중괄호가 어떤 줄로 표시되는지 요약합니다.

### A.3. 설문조사 결과

```
indent_type:
    tab: 7
    2: 1
    4: 14
line_length_limit_soft:
    ?: 2
    no: 3
    75: 4
    80: 6
    85: 1
    100: 1
    120: 4
    150: 1
line_length_limit_hard:
    ?: 2
    no: 11
    85: 4
    100: 3
    120: 2
class_names:
    ?: 1
    lower: 1
    lower_under: 1
    studly: 19
class_brace_line:
    next: 16
    same: 6
constant_names:
    upper: 22
true_false_null:
    lower: 19
    upper: 3
method_names:
    camel: 21
    lower_under: 1
method_brace_line:
    next: 15
    same: 7
control_brace_line:
    next: 4
    same: 18
control_space_after:
    no: 2
    yes: 20
always_use_control_braces:
    no: 3
    yes: 19
else_elseif_line:
    next: 6
    same: 16
case_break_indent_from_switch:
    0/1: 4
    1/1: 4
    1/2: 14
function_space_after:
    no: 22
closing_php_tag_required:
    no: 19
    yes: 3
line_endings:
    ?: 5
    LF: 17
static_or_visibility_first:
    ?: 5
    either: 7
    static: 4
    visibility: 6
control_space_parens:
    ?: 1
    no: 19
    yes: 2
blank_line_after_php:
    ?: 1
    no: 13
    yes: 8
class_method_control_brace:
    next/next/next: 4
    next/next/same: 11
    next/same/same: 1
    same/same/same: 6
```


