Skip to content
Open
11 changes: 10 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ php:
- hhvm
- nightly

services:
- mysql

matrix:
include:
- php: "5.3"
Expand All @@ -20,5 +23,11 @@ matrix:
- php: nightly
- php: hhvm

before_install:
- mysql -e 'CREATE DATABASE IF NOT EXISTS test;'

before_script:
- composer install

script:
- php -dshort_open_tag=Off -dmagic_quotes_gpc=Off tests/index.php
- "cd tests && php -dshort_open_tag=Off -dmagic_quotes_gpc=Off run.php"
3 changes: 3 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,8 @@
},
"autoload": {
"psr-0": { "Doctrine_": "lib/" }
},
"autoload-dev": {
"psr-4": { "": "tests/autoloaded/" }
}
}
1 change: 1 addition & 0 deletions lib/Doctrine/Event.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class Doctrine_Event
const RECORD_DQL_SELECT = 28;
const RECORD_DQL_UPDATE = 29;
const RECORD_VALIDATE = 30;
const RECORD_POST_SETUP = 31;

/**
* @var mixed $_nextSequence the sequence of the next event that will be created
Expand Down
7 changes: 7 additions & 0 deletions lib/Doctrine/Record.php
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,13 @@ public function preHydrate($event)
public function postHydrate($event)
{ }

/**
* Empty template method to provide Record classes with the ability to alter setup
* after it runs
*/
public function postSetUp($event)
{ }

/**
* Get the record error stack as a human readable string.
* Useful for outputting errors to user via web browser
Expand Down
36 changes: 31 additions & 5 deletions lib/Doctrine/Record/Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -153,17 +153,36 @@ public function initialize(Doctrine_Table $table)
$ownerClassName = $this->_options['table']->getComponentName();
$className = $this->_options['className'];
$this->_options['className'] = str_replace('%CLASS%', $ownerClassName, $className);
$componentName = $this->_options['className'];

if (isset($this->_options['tableName'])) {
$ownerTableName = $this->_options['table']->getTableName();
$tableName = $this->_options['tableName'];
$this->_options['tableName'] = str_replace('%TABLE%', $ownerTableName, $tableName);
}

// check that class doesn't exist (otherwise we cannot create it)
if ($this->_options['generateFiles'] === false && class_exists($this->_options['className'])) {
$this->_table = Doctrine_Core::getTable($this->_options['className']);
return false;
$connection = $table->getConnection();
$hasTableCache = $connection->getAttribute(Doctrine_Core::ATTR_TABLE_CACHE);
if ($hasTableCache) {
// Load from cache
$tableCacheDriver = $connection->getTableCacheDriver();
$hash = md5($componentName.'DOCTRINE_TABLE_CACHE_SALT');
$cached = $tableCacheDriver->fetch($hash);

if ($cached) {
$this->_table = unserialize($cached);
$this->_table->initializeFromCache($connection, $this);

$connection->addTable($this->_table);

$this->buildRelation();

$this->generateClassFromTable($this->_table);

$this->buildChildDefinitions();

return;
}
}

$this->buildTable();
Expand All @@ -182,6 +201,11 @@ public function initialize(Doctrine_Table $table)
$this->buildChildDefinitions();

$this->_table->initIdentifier();

if ($hasTableCache) {
// Save cached table
$tableCacheDriver->save($hash, serialize($this->_table), $connection->getTableCacheLifeSpan());
}
}

/**
Expand Down Expand Up @@ -461,7 +485,9 @@ public function generateClass(array $definition = array())
} else {
throw new Doctrine_Record_Exception('If you wish to generate files then you must specify the path to generate the files in.');
}
} else {
} elseif (!class_exists($definition['className'])) {
// The class is not defined then we can load the definition.

$def = $builder->buildDefinition($definition);

eval($def);
Expand Down
5 changes: 0 additions & 5 deletions lib/Doctrine/Search.php
Original file line number Diff line number Diff line change
Expand Up @@ -311,11 +311,6 @@ public function setTableDefinition()

$className = $this->getOption('className');

$autoLoad = (bool) ($this->_options['generateFiles']);
if (class_exists($className, $autoLoad)) {
return false;
}

// move any columns currently in the primary key to the end
// So that 'keyword' is the first field in the table
$previousIdentifier = array();
Expand Down
8 changes: 8 additions & 0 deletions lib/Doctrine/Table.php
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,10 @@ public function __construct($name, Doctrine_Connection $conn, $initDefinition =
if ($this->isTree()) {
$this->getTree()->setUp();
}

$event = new Doctrine_Event($this->record, Doctrine_Event::RECORD_POST_SETUP);

$this->record->postSetUp($event);
} else {
if ( ! isset($this->_options['tableName'])) {
$this->setTableName(Doctrine_Inflector::tableize($this->_options['name']));
Expand Down Expand Up @@ -3057,6 +3061,10 @@ public function initializeFromCache(Doctrine_Connection $conn)
$this->getTree()->setUp();
}

$event = new Doctrine_Event($this->record, Doctrine_Event::RECORD_POST_SETUP);

$this->record->postSetUp($event);

$this->_filters[] = new Doctrine_Record_Filter_Standard();
if ($this->getAttribute(Doctrine_Core::ATTR_USE_TABLE_REPOSITORY)) {
$this->_repository = new Doctrine_Table_Repository($this);
Expand Down
5 changes: 4 additions & 1 deletion tests/Connection/CustomTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ public function testConnection()

class Doctrine_Connection_Test extends Doctrine_Connection_Common
{

/**
* @var string $driverName the name of this connection driver
*/
protected $driverName = 'Mock';
}

