수색…
클로저의 기본 사용법
클로저 는 PHP와 같은 익명의 함수입니다. 이름이없는 함수. 그것이 기술적으로 올바르지 않더라도 클로저의 동작은 몇 가지 추가 기능이있는 함수와 동일하게 유지됩니다.
클로저는 이름없이 함수를 선언함으로써 생성되는 Closure 클래스의 객체 일뿐입니다. 예 :
<?php
$myClosure = function() {
echo 'Hello world!';
};
$myClosure(); // Shows "Hello world!"
$myClosure
는 Closure
의 인스턴스이므로, $myClosure
를 사용하여 수행 할 수있는 작업을 인식 할 수 있습니다 (cf. http://fr2.php.net/manual/en/class.closure.php )
당신이 제공해야 할 때 당신은 폐쇄를 필요 고전적인 경우는 callable
인스턴스를 들어, 기능 에 usort .
다음은 배열이 각 사람의 형제 수에 따라 정렬되는 예제입니다.
<?php
$data = [
[
'name' => 'John',
'nbrOfSiblings' => 2,
],
[
'name' => 'Stan',
'nbrOfSiblings' => 1,
],
[
'name' => 'Tom',
'nbrOfSiblings' => 3,
]
];
usort($data, function($e1, $e2) {
if ($e1['nbrOfSiblings'] == $e2['nbrOfSiblings']) {
return 0;
}
return $e1['nbrOfSiblings'] < $e2['nbrOfSiblings'] ? -1 : 1;
});
var_dump($data); // Will show Stan first, then John and finally Tom
외부 변수 사용하기
클로저 내부에서 특수 키워드 use 를 사용하여 외부 변수를 사용할 수 있습니다 . 예를 들면 :
<?php
$quantity = 1;
$calculator = function($number) use($quantity) {
return $number + $quantity;
};
var_dump($calculator(2)); // Shows "3"
"동적"클로저를 작성하면 더 나아갈 수 있습니다. 추가하려는 수량에 따라 특정 계산기를 반환하는 함수를 만들 수 있습니다. 예 :
<?php
function createCalculator($quantity) {
return function($number) use($quantity) {
return $number + $quantity;
};
}
$calculator1 = createCalculator(1);
$calculator2 = createCalculator(2);
var_dump($calculator1(2)); // Shows "3"
var_dump($calculator2(2)); // Shows "4"
기본 클로저 바인딩
이전에 볼 수 있듯이 클로저는 Closure 클래스의 인스턴스 일 뿐이며 여러 메서드가 호출 될 수 있습니다. 그 중 하나는 bindTo
이며, 주어진 클로저는 주어진 객체에 바인딩 된 새 객체를 반환합니다. 예 :
<?php
$myClosure = function() {
echo $this->property;
};
class MyClass
{
public $property;
public function __construct($propertyValue)
{
$this->property = $propertyValue;
}
}
$myInstance = new MyClass('Hello world!');
$myBoundClosure = $myClosure->bindTo($myInstance);
$myBoundClosure(); // Shows "Hello world!"
클로저 바인딩 및 범위
이 예제를 살펴 보겠습니다.
<?php
$myClosure = function() {
echo $this->property;
};
class MyClass
{
public $property;
public function __construct($propertyValue)
{
$this->property = $propertyValue;
}
}
$myInstance = new MyClass('Hello world!');
$myBoundClosure = $myClosure->bindTo($myInstance);
$myBoundClosure(); // Shows "Hello world!"
property
가시성을 protected
또는 private
로 변경하십시오. 이 속성에 대한 액세스 권한이 없다는 것을 나타내는 치명적인 오류가 발생합니다. 실제로 클로저가 객체에 묶여 있다고해도 클로저가 호출되는 범위는 해당 액세스를 필요로하는 범위가 아닙니다. 이것이 bindTo
의 두번째 인수가되는 bindTo
.
속성이 private
경우 액세스 할 수있는 유일한 방법은 속성을 허용하는 범위에서 액세스하는 것입니다. 클래스의 범위. 바로 앞의 코드 예제에서 범위는 지정되지 않았으므로 클로저가 만들어 졌던 곳과 동일한 범위에서 클로저가 호출되었습니다. 그걸 바꾸자.
<?php
$myClosure = function() {
echo $this->property;
};
class MyClass
{
private $property; // $property is now private
public function __construct($propertyValue)
{
$this->property = $propertyValue;
}
}
$myInstance = new MyClass('Hello world!');
$myBoundClosure = $myClosure->bindTo($myInstance, MyClass::class);
$myBoundClosure(); // Shows "Hello world!"
방금 말했듯이이 두 번째 매개 변수가 사용되지 않으면 클로저는 클로저가 만들어진 위치와 동일한 컨텍스트에서 호출됩니다. 예를 들어, 객체 컨텍스트에서 호출되는 메소드의 클래스 내부에서 생성 된 클로저는 메소드의 범위와 동일합니다.
<?php
class MyClass
{
private $property;
public function __construct($propertyValue)
{
$this->property = $propertyValue;
}
public function getDisplayer()
{
return function() {
echo $this->property;
};
}
}
$myInstance = new MyClass('Hello world!');
$displayer = $myInstance->getDisplayer();
$displayer(); // Shows "Hello world!"
하나의 호출에 대한 클로저 바인딩
PHP7 이후로 call
메소드 덕분에 하나의 호출에 대해서만 클로저를 바인딩 할 수 있습니다. 예를 들면 :
<?php
class MyClass
{
private $property;
public function __construct($propertyValue)
{
$this->property = $propertyValue;
}
}
$myClosure = function() {
echo $this->property;
};
$myInstance = new MyClass('Hello world!');
$myClosure->call($myInstance); // Shows "Hello world!"
bindTo
메서드와는 달리 걱정할 영역이 없습니다. 이 호출에 사용 된 범위는 $myInstance
의 속성에 액세스하거나 호출 할 때 사용되는 범위와 동일합니다.
관찰자 패턴 구현을 위해 클로저 사용
일반적으로 관찰자는 관찰 된 객체에 대한 작업이 발생할 때 특정 메소드가 호출되는 클래스입니다. 특정 상황에서 클로저는 관찰자 디자인 패턴을 구현하기에 충분할 수 있습니다.
이러한 구현의 자세한 예가 있습니다. 관찰자의 속성이 변경 될 때 관찰자에게 알리는 것이 목적 인 클래스를 먼저 선언합시다.
<?php
class ObservedStuff implements SplSubject
{
protected $property;
protected $observers = [];
public function attach(SplObserver $observer)
{
$this->observers[] = $observer;
return $this;
}
public function detach(SplObserver $observer)
{
if (false !== $key = array_search($observer, $this->observers, true)) {
unset($this->observers[$key]);
}
}
public function notify()
{
foreach ($this->observers as $observer) {
$observer->update($this);
}
}
public function getProperty()
{
return $this->property;
}
public function setProperty($property)
{
$this->property = $property;
$this->notify();
}
}
그런 다음 다른 관찰자를 대표 할 클래스를 선언합시다.
<?php
class NamedObserver implements SplObserver
{
protected $name;
protected $closure;
public function __construct(Closure $closure, $name)
{
$this->closure = $closure->bindTo($this, $this);
$this->name = $name;
}
public function update(SplSubject $subject)
{
$closure = $this->closure;
$closure($subject);
}
}
마침내 이것을 시험해 봅시다.
<?php
$o = new ObservedStuff;
$observer1 = function(SplSubject $subject) {
echo $this->name, ' has been notified! New property value: ', $subject->getProperty(), "\n";
};
$observer2 = function(SplSubject $subject) {
echo $this->name, ' has been notified! New property value: ', $subject->getProperty(), "\n";
};
$o->attach(new NamedObserver($observer1, 'Observer1'))
->attach(new NamedObserver($observer2, 'Observer2'));
$o->setProperty('Hello world!');
// Shows:
// Observer1 has been notified! New property value: Hello world!
// Observer2 has been notified! New property value: Hello world!
이 예제는 관찰자가 동일한 성격을 공유하기 때문에 작동합니다 (둘 다 "명명 된 관찰자"임).