PHP Annotated — May 2021

Php_annotated 이미지

Roman Pronskiy가 쓰고 JetBrains에서 제공하는 PHP Annotated 2021년 5월호의 번역/해석본입니다.

이 중에서 몇 가지 제 취향껏 골라 그 안의 내용도 좀 뒤져보고 개발새발 번역해서 소개합니다.


Composer의 통계에 따르면 PHP 8.0이 추진력을 얻고 있으며 더 많이 채택되고 있습니다.

한편, PHP internals에선 번들로 제공되는 PHP 확장의 네임 스페이스, 새로운 never 유형, 더 이상 사용되지 않을 일부 기능을 포함한 PHP 8.1에 대한 많은 변경 사항을 수용했습니다.

PHP 8.1 릴리스 관리자(release managers)가 선택됐으며 속성 접근자(property accessors), 순수 교차 유형(pure intersection types) 및 final 상수(final constants)를 포함하여 몇 가지 흥미로운 제안이 논의 중입니다.

⚡️ News

PHP Versions Stats – 2021.1

https://blog.packagist.com/php-versions-stats-2021-1-edition/

Composer가 packagist.org에 연결할 때 전송하는 데이터를 기반으로하는 전통적인 통계 기록:

  • PHP 7.4: 45.92% (+3.31)
  • PHP 7.3: 21.30% (-5.75)
  • PHP 7.2: 12.89% (-2.39)
  • PHP 8.0: 9.44% (+9.17)
  • PHP 7.1: 5.21% (-2.24)

PHP is available on Google Cloud Functions

https://cloud.google.com/blog/products/application-development/php-comes-to-cloud-functions

Google Cloud의 서버리스 플랫폼은 이제 PHP를 정식으로 지원합니다. GoogleCloudPlatform/functions-framework-php를 사용하면 PHP 7.4 기반 런타임에서 function을 실행할 수 있습니다.

Composer Command Injection Vulnerability

https://blog.packagist.com/composer-command-injection-vulnerability/

이번 Composer의 취약점은 Mercurial이 설치된 시스템에만 영향을줍니다. 그러나 수정 사항이 포함된 2.0.13 및 1.10.22 버전으로 즉시 업데이트하는 것이 좋습니다. 취약점에 대한 더 자세한 기술 분석을 확인하십시오.

Follow up on git.php.net attack

https://externals.io/message/113981

공격자들은 Rasmus Lerdorf와 Nikita Popov를 대신하여 두 개의 커밋을 PHP 소스에 푸시했습니다. 문제는 신속하게 감지되고 해결되었습니다. 악성 코드는 사용자에게 전달되지 않았습니다.

git.php.net 서버는 HTTPS를 통한 커밋을 허용하고 md5 해시를 secret으로 허용하는 다이제스트 인증을 사용한 것으로 나타났습니다. 모든 contributor의 해시 데이터베이스는 master.php.net에서 공격자에 의해 획득되었으며, 아마 원래는 손상된 서버일 것입니다.

결과적으로 모든 개발이 GitHub로 완전히 옮겨져 언어 개발자의 삶이 더 편해졌습니다.

PHP 7.4.19, PHP 8.0.6

https://www.php.net/ChangeLog-7.php#7.4.19
https://www.php.net/ChangeLog-8.php#8.0.6

With a fix in PDO_pgsql.

🐘 PHP Internals

✅ [RFC] never type

PHP 8.1에는 반환 값에 never라는 새로운 type이 추가됩니다.

never type으로 선언된 함수 또는 메서드는 값을 반환하지 않을 것임을 나타내며 예외를 throw하거나 die(), exit(), trigger_error() 유형의 호출로 종료됩니다.

이는 bottom type이며 다른 모든 type의 하위 type이며 Python, Rust, Kotlin 및 TypeScript에 유사한 type이 있습니다. 이 type은 정적 분석을 향상시킵니다.

function redirect(string $uri): never {
header('Location: ' . $uri);
exit();
}

function redirectToLoginPage(): never {
redirect('/login');
}