class Doctrine_Adapter_Test implements Doctrine_Adapter_Interface
Expand Down
8 changes: 8 additions & 0 deletions tests/DoctrineTest/UnitTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ public function fail($message = "")
$this->_fail($message);
}

public function failFromException($e)
{
$this->fail(sprintf('Unexpected exception "%s" %s',
$e->getMessage(),
"\n\n".$e->getTraceAsString()
));
}

public function _fail($message = "")
{
$trace = debug_backtrace();
Expand Down
20 changes: 15 additions & 5 deletions tests/ManagerTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -164,14 +164,24 @@ public function testDropDatabases()

public function testConnectionInformationDecoded()
{
$e = null;
$dsn = 'mysql://' . urlencode('test/t') . ':' . urlencode('p@ssword') . '@localhost/' . urlencode('db/name');

$conn = Doctrine_Manager::connection($dsn);
$options = $conn->getOptions();
try {
$conn = Doctrine_Manager::connection($dsn);
$options = $conn->getOptions();

$this->assertEqual($options['username'], 'test/t');
$this->assertEqual($options['password'], 'p@ssword');
$this->assertEqual($options['dsn'], 'mysql:host=localhost;dbname=db/name');
$this->assertEqual($options['username'], 'test/t');
$this->assertEqual($options['password'], 'p@ssword');
$this->assertEqual($options['dsn'], 'mysql:host=localhost;dbname=db/name');
} catch (Exception $e) {
}

Doctrine_Manager::getInstance()->closeConnection($conn);

if (null !== $e) {
throw $e;
}
}
public function prepareData() { }
public function prepareTables() { }
Expand Down
22 changes: 21 additions & 1 deletion tests/Record/GeneratorTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,22 @@ public function testGeneratorComponentBinding()
$this->fail($e->getMessage());
}
}

public function testGeneratorModelAutoload()
{
Doctrine_Manager::connection('sqlite::memory:', 'test_tmp_conn', false);
Doctrine_Manager::getInstance()->bindComponent('I18nGeneratorModelAutoload', 'test_tmp_conn');
Doctrine_Core::createTablesFromArray(array('I18nGeneratorModelAutoload'));

try {
$record = new I18nGeneratorModelAutoload();
$record->Translation['EN']->title = 'en test';

$this->fail('The generated record class is autoloadable then it must be used.');
} catch (LogicException $e) {
$this->assertEqual('This record is expected to be instanciated as generateFiles option on record generator is false and it is autoloadable.', $e->getMessage());
}
}
}

class I18nGeneratorComponentBinding extends Doctrine_Record
Expand All @@ -69,4 +85,8 @@ public function setUp()
{
$this->actAs('I18n', array('fields' => array('title')));
}
}
}

class I18nGeneratorModelAutoload extends I18nGeneratorComponentBinding
{
}
53 changes: 53 additions & 0 deletions tests/TableTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class Doctrine_Table_TestCase extends Doctrine_UnitTestCase
public function prepareTables()
{
$this->tables[] = 'FieldNameTest';
$this->tables[] = 'I18nFilterTest';
parent::prepareTables();
}

