696 lines
26 KiB
PHP
696 lines
26 KiB
PHP
<?php
|
|
/*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* This software consists of voluntary contributions made by many individuals
|
|
* and is licensed under the MIT license. For more information, see
|
|
* <http://www.doctrine-project.org>.
|
|
*/
|
|
|
|
namespace Doctrine\Tests\Common\Proxy;
|
|
|
|
use Doctrine\Common\Proxy\ProxyGenerator;
|
|
use Doctrine\Common\Proxy\Proxy;
|
|
use Doctrine\Common\Proxy\Exception\UnexpectedValueException;
|
|
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
|
|
use PHPUnit_Framework_TestCase;
|
|
|
|
/**
|
|
* Test the generated proxies behavior. These tests make assumptions about the structure of LazyLoadableObject
|
|
*
|
|
* @author Marco Pivetta <ocramius@gmail.com>
|
|
*/
|
|
class ProxyLogicTest extends PHPUnit_Framework_TestCase
|
|
{
|
|
/**
|
|
* @var \PHPUnit_Framework_MockObject_MockObject
|
|
*/
|
|
protected $proxyLoader;
|
|
|
|
/**
|
|
* @var ClassMetadata
|
|
*/
|
|
protected $lazyLoadableObjectMetadata;
|
|
|
|
/**
|
|
* @var LazyLoadableObject|Proxy
|
|
*/
|
|
protected $lazyObject;
|
|
|
|
protected $identifier = array(
|
|
'publicIdentifierField' => 'publicIdentifierFieldValue',
|
|
'protectedIdentifierField' => 'protectedIdentifierFieldValue',
|
|
);
|
|
|
|
/**
|
|
* @var \PHPUnit_Framework_MockObject_MockObject|Callable
|
|
*/
|
|
protected $initializerCallbackMock;
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function setUp()
|
|
{
|
|
$this->proxyLoader = $loader = $this->getMock('stdClass', array('load'), array(), '', false);
|
|
$this->initializerCallbackMock = $this->getMock('stdClass', array('__invoke'));
|
|
$identifier = $this->identifier;
|
|
$this->lazyLoadableObjectMetadata = $metadata = new LazyLoadableObjectClassMetadata();
|
|
|
|
// emulating what should happen in a proxy factory
|
|
$cloner = function (LazyLoadableObject $proxy) use ($loader, $identifier, $metadata) {
|
|
/* @var $proxy LazyLoadableObject|Proxy */
|
|
if ($proxy->__isInitialized()) {
|
|
return;
|
|
}
|
|
|
|
$proxy->__setInitialized(true);
|
|
$proxy->__setInitializer(null);
|
|
$original = $loader->load($identifier);
|
|
|
|
if (null === $original) {
|
|
throw new UnexpectedValueException();
|
|
}
|
|
|
|
foreach ($metadata->getReflectionClass()->getProperties() as $reflProperty) {
|
|
$propertyName = $reflProperty->getName();
|
|
|
|
if ($metadata->hasField($propertyName) || $metadata->hasAssociation($propertyName)) {
|
|
$reflProperty->setAccessible(true);
|
|
$reflProperty->setValue($proxy, $reflProperty->getValue($original));
|
|
}
|
|
}
|
|
};
|
|
|
|
$proxyClassName = 'Doctrine\Tests\Common\ProxyProxy\__CG__\Doctrine\Tests\Common\Proxy\LazyLoadableObject';
|
|
|
|
// creating the proxy class
|
|
if (!class_exists($proxyClassName, false)) {
|
|
$proxyGenerator = new ProxyGenerator(__DIR__ . '/generated', __NAMESPACE__ . 'Proxy', true);
|
|
$proxyGenerator->generateProxyClass($metadata);
|
|
require_once $proxyGenerator->getProxyFileName($metadata->getName());
|
|
}
|
|
|
|
$this->lazyObject = new $proxyClassName($this->getClosure($this->initializerCallbackMock), $cloner);
|
|
|
|
// setting identifiers in the proxy via reflection
|
|
foreach ($metadata->getIdentifierFieldNames() as $idField) {
|
|
$prop = $metadata->getReflectionClass()->getProperty($idField);
|
|
$prop->setAccessible(true);
|
|
$prop->setValue($this->lazyObject, $identifier[$idField]);
|
|
}
|
|
|
|
$this->assertFalse($this->lazyObject->__isInitialized());
|
|
}
|
|
|
|
public function testFetchingPublicIdentifierDoesNotCauseLazyLoading()
|
|
{
|
|
$this->configureInitializerMock(0);
|
|
|
|
$this->assertSame('publicIdentifierFieldValue', $this->lazyObject->publicIdentifierField);
|
|
}
|
|
|
|
public function testFetchingIdentifiersViaPublicGetterDoesNotCauseLazyLoading()
|
|
{
|
|
$this->configureInitializerMock(0);
|
|
|
|
$this->assertSame('protectedIdentifierFieldValue', $this->lazyObject->getProtectedIdentifierField());
|
|
}
|
|
|
|
public function testCallingMethodCausesLazyLoading()
|
|
{
|
|
$this->configureInitializerMock(
|
|
1,
|
|
array($this->lazyObject, 'testInitializationTriggeringMethod', array()),
|
|
function (Proxy $proxy) {
|
|
$proxy->__setInitializer(null);
|
|
}
|
|
);
|
|
|
|
$this->lazyObject->testInitializationTriggeringMethod();
|
|
$this->lazyObject->testInitializationTriggeringMethod();
|
|
}
|
|
|
|
public function testFetchingPublicFieldsCausesLazyLoading()
|
|
{
|
|
$test = $this;
|
|
$this->configureInitializerMock(
|
|
1,
|
|
array($this->lazyObject, '__get', array('publicPersistentField')),
|
|
function () use ($test) {
|
|
$test->setProxyValue('publicPersistentField', 'loadedValue');
|
|
}
|
|
);
|
|
|
|
$this->assertSame('loadedValue', $this->lazyObject->publicPersistentField);
|
|
$this->assertSame('loadedValue', $this->lazyObject->publicPersistentField);
|
|
}
|
|
|
|
public function testFetchingPublicAssociationCausesLazyLoading()
|
|
{
|
|
$test = $this;
|
|
$this->configureInitializerMock(
|
|
1,
|
|
array($this->lazyObject, '__get', array('publicAssociation')),
|
|
function () use ($test) {
|
|
$test->setProxyValue('publicAssociation', 'loadedAssociation');
|
|
}
|
|
);
|
|
|
|
$this->assertSame('loadedAssociation', $this->lazyObject->publicAssociation);
|
|
$this->assertSame('loadedAssociation', $this->lazyObject->publicAssociation);
|
|
}
|
|
|
|
public function testFetchingProtectedAssociationViaPublicGetterCausesLazyLoading()
|
|
{
|
|
$this->configureInitializerMock(
|
|
1,
|
|
array($this->lazyObject, 'getProtectedAssociation', array()),
|
|
function (Proxy $proxy) {
|
|
$proxy->__setInitializer(null);
|
|
}
|
|
);
|
|
|
|
$this->assertSame('protectedAssociationValue', $this->lazyObject->getProtectedAssociation());
|
|
$this->assertSame('protectedAssociationValue', $this->lazyObject->getProtectedAssociation());
|
|
}
|
|
|
|
public function testLazyLoadingTriggeredOnlyAtFirstPublicPropertyRead()
|
|
{
|
|
$test = $this;
|
|
$this->configureInitializerMock(
|
|
1,
|
|
array($this->lazyObject, '__get', array('publicPersistentField')),
|
|
function () use ($test) {
|
|
$test->setProxyValue('publicPersistentField', 'loadedValue');
|
|
$test->setProxyValue('publicAssociation', 'publicAssociationValue');
|
|
}
|
|
);
|
|
|
|
$this->assertSame('loadedValue', $this->lazyObject->publicPersistentField);
|
|
$this->assertSame('publicAssociationValue', $this->lazyObject->publicAssociation);
|
|
}
|
|
|
|
public function testNoticeWhenReadingNonExistentPublicProperties()
|
|
{
|
|
$this->configureInitializerMock(0);
|
|
|
|
$class = get_class($this->lazyObject);
|
|
$this->setExpectedException(
|
|
'PHPUnit_Framework_Error_Notice',
|
|
'Undefined property: ' . $class . '::$non_existing_property'
|
|
);
|
|
|
|
$this->lazyObject->non_existing_property;
|
|
}
|
|
|
|
public function testFalseWhenCheckingNonExistentProperty()
|
|
{
|
|
$this->configureInitializerMock(0);
|
|
|
|
$this->assertFalse(isset($this->lazyObject->non_existing_property));
|
|
}
|
|
|
|
public function testNoErrorWhenSettingNonExistentProperty()
|
|
{
|
|
$this->configureInitializerMock(0);
|
|
|
|
$this->lazyObject->non_existing_property = 'now has a value';
|
|
$this->assertSame('now has a value', $this->lazyObject->non_existing_property);
|
|
}
|
|
|
|
public function testCloningCallsClonerWithClonedObject()
|
|
{
|
|
$lazyObject = $this->lazyObject;
|
|
$test = $this;
|
|
$cb = $this->getMock('stdClass', array('cb'));
|
|
$cb
|
|
->expects($this->once())
|
|
->method('cb')
|
|
->will($this->returnCallback(function (LazyLoadableObject $proxy) use ($lazyObject, $test) {
|
|
/* @var $proxy LazyLoadableObject|Proxy */
|
|
$test->assertNotSame($proxy, $lazyObject);
|
|
$proxy->__setInitializer(null);
|
|
$proxy->publicAssociation = 'clonedAssociation';
|
|
}));
|
|
|
|
$this->lazyObject->__setCloner($this->getClosure(array($cb, 'cb')));
|
|
|
|
$cloned = clone $this->lazyObject;
|
|
$this->assertSame('clonedAssociation', $cloned->publicAssociation);
|
|
$this->assertNotSame($cloned, $lazyObject, 'a clone of the lazy object is retrieved');
|
|
}
|
|
|
|
public function testFetchingTransientPropertiesWillNotTriggerLazyLoading()
|
|
{
|
|
$this->configureInitializerMock(0);
|
|
|
|
$this->assertSame(
|
|
'publicTransientFieldValue',
|
|
$this->lazyObject->publicTransientField,
|
|
'fetching public transient field won\'t trigger lazy loading'
|
|
);
|
|
$property = $this
|
|
->lazyLoadableObjectMetadata
|
|
->getReflectionClass()
|
|
->getProperty('protectedTransientField');
|
|
$property->setAccessible(true);
|
|
$this->assertSame(
|
|
'protectedTransientFieldValue',
|
|
$property->getValue($this->lazyObject),
|
|
'fetching protected transient field via reflection won\'t trigger lazy loading'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Provided to guarantee backwards compatibility
|
|
*/
|
|
public function testLoadProxyMethod()
|
|
{
|
|
$this->configureInitializerMock(2, array($this->lazyObject, '__load', array()));
|
|
|
|
$this->lazyObject->__load();
|
|
$this->lazyObject->__load();
|
|
}
|
|
|
|
public function testLoadingWithPersisterWillBeTriggeredOnlyOnce()
|
|
{
|
|
$this
|
|
->proxyLoader
|
|
->expects($this->once())
|
|
->method('load')
|
|
->with(
|
|
array(
|
|
'publicIdentifierField' => 'publicIdentifierFieldValue',
|
|
'protectedIdentifierField' => 'protectedIdentifierFieldValue',
|
|
),
|
|
$this->lazyObject
|
|
)
|
|
->will($this->returnCallback(function ($id, LazyLoadableObject $lazyObject) {
|
|
// setting a value to verify that the persister can actually set something in the object
|
|
$lazyObject->publicAssociation = $id['publicIdentifierField'] . '-test';
|
|
return true;
|
|
}));
|
|
$this->lazyObject->__setInitializer($this->getSuggestedInitializerImplementation());
|
|
|
|
$this->lazyObject->__load();
|
|
$this->lazyObject->__load();
|
|
$this->assertSame('publicIdentifierFieldValue-test', $this->lazyObject->publicAssociation);
|
|
}
|
|
|
|
public function testFailedLoadingWillThrowException()
|
|
{
|
|
$this->proxyLoader->expects($this->any())->method('load')->will($this->returnValue(null));
|
|
$this->setExpectedException('UnexpectedValueException');
|
|
$this->lazyObject->__setInitializer($this->getSuggestedInitializerImplementation());
|
|
|
|
$this->lazyObject->__load();
|
|
}
|
|
|
|
public function testCloningWithPersister()
|
|
{
|
|
$this->lazyObject->publicTransientField = 'should-not-change';
|
|
$this
|
|
->proxyLoader
|
|
->expects($this->exactly(2))
|
|
->method('load')
|
|
->with(array(
|
|
'publicIdentifierField' => 'publicIdentifierFieldValue',
|
|
'protectedIdentifierField' => 'protectedIdentifierFieldValue',
|
|
))
|
|
->will($this->returnCallback(function () {
|
|
$blueprint = new LazyLoadableObject();
|
|
$blueprint->publicPersistentField = 'checked-persistent-field';
|
|
$blueprint->publicAssociation = 'checked-association-field';
|
|
$blueprint->publicTransientField = 'checked-transient-field';
|
|
|
|
return $blueprint;
|
|
}));
|
|
|
|
$firstClone = clone $this->lazyObject;
|
|
$this->assertSame(
|
|
'checked-persistent-field',
|
|
$firstClone->publicPersistentField,
|
|
'Persistent fields are cloned correctly'
|
|
);
|
|
$this->assertSame(
|
|
'checked-association-field',
|
|
$firstClone->publicAssociation,
|
|
'Associations are cloned correctly'
|
|
);
|
|
$this->assertSame(
|
|
'should-not-change',
|
|
$firstClone->publicTransientField,
|
|
'Transient fields are not overwritten'
|
|
);
|
|
|
|
$secondClone = clone $this->lazyObject;
|
|
$this->assertSame(
|
|
'checked-persistent-field',
|
|
$secondClone->publicPersistentField,
|
|
'Persistent fields are cloned correctly'
|
|
);
|
|
$this->assertSame(
|
|
'checked-association-field',
|
|
$secondClone->publicAssociation,
|
|
'Associations are cloned correctly'
|
|
);
|
|
$this->assertSame(
|
|
'should-not-change',
|
|
$secondClone->publicTransientField,
|
|
'Transient fields are not overwritten'
|
|
);
|
|
|
|
// those should not trigger lazy loading
|
|
$firstClone->__load();
|
|
$secondClone->__load();
|
|
}
|
|
|
|
public function testNotInitializedProxyUnserialization()
|
|
{
|
|
$this->configureInitializerMock();
|
|
|
|
$serialized = serialize($this->lazyObject);
|
|
/* @var $unserialized LazyLoadableObject|Proxy */
|
|
$unserialized = unserialize($serialized);
|
|
$reflClass = $this->lazyLoadableObjectMetadata->getReflectionClass();
|
|
|
|
$this->assertFalse($unserialized->__isInitialized(), 'serialization didn\'t cause intialization');
|
|
|
|
// Checking identifiers
|
|
$this->assertSame('publicIdentifierFieldValue', $unserialized->publicIdentifierField, 'identifiers are kept');
|
|
$protectedIdentifierField = $reflClass->getProperty('protectedIdentifierField');
|
|
$protectedIdentifierField->setAccessible(true);
|
|
$this->assertSame(
|
|
'protectedIdentifierFieldValue',
|
|
$protectedIdentifierField->getValue($unserialized),
|
|
'identifiers are kept'
|
|
);
|
|
|
|
// Checking transient fields
|
|
$this->assertSame(
|
|
'publicTransientFieldValue',
|
|
$unserialized->publicTransientField,
|
|
'transient fields are kept'
|
|
);
|
|
$protectedTransientField = $reflClass->getProperty('protectedTransientField');
|
|
$protectedTransientField->setAccessible(true);
|
|
$this->assertSame(
|
|
'protectedTransientFieldValue',
|
|
$protectedTransientField->getValue($unserialized),
|
|
'transient fields are kept'
|
|
);
|
|
|
|
// Checking persistent fields
|
|
$this->assertSame(
|
|
'publicPersistentFieldValue',
|
|
$unserialized->publicPersistentField,
|
|
'persistent fields are kept'
|
|
);
|
|
$protectedPersistentField = $reflClass->getProperty('protectedPersistentField');
|
|
$protectedPersistentField->setAccessible(true);
|
|
$this->assertSame(
|
|
'protectedPersistentFieldValue',
|
|
$protectedPersistentField->getValue($unserialized),
|
|
'persistent fields are kept'
|
|
);
|
|
|
|
// Checking associations
|
|
$this->assertSame('publicAssociationValue', $unserialized->publicAssociation, 'associations are kept');
|
|
$protectedAssociationField = $reflClass->getProperty('protectedAssociation');
|
|
$protectedAssociationField->setAccessible(true);
|
|
$this->assertSame(
|
|
'protectedAssociationValue',
|
|
$protectedAssociationField->getValue($unserialized),
|
|
'associations are kept'
|
|
);
|
|
}
|
|
|
|
public function testInitializedProxyUnserialization()
|
|
{
|
|
// persister will retrieve the lazy object itself, so that we don't have to re-define all field values
|
|
$this->proxyLoader->expects($this->once())->method('load')->will($this->returnValue($this->lazyObject));
|
|
$this->lazyObject->__setInitializer($this->getSuggestedInitializerImplementation());
|
|
$this->lazyObject->__load();
|
|
|
|
$serialized = serialize($this->lazyObject);
|
|
$reflClass = $this->lazyLoadableObjectMetadata->getReflectionClass();
|
|
/* @var $unserialized LazyLoadableObject|Proxy */
|
|
$unserialized = unserialize($serialized);
|
|
|
|
$this->assertTrue($unserialized->__isInitialized(), 'serialization didn\'t cause intialization');
|
|
|
|
// Checking transient fields
|
|
$this->assertSame(
|
|
'publicTransientFieldValue',
|
|
$unserialized->publicTransientField,
|
|
'transient fields are kept'
|
|
);
|
|
$protectedTransientField = $reflClass->getProperty('protectedTransientField');
|
|
$protectedTransientField->setAccessible(true);
|
|
$this->assertSame(
|
|
'protectedTransientFieldValue',
|
|
$protectedTransientField->getValue($unserialized),
|
|
'transient fields are kept'
|
|
);
|
|
|
|
// Checking persistent fields
|
|
$this->assertSame(
|
|
'publicPersistentFieldValue',
|
|
$unserialized->publicPersistentField,
|
|
'persistent fields are kept'
|
|
);
|
|
$protectedPersistentField = $reflClass->getProperty('protectedPersistentField');
|
|
$protectedPersistentField->setAccessible(true);
|
|
$this->assertSame(
|
|
'protectedPersistentFieldValue',
|
|
$protectedPersistentField->getValue($unserialized),
|
|
'persistent fields are kept'
|
|
);
|
|
|
|
// Checking identifiers
|
|
$this->assertSame(
|
|
'publicIdentifierFieldValue',
|
|
$unserialized->publicIdentifierField,
|
|
'identifiers are kept'
|
|
);
|
|
$protectedIdentifierField = $reflClass->getProperty('protectedIdentifierField');
|
|
$protectedIdentifierField->setAccessible(true);
|
|
$this->assertSame(
|
|
'protectedIdentifierFieldValue',
|
|
$protectedIdentifierField->getValue($unserialized),
|
|
'identifiers are kept'
|
|
);
|
|
|
|
// Checking associations
|
|
$this->assertSame('publicAssociationValue', $unserialized->publicAssociation, 'associations are kept');
|
|
$protectedAssociationField = $reflClass->getProperty('protectedAssociation');
|
|
$protectedAssociationField->setAccessible(true);
|
|
$this->assertSame(
|
|
'protectedAssociationValue',
|
|
$protectedAssociationField->getValue($unserialized),
|
|
'associations are kept'
|
|
);
|
|
}
|
|
|
|
public function testInitializationRestoresDefaultPublicLazyLoadedFieldValues()
|
|
{
|
|
// setting noop persister
|
|
$this->proxyLoader->expects($this->once())->method('load')->will($this->returnValue($this->lazyObject));
|
|
$this->lazyObject->__setInitializer($this->getSuggestedInitializerImplementation());
|
|
|
|
$this->assertSame(
|
|
'publicPersistentFieldValue',
|
|
$this->lazyObject->publicPersistentField,
|
|
'Persistent field is restored to default value'
|
|
);
|
|
$this->assertSame(
|
|
'publicAssociationValue',
|
|
$this->lazyObject->publicAssociation,
|
|
'Association is restored to default value'
|
|
);
|
|
}
|
|
|
|
public function testSettingPublicFieldsCausesLazyLoading()
|
|
{
|
|
$test = $this;
|
|
$this->configureInitializerMock(
|
|
1,
|
|
array($this->lazyObject, '__set', array('publicPersistentField', 'newPublicPersistentFieldValue')),
|
|
function () use ($test) {
|
|
$test->setProxyValue('publicPersistentField', 'overrideValue');
|
|
$test->setProxyValue('publicAssociation', 'newAssociationValue');
|
|
}
|
|
);
|
|
|
|
$this->lazyObject->publicPersistentField = 'newPublicPersistentFieldValue';
|
|
$this->assertSame('newPublicPersistentFieldValue', $this->lazyObject->publicPersistentField);
|
|
$this->assertSame('newAssociationValue', $this->lazyObject->publicAssociation);
|
|
}
|
|
|
|
public function testSettingPublicAssociationCausesLazyLoading()
|
|
{
|
|
$test = $this;
|
|
$this->configureInitializerMock(
|
|
1,
|
|
array($this->lazyObject, '__set', array('publicAssociation', 'newPublicAssociationValue')),
|
|
function () use ($test) {
|
|
$test->setProxyValue('publicPersistentField', 'newPublicPersistentFieldValue');
|
|
$test->setProxyValue('publicAssociation', 'overrideValue');
|
|
}
|
|
);
|
|
|
|
$this->lazyObject->publicAssociation = 'newPublicAssociationValue';
|
|
$this->assertSame('newPublicAssociationValue', $this->lazyObject->publicAssociation);
|
|
$this->assertSame('newPublicPersistentFieldValue', $this->lazyObject->publicPersistentField);
|
|
}
|
|
|
|
public function testCheckingPublicFieldsCausesLazyLoading()
|
|
{
|
|
$test = $this;
|
|
$this->configureInitializerMock(
|
|
1,
|
|
array($this->lazyObject, '__isset', array('publicPersistentField')),
|
|
function () use ($test) {
|
|
$test->setProxyValue('publicPersistentField', null);
|
|
$test->setProxyValue('publicAssociation', 'setPublicAssociation');
|
|
}
|
|
);
|
|
|
|
$this->assertFalse(isset($this->lazyObject->publicPersistentField));
|
|
$this->assertNull($this->lazyObject->publicPersistentField);
|
|
$this->assertTrue(isset($this->lazyObject->publicAssociation));
|
|
$this->assertSame('setPublicAssociation', $this->lazyObject->publicAssociation);
|
|
}
|
|
|
|
public function testCheckingPublicAssociationCausesLazyLoading()
|
|
{
|
|
$test = $this;
|
|
$this->configureInitializerMock(
|
|
1,
|
|
array($this->lazyObject, '__isset', array('publicAssociation')),
|
|
function () use ($test) {
|
|
$test->setProxyValue('publicPersistentField', 'newPersistentFieldValue');
|
|
$test->setProxyValue('publicAssociation', 'setPublicAssociation');
|
|
}
|
|
);
|
|
|
|
$this->assertTrue(isset($this->lazyObject->publicAssociation));
|
|
$this->assertSame('setPublicAssociation', $this->lazyObject->publicAssociation);
|
|
$this->assertTrue(isset($this->lazyObject->publicPersistentField));
|
|
$this->assertSame('newPersistentFieldValue', $this->lazyObject->publicPersistentField);
|
|
}
|
|
|
|
/**
|
|
* Converts a given callable into a closure
|
|
*
|
|
* @param callable $callable
|
|
* @return \Closure
|
|
*/
|
|
public function getClosure($callable) {
|
|
return function () use ($callable) {
|
|
call_user_func_array($callable, func_get_args());
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Configures the current initializer callback mock with provided matcher params
|
|
*
|
|
* @param int $expectedCallCount the number of invocations to be expected. If a value< 0 is provided, `any` is used
|
|
* @param array $callParamsMatch an ordered array of parameters to be expected
|
|
* @param callable $callbackClosure a return callback closure
|
|
*
|
|
* @return \PHPUnit_Framework_MockObject_MockObject|
|
|
*/
|
|
protected function configureInitializerMock(
|
|
$expectedCallCount = 0,
|
|
array $callParamsMatch = null,
|
|
\Closure $callbackClosure = null
|
|
) {
|
|
if (!$expectedCallCount) {
|
|
$invocationCountMatcher = $this->exactly((int) $expectedCallCount);
|
|
} else {
|
|
$invocationCountMatcher = $expectedCallCount < 0 ? $this->any() : $this->exactly($expectedCallCount);
|
|
}
|
|
|
|
$invocationMocker = $this->initializerCallbackMock->expects($invocationCountMatcher)->method('__invoke');
|
|
|
|
if (null !== $callParamsMatch) {
|
|
call_user_func_array(array($invocationMocker, 'with'), $callParamsMatch);
|
|
}
|
|
|
|
if ($callbackClosure) {
|
|
$invocationMocker->will($this->returnCallback($callbackClosure));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets a value in the current proxy object without triggering lazy loading through `__set`
|
|
*
|
|
* @link https://bugs.php.net/bug.php?id=63463
|
|
*
|
|
* @param string $property
|
|
* @param mixed $value
|
|
*/
|
|
public function setProxyValue($property, $value)
|
|
{
|
|
$reflectionProperty = new \ReflectionProperty($this->lazyObject, $property);
|
|
$initializer = $this->lazyObject->__getInitializer();
|
|
|
|
// disabling initializer since setting `publicPersistentField` triggers `__set`/`__get`
|
|
$this->lazyObject->__setInitializer(null);
|
|
$reflectionProperty->setValue($this->lazyObject, $value);
|
|
$this->lazyObject->__setInitializer($initializer);
|
|
}
|
|
|
|
/**
|
|
* Retrieves the suggested implementation of an initializer that proxy factories in O*M
|
|
* are currently following, and that should be used to initialize the current proxy object
|
|
*
|
|
* @return \Closure
|
|
*/
|
|
protected function getSuggestedInitializerImplementation()
|
|
{
|
|
$loader = $this->proxyLoader;
|
|
$identifier = $this->identifier;
|
|
|
|
return function (LazyLoadableObject $proxy) use ($loader, $identifier) {
|
|
/* @var $proxy LazyLoadableObject|Proxy */
|
|
$proxy->__setInitializer(null);
|
|
$proxy->__setCloner(null);
|
|
|
|
|
|
if ($proxy->__isInitialized()) {
|
|
return;
|
|
}
|
|
|
|
$properties = $proxy->__getLazyProperties();
|
|
|
|
foreach ($properties as $propertyName => $property) {
|
|
if (!isset($proxy->$propertyName)) {
|
|
$proxy->$propertyName = $properties[$propertyName];
|
|
}
|
|
}
|
|
|
|
$proxy->__setInitialized(true);
|
|
|
|
if (method_exists($proxy, '__wakeup')) {
|
|
$proxy->__wakeup();
|
|
}
|
|
|
|
if (null === $loader->load($identifier, $proxy)) {
|
|
throw new \UnexpectedValueException('Couldn\'t load');
|
|
}
|
|
};
|
|
}
|
|
} |