RFC는 이름부터 noreturn으로 제안이 됐지만, 투표로 최종 never로 결정됐습니다. RFC의 Naming 섹션 참고하세요.

자세한 내용은 PHP.Watch에서 보거나 Matt Brown과 Ondřej Mirtes가 함께 하는 PHP Internals News podcast에서 들을 수 있습니다.

✅ [RFC] Deprecate implicit non-integer-compatible float to int conversions

https://wiki.php.net/rfc/implicit-float-int-deprecate

PHP 8.1에서는 float가 int로 변환되고 소수 부분이 손실되면 E_DEPRECATED 알림이 발생합니다. 나중에 PHP 9.0에서는 TypeError가 발생합니다.

function acceptInt(int $i) {
var_dump($i);
}
acceptInt(3.1415);
> int(3) // Deprecation notice

George Peter Banyard와 함께 하는 🔈 PHP Internals News podcast #83에서 자세히 알아보세요.

✅ [RFC] Phasing out Serializable

https://wiki.php.net/rfc/phase_out_serializable

PHP 8.1에서는 Serializable 인터페이스가 더 이상 사용되지 않습니다. 클래스가 오직 이 인터페이스만 사용하는 경우, 즉 클래스에 새로운 매직 메서드인 __serialize() 및 __unserialize()가 추가로 없는 경우 deprecation 알림이 발생합니다.

✅ [RFC] Namespaces in bundled PHP extensions

https://wiki.php.net/rfc/namespaces_in_bundled_extensions

이것은 더 깨끗한 PHP를 향한 작은 발걸음입니다! Extension의 새로운 심볼(클래스, 인터페이스 등)은 이제 네임 스페이스를 사용해야합니다.

여기 예시가 있습니다. resource type은 PHP에서 사실상 더 이상 사용되지 않으며 기존의 모든 리소스가 천천히 object로 마이그레이션되고 있습니다. PHP 8.1에서 일부 리소스는 다음 네임 스페이스 클래스로 대체됩니다.

IMAPConnection -> IMAP\Connection
FTPConnection -> FTP\Connection
LDAP -> LDAP\Connection
LDAPResult -> LDAP\Result
LDAPResultEntry -> LDAP\ResultEntry
PgSql -> PgSql\Connection
PgSqlResult -> PgSql\Result
PgSqlLob -> PgSql\Lob

✅ [RFC] Add return type declarations for internal methods

https://wiki.php.net/rfc/internal_method_return_types

PHP 8.0에 내장된 대부분의 메소드는 매개 변수를 받고 유형 선언을 반환합니다. 그러나 문제는 public non-final 메서드의 반환 값과 같이 사용자 코드에서 재정의 될 수 있는 경우입니다.

class SomeStandardClass
{
public function method(): int {}
}

class UserClass extends SomeStandardClass
{
public function method() {}
}

// Fatal error: Declaration of UserClass::method() must be compatible with SomeStandardClass::method()

공변(covariance) 규칙에 따라, 재정의된 메소드에 다른 반환 type을 추가할 수 있지만, 메소드 시그니처가 다르면 하위 호환이 깨집니다. PHP 프로젝트 리더들은 아직 큰 하위 호환 문제를 일으키지 않도록 장기적/점진적으로 마이그레이션을 할 수 있게 지원하기로 했습니다.

PHP 8.1에서 모든 내부 메서드는 누락된 type도 가져옵니다. 사용자 코드에서 재정의 된 경우 사용 중단 알림이 표시됩니다.

class MyDateTime extends DateTime
{
public function modify(string $modifier) { return false; }
}

// Deprecated: Declaration of MyDateTime::modify(string $modifier) should be compatible with DateTime::modify(string $modifier): DateTime|false

Release managers for PHP 8.1 have been selected

https://wiki.php.net/todo/php81#release_managers

이번 릴리스에서는 세 명의 release manager가 있습니다.
경험이 풍부한 Joe Watkins (pthreads, parallel, pcov)와 두 명의 신참 인 Patrick Allaert (blackfire.io) 및 Ben Ramsey (ramsey/uuid)의 세 명의 관리자가 있습니다. Ben과 Patrick이 함께 하는 PHP Internals News podcast #84를 확인하십시오.