Expand All @@ -57,6 +58,58 @@ public function testSerialize()
$this->assertEqual($table->getColumns(), $unserializedTable->getColumns());
}

public function testTableCacheWithI18nFilter()
{
try {
// Remove the table from internal class cache.
$tables = $this->conn->getTables();
$relf = new ReflectionProperty($this->conn, 'tables');
unset($tables['I18nFilterTest']);
unset($tables['I18nFilterTestTranslation']);
$relf->setAccessible(true);
$relf->setValue($this->conn, $tables);
$relf->setAccessible(false);

$this->conn->setAttribute(Doctrine_Core::ATTR_TABLE_CACHE, $cache = new Doctrine_Cache_Array());
$table = $this->conn->getTable('I18nFilterTest');

// The cache is filled.
$this->assertTrue($cache->contains(md5('I18nFilterTestDOCTRINE_TABLE_CACHE_SALT')));
$this->assertTrue($cache->contains(md5('I18nFilterTestTranslationDOCTRINE_TABLE_CACHE_SALT')));

$record = $table->create();
$record['name'] = 'foo';
$this->assertEqual('foo', $record['name']);

// Test the I18nFilterTest record that include the second filter.
$this->assertTrue(in_array('I18nFilterTestFilter', array_map('get_class', $table->getFilters())));
$expectedFilterNames = array_map('get_class', $table->getFilters());

// Remove the table from internal class cache.
$tables = $this->conn->getTables();
$relf = new ReflectionProperty($this->conn, 'tables');
unset($tables['I18nFilterTest']);
unset($tables['I18nFilterTestTranslation']);
$relf->setAccessible(true);
$relf->setValue($this->conn, $tables);
$relf->setAccessible(false);

// Get table from cache.
$cachedTable = $this->conn->getTable('I18nFilterTest');

$record = $cachedTable->create();

$this->assertEqual($expectedFilterNames, array_map('get_class', $cachedTable->getFilters()));

$record['name'] = 'foo';
$this->assertEqual('foo', $record['name']);
} catch (Exception $e) {
$this->failFromException($e);
}

$this->conn->setAttribute(Doctrine_Core::ATTR_TABLE_CACHE, null);
}

public function testFieldConversion()
{
$this->dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_UPPER);
Expand Down
9 changes: 9 additions & 0 deletions tests/autoloaded/I18nGeneratorModelAutoloadTranslation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

class I18nGeneratorModelAutoloadTranslation extends Doctrine_Record
{
public function __construct()
{
throw new LogicException('This record is expected to be instanciated as generateFiles option on record generator is false and it is autoloadable.');
}
}
2 changes: 1 addition & 1 deletion tests/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

define('DOCTRINE_DIR', $_SERVER['DOCTRINE_DIR']);

require_once(DOCTRINE_DIR . '/lib/Doctrine/Core.php');
require_once DOCTRINE_DIR.'/vendor/autoload.php';

spl_autoload_register(array('Doctrine_Core', 'autoload'));
spl_autoload_register(array('Doctrine_Core', 'modelsAutoload'));
Expand Down
58 changes: 58 additions & 0 deletions tests/models/I18nFilterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

class I18nFilterTest extends Doctrine_Record
{
/**
* {@inheritdoc}
*/
public function setTableDefinition()
{
$this->hasColumn('name', 'string', 200);
$this->hasColumn('title', 'string', 200);
}

/**
* {@inheritdoc}
*/
public function setUp()
{
$this->actAs('I18n', array('fields' => array('name', 'title')));
}

/**
* {@inheritdoc}
*/
public function postSetUp($event)
{
parent::postSetUp($event);

$table = $this->getTable();

if ($table->hasRelation('Translation')) {
$table->unshiftFilter(new I18nFilterTestFilter());
}
}
}

class I18nFilterTestFilter extends Doctrine_Record_Filter_Standard
{
/**
* {@inheritdoc}
*/
public function filterSet(Doctrine_Record $record, $name, $value)
{
return $record['Translation']['en'][$name] = $value;
}

/**
* {@inheritdoc}
*/
public function filterGet(Doctrine_Record $record, $name)
{
$trans = $record['Translation'];

if (isset($trans['en'])) {
return $trans['en'][$name];
}
}
}