ha-ah

로그, 게으른 로그

연재




행위 검증 vs 상태 검증

첫 시간에 동영상을 보며 이야기할 때 몇가지 해소가 안된 문제가 있었는데, 하나는 테스트 더블의 종류와 사용 목적이었고 하나는 행위 검증과 상태 검증이었다.

테스트 더블은 다른 분이 발표했기 때문에 이 블로그로는 자료를 가져오진 않겠지만, http://xunitpatterns.com/ 에서 주요 개념을 알 수 있다.

OKKYCON의 동영상에서도 행위 검증과 상태 검증에 관함 이야기가 많이 나왔는데, 정확히 언제/왜 사용해야 하는지 명확히 알기가 어려워 조금 더 찾아보게 됐다. 설계든 TDD든 정답이 없는 문제라서 초보자들은 갖은 상황을 떠올리며 이럴 때는 저럴 때는 어떻게 해야 하는가 하면서 논쟁이 지속되었다. 여전히 어렵긴 한데 중간 정리를 한번 해봤다.

행위 검증과 상태 검증에 관해서는 몇가지 블로그를 토대로 정리해본다.

왜 테스트가 자주 실패할까?

https://minslovey.tistory.com/97

켄트벡의 “Where, Oh Where to Test?”를 소개한다

  • 원문 링크는 깨졌고, 여기 이 글을 소개하는 페이지가 있다
  • 테스트를 위해 System Level Test를 해야 하는지 Unit Level Test를 해야 하는지 판단할 때에 Cost, Stability, Reliability를 반드시 고려해야 한다
  • Cost
    • 매우 포괄적인 개념으로 Stability와 Reliability를 포함
    • 테스트를 작성하고 수행하는 데 걸리는 시간, 그리고 유지보수에 들어가는 비용
    • 비용을 줄이기 위한 가장 좋은 방법
      • 테스트 스크립트 수와 mock object와 같이 부가적으로 필요한 코드도 최대한 줄이기
  • Stability
    • Stability가 낮은 경우
      • 테스트가 성공했는데, 결함이 있을 때
      • 테스트가 실패했는데, 결함이 없을 때
    • 만약 중요하지 않은 부분이면서 Stability를 갖춘 테스트를 작성하기가 어렵다면 작성하지 않는 것이 좋다
    • 수동으로 하거나 이후에 QA에 의해 테스트하는 것이 비용대비 효과가 높다
  • Reliability
    • 테스트는 본질적으로 문제가 없음을 보장할 수 없고, 우리에게 주어진 리소스에도 한계가 있다
    • ‘버그가 있을 확률 × 버그에 의한 피해’가 높은 순서대로 테스트를 작성

구현에 의존적인 테스트를 만들지 말자

  • 알람을 위해 특정 외부 모듈을 호출한다는 것을 보장하는 게 중요한 일일 때
    • 최종 목적이 알람이라면, 의존하는 모듈이 바뀔 때 코드가 수정되어야 한다. (Stability가 낮다)
    • 통합 테스트가 대안일 수 있다(how가 아닌 what에 집중)
    • 이때 로그를 확인하는 식으로 통합테스트를 작성하면 구현에 관계없이 기능을 검증할 수 있다
      • 그러나 시간이 더 걸린다
      • 오류가 생겼을 때, 어디에서 문제가 생겼는지 상대적으로 알기 어렵다
      • SUT가 아닌 다른 곳에 의존성이 생길 수가 있다(이 경우는 로그)

구현에 의존적인 테스트를 만들어야 한다면 최대한 의존하는 부분을 줄이며 테스트를 만들자

  • 느슨한 검증
    • 호출하는 메소드가 많다면, 특정 코드 블럭이 실행되는지(조건문 안에 들어가는지) 정도 확인하는 것으로 충분할 수 있다
    • 특정 조건문에 의해 실행 여부만 확인하는 것이라면 조건문만 통과하는 지를 확인하면 된다
  • Law of Demeter
    • Tell, Don’t Ask
    • 접점/커플링을 줄인다

Mocks Aren’t Stubs

https://martinfowler.com/articles/mocksArentStubs.html

행위 검증과 상태 검증, Mock과 stub의 차이를 이해하는데 매우 도움이 많이 된 글이다. 아래 요약글을 읽느니 번역된 글을 몇 번 더 읽는 것을 추천한다. 이건 내 블로그니까 나중에 다시 돌아볼 목적으로 정리해보자면,

Mocks과 Stubs 차이

  • 테스트 결과가 검증되는 방식 - 상태 검증과 행위 검증
  • 테스팅과 설계가 어우러지는 방식에 대한 철학

용어 구분

  • SUT(System Under Test) = 주요 객체(primary object)
  • 협력객체(collaborator) = 부차적 객체(secondary objects)

일반적인 테스트 (Regular Tests)

  • 협력객체가 필요한 이유
    • 테스트하려는 동작이 최소한 실행은 되었는지 확인하기 위해
    • 검증에 필요하기 때문. order.fill()에는 warehouse의 상태에 잠재적 변화를 일으키니까
  • 첫 예제는 상태 검증이다
    • 메소드가 수행된 후 SUT와 협력객체의 상태를 살펴봄으로써 실행된 메소드가 올바로 동작했는지 판단

모의객체를 이용한 테스트(Tests with Mock Objects)

  • 예측을 준비하고 → SUT를 돌려본 다음 → 검증
    • SUT에 대해 assert하는 것은 예전과 같다
    • 모의객체를 검증하는 것 - 예측에 맞게 호출 되었는지 확인
  • 여기서 핵심적인 차이는 order가 warehouse와 상호작용할 때 올바로 수행되었는지 어떻게 확인하느냐이다
  • 첫번째 테스트
    • 상태 검증에서는 order나 warehouse의 상태를 assert 함으로써 할 수 있다
    • mock은 행위 검증을 한다
    • setup에서 모의객체에게 예측치를 알려주고 검증시에는 스스로 확인해보라고 한다
    • assert로 확인하는 것은 order뿐이고, 수행한 메소드가 order의 상태를 변경하지 않는다면 assert는 아예 필요 없다
  • 두번째 테스트
    • 생성자가 아닌 MockObjectTestCase의 mock 메소드를 사용(명시적으로 verify를 호출할 필요가 없다)
    • withAnyArguments를 써서 예측의 제약을 완화시켰다. 사실 withAnyArguments가 default라서 생략해도 괜찮았다

Test Double

  • Dummy 객체는 전달되기만 하고 실제 사용되지는 않는다. 보통 파라미터 리스트를 채우는데에 사용된다
  • Fake 객체는 동작하는 구현이 있다. 하지만 운영시에는 사용할 수 없는 간단한 형태이다. (인메모리 데이타베이스가 좋은 예이다)
  • Stubs은 테스트시 호출이 되면 미리 준비된 답변으로 응답하는데, 테스트 시에 프로그램된 것 이외의 것에 대해서는 응답하지 않는다. 스텁은 호출에 대한 정보를 기록할 수도 있을 것이다. 이메일 게이트웨이 스텁은 ‘보낸’ 메시지들 혹은 몇개의 메시지를 ‘보냈’는가를 기억하는 것이다
  • Mocks는 수신하기를 기대하는 호출의 명세(specification)인 예측으로 미리 프로그램 된 객체이다
    • Mocks만 행위 검증 사용을 추구한다
    • excercise 단계에서는 다른 더블과 같이 동작하지만, setup과 verification단계에서는 다르다
    • 많은 사람들은 실제 객체를 다루기 불편할 때에만 테스트더블을 사용한다