New proposals for PHP 8.1:

[RFC] Partial Function Application

https://wiki.php.net/rfc/partial_function_application

Partial Function Application은 일부 인수만 함수 호출에 바인딩되고 나머지 인수는 나중에 전달될 매개 변수로 남아있는 경우입니다.

예를 들어, 여기 전체 함수가 있습니다.

function whole($one, $two) {
/* ... */
}

이 전체 함수를 기초로 부분 함수가 있습니다.

$partial = whole(?, 2);

이제 이 부분 함수의 시그니처는 아래와 같습니다.

function($one) {
/* ... */
}

이게 왜 필요하죠?

첫째, 이제 모든 함수 또는 메서드에 대한 참조를 가져 와서 Callable이 예상되는 곳에 전달할 수 있습니다. 예를 들어 다음과 같이 할 수 있습니다.

array_map(Something::toString(?), [1, 2, 3]);
array_map(strval(?), [1, 2, 3]);

// 아래 코드 대신
array_map([Something::class, 'toString'], [1, 2, 3])
array_map('strval', [1, 2, 3]);

둘째, 결과적으로 파이프 연산자 |>를 구현할 수 있습니다.

$result = "Hello World"
|> htmlentities(?)
|> explode(?);

RFC 및 구현에 대해 Larry Garfield, Joe Watkins, Levi Morrison 및 Paul Crovella에게 감사드립니다.

[RFC] Property Accessors

https://wiki.php.net/rfc/property_accessors

Nikita는 속성 접근자의 구현을 완료했으며 현재 제안은 공식적으로 논의 중입니다.

결론은 기존의 getter 및 setter는 사용하기 편리하지 않으며 매직 메서드 __get 및 __set은 구체적이지 않다는 것입니다. 새로운 접근자는 이러한 문제를 해결하기위한 것입니다.

C#에서 영감을 얻은 문법:

class Foo {
public $prop {
get { /* ... */ }
set { /* ... */ }
}
}

이를 사용하여 읽기 전용 속성을 구현할 수 있습니다.

class User {
public string $name { get; }

public function __construct(string $name) {
$this->name = $name;
}
}

비대칭 액세스를 지정할 수 있습니다. 즉, 읽기 및 쓰기를 위해 별도로 공개 또는 비공개로 설정할 수 있습니다.

class User {
public string $name { get; private set; }

...
}

또는 유효성 검사 또는 기타 작업을 위한 완전히 구현된 메소드로 사용할 수 있습니다.

class Foo {
public int $bar {
get {
error_log('Getting $bar');
return $this->bar;
}
set {
assert($bar > 42);
$this->bar = $bar;
}
}
}

[RFC] Pure intersection types

https://wiki.php.net/rfc/pure-intersection-types

통합 유형은 PHP 8.0에 추가되었으며 이 RFC는 교차 유형 추가를 제안합니다.

구문은 TypeA & TypeB이며, 이는 변수가 TypeA 및 TypeB의 인스턴스여야 함을 의미합니다.

class A {
private Traversable&Countable $countableIterator;

public function setIterator(Traversable&Countable $countableIterator): void {
$this->countableIterator = $countableIterator;
}

public function getIterator(): Traversable&Countable {
return $this->countableIterator;
}
}

이 제안을 “순수 교차 type”이라고합니다. union type의 조합은 지원되지 않으며 향후 고려할 수 있도록 남아 있기 때문입니다. 복잡한 유형에 대한 별칭도 미래를 위해 남아 있습니다.

[RFC] Deprecate ticks

https://wiki.php.net/rfc/deprecate_ticks

PHP에는 틱 매커니즘이 있습니다 : declare(ticks=1). 원래 pcntl 신호를 추적하는 데 필요했습니다. 이제 pcntl_signal() 및 pcntl_async_signals()를 대신 사용할 수 있습니다. 이것이 PHP 8.1에서 틱을 폐기하고 PHP 9에서 완전히 제거하라는 제안이 있었던 이유입니다.

[RFC] Final class constants

https://wiki.php.net/rfc/final_class_const

이 RFC의 작성자는 상수에 대한 final 수정자를 제안하므로 자식 클래스에서 재정의 할 수 없습니다.

class Foo
{
final public const X = "foo";
}

class Bar extends Foo
{
public const X = "bar";
}

// Fatal error: Bar::X cannot override final constant Foo::X

흥미로운 사실은 인터페이스 상수는 이미 final이라는 것입니다.

PHP 소스에 기여하고 싶은 사람들을위한 몇 가지 추가 링크

Using CLion with php-src

  • PHP 8.1의 릴리스 관리자 중 하나인 Ben Ramsey의 지침입니다.

How to compile PHP from source on Debian/Ubuntu

  • Beginner’s guide on PHP.Watch.

🛠 Tools

spatie/data-transfer-object v3

https://github.com/spatie/data-transfer-object

spatie가 PHP 8에서 type이 정의된 DTO를 쉽게 만들기 위한 패키지를 만들었습니다.

spatie/fork

https://github.com/spatie/fork

pcntl_fork를 위한 래퍼로 PHP 스크립트를 병렬로 간단하고 편리하게 실행할 수 있습니다. 이 블로그 게시물과 이 비디오에서 자세히 알아보십시오.

phpbench/phpbench 1.0.0

https://github.com/phpbench/phpbench/releases/tag/1.0.0

코드를 벤치마킹하는 데 유용한 도구입니다. 새 버전에는 PHP 8 지원을 포함하여 많은 업데이트가 있습니다.

Dan Leech와 함께 📺 Release Radar #10에서 더 알아보세요.

rybakit/phpunit-extras

https://github.com/rybakit/phpunit-extras

테스트를 더 깔끔하게 만들 PHPUnit을 위한 커스텀 annotation과 expect*() 메소드.

infection 0.23.0

https://infection.github.io/2021/05/13/whats-new-in-0.23.0/#Pest-Test-Framework-support

Mutation 테스트 지원 도구가 업데이트 됐습니다. 이제 pestphp/pest를 지원합니다.

captainhookphp/captainhook

https://github.com/captainhookphp/captainhook

PHP 프로젝트를 위한 Git hook 관리. 예를 들어, 스타일 검사를 쉽게 설정하거나 저장소로 푸시하기 전에 테스트를 실행할 수 있습니다.

0xABADCAFE/php-demo-engine

https://github.com/0xABADCAFE/php-demo-engine

재미로 만든 PHP의 데모씬 엔진. 콘솔에 ASCII 기호가있는 그래픽을 표시합니다.

demoscene sample graphic

Symfony

Laravel

Yii

PhpStorm

💡 기타 읽을 만한 글

Upgrading a Project to PHP 8.0

https://medium.com/oro-development/upgrade-to-php-8-64f770ae4479

PHP 7.4에서 8.0으로 업그레이드 할 때의 업데이트 계획 및 코드베이스를 주요 잠재적 어려움에 대한 설명.

작성자가 업그레이드 했던 코드는 Symfony 4.4 LTS, 300만 이상의 LoC, 60만 PHP class, test 코드 포함, vendor 제외.

결론

  • 오픈 소스 커뮤니티는 PHP 릴리스를 적극적으로 모니터링하고 새로운 버전의 PHP에 대한 지원을 라이브러리에 추가합니다.
  • composer.json에서 실제로 지원되는 PHP 버전 만 나열하면 composer가 기본 제공하는 종속성 분석기(composer why-not php 8) 덕분에 프로젝트에서 이런 라이브러리를 사용하는 개발자에게 큰 도움이됩니다.
  • PHPStorm은 코드 작성을위한 뛰어난 IDE 일뿐만 아니라 코드베이스를 최신 버전의 PHP로 마이그레이션하는 데 도움이되는 정적 코드 분석 도구를 제공합니다. (작성자는 전체 코드 혹은 특정 디렉토리를 기준으로 정적 분석 도구를 사용했고 PhpStorm이 가장 많이 도움이 됐다고 합니다)
  • 자동화 된 테스트를 통한 높은 적용 범위는 마이그레이션 프로세스를 크게 단순화합니다. (그럼요!)
  • 새로운 주요 버전의 PHP로 업그레이드하는 것은 언뜻보기에 그리 어렵지 않습니다. (설마요!)