MailServiceStub 예제

  • public class MailServiceStub implements MailService {
  • 메시지가 보내졌다는 것을 테스트한다
  • 올바른 수신자에게 보내졌는지, 올바른 내용으로 보내졌는지는 테스트하지 않았지만 핵심을 보여주고 있다
  • 스텁에 상태검증을 하려고 검증에 도움이 될 추가적인 메소드를 만들었다. 스텁은 MailService 인터페이스를 구현하고 추가된 테스트 메소드도 있다

Mock objects는 항상 행위검증을 사용하고, 스텁은 둘 다 가능하다. Meszaros는 행위검증을 하는 스텁을 가리켜 테스트스파이(Test Spy)라고 부른다

  • 차이점은 더블이 얼마나 정확히 수행과 검증을 하는가이다

언제 모의객체(혹은 다른 더블)를 사용할 것인가

  • classical TDD 스타일은 가능하면 진짜 객체를 사용하고 진짜 객체를 사용하기가 만만하지 않으면 더블을 사용한다. 그러므로 고전적 TDD 실천가들은 warehouse에는 진짜 warehouse를 사용하고 메일 서비스에는 더블을 사용할 것이다. 더블의 종류는 그리 중요하지 않다.
  • mockist TDD 실천가는 관심있는 행위를 가진 모든 객체에 모의객체를 사용하려 한다. 예제에서는 warehouse와 메일서비스 모두에 대해서이다.
  • (BDD는 모의객체 접근법을 취한다.)
  • 차이점 중에서 선택하기
    • 컨텍스트
      • order와 warehouse 사이처럼 객체들간의 협력이 간단한가 아니면 order와 메일서비스처럼 쉽지 않은가?
      • 간단한 협력이라면 고민할 게 없다. classical TDD에선 진짜 객체, mockist TDD에선 mock
      • 간단하지 않다면, mockist TDD에선 당연히 mock과 행위 검증. classical TDD에선 가장 쉬운 방법을 선택
      • 캐시처럼 다루기 힘든 협력관계가 아닌데도 상태검증을 하기 쉽지 않은 경우는 행위 검증이 좋을 것이다. 그 반대의 경우도 존재함
    • mock object는 XP 커뮤니티로부터 왔다.
      • 만들고자 하는 시스템 외부에서 SUT의 인터페이스를 만들면서 테스트를 작성함으로써 스토리 개발을 시작한다 (outside-in)
        • 모의객체에 대한 예측은 다음 단계의 명세가 되고 테스트들의 시작점이 된다
    • classical TDD는 어떤 기능을 택하고 이 기능이 동작하려면 도메인에서 무엇이 필요한지 결정한다 (middle-out)
      • 필요한 것을 도메인 객체들이 수행하게 하고 일단 동작하면 그 위에 UI를 올린다
  • 픽스처 준비 (Fixture Setup)
    • 고전주의 TDD에서는, SUT 뿐만 아니라 테스트의 필요에 따라 SUT가 필요로 하는 모든 협력 객체들도 만든다.
      • 복잡한 픽스처를 가능한 한 재사용하려 한다
      • 대부분의 픽스처는 생성 비용이 크지 않고, 만약 크다면 보통 더블이 사용된다
    • 모의객체를 이용한 테스트에서는, SUT와 그에 인접한 모의객체만을 작성하면 된다. 복잡한 픽스처를 구성하는 작업을 피할 수 있다 (최소한 이론적으로는)
  • 테스트의 고립성 (Test Isolation)
    • mockist TDD에선 시스템에서 버그가 생기면, 보통 SUT가 버그를 가진 테스트만 실패하게 된다
      • 대신 협력 객체에 대해 정교한 테스트를 만들어야 한다는 것이 명확하다
    • classical TDD에선 이 객체를 이용하는 클라이언트가 있는 모든 테스트도 실패하며, 다른 객체를 테스트할 때 버그를 가진 객체가 협력객체로 사용되는 곳에서 모두 실패하게 된다
      • 결과적으로 매우 많이 사용되는 객체가 실패하면 시스템 전체에 걸쳐 실패하는 테스트의 파장이 일어난다
      • 하나의 테스트가, 하나의 객체가 아닌 여러 객체의 클러스터에 대해 수행되는 주요 테스트로서의 역할을 하고 있다
      • 테스트가 너무 정교하지 않은 경향이 있지만, 디버깅에 어려움을 겪는다면 정교한 테스트를 (TDD 기법으로) 만들어 나가야 한다.
    • 본질적으로 고전적 xunit 테스트는 단지 유닛테스트일 뿐 아니라, 작은-통합 테스트이기도 하다
      • 한 객체를 테스트할 때에 놓쳤을지 모를 에러를, 클라이언트 테스트가 잡을 수 있다
      • 모의객체 테스트는 그런 품질을 잃게 된다
    • 어떤 스타일의 테스트를 하든, 전체 시스템에 걸쳐 수행되는 거친 정밀도의 인수테스트와 결합해야 한다
  • 테스트와 구현의 결합성 (Coupling Tests to Implementations)
    • 모의객체 테스트를 작성하는 것은, SUT가 그것의 공급자와 올바로 대화하는지 확인하기 위해 SUT의 나가는 호출을 테스트하는 것
      • 따라서 모의객체 테스트는 메소드의 구현에 많이 결합되어 있다
      • 모의객체 테스팅에서는 테스트 작성시 행위 구현에 대해 생각하게 만든다
    • 고전주의 테스트는 최종 상태에만 신경 쓴다
      • 고전주의자들은 외부 인터페이스 호출로부터 어떤 일이 일어나는지에 대해서만 생각하고 구현의 모든 고려사항들은 테스트 작성이 끝난 이후로 남기는 것이 중요하다고 생각
    • 구현에 결합되는 것은 리팩토링에도 간섭을 일으키는데, 구현이 바뀌면 고전적 테스팅보다 테스트를 깨트릴 가능성이 훨씬 더 많기 때문이다
    • Tell, Don’t ask
      • 상태기반 검증에서 알려진 이슈가 단지 검증에 쓰려고 접근자 메소드를 만들게 된다는 것이다
        • 테스팅만을 위해서 어떤 객체의 API에 메소드를 추가하는 것은 전혀 납득할 수 없는 것인데, 행위 검증을 사용하면 그런 문제를 피할 수 있다
      • 모의객체주의자들은 역할 인터페이스(role interfaces) 를 좋아하고 모의객체 스타일이 역할 인터페이스를 장려한다고 단언한다
  • mockist가 도움이 될 때
    • 당신이 실패하는 테스트를 디버깅하느라 많은 시간을 보내고 있는데 그 실패가 깔끔하지 않고 문제가 어딘지 알려주지 않는 경우
    • 객체가 충분히 행위를 가지고 있지 않아서, 모의객체 테스팅이 개발팀에게 행위가 더 풍부한 객체를 만들도록 장려할 수 있을 때

기타 더 읽어 볼 것

Mock에 대해(Test double 포함)

왜 Mockito가 좋은가?

Mockist? Classicist?

  • “테스트 대역은 일종의 가정이고 가정이 늘어날 수록 실제 결과는 예측하기 어려워진다.”

Stub vs Mock?

결론

마틴 파울러 옹이나 여러 선구자들의 의견을 종합해볼 때 mock을 사용하는 것은 좋지 않다고 생각이 들지도 모르겠다. 그런데 “테스트 주도 개발로 배우는 객체 지향 설계와 실천”에서도 이 분들이 강조하는 소프트웨어 설계 원칙을 매우 강조하면서도 mock을 써야할 이유를 설명한다. Mock을 SUT의 행위를 검증하는 것 정도로만 보지 않고 많은 개발 언어에서 충분히 표현하기 어려운(인터페이스 만으로는 관계를 보여주는데 한계가 있다고도 주장한다) 객체간의 관계를 mock을 통한 테스트로 더 명확히 보여줄 수 있다는 생각이다. 정말 그런 가치가 있을지는 책을 다 읽어보고 판단할 일이다. 이 책은 단순히 mock을 잘 쓰는 법만 알려주진 않는다.

내가 추천하면 아무도 안 믿을테니 켄트백의 추천사 중 일부를 소개해본다.

이 책에 나온 테스트 주도 개발 방식은 내가 연습했던 것과는 다르다. 아직까진 그 차이를 분명하게 표현할 수 없지만 지은이들이 자신의 기법을 명확하고 자신감있게 발표하는 것을 보고 배운 바가 있다. 기법의 다양성은 나만의 개발 방법을 더욱 다듬는 새로운 영감의 원천이 되었다. “테스트 주도 개발로 배우는 객체 지향 설계와 실천”에서는 다양한 기법이 서로를 떠받치는 논리정연하고 일관된 개발 체계를 제시한다.

연재




TDD를 시작했다는 건 아니고, TDD 공부를 시작했다는 말이다.

스터디 조직

TDD 참관 이후 뭔가 해야겠다 마음만 먹고 있다가, 같이 일하는 주니어 친구가 TDD에 관심있어 한 것을 눈여겨 보다가, 마침 사내 UX 스터디도 끝나는 분위기라 사내 스터디를 조직해봤다. 교재는 그 친구가 관심있어 했고, 나도 한번 맹대표님 스터디 S66 참여로 어설프게 읽어본 적 있는 테스트 주도 개발로 배우는 객체 지향 설계와 실천으로 정했다.

테스트 주도 개발로 배우는 객체 지향 설계와 실천 이미지

같이 참여한 또 다른 열정적인 친구는 이 책으로 동시에 다른 스터디를 조직해서 공부하는 열정을 보이기도 했다. 그 멤버 구인 글에 박성철님은

스터디를 응원합니다. 그런데 교재가 TDD를 공부하기에 좋은 책인지 의문입니다. 저기서 말하는 TDD는 흔히 말하는 TDD가 아니라서요.

라고 코멘트를 다셨는데, 왜 흔히 말하는 TDD가 아닌지는 아직 말씀을 안 하셔서 모르겠다. ‘흔히 말하는 TDD’가 뭔지…사람들이 흔히 말하는 TDD는 Mock과 프레임웍이 덕지덕지 엉킨 코드가 아니던가!(농담이다. 넘어가자)

어쨌든 이 책이 보여주는 특징 중에서 아래 두가지 때문일까 생각만 해봤다.

  1. 저자들은 단위테스트 뿐 아니라 정기 회의, CI, CD, 인수테스트 등 촘촘한 중첩된 피드백 계층을 만드는 것을 중요하게 생각한다는 점(책에서는 기술적인 내용을 중심으로 풀고 있지만)
  2. Mock을 적극 사용한다는 점(처음에 책을 쓰기 시작한 이유는 Mock을 제대로 쓰는 법을 알려주고 싶었기 때문이라고 한다)

이유야 어찌됐든 이 책은 TDD를 시작하는 사람들에게 적당한 책은 아니다. 서문에도 ‘지식이 풍부한’ 독자를 대상으로 한다고 나와있다. 제목을 다시 보자.

테스트 주도 개발로 배우는 객체 지향 설계와 실천

원제는 Growing Object-Oriented Software, Guided by Tests. 테스트의 안내를 받으며 진화하는 OO 소프트웨어라는 의미. TDD는 기본으로 깔고 설계를 논하는 책이다. 무려 8장까지 저자들이 생각하는 좋은 설계와 관점을 이야기 한다. 목차

1부 서론

  • 1장 테스트 주도 개발의 핵심은 무엇인가?
  • 2장 객체를 활용한 테스트 주도 개발
  • 3장 도구 소개

2부 테스트 주도 개발 과정

  • 4장 테스트 주도 주기 시작
  • 5장 테스트 주도 개발 주기의 유지
  • 6장 객체 지향 스타일
  • 7장 객체 지향 설계의 달성
  • 8장 서드 파티 코드를 기반으로 한 개발

요즘 TDD를 많이 전파하시는 분들로부터 ‘나는 한때 mockist였지만 이제는 거의 mock을 사용하지 않는다’는 고백을 종종 보게 되는데, 나도 한때는 mockist 좀 되어 볼 요량으로 계속 공부 해보기로 한다. mockist에 관한 이야기는 다음 포스트에 더 하기로.

첫 시간은 무얼 해야 하는가

의외로 10명이나 되는 많은 분들이 관심을 보였는데, 그러다보니 TDD에 대한 다양한 관점과 다양한 이해도가 섞여 바로 진도를 나가는 게 맞는지 의문이 들었다. 우선 매주 월요일 한시간 모여 진행하기로 했는데, 역시나 진행해보니 확실히 시간이 부족했다. 난 두시간은 했으면 했지만 다들 너무 바쁘기도 했고, 퇴근 시간에 하려 해도 자율 출퇴근이라 퇴근하는 시간이 모두 달랐고.

첫 주제로는 마침 OKKYCON: 2018 발표 영상이 올라온 지 얼마 안 되었을 때라, 이것부터 보고 같이 이야기 하기로 했다. 영상은 모두가 보지만, 한사람씩 하나의 영상을 맡아서 요점 정리하기로 했다. 총 7개 영상인데, 그 중에 5개 영상을 선정했고 다음주에 모였다.

한 10분 정도 요약한 내용을 이야기하고 정리하면 되겠다 예상했지만, 다들 열심히 준비를 하셔서 3개의 주제에 대해서 이야기 하는데 한시간이 훌쩍 넘어갔다. 결국 다음 시간까지 이어졌다.

  1. 정진욱님 - 테스트하기 쉬운 코드로 개발하기
  1. 박재성님 - 의식적인 연습으로 TDD, 리팩토링 연습하기
  1. 이혜승님 - 테알못 신입은 어떻게 테스트를 시작했을까?
  1. 양완수님 - 테스트를 돌보기 위한 매우 간단한 실천 방법들, 그리고 효과
  1. 이규원님 - 당신들의 TDD가 실패하는 이유

TDD 맛보기

세번째 시간에 모이기 전까지는 백명석님의 클린 코더스 강의9. TDD 4 - primefactors 실습해오기로 했다. 이건 숙제 검사까지는 안 하고, 당일 모여서는 단체 실습을 하기로 했다.

Java를 주 언어로 쓰는 사람이 많고 모두 Java 코드는 읽을 수 있으므로 언어는 Java로 선택. 한명씩 돌아가며 다음 단계를 어떻게 진행할 지 이야기하기.

실습 주제로는 한시간 안에 해치울 수 있을 정도로 간단한 것을 원했는데, 로또 번호 추첨기, FizzBuzz, 가위바위보 같은 걸 생각해봤었다. 이전 회사에서는 가위바위보 게임을 이런 방식으로 만들어 본 경험이 있었는데, 이런 쉬운 주제도 다들 생각하는 방향이 다르면 한시간도 충분치 않았던 기억이 있었다. 하지만 간단하면서도 여러 객체의 관계를 고려할 만한 주제가 좋을 것 같아서 급하게 기획서를 만들었다. 대충 이렇다.

- 우리는 사용자를 부추겨 포인트를 탕진하게 만드는 것이 목표입니다
- 한 세트당 번호 6개를 받고 100원씩 포인트를 차감합니다
- 사용자는 원하는 세트 수를 고를 수 있습니다
- 당첨 확률은 관심 없으니 그냥 랜덤하게 숫자만 6개씩 나와도 됩니다
- 한 시간 안에 충분히 개발 가능하다고 이미 CEO 보고 들어갔습니다
- 오류가 발견되면 당신은 해고입니다

이를 토대로 기본 뼈대를 만들어 공유했다. Java로 10년 만에 코딩한 것 같은데, TDD로 기본 예제를 만들어 나간 과정이 재밌었다 - 설계는 엉망이었지만. 당일에는 한시간 밖에 시간이 없으니 User와 Point의 인터페이스는 미리 정의했고, Lotto 서비스의 구현에 집중하려고 했다.

구현은 아래 과정을 통해 진행하려고 했다.

  • 요구사항 검토
  • 명세 작성
  • 설계
  • 실습 준비
  • TDD
  • 완성 + 박수!

명세를 만들 때는 아래의 규칙으로 작성하기로 했..으나 그게 참 쉽지가 않다. 총체적 혼란이었다.

누가 / 무엇을 위해 / 어떤 행동을 한다

참고 :

사실 예제를 리허설하며, 서둘러 코딩부터 진행하면 빠질 수 있는 함정을 하나 준비하기도 했는데, 주원님이 코딩을 멈추고 명세부터 확실히 하고 가자고 하셔서 미리 준비한 허술한 꼬임은 실패했다. 어쨌든 좋은 방향이라서 좋았다. 여기서 이야기를 나누고 기능을 확정하는데까지 시간을 거의 다 써서 코딩 시간에는 한사람씩 겨우 돌아가는 수준으로밖에 진행하지 못했다. 결국 남은 건 각자 코딩해서 PR 올리기로 했고, 서로 코드 리뷰를 했다.

이렇게 DAY 3까지 완료했고, 교재를 보기 시작했다.

2편에서 계속.

Php_annotated_monthly 이미지

JetBrains에서 제공하는 월간 PHP Annotated Monthly 3월호입니다.

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


News & Releases

PHP

Xdebug 2.7.0

https://xdebug.org/#2019_03_06

오래 기다리셨어요. 이제 7.3을 지원합니다.

ircmaxell/php-compiler

https://github.com/ircmaxell/php-compiler

Anthony Ferrara가 만든 PHP compiler를 소개합니다. PHP도 (JVM과는 다르지만) VM에서 돌아가는데, 이 컴파일러는 PHP7.4부터 지원될 FFI를 활용해서 VM 위에서 돌지 않도록 바로 native machine code를 생성해서 실행가능한 코드(standalone binary)를 만들어 준다고 합니다.

링크에 들어가 보시면, 세가지 컴파일 모드를 소개하는데, PHP로 만든 첫번째 모드는 엄청 느리고 VM에서 도는 버전이고, 두번째 JIT(Just In Time) 모드에선 machine code를 생성해서 실행하는데 PHP7.4 버전보다 실행은 빠르지만 PHP에서 컴파일 되느라 컴파일 속도가 너무 느렸다고 합니다. 매번 실행할 때마다 컴파일을 새로 한다는군요. 마지막 compile 버전에선 아예 실행 가능한 바이너리로 만들어서 이론적으로는 native C 만큼의 속도가 나왔다고 합니다.

아직은 PoC(Proof of Concept)라서 매우 제한적으로 지원하지만 희망찬 미래를 보여줬다고 평가하는군요.

무궁무진한 미친 짓을 할 수 있답니다.

PHP Internals

The proposal to abolish 50% threshold on voting

https://wiki.php.net/rfc/abolish-narrow-margins

RFC 프로세스가 바뀌었다고 합니다.

두 카테고리로 나뉘었고

  • PHP에 변화를 주는 것
  • 정책 문서를 변경하거나 생성하는 것

통과 되려면 투표로 2/3 이상의 승인을 받아야 합니다.

overview of the unaccepted RFCs

https://github.com/Danack/RfcCodex

사람들이 관심이 많지만 아직 받아들여지지 않은 RFC가 어떻게 되고 있는지 정리한 글입니다.

아직 논의 중인 것

  • 어노테이션Annotations
    • 몇몇 사람은 여전히 어노테이션을 싫어하고
    • 이미 주석을 통한 어노테이션을 쓰는 사람들은 RFC가 자신들의 use case에 충분하지 않아 좀 더 개선하길 바람
  • 더 간략한 클로저 문법Briefer closure syntax
    • 아주 큰 혜택이 없기 때문에 몇몇 사람들은 그다지 원하지 않는다
    • JavaScript 같은 경우는 자연스럽게 parameter를 상위 scope으로부터 받을 수 있지만 PHP에선 명시적으로 넣어줘야 하는데, 어떤 문법을 써야 할까?
    • 리턴 타입도 결정해야 하는데, 어떤 문법을 사용해야 할까?
  • 클래스 scope 개선Class scoping improvements
    • 같은 패키지 내에만 공개해야할 메소드도 public으로 제공해야 하는 문제를 개선해야 함
    • 마땅히 구현할 만한 방법이 없음
    • C++의 friendship도 적당하지 않음
  • Consistent callables
    • Callable을 일관성있게 변경하는 RFC가 등록되어 있음
  • Enums
    • enum을 어떻게 정의할 것인가에 대한 다른 관점이 있음
    • Draft RFC가 등록되어 있으나 시간과 에너지를 쏟을 사람이 없음. 누군가 (이 RFC를 올린) Levi나 Bob에게 몇 달 치 월급을 주면 가능할 것이라고…
  • Generics
    • 제네릭의 필요성은 놀랄 만큼 일치하고 있지만 구현하기가 어렵다(인터프리터 방식에서 오는 한계인 것으로 보입니다)
    • 정말 제네릭이 꼭 필요하다면 https://preprocess.io/ 를 활용할 수는 있다
  • Method overloading
    • PHP는 정적 타입 언어와 달리 런타임에 타입이 변할 수 있기 때문에 쉽지 않다
    • 성능 문제도 발생할 수 있다
    • 메소드 오버로딩이 왜 필요한가에 대한 명확한 근거가 없다
    • 정 원하면 이를 대체할 방법도 알려줍니다
  • Standardise core library
    • Core 라이브러리 함수의 비일관성(함수명, 파라미터…)
    • 모든 비일관성이 전부 비일관적인 건 아니다(다 이유가 있다는 뜻)
    • 기존 수많은 코드를 전부 변경할 수도 없고, 누구도 긴 시간동안 수많은 alias를 유지하고 싶어하지도 않음
    • 관련 링크
  • Union types
    • parameter나 return type에 여러 타입을 전달/리턴할 수 있도록 하는 union_types RFC
    • 사람들이 타입시스템에 익숙하지 않은 것이 걸림돌이라고 하는데…글쎄요

앞으로 할 것

  • Async
  • Structs
  • Explicit defaults

[RFC] Consistent type errors for internal functions

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

이 RFC가 accepted 되어서 PHP 8 부터는 모든 내장 함수에서 잘못된 argument 타입이 들어오면 TypeError를 발생시킵니다.

[RFC] Weak References

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

PHP 7.4부터 WeakReference가 도입됩니다. 가비지 컬렉터로 객체가 파괴되는 것을 막지 않으면서 객체에 대한 참조를 유지할 수 있습니다.

[RFC] Saner string to number comparisons

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

Nikita Popov가 PHP 8을 목표로 좀 더 납득할 만한 문자열 - 숫자 비교 연산을 제안했습니다.

php-numeric-string-comparison

이에 대한 많은 논쟁이 있는 것으로 보입니다만, 그 동안은 PHP 7.4에서 Warning을 받을 수 있습니다.

[RFC] Locked Classes

https://wiki.php.net/rfc/locked-classes

새로운 클래스용 키워드인 locked를 제안했군요. property를 동적으로 할당할 수 없게 합니다.

Allow throwing from __toString()

http://news.php.net/php.internals/104535

Nikita가 __toString method에서는 exception을 throw할 수 없게 되어 있는데, 이를 가능하게 하는 PR이 올라왔습니다. PHP sadness에 있던 것 중 하나였죠.

[RFC] Arrow Functions 2.0

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

(위 Briefer closure syntax에서도 언급했던) 짧은 클로저 문법에 관한 세번째 시도입니다. 이번엔 Nikita, Bob, Levi가 함께 붙었습니다!

[RFC] Generator comprehensions

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

Generator를 편하게 사용할 수 있는 draft RFC입니다.

$a = [1, 2, 3];
$mul = 3;
$c = [ for $a as $v yield $mul * $v ];

PHP Internals News: Episode 1

https://derickrethans.nl/phpinternalsnews-01.html

Xdebug의 author인 Derick Rethans가 새로운 팟캐스트를 시작했습니다. 첫 에피소드는 Nikita Popov와 함께 한 PHP Internals News랍니다.

이런 내용을 이야기 했군요.

  • RFC Watch — a web site to keep an eye on RFC voting progress
  • RFC: Permit trailing whitespace in numeric strings
  • RFC: JIT

기타 읽을 만한 글

Moving away from magic — or: why I don’t want to use Laravel anymore

https://medium.freecodecamp.org/moving-away-from-magic-or-why-i-dont-want-to-use-laravel-anymore-2ce098c979bd

라라벨에서 너무 많은 것을 자동으로 처리해주기 때문에 라라벨을 떠난다는 글입니다.

  • Eloquent ORM은 Active record 패턴을 사용하고 개발자가 코드를 덜 쓰기를 바라길 원하는데, 이런 특성이 코드를 이해하거나 정적 분석 혹은 IDE를 활용하기에도 좋지 않고, 계속 메소드를 추가하게 되어 단일 책임 원칙을 위반하도록 유도 합니다
  • Global 헬퍼 함수는 더 좋은 방식으로 대체할 수 있는 방법이 있음에도 단지 사용하기 쉬운 방법을 제공하기 위해 global namespace를 더럽히고 설계면에서 희생해야 합니다. 5.8에서는 일부 헬퍼 함수가 제거되어 다행이지만, 모든 헬퍼 함수를 사용하지 않게 안내해야 합니다
  • Facade도 실제론 static 메소드가 아닌 어떤 메소드에 쉽게 접근할 수 있는 좋은 툴 같지만 모든 걸 magic method에 전달하면서 복잡도만 증가했습니다. IDE의 지원을 받기 어려운 문제도 IDE helper package를 통해 해결할 수 있지만, 애초에 필요가 없는 라이브러리입니다. 게다가 사실 이건 Gang of Four에서 말하는 Facade 디자인 패턴도 아닙니다. 차라리 static service locators라고 불러야 합니다.
  • 모든 것을 가능한 한 쉽게 만드는 Laravel의 접근법은 좋습니다. 그러나 앱이 성장할 수록 관리하기 어렵습니다. 나는 IDE 지원, 더 강한 타이핑, 실제 객체, 그리고 좋은 엔지니어링을 선호합니다. 더 작은 앱을 쓰고 싶을 때 Laravel로 돌아갈 수도 있습니다.
  • 요점은 Laravel의 잘못이 아닙니다. 내 요구 사항에 더 잘 맞는 툴킷으로 전활할 것입니다. 다른 프레임 워크와 도구는 기본 설계가 더 좋고 마법이 적습니다.

PhpRedis vs Predis: comparison on real production data

https://medium.com/@akalongman/phpredis-vs-predis-comparison-on-real-production-data-a819b48cbadb

PhpRedis는 Redis storage와 통신할 수 있는 PHP extension입니다.

Predis는 C extension없이 순수 PHP에서 사용할 수 있는 대체품입니다.

(당연하게도) PhpRedis가 6배 빠른 속도를 보였고, igbinary serializer를 사용하면 저장 공간이 3배는 절약된다고 합니다.

What is a command bus and why should you use it

https://barryvanveen.nl/blog/49-what-is-a-command-bus-and-why-should-you-use-it

Command bus를 왜 사용해야 하는지 적은 글입니다.

  • 결합도를 낮춰주고
  • 테스트와 확장을 용이하게 해줍니다

그러나 복잡도를 늘리고 오버 엔지니어링이 되기도 합니다.

Design Patterns in PHP — Updated according to PHP 7.3 + real-world examples added

https://github.com/RefactoringGuru/design-patterns-php/

PHP 디자인 패턴인데, 7.3버전에 맞춰 업데이트 되었다고 합니다.

Predict air pollution with k-Nearest neighbors and PHP — using php-ai/php-ml

https://arkadiuszkondas.com/predict-air-pollution-with-k-nearest-neighbors-and-php/

php-ai/php-ml를 활용해 공기 오염을 예측하는 프로젝트입니다.

Write your own simple chess AI in PHP

https://arkadiuszkondas.com/write-your-own-simple-chess-ai-in-php/

PHP로 간단한 체스 AI를 만드는 방법입니다.

Binary heap implementation in PHP

https://arkadiuszkondas.com/binary-heap-implementation-in-php/

PHP로 Binary heap을 구현했다고 합니다.

위에 언급한 predict air pollution, check AI까지 다 이 사람(Arkadiusz Kondas)이 만들었는데요. 뭐하는 사람일까요?

Matthias Noback: Handwritten service containers

https://matthiasnoback.nl/2019/03/hand-written-service-containers/

Matthias Noback이 손수 구현한 service containers입니다.

Php_annotated_monthly 이미지

JetBrains에서 제공하는 월간 PHP Annotated Monthly 2월호입니다.

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


News & Releases

PSR

https://github.com/php-fig/fig-standards/blob/master/proposed/event-dispatcher.md

PSR-14 Event Dispatcher가 review stage로 넘어갔다는 소식.

아시겠지만(‘나는 알지롱’이란 뜻), PSR Workflow에 따르면 PSR이 제정되는 단계는 아래와 같습니다.

Pre-Draft -> Draft -> Review -> Accepted (-> DEPRECATED)

https://github.com/php-fig/fig-standards/blob/master/proposed/extended-coding-style-guide.md

PSR-12 Extended Code Style도 review stage에 도달했습니다.

PSR-2가 2012에 accepted됐고, 그동안 PHP에 많은 변화가 있어서(PHP 7이 2015년 12월에 release됐죠) 최신 PHP 스펙을 반영하자는 취지로 보입니다.

PSR-12는 PSR-1에 기초하고 있고, PSR-2를 대체합니다.

PSR이 review 단계에 왔다는 것은 시험 구현체(trial implementations)를 검토하게 되는데요. JetBrains, PHPCS 등에도 이를 반영해야 한다는 논의가 되고 있습니다.

바로 뒤에 소개할 2019.1 EAP에서도 ‘Sort use statements’쪽을 보시면 use statement 순서를 조정하는 것으로 적용되었습니다.

PhpStorm 2019.1 EAP

https://blog.jetbrains.com/phpstorm/2019/01/phpstorm-2019-1-early-access-program-is-open/

Focus mode 재밌네요.

요즘 EAP를 보면 이제 할 수 있는 건 이미 다 했다의 느낌이기도 하고요.

PHPUnit Version 8

https://phpunit.de/announcements/phpunit-8.html

PHPUnit 8 버전이 새로 나왔고 PHP7.2이상을 요구합니다.

PEAR was hacked

https://thephp.cc/news/2019/02/blast-from-the-past

슬픈 소식입니다.

If you downloaded go-pear.phar between December 20, 2018 and January 19, 2019 and used it on a system that has sh and perl available then you must consider that system to be compromised, and take responsible action.

저도 나이 드니까 여기 저기 아프더라고요.

PHP Internals

PHP-7.4 branched, master is PHP 8.0

https://externals.io/message/103862

드디어 PHP 8.0 개발이 시작됐습니다. PHP 7.4와 PHP 8.0을 병렬로 개발한다는 소식입니다.

Deprecated된 스펙은 이미 지워버렸고, 도커 허브의 phpdaily/php를 통해 체험해보실 수 있습니다.

[RFC] JIT

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

Dmitry Stogov가 제안을 넣었습니다.

RFC에 따르면 OPcache와는 독립된 부분으로 개발되고, OPcache shared memory 영역을 사용할 것으로 보입니다.

PHP JIT is implemented as an almost independent part of OPcache… When enabled, native code of PHP files is stored in an additional region of the OPcache shared memory…

Nikita Popov의 벤치마크에 따르면, 프로젝트(PHP-Parser, amphp/http-server)에 따라 5% ~ 30% 정도의 성능 향상이 있었다고 합니다.

php-jit 도커 이미지에서 체험하실 수 있습니다.

추가로 PHP JIT에 관한 좋은 글도 소개주고 있는데요. 극단적인 상황에서 PHP7과 PHP-JIT의 성능을 비교해주기도 하고, 그러나 웹 어플리케이션에서는 그만큼의 성능 향상을 보일 수는 없고, PHP 사용자나 PHP 언어 자체의 유지보수를 어렵게 할 수 있다고 우려하기도 합니다. 아직 linux 환경에서만 가능하고 일반적인 개발환경(Windows, Mac)에서의 지원 여부도 아직은 불투명한 상황이라고 합니다.

[RFC] Weak References

저는 다른 언어에서 Weak References를 써본 적이 없고 왜 필요한 지는 잘 모르겠습니다만, 현재 extension을 통해 PHP에서도 사용할 수는 있다는군요.

[RFC] Null Coalescing Assignment Operator

2년 전에 accepted된 제안인데, 7.4에 구현되었다고 합니다.

function (array $parameters = []) {
$parameters['property'] ??= 'default';
}

이 코드는 아래와 동등합니다.

function (array $parameters = []) {
$parameters['property'] = $parameters['property'] ?? 'default';
}

PHP의 축약형 연산자는 Shorthand comparisons in PHP에서 더 확인하실 수 있습니다.

[RFC] New custom object serialization mechanism

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

PHP는 두가지 커스텀 직렬화 매커니즘을 지원하는데요.

  • __sleep() / __wakeup() magic methods
  • Serializable interface

여기에는 사용성 면에서나 아래 코드처럼 상속관계에서 중첩된 직렬화를 시도할 때 등 몇가지 문제가 있습니다.

class A implements Serializable {
...
public function serialize() {
return serialize([$this->prop, parent::serialize()])
}
...

새로운 RFC에서는 두가지 magic method를 추가합니다.

// Returns array containing all the necessary state of the object.
public function __serialize(): array;

// Restores the object state from the given data array.
public function __unserialize(array $data): void;

왜 interface가 아닌 magic method냐는 질문에는, PHP는 기본적으로 모든 object를 직렬화할 수 있고 Serializable interface를 구현하지 않더라도 여전히 가능하기 때문에 magic method가 좀 더 시맨틱하기 때문이라는군요.

[RFC] Consistent type errors for internal functions

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

사용자 정의 함수는 잘못된 parameter가 전달되면 TypeError를 뱉지만, 내장 함수는 상황에 따라 다르지만 기본적으로 warning을 발생시키고 null을 리턴합니다. 이 RFC는 예외없이 모든 파라미터 오류에 대해 TypeError exception을 던지도록 제안합니다.

PHP 8.0을 target으로 하고 있고 하위 호환을 해치므로 주의해야할 것입니다.

기타

[RFC] Allow void return type variance

  • 아직 Under Discussion 상태인데, return type이 void인 경우 하위 클래스에서 다른 type을 리턴할 수 있께 선언할 수 있습니다. 하지만 void return type이 아닌 경우 다시 void로 지정하는 것은 안 됩니다.

PHP RFC: Annotations 2.0

  • 많은 분들이 기다리시는 annotation을 위한 RFC이고, 현재 Draft 상태입니다.

기타 읽을 만한 글

How to Kill Parents

https://www.tomasvotruba.cz/blog/2019/01/24/how-to-kill-parents/

When to declare classes final라는 글을 쓴 Marco Pivetta가 4년 후 더 자극적인 제목으로 추가 포스팅을 올렸습니다.

개발자들은 상속을 통해 문제를 해결하는 경향이 있는데, 상속 보다는 구성을 통해 해결하기 위해, 더 나은 설계를 위해, 가능하면 항상 final을 쓰라고 권고합니다.

Moving from Go to PHP again

https://dannyvankooten.com/from-go-back-to-php-again/

PHP(Laravel)을 떠나 Go로 돌아갔던 저자가 다시 PHP로 돌아왔다는 이야기입니다. GO는 여전히 좋고 퍼포먼스가 뛰어나서 필요할 땐 여전히 사용할 용의가 있지만 서비스 운영에는 쉽지 않았던 모양입니다. Symfony4가 엄청 엄청 좋다는 게 결론입니다.

it’s great. Really, really great.

Video

Beachcasts Tech Videos

http://beachcasts.com/

Adam Culp란 사람의 동영상 강의 시리즈입니다.

Astrocasts

https://astrocasts.com/browse

Beau Simensen란 사람의 동영상 강의도 추천한답니다.

Php_annotated_monthly 이미지

JetBrains에서 제공하는 월간 PHP Annotated Monthly입니다.

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


News & Releases

Nikita Popov Joined PhpStorm Team

https://blog.jetbrains.com/phpstorm/2019/01/nikita-popov-joins-phpstorm-team/

Nikita하면 (제 머릿속에) 떠오르는 두 사람 중 하나. 그 니키타가 아닌 Nikita Popov가 PhpStorm 팀에 합류했다는 소식입니다.

그가 어떤 일을 했는지 대충 소개하자면,

Nikita is the author of generators, variadic functions and argument unpacking, AST as an internal structure in PHP, engine exceptions, uniform variable syntax, and many other PHP contributions.
He worked with Dmitry Stogov and Xinchen Hui on NG project, which later became PHP 7.
Nikita is also known for PHP Parser which laid the groundwork for many other tools such as PHPStan, Psalm, BetterReflection, Infection, SuperClosure, and PHP-DI, just to name a few.

PHP 5.6.40

http://php.net/ChangeLog-5.php#5.6.40

PHP5.6이 이미 작년 12월부터 unsupported 되었음에도 불구하고, 심각한 보안 문제가 발생하면 PHP 팀이 업데이트 해줄 수도 있다는 이야기입니다. 정이 많네요, 이 친구들.

심각한 오류가 무었이었는지 한번 봅시다.

http://php.net/ChangeLog-5.php#5.6.40

Heap buffer overflow 문제가 주를 이루고 있네요. Mbstring 쪽 bug fix를 보면,

  • Fixed bug #77370 (Buffer overflow on mb regex functions - fetch_token).
  • Fixed bug #77371 (heap buffer overflow in mb regex functions - compile_string_node).
  • Fixed bug #77381 (heap buffer overflow in multibyte match_at).
  • Fixed bug #77382 (heap buffer overflow due to incorrect length in expand_case_fold_string).
  • Fixed bug #77385 (buffer overflow in fetch_token).
  • Fixed bug #77394 (Buffer overflow in multibyte case folding - unicode).
  • Fixed bug #77418 (Heap overflow in utf32be_mbc_to_code).

5.6.39 버전을 쓰시는 분들은 얼른 업데이트 하시길 빌어요. 아니죠, PHP7.1 이상으로 올리시는 것은 어떨까요?

  • PHP 7.3.1
  • PHP 7.2.14
  • PHP 7.1.26

PHP Internals

RFC: Typed Properties 2.0

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

위에 언급한 Nikita Popov가 언급하길, Typed Properties가 이제 쓸 준비가 되었고 master에 머지됐다고 합니다.

RFC에 올라온 기존 방식의 코드 예시를 보면, return type을 고정하기 위해선 함수 선언부에 return type을 지정해야 했고, IDE에서 자동 완성을 지원하기 위해 아래와 같이 property에 type hint를 넣어야 했습니다.

class User {
/** @var int $id */
private $id;
/** @var string $name */
private $name;

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

public function getId(): int {
return $this->id;
}
public function setId(int $id): void {
$this->id = $id;
}

...
}

이를 좀 더 간략하고 편하게 작성할 수 있게 됐습니다. default value, nullable 선언도 가능합니다. (var 선언도 아직 지원하는 군요??)

class Example {
// All types with the exception of "void" and "callable" are supported
public int $scalarType;
protected ClassName $classType;
private ?ClassName $nullableClassType;

// Types are also legal on static properties
public static iterable $staticProp;

// Types can also be used with the "var" notation
var bool $flag;

// Typed properties may have default values (more below)
public string $str = "foo";
public ?string $nullableStr = null;

// The type applies to all properties in one declaration
public float $x, $y;
// equivalent to:
public float $x;
public float $y;
}

RFC에 다양한 예시가 포함되어 있으니 확인하시길 바랍니다.

이 RFC에 대해 Rasmus Schultz란 사람이 PHP Typed Properties: Think Twice.란 글을 썼습니다. 이 분 좀 트위터를 뒤져보니 조금 보수적인 관점으로 최근의 PHP의 방향에 불만을 갖고 계신 것 같습니다. 이런 의견도 무시하지 말아야겠죠. 이 글에선 기존에 PHP를 사용하는 관점에서 typed property를 사용하면 어떤 단점이 있는지 알고 써야 한다는 입장인데, OOP를 지향하는 프로그래머들에게 많은 비판을 받기도 합니다. 저런 예시는 나쁜 코드일 뿐이라고 말이죠. 저도 딱히 Rasmus의 의견에는 동조하기 어렵습니다.

또한 트위터에서 Bob Weinand 사람이 올린 글에서 소개된 재미있는 코드도 소개가 됐는데요.

<?php declare(strict_types = 1);

$a = new class { public ?string $v; };
$typedVariable = &$a->v;

$typedVariable = 'foo'; // Ok
$typedVariable = 123; // TypeError

이렇게 꼼수를 쓰면 PHP에서도 typed 변수를 쓸 수 있게 됩니다. 이 아이디어를 응용한 azjezz/typed란 라이브러리도 나왔다는 군요.

[PHP] FFI

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

지난 12월의 글에서 언급된 스펙인데요. 보안이나 안정성에 여전히 의문이 들지만 이 제안은 결국 통과됐다는 이야기입니다. 다음 메이저 업데이트에 포함될 가능성이 높습니다.

Tools, Symfony, Laravel, Yii, Async PHP

흥미로운 소식이 많습니다…만 필요하신 분들이 알아서 보시기를…

Security

보안 관련 소식입니다.

Exploit PHP Remotely – WAF Rule & Filter Bypass

https://www.secjuice.com/php-rce-bypass-filters-sanitization-waf/

PHP에서 필터, 입력값 검증, WAF(Web Application Firewall) 규칙을 우회하여 원격 코드 실행을 악용하는 방법을 보여줍니다. 저자는 CloudFlare를 예시로 들었는데, 특별히 문제가 있다기 보다는 쉽고 널리 알려졌기 때문이고, 다른 WAF도 마찬가지라고 합니다.

이 문서에서는 system 함수를 파라미터로 넘겨서 실행하는 악질적인 코드를 만들어 실행하는 과정을 보여줍니다.

<?php

if (preg_match('/system|exec|passthru/', $_GET['code'])) { // Filter
echo 'invalid syntax';
} else {
eval($_GET(['code']));
}

파라미터로 ...?code=system("cat /etc/passwd"); 이런 코드를 넘겨 실행할 수 있게 시도하고,
이걸 막는 코드(preg_match문 참고)를 추가하고,
이를 다시 우회하는 코드를 만들어가고 있습니다.

WAF 회피 기술에 대한 연재글에서 더 확인해보세요.

Find bypass disable_functions automatically

https://x-c3ll.github.io//posts/find-bypass-disable_functions/

취약점이 발견된 함수 실행으로 OS 명령어를 실행하기 위해, disable_functions를 우회할 수 있는 함수를 자동으로 찾는 방법을 보여줍니다. (스스로 이건 좀 순진한 접근 방식이라고는 합니다만)

찾는 방법은 이렇습니다.

  • 모든 PHP 함수의 파라미터를 추출합니다(PHP.net에 공개되지 않은 것도 있으므로)
  • 올바른 파라미터로 함수를 실행하고 trace합니다
  • trace에서 execve와 같은 잠재적으로 위험한 함수를 찾습니다

guardrailsio/awesome-php-security

https://github.com/guardrailsio/awesome-php-security

PHP 보안에 관련한 자료를 모아둔 awesome 시리즈입니다.

ollyxar/php-malware-detector

정규식으로 문제가 되는 구문을 간단히 찾아볼 수 있게 구현한 라이브러리입니다.

public static $suspicious = [
'non-printable' => '/(function|return|base64_decode).{,256}[^\x09-\x0d\x20-\x7E]{3}/',
'evaluation' => '/\b(eval|create_function|system|assert|passthru|proc_open|(pcntl_|shell_)?exec|call_user_func(_array)?)\b\s*(\/\*(.*)\*\/)?\s*\(/',
'networking' => '/\b(fputs|file_get_contents|fsockopen|curl_exec|curl_multi_exec)\b\s*\(/',
'double-variable' => '/\${?\$[0-9a-zA-z]+/',
'var-as-function' => '/\$_(GET|POST|COOKIE|REQUEST|SERVER)\s*\[[^\]]+\]\s*\(/',
'callback' => '/\b(eval|assert|passthru|exec|include|system|pcntl_exec|shell_exec|base64_decode|`|array_map|ob_start|call_user_func(_array)?)\s*\(\s*(base64_decode|php:\/\/input|str_rot13|gz(inflate|uncompress)|getenv|pack|\\\\?\$_(GET|REQUEST|POST|COOKIE|SERVER))/',
'callback-2' => '/\b(array_(diff|intersect)_u(key|assoc)|array_(udiff|filter|reduce|walk(_recursive)?)|assert_options|uasort|uksort|usort|preg_replace_callback|iterator_apply)\s*\(\s*.*[,]+(base64_decode|php:\/\/input|str_rot13|gz(inflate|uncompress)|getenv|pack|\\\\?\$_(GET|REQUEST|POST|COOKIE|SERVER))/',
'preg_replace-e' => '/\b(preg_replace)\s*\(\s*((("|\').*(\/e)("|\'))|base64_decode|php:\/\/input|str_rot13|gz(inflate|uncompress)|getenv|pack|\\\\?\$_(GET|REQUEST|POST|COOKIE|SERVER))/',
'obfuscation' => '/((\\\\x[0-9a-zA-z]{2,3}){3})|((chr\([\d]+\)\s?\.\s?){5})|((\$|"|\')[0-9a-zA-z]+(\'|")?+\s?\.\s?){4}|(\bexplode\b\s*\(\s*\bchr\b\()/',
'php-params' => '/ini_(get|set|restore)\s*\(\s*[\'"](safe_mode|open_basedir|disable_(function|classe)s|safe_mode_exec_dir|safe_mode_include_dir|register_globals|allow_url_include)/',
'include-images' => '/include\s*(\(|"|\')\s*[^\.]+\.(png|jpg|gif|bmp|ico)/',
'register-function' => '/register_[a-z]+_function\s*\(\s*[\'"]\s*(eval|assert|passthru|exec|include|system|shell_exec|`)/',
'extraction' => '/\bextract\b\s*\(\s*\$_(GET|REQUEST|POST|COOKIE|SERVER|)/'
];

기타 읽을 만한 글

New in PHP 7.4

https://stitcher.io/blog/new-in-php-74

PHP 7.4에 새로 추가된 기능을 간단히 잘 정리해두었습니다.

Tips to speed up PHPUnit tests

https://laravel-news.com/tips-to-speed-up-phpunit-tests

Unit Test를 빠르게 실행할 수 있는 여러가지 TIP이 있습니다.

  • ParaTest라는 병렬 실행 라이브러리 사용
  • 실패한 테스트만 다시 돌리기
  • 느린 테스트를 Group으로 지정하고 따로 돌리기
  • 필터하기
  • Password hash rounds를 bcrypt가 허용하는 최소 횟수인 4까지 줄여놓기
  • In-memory database 사용
  • Xdebug 비활성화 하기
  • 가장 좋은 것은, 느린 테스트를 고치기!

Tips on how to get Faster Code Coverage

https://thephp.cc/news/2019/01/faster-code-coverage

더 빠르게 코드 커버리지를 구하는 방법

Writing serverless Hello World in PHP on AWS Lambda using serverless.com.

https://akrabat.com/serverless-php-on-aws-lambda/

Sergey Zhuk: How to speedup code reviews

더 빠르게 코드리뷰를 하는 방법입니다. PR을 올리는 방법이 중요하다는데요.

아래의 카테고리로 PR할 것을 구분해서 보내고

  • Features - functional changes.
  • Structure refactoring - changes in classes, interfaces, moving methods between classes.
  • Simple refactoring - may be done by your IDE, e.g. extracting variables/methods/constants, simplifying conditionals).
  • Renaming and moving classes - reorganizing namespaces.
  • Removing unused (dead) code.
  • Code style fixes - e.g. using autowiring, removing redundant doc-blocks.
  • Formatting fixes.

각 카테고리 별로 코드리뷰 전략을 다르게 가져갑니다.

  • Feature changes: fulfillment of business requirements and design.
  • Structure refactoring: backward compatibility and design improvements.
  • Simple refactoring: readability improvements. Because these changes are mostly may be done by IDE.
  • Renaming/removing classes: whether the namespaces structure has become better.
  • Removing unused code: backward compatibility.
  • Code style fixes: in most cases instant merge.
  • Formatting fixes in most cases instant merge.

PR을 보내기 전에 명심할 것은,

  • 당신의 코드를 읽기 좋게 최적화 하십시오. 코드는 쓰이는 것보다 더 많이 읽힙니다
  • 변경 사항의 컨텍스트 정보를 제공하기 위한 설명을 적어야 합니다
  • Request 보내기 전에 당신의 코드를 리뷰하세요. 당신의 코드가 아닌 것처럼 리뷰하세요. 간혹 당신이 놓친 걸 찾을 수 있을 겁니다. 이 과정이 PR 거절/수정 주기를 줄여줄 것입니다.

Nikola Poša: Some naming antipatterns

https://blog.nikolaposa.in.rs/2019/01/06/better-naming-convention/

네이밍 안티 패턴을 다룹니다.

tips on naming이란 글도 함께 읽어보세요.

Marcel Pociot: Semantic versioning explained

https://marcelpociot.de/blog/semantic-versioning-explained

시맨틱 버저닝을 설명합니다.

Marcel Pociot: Polishing your code

https://marcelpociot.de/blog/polish-your-code

여러분의 코드를 갈고 닦으세요!

John Mackenzie: My Modern PHP Development Setup

https://johnmackenzie.co.uk/post/my-modern-php-development-setup/

저자의 Modern PHP 개발 환경 설정이라고 합니다.

  • Use a Makefile and make good use of it
  • Run everything in Docker
  • Use CS Fixer To Adhere To Coding Standards
  • Use PHP Stan
  • Syncronise your team IDE’s with .editorconfig

phpapprentice.com

https://phpapprentice.com/

이제 막 PHP를 배우는 초심자를 위한 온라인 도서입니다. 마스코트가 아기 코끼리인가 봅니다.

리팩토링: 코드 품질을 개선하는 객체지향 사고법

리팩토링 표지 이미지

프로그램의 가치를 높이는 코드 정리 기술

많은 분들이 추천해서 관심이 가던 차에 가볍게 읽으려고 샀던 것 같다. 처음에는 분명 엄청 만족스럽게 읽기 시작했는데, 후반부에는 조금 띄엄띄엄 적당히 읽었다. 왜 리팩토링을 해야하는지, 왜 이렇게 바꿔야 하는지, 어떻게 안정감을 얻을 수 있는지 상세하고 꼼꼼하게 소개한다.

그런데 카탈로그식 구성이라서 중후반부터는 한장 한장 모든 것이 의미있다기 보다는 - 굳이 없다고도 할 수 없지만 - 왜 이런 선택을 하는지 가이드를 받을 수 있는 수준이라고 보면 되겠다. 계속 반복되는 패턴이라 (실무에서 리팩토링이 익숙하게 될 것인가와는 별개로) 책의 전개가 뻔히 보이기 때문에 후반부는 크게 영양가가 없었다는 느낌이다. 다시 읽어보면 다르려나?

이를 토대로 실무에서 다루는 코드를 리팩토링 하는 스터디를 한다면 매우 좋을 것 같다. 실제로 이 책을 읽은 후에 리팩토링을 더 조심스레 하게 되기도 했던 것 같고, 후련한 커밋도 몇개 날렸더랬다.

책의 구성은 정말 옛날 책이란 걸 느끼게 해주고, 코드 들여쓰기나 띄어쓰기가 잘못된 부분도 있고, 어색한 번역도 보였던 것 같다.

저자는 리팩토링 방법을 카탈로그로 만들어 이름을 붙여놓고 책 곳곳에서 참조하고 있다. 일단 번역을 한번 해놓은 단어라서 눈에 잘 들어오지도 않고, 실무에서 그런 용어를 쓸 일도 없기 때문에 적당히 걸러 봐야 한다. 책을 읽는 방법을 추천하라면, 앞부분은 주의깊게 읽고 카탈로그는 도입부를 읽고 바로 예제부터 보라고 하겠다. 예제가 오히려 설명보다 읽기가 수훨하다.

최고의 팀은 무엇이 다른가

최고의 팀은 무엇이 다른가 표지 이미지

다른 책은 전부 물에 던져 버려라!

왜 하필 물에 던져야 하는지는 잘 모르겠다만, 최고의 팀이 되는 비결을 비교적 간단하게 전파한다.

CTO님이 추천했으니 열어나 봤지. (4차 산업혁명이란 말을 표지에 꺼내 놓는 류의 책처럼) 제목만 보면 질려서 읽기 싫은 책이었는데, 서점에서 한번 훑어보고 바로 들고 왔다(물론 샀다). 예쁘고(색깔이 좀 들어갔다는 의미) 가볍고 여백도 충분해서 읽기 좋았기도 하고.

책에서 말하는 최고의 팀이 되는 비결은,

  • 안정감을 느끼는 것
  • 서로 이어져 있다는 신호를 쏘는 것
  • 아날로그 소통법
  • 공간의 힘
  • 내 부족한 것을 보여주는 것
  • 협동의 근육 만들기
  • 질문을 잘하는 문화
  • 공동의 이정표
  • 중요한 가치에 이름 붙이기
  • 등등

정확히 저런 소제목은 아니고, 대충 목차보고 골라본 것이다. 이 목록만 보면 아주 진절머리가 날 지경인데, 이게 다 대대로 내려오는 꼰대들이 ‘꼭 필요하다’고 주장하는 것들이다. 초반에 소개되는 안정감을 느끼는 것 등은 비교적 최근에 구글 문화를 통해 좀 널리 퍼지긴 했으나, 다른 모든 항목은 이미 어지간한 중견회사라면 인사팀(요즘은 피플팀이라고 하던가)이나 대표가 입이 침이 마르도록 강조하는 것들이다. 작가는 저런 행동강령이 어색하지만 생활화되면 효과를 발휘한다는 이야기를 곳곳에서 하고 있다.

그런데 이 책이 좋은 점은 저런 내용을 별 거부감 없이 받아들이게 해준다는 점이다. 실제 사례를 중심으로 관찰 결과를 소개할 뿐이지 훈계하지 않는다는 점이 새로웠던 것 같다.

최고의 팀이 되는 방법을 우리는 이미 알고 있다고 봐야 하지 않으려나? 나는 그 사람들의 시도가 뭐가 그렇게 싫었는지 잘 생각해봐야겠다.

읽은 책

  • 01월 심플을 생각한다
  • 01월 SQL 코딩의 기술
  • 01월 그럼에도 불구하고 너무나 인간적인 - 포기
  • 01월 라디오헤드 - 멈춤
  • 02월 Pro Git 2/E - 멈춤
  • 03월 대량살상 수학무기
  • 03월 부모공부
  • 03월 그들이 어떻게 해내는지 나는 안다
  • 04월 1달러 프로토타입
  • 04월 디자인 오브 디자인
  • 05월 소프트웨어 객체의 생애 주기
  • 05월 유혹하는 글쓰기
  • 05월 성공과 실패를 결정하는 1%의 네트워크 원리
  • 06월 직업의 지리학
  • 06월 불안한 엄마 무관심한 아빠
  • 07월 AWS 기반 서비리스 아키텍처
  • 08월 SQL 코딩의 기술 - 다시 읽음
  • 08월 AWS 기반 서비리스 아키텍처 - 다시 읽음
  • 08월 얼라이언스
  • 09월 GoF의 디자인 패턴
  • 10월 조엘온소프트웨어
  • 11월 대체 뭐가 문제야?
  • 11월 요리는 화학이다 - 멈춤
  • 12월 리팩토링
  • 12월 최고의 팀은 무엇이 다른가

작년 회고에, 올해는 책을 양적으로 좀 더 읽어보기로 다짐했지만, 특별히 더 읽었다고 하기 어렵다. 대신 무리해서 읽지 않고, 필요한 곳만 읽고 멈추거나 그만 읽어도 될 것은 중도 포기한 건 잘했다. 여러번 읽어보고 싶었던 것도 있는데 올해 이어서 읽으면 된다.

올해는 ‘조엘온소프트웨어’, ‘리팩토링’, ‘GoF의 디자인 패턴’ 같은 고전도 몇가지 읽었는데, 역시 난 새 건물, 새 책, 새 영화 같은 게 좋다. 분명 훌륭한 책들이긴 하나 ‘GoF의 디자인 패턴’은 워낙 설명이 깔끔해서 종종 열어 볼 것 같다.

생산성에 고민이 많아서 읽었던 ‘그들이 어떻게 해내는지 나는 안다’는 꽤 많은 도움이 됐고, 몇가지는 실천하기도 했다. 올해도 한번 훑어보고 또 몇가지 시도해보고 또 개선해 나가야지. 그래도 꾸준히 지켰던 것은, 한 주를 시작할 때와 하루를 시작할 때 계획을 하는 것, 매주 주단위 회고를 하는 것(온통 반성 뿐인…). 그리고 하는 일을 시간 단위로 적어 놓는 것이 습관이 됐다. 이걸로 통계를 뽑거나 하진 않는데, 한번에 하나의 일만 그리고 얼마나 집중했는지 그때 그때 자각하는 용도로 좋다.

‘불안한 엄마 무관심한 아빠’는 내용이 참 좋아서 여기저기 선물을 해주고 다녔는데, 이 책을 원한 사람들은 거의 ‘이미 잘 하고 있는 사람들’이어서 좀 아쉬웠다. 올해도 한번 더 읽어볼 생각이다.

회사

이렇게 1년을 꽉 채워 열심히 일해던 적이 있었을까 싶다. 특히 내 업무는 기능 개발과 연구보다는 운영 업무가 주를 이뤘고, 가끔은 엄청 스트레스도 받고 여기 계속 못다니겠다는 생각도 몇 번 했었는데, 지금은 팀원도 늘어 여유가 많이 생겼다. 이제 슬슬 안개가 걷히나 싶은 순간, 올 해는 매니징 업무를 병행하게 됐다. 전통적인 팀장 개념이 아닌 평등한 위치에서, 이미 잘 하고 있는 사람들을 회사의 의도에 맞게 더 잘하게 유도해야 한다. 좋은 말만 하고 듣고 싶은 나로서는 매우 큰 도전이 될 것 같다.

살면서 어디서든 열심히만 하면 중간은 했던 것 같은데, 이 회사에선 내가 제일 못하는구나 싶은 순간이 참 많았다. 세상엔 이상하거나 튀거나 게으르거나 화내는 사람이 은근 많아서, 화 안내고 열심히 꾸준히만 살면 중간정도 인정받는 것은 어렵지 않았다. 시니어 비중이 많고 기술 욕심이 많은 회사로 이직을 원했고 딱 원하는 회사에 왔지만, 다들 실력있고 내 눈을 의심할 정도로 열정적이고 호의적이다 - 그 분들이 회사에 만족하느냐와는 별개로. 이래서야 내가 적당히 살 수는 없겠다 싶다.

슬슬 누군가에게 도움이 되기 시작한 것 같고, 작년 한해 피해를 준 분들에게는 올해 조금씩 보답을 해나가야지.

공부

완전히 불만족스럽다.

양적으로 내가 모르는 분야를 넓혀 나가서 좋았지만 질적으로 깊이 파고든 게 하나도 없는 것 같다. 출퇴근 길 책을 읽는 시간이 더 늘긴 했으나 출퇴근에 빠지는 시간과 에너지 때문에 오랜 시간 집중할 시간을 확보를 못했다.

일을 회사 밖으로 가져오기 싫어서 업무 시간 외에는 가능한 업무와 직접적으로 연관이 있는 공부를 피했는데, 이제는 생각이 조금 바뀌었다. 실무에 피드백을 줄 수 있어야 확실히 내것이 되는 것 같다. 보람도 생길 것 같고. 그 균형을 잡기가 참 힘들 것으로 보이는데, ‘단순 업무는 집으로 가져오지 않는다’ 정도면 되지 않으려나? 회사에선 우선 순위에서 밀렸느나 내가 재밌게 할 수 있는 것 위주로 시작해보려고 한다.

블로그 글은 총 26편. PHP Annotated Monthly를 소개하기 시작한 것이 있어 그나마 다행이었고, 시니어들이 읽어볼 만한 글이 거의 없었다. 올해 5편 정도는 난이도가 있는 글을 써보고 싶다.

가족

먼 회사로 가서 같이 식사하는 시간도 확연히 줄고, 이제는 뭔가 하다 말고 갑자기 노트북을 여는 모습이 익숙한 풍경이 됐다. 아내는 항상 배려해주지만 참 미안하다. 제주도 다녀올 때 마음의 짐을 조금이나마 덜었는데, 올해도 뭔가 준비를 해야하지 않으려나? 내가 올해는 맥북 프로를 새로 살 예정인데, 옷 만드는 게 취미인 아내의 장비도 생각해줘야지. 결혼 잘했다고 여러번 느꼈다.

어디가서 아이를 낳으라고 추천을 하지는 않지만, 아이에게 받는 행복은 말로 다 표현할 수가 없다. 아이가 기뻐하는 모습을 보는 것은 무척이나 기쁘다. 질리지도 않고. 하연이는 2018년도 어디서도 들은 적 없는 훌륭한 6살 어린이였고 대단한 성과를 냈다. 어마어마한 성장이다. 가을/겨울 시즌에는 내 체력이 많이 떨어져 신나게 놓아주지 못했다. 멀어졌지만 자율 출근인 회사로 옮기면서 아침에 유치원 차 태워 보낸 시간은 좋았지만 저녁 시간을 오래 보내지 못하는 것이 계속 마음에 걸린다.

올해는 이사를 간다. 더 좋고 넓은 집으로 갈 것이다. 여기보다 더 안 좋은 아파트는 만나기 쉽지 않으니. 여기보다 싼 아파트도 없으니 드디어 대출의 늪으로. 서울에 살면서 부모님 덕에, 무지개아파트 덕에 잘 버텼다.

아버지의 수술이 있었고 잘 끝났다. 연세가 있어서 여전히 여기저기 고장이 많이 나셨는데, 가끔은 가서 당구라도 치고 치킨 내기라도 해야겠다.

건강

피곤이 쌓여 술이 별로 안 땡길 정도. 운동은 거의 못한다. 삶에서 운동의 비중을 높이기로.

요리

새로운 메뉴를 거의 시도하지 못한 기분이다.

만드는 빵의 종류도 그대로. 19번의 매번 그런(?) 빵을 만들었다. 첨가물이 없는 독일 밀가루만 쓰고 있는데, 단백질 함량도 모르겠고, 기존 밀가루는 레시피에서 물을 뺐다면, 이건 더해야 할 정도로 뻑뻑하다.

아내가 스팀 나오는 오븐 뽐뿌를 넣어서 살짝 흔들렸지만, 나중에 이사나 가면 고려해봐야겠다. 신혼때 산 동양매직 렌지겸용 오븐은 무쇠 냄비를 감당하기 힘들었는지 요즘 조금 덜덜 거린다. 조금만 더 버텨다오.

오랜만에 노트북을 열고 PHP CLI를 실행하는데 아래와 같은 오류 문구가 보이는 겁니다.

dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.62.dylib
Referenced from: /usr/local/Cellar/php@7.0/7.0.31/bin/php
Reason: image not found
[1] 72024 abort /usr/local/Cellar/php@7.0/7.0.31/bin/php

여기를 읽어보면서 PHP를 다시 설치하려고 했죠.

❯ brew reinstall php@7.0
==> Reinstalling php@7.0
Error: An exception occurred within a child process:
FormulaUnavailableError: No available formula with the name "/usr/local/opt/php@7.0/.brew/php@7.0.rb"

음? 이상한데…하면서 지우고 다시 설치하려고 했습니다.

❯ brew install php@7.0

이런 문구가 보이는 겁니다.

php@7.0 was deleted from homebrew/core in commit 7e111a877:
php@7.0 removal due to EOL (https://github.com/Homebrew/homebrew-core/issues/34739)

PHP7.0은 EOL(End Of Life)이 됐으니 Homebrew에서 제거했다는 이야기.

Supported Versions 문서에 의하면,
PHP5.6은 12월 31일, PHP7.0은 12월 3일까지입니다.

얼마 전(12.06)에 PHP7.3 나왔다고 설레발치면서 7.0 지원 종료된 건 못봤나 봅니다. 회사에선 (다른 분들이) PHP 버전업을 준비하고는 있지만, 아직 상용 시스템은 7.0을 사용하는 관계로 노트북의 PHP 버전도 이와 동일하게 구성해놓고 있습니다.

Homebrew에서 PHP@7.0 설치하기

homebrew/core에서 공식 지원하진 않기 때문에 우회해야 합니다.

Homebrew document에 보시면, How to Create and Maintain a Tap에서 비공식 Tap을 관리하는 방법을 안내하고 있습니다. Tap이란 건 online Git repository일 뿐이고, Github에서 Homebrew/homebrew-core 저장소를 참고해서 설치 파일을 올려두면 된다는 것.

그래서 저도 core를 fork해서 7.0이 삭제되기 전 시점으로 돌려놓으면 원하는 rb 파일을 얻을 수 있겠다 생각을 했는데…

reddit에서 PHP 7.0 removed from Homebrew란 글을 읽고 있자니 누군가 먼저 이걸 해결해 놨더군요.

https://github.com/eXolnet/homebrew-deprecated

여기 들어가보시면 7.0.33 버전의 Formula를 얻을 수 있습니다. 곧 같은 배를 타게 될, 5.6 버전도 함께요.

설치해봅니다.

저장소 이름의 homebrew-는 homebrew가 인식하기 위한 prefix니까 떼어버리고, 아래처럼 user/저장소/formula 순으로 지정하면 됩니다.

❯ brew install eXolnet/deprecated/php@7.0

그럼 XDebug도 다시 설치해야 하겠죠? 여기부터는 XDebug를 어떻게 설치했는냐에 따라 다르겠습니다만…

❯ pecl install xdebug

저는 XDebug 설정 파일을 /usr/local/etc/php/7.0/conf.d/ext-xdebug.ini 위치에 따로 분리했는데요. 여기서 모듈 위치를 변경해줬습니다.

zend_extension=/usr/local/Cellar/php@7.0/7.0.33/pecl/20151012/xdebug.so

확인하기.

❯ php -v
PHP 7.0.33 (cli) (built: Dec 14 2018 16:20:36) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2017 Zend Technologies
with Zend OPcache v7.0.33, Copyright (c) 1999-2017, by Zend Technologies
with Xdebug v2.6.1, Copyright (c) 2002-2018, by Derick Rethans

Php_annotated_monthly 이미지

JetBrains에서 (거의) 매월 발행하는 PHP Annotated Monthly에서 적당히 제 입맛에 맞는 것만 추려 소개합니다. 언제나 그렇듯이 성급한 오역 죄송합니다.

이번달은 내용이 많아 쪼개서 올리고 있습니다. 세번째는 기타 읽을 만한 글!


Some nice posts on why it’s better to use classes instead of arrays

연관 배열 대신 클래스를 사용하면 좋은 이유를 설명한 세가지 글이 소개됐습니다.

PHP code static analysis based on the example of PHPStan, Phan, and Psalm

https://badootech.badoo.com/php-code-static-analysis-based-on-the-example-of-phpstan-phan-and-psalm-a20654c4011d

글쓴이의 회사에 새로 들어온 사람이 전체 코드에서 strict_types + scalar type hints를 의무화하자 버그 수가 엄청 줄었다는 말을 듣고 논의를 했는데, PHP는 컴파일 과정이 없는데 테스트 커버리지도 충분하지 않다면 문제가 더 발생할 것이라고 우려했다고 합니다. 그래서 정적 분석기를 도입한 이야기를 시작합니다. 세가지를 소개하는데요. 현재 시점의 watch/star/fork도 같이 찾아봤습니다.

  1. PHPStan
  • watch 171, star 5,509, fork 377
  1. Psalm
  • watch 46, star 1,212, fork 68
  1. Phan
  • watch 128, star 3,893, fork 249

셋 모두 사용법은 비슷하고, 활발하게 개발되고 있고, maintainer들이 24시간에 응답하고, 며칠 이내로 버그가 수정되는 편이라고 하는데요. 이 maintainer들 끼리도 서로에게 영향을 미치고 버그 리포팅이나 PR도 보낸다고 합니다.

그리고 각 analyser를 어떻게 사용하는지, 자기 회사에서 어떻게 적용했는지 소개합니다. 중간중간 PHPStorm과의 궁합도 설명해주고 있어요.

그래서 셋 중에 어떤 게 뽑혔을지 궁금하시죠? 다 쓴답니다.

analyser reports

14 Tips to Write PHP Code that is Hard to Maintain and Upgrade

https://www.tomasvotruba.cz/blog/2018/11/26/14-tips-to-write-php-code-that-is-hard-to-maintain-and-upgrade/

유지보수를 어렵게 코딩하는 방법 14가지입니다. 따라하지 마세요.

  1. Never use final
  2. Use protected instead of private
  3. Use Non-String Method Names
  4. Don’t Always use PSR-4
  5. Use Your Own Autoloader
  6. Hide Your Dependencies in Constructor
  7. Put Different Kinds of Objects to One Directory
  8. Use Annotations to Define Magic Methods
  9. Use Traits with Annotations to Define Magic Methods
  10. Don’t use a Different Naming to separate Interface, Trait from Class
  11. Use as Short Naming as Possible
  12. Don’t use a Different Naming to separate Abstract classes
  13. Use Fluent API with Different Return Values
  14. Use Fluent API with Different Return Values (the next level)

Sebastian De Deyne: Readability is relative

https://sebastiandedeyne.com/readability-is-relative/

가독성은 상대적이라는 글. 어떤 게 더 좋은 API인지를 묻는 글입니다.

friendsofphp.org

https://friendsofphp.org/

— The map of all PHP meetups around the world

전세계의 PHP 모임을 모아 보여주는 사이트입니다. 관련 블로그를 읽어보세요.

Php_annotated_monthly 이미지

JetBrains에서 (거의) 매월 발행하는 PHP Annotated Monthly에서 적당히 제 입맛에 맞는 것만 추려 소개합니다. 언제나 그렇듯이 성급한 오역 죄송합니다.

이번달은 내용이 많아 쪼개서 올리고 있습니다. 두번째는 PHP Internals!


[RFC] Preloading

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

— The proposal is now accepted! It means that in 7.4, we’ll be able to preload any files into opcache. Any functions or classes declared in preloaded files will be available to all subsequent requests as if they were internal elements like strlen() or Exception.

There is an ongoing discussion about supporting preloading in Composer. Dmitry Stogov also proposed an ability to disable opcache caching per script, using declare(cache=0) at the start of PHP files.

다음 버전인 PHP7.4부터 preloading을 지원합니다. opcache.preload에 PHP 파일을 지정하고, 여기서 preloading할 파일을 지정할 수 있습니다. 이렇게 컴파일된 코드는 공유 메모리에 상주하게 되고, preload된 파일이 후에 변경된다 하더라도 서버 재시작 되기 전까지는 동일한 결과를 돌려주게 됩니다.

Composer를 지원할지에 대해선 여전히 논의중이라고 하는군요.

위 RFC 링크에 들어가면 더 자세한 설명이 들어있습니다. 여기에선 Zend 라이브러리를 통째로 올리는 예제가 있는데요. Laraval처럼 무거운 프레임웍도 미리 올려두고 쓰면 성능이 비약적으로 좋아지지 않을까 예상됩니다.

몇가지 주의할 점도 언급하고 있는데요.

And also, this approach will not be compatible with servers that host multiple applications, or multiple versions of applications - that would have different implementations for certain classes with the same name - if such classes are preloaded from the codebase of one app, it will conflict with loading the different class implementation from the other app(s).

동일한 서버에서 여러 어플리케이션을 사용할 때, 동일한 클래스명을 사용하면 preload된 클래스를 사용하게 되므로 문제가 발생할 수 있다는 것입니다.

[RFC] Spread Operator in Array

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

— The document proposes supporting the … operator in arrays. For example,

$parts = ['apple', 'pear'];
$fruits = ['banana', 'orange', ...$parts, 'watermelon'];
// ['banana', 'orange', 'apple', 'pear', 'watermelon'];

Spread Operator도 나올 때가 됐지요! 기대됩니다.

[RFC] FFI – Foreign Function Interface

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

— Dmitry Stogov’s experiment is now official RFC. It allows calling C functions and using C data types from pure PHP. See for example using TensorFlow with FFI.

Dmitry란 이름을 가진 사람은 다 개발을 잘하는 것 같습니다(농담입니다…).

PHP 상에서 C의 함수와 데이터 타입에 접근할 수 있는 기능입니다. 링크를 타고 들어가면 여러가지 예제를 볼 수 있습니다. 그중 shared library에서 함수를 가져와 실행하는 예제를 보시죠.

<?php
// create FFI object, loading libc and exporting function printf()
$ffi = FFI::cdef(
"int printf(const char *format, ...);", // this is regular C declaration
"libc.so.6");
// call C printf()
$ffi->printf("Hello %s!\n", "world");

FFI 역시 위에 언급한 preloading과 잘 연계되도록 설계가 되었고, default로 preload 모드로 동작합니다.

과연 이걸 직접 사용할 날이 오겠냐마는, 이를 활용한 라이브러리가 여럿 등장하지 않을까 기대됩니다. 어디에 써먹냐고요? 그건 제가 알 바가…

[RFC] Covariant Returns and Contravariant Parameters

https://wiki.php.net/rfc/covariant-returns-and-contravariant-parameters

공변 리턴과 반변 파라미터도 소개가 됐습니다.

interface X {
function m(Y $z): X;
}
interface Y extends X {
// not permitted but type-safe
function m(X $z): Y;
}

Super type인 X의 메소드에서 파라미터로 Y를 받게 되어있다면 sub type인 Y의 메소드에서도 Y를 받게 되어 있습니다. Y가 X의 sub type이라 해도 Y대신 더 추상적 개념인 X를 받을 수 없는 상황이죠. 반대로 리턴 타입의 경우 X를 리턴하기로 했으면 더 구체적인 클래스인 Y를 리턴할 수 없었습니다.

이 RFC는 parameter type은 super type이 대리할 수 있고, return type은 sub type이 대리할 수 있게 허용하는 것입니다.

0%