Do not mock what you do not own

https://thephp.cc/articles/do-not-mock-what-you-do-not-own

소유하지 않은 것은 mocking하지 말 것. 대체된 종속성을 제어하지 않으면, 다른 것도 제어가 안 되기 시작할 겁니다.

Named-entity recognition in PHP

https://www.we-rc.com/blog/2021/04/04/named-entity-recognition-in-php

RubixML을 사용하여 간단한 ML 문제를 해결하는 예입니다.

Using exceptions and retries when working with network services.

https://blog.frankdejonge.nl/back-the-func-off/

실패를 수용하는 추상 레이어 디자인을 소개합니다.

controller같은 사용자 레이어에서 아래와 같은 코드는 DatabaseException 같은 구현 세부 사항을 알아야 하기 때문에 추상화 누출 사고가 납니다.

try {
$this->fleet->markUnavailable($command->carId());
} catch (DatabaseException $e) {
// handle exception
}

아래 코드처럼 뒤에 어떤 구현이 있는지 몰라도 이해할 수 있게 Exception을 대체하는 방식을 소개합니다.

final class UnableToMarkCarUnavailable extends RuntimeException
{
public function because(string $reason, Throwable $previous): self
{
return new self(
"Unable to mark car unavailable. $reason",
0,
$previous
);
}
}

그리고 HTTP 기반 API는 광범위한 네트워크 관련 문제에 노출되기 때문에, 서비스 재시도 매커니즘을 두어야 한다고 말합니다. 이때 재시도 매커니즘을 사용자 레이어 안에 배치할지 구현 레이어 안에 배치할 지는 각각 장단점이 있습니다. 점진적으로 간격을 늘려주는 back-off 전략이 필요하다면 eventsauce/backoff 패키지를 사용할 수도 있습니다.

Algorithmic complexity (big O notation) of built-in PHP functions.

https://stackoverflow.com/questions/2473989/list-of-big-o-for-php-functions/2484455#2484455

내장 PHP 함수의 알고리즘 복잡성 (big O 표기법).

몇 가지 흥미로운 점

  • isset / array_key_exists가 in_array / array_search 보다 훨씬 빠르다
  • +(union)이 array_merge보다 훨씬 빠르다(보기도 좋다). 물론 동작 방식은 다르지만.
  • shuffle은 array_rand와 동일한 Big-O tier에 존재한다.
  • array_pop / array_push는 array_shift / array_unshift 보다 빠르다. 재 인덱스 때문.

참고로 2010에 쓴 답변입니다.

https://blog.exussum.co.uk/licence.html

Packagist에서 가장 인기있는 패키지와 해당 라이선스 살펴보기. 많은 인기있는 패키지에는 라이선스가 없거나 잘못 설정되어 있습니다.

📺 Videos

Modern PHP with Rasmus Lerdorf

https://www.youtube.com/watch?v=Hc4S74LCXHo

PHP의 아버지가 약간의 Etsy의 코드와 PHP 8에 대해 이야기합니다.

Videos from Derick Rethans on Xdebug 3

🔈 Podcasts

PHP Ugly podcast:
#236: – Memory Leaks.
#235: – Ugly Hot Tub PHP Coding.

PHP Release Radar:
Episode #9 – With Andreas Braun discussing Doctrine Cache 2.0.

PHP Internals News podcast:
Episode #82 – About auto-capturing multi-statement closures (RFC) with Larry Garfield and Nuno Maduro.
Episode #79 – About the new operator in initializers with Nikita Popov.

🙌 Community

PHP’s bus factor is 2

https://blog.krakjoe.ninja/2021/05/avoiding-busses.html

Joe Watkins는 PHP 소스에 대해 충분히 알고있는 사람은 두 사람뿐이므로 PHP에 새로운 기능을 추가 할 때이 점을 고려해야합니다. 그는 또한 코어에서 작업하는 더 많은 개발자가 필요하다고 말합니다.