Skip to content

Commit

Permalink
Merge pull request #71 from moufmouf/blob
Browse files Browse the repository at this point in the history
Add support for Blob and binary types in bean generation fixes (reworked)
  • Loading branch information
moufmouf authored Mar 15, 2018
2 parents 58f7a71 + 66efaca commit 7b934b7
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 22 deletions.
7 changes: 7 additions & 0 deletions src/TDBMInvalidArgumentException.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,11 @@

class TDBMInvalidArgumentException extends \InvalidArgumentException
{
/**
* @param mixed $value
*/
public static function badType(string $expectedType, $value, string $location): self
{
return new self("Invalid argument passed to '$location'. Expecting a $expectedType. Got a ".gettype($value).'.');
}
}
7 changes: 7 additions & 0 deletions src/Utils/AbstractBeanPropertyDescriptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,11 @@ public function isAlternativeName(): bool
* @return null|string
*/
abstract public function getCloneRule(): ?string;

/**
* Tells if this property is a type-hintable in PHP (resource isn't for example)
*
* @return bool
*/
abstract public function isTypeHintable() : bool;
}
2 changes: 1 addition & 1 deletion src/Utils/BeanDescriptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ public function __construct(%s)
$parentConstructorArguments = [];

foreach ($constructorProperties as $property) {
$arguments[] = $property->getPhpType().' '.$property->getVariableName();
$arguments[] = ($property->isTypeHintable()?($property->getPhpType().' '):'').$property->getVariableName();
$paramAnnotations[] = $property->getParamAnnotation();
if ($property->getTable()->getName() === $this->table->getName()) {
$assigns[] = $property->getConstructorAssignCode()."\n";
Expand Down
10 changes: 10 additions & 0 deletions src/Utils/ObjectBeanPropertyDescriptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -198,4 +198,14 @@ public function getCloneRule(): ?string
{
return null;
}

/**
* Tells if this property is a type-hintable in PHP (resource isn't for example)
*
* @return bool
*/
public function isTypeHintable() : bool
{
return true;
}
}
57 changes: 50 additions & 7 deletions src/Utils/ScalarBeanPropertyDescriptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -218,12 +218,23 @@ public function getGetterSetterCode()
// A column type can be forced if it is not nullable and not auto-incrementable (for auto-increment columns, we can get "null" as long as the bean is not saved).
$isNullable = !$this->column->getNotnull() || $this->isAutoincrement();

$resourceTypeCheck = '';
if ($normalizedType === 'resource') {
$resourceTypeCheck .= <<<EOF
if (!\is_resource($%s)) {
throw \TheCodingMachine\TDBM\TDBMInvalidArgumentException::badType('resource', $%s, __METHOD__);
}
EOF;
$resourceTypeCheck = sprintf($resourceTypeCheck, $this->column->getName(), $this->column->getName());
}

$getterAndSetterCode = ' /**
* The getter for the "%s" column.
*
* @return %s
*/
public function %s() : %s%s
public function %s()%s%s%s
{
return $this->get(%s, %s);
}
Expand All @@ -233,8 +244,8 @@ public function %s() : %s%s
*
* @param %s $%s
*/
public function %s(%s%s $%s) : void
{
public function %s(%s%s$%s) : void
{%s
$this->set(%s, $%s, %s);
}
Expand All @@ -246,19 +257,21 @@ public function %s(%s%s $%s) : void
$this->column->getName(),
$normalizedType.($isNullable ? '|null' : ''),
$columnGetterName,
($isNullable ? '?' : ''),
$normalizedType,
($this->isTypeHintable() ? ' : ' : ''),
($isNullable && $this->isTypeHintable() ? '?' : ''),
($this->isTypeHintable() ? $normalizedType: ''),
var_export($this->column->getName(), true),
var_export($this->table->getName(), true),
// Setter
$this->column->getName(),
$normalizedType.($isNullable ? '|null' : ''),
$this->column->getName(),
$columnSetterName,
$this->column->getNotnull() ? '' : '?',
$normalizedType,
($this->column->getNotnull() || !$this->isTypeHintable()) ? '' : '?',
$this->isTypeHintable() ? $normalizedType . ' ' : '',
//$castTo,
$this->column->getName(),
$resourceTypeCheck,
var_export($this->column->getName(), true),
$this->column->getName(),
var_export($this->table->getName(), true)
Expand All @@ -274,6 +287,10 @@ public function getJsonSerializeCode()
{
$normalizedType = $this->getPhpType();

if (!$this->canBeSerialized()){
return '';
}

if ($normalizedType == '\\DateTimeImmutable') {
return ' $array['.var_export($this->namingStrategy->getJsonProperty($this), true).'] = ($this->'.$this->getGetterName().'() === null) ? null : $this->'.$this->getGetterName()."()->format('c');\n";
} else {
Expand Down Expand Up @@ -303,4 +320,30 @@ public function getCloneRule(): ?string
}
return null;
}

/**
* tells is this type is suitable for Json Serialization
*
* @return string
*/
public function canBeSerialized() : string
{
$type = $this->column->getType();
return TDBMDaoGenerator::isSerializableType($type);
}

/**
* Tells if this property is a type-hintable in PHP (resource isn't for example)
*
* @return bool
*/
public function isTypeHintable() : bool
{
$type = $this->getPhpType();
$invalidScalarTypes = [
'resource'
];

return \in_array($type, $invalidScalarTypes, true) === false;
}
}
21 changes: 18 additions & 3 deletions src/Utils/TDBMDaoGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,7 @@ private function dumpFile(string $fileName, string $content) : void
*
* @return string The PHP type
*/
public static function dbalTypeToPhpType(Type $type)
public static function dbalTypeToPhpType(Type $type) : string
{
$map = [
Type::TARRAY => 'array',
Expand All @@ -659,12 +659,27 @@ public static function dbalTypeToPhpType(Type $type)
Type::SMALLINT => 'int',
Type::STRING => 'string',
Type::TEXT => 'string',
Type::BINARY => 'string',
Type::BLOB => 'string',
Type::BINARY => 'resource',
Type::BLOB => 'resource',
Type::FLOAT => 'float',
Type::GUID => 'string',
];

return isset($map[$type->getName()]) ? $map[$type->getName()] : $type->getName();
}

/**
* Tells if a given column type can be Json Serialized (Blob and Binary are not for instance)
* @param Type $type
* @return bool
*/
public static function isSerializableType(Type $type) : bool
{
$unserialisableTypes = [
Type::BLOB,
Type::BINARY
];

return \in_array($type->getName(), $unserialisableTypes, true) === false;
}
}
4 changes: 4 additions & 0 deletions tests/TDBMAbstractServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,10 @@ private static function initSchema(Connection $connection): void
->column('id')->string(36)->primaryKey()->comment('@UUID v4')
->column('content')->string(255);

$db->table('files')
->column('id')->integer()->primaryKey()->autoIncrement()->comment('@Autoincrement')
->column('file')->blob();

$toSchema->getTable('users')
->addUniqueIndex([$connection->quoteIdentifier('login')], 'users_login_idx')
->addIndex([$connection->quoteIdentifier('status'), $connection->quoteIdentifier('country_id')], 'users_status_country_idx');
Expand Down
70 changes: 59 additions & 11 deletions tests/TDBMDaoGeneratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
use TheCodingMachine\TDBM\Test\Dao\Bean\CategoryBean;
use TheCodingMachine\TDBM\Test\Dao\Bean\CountryBean;
use TheCodingMachine\TDBM\Test\Dao\Bean\DogBean;
use TheCodingMachine\TDBM\Test\Dao\Bean\FileBean;
use TheCodingMachine\TDBM\Test\Dao\Bean\Generated\UserBaseBean;
use TheCodingMachine\TDBM\Test\Dao\Bean\PersonBean;
use TheCodingMachine\TDBM\Test\Dao\Bean\RefNoPrimKeyBean;
Expand All @@ -50,6 +51,7 @@
use TheCodingMachine\TDBM\Test\Dao\ContactDao;
use TheCodingMachine\TDBM\Test\Dao\CountryDao;
use TheCodingMachine\TDBM\Test\Dao\DogDao;
use TheCodingMachine\TDBM\Test\Dao\FileDao;
use TheCodingMachine\TDBM\Test\Dao\Generated\UserBaseDao;
use TheCodingMachine\TDBM\Test\Dao\RefNoPrimKeyDao;
use TheCodingMachine\TDBM\Test\Dao\RoleDao;
Expand All @@ -72,20 +74,20 @@ protected function setUp()
$schemaAnalyzer = new SchemaAnalyzer($schemaManager);
$tdbmSchemaAnalyzer = new TDBMSchemaAnalyzer($this->tdbmService->getConnection(), new ArrayCache(), $schemaAnalyzer);
$this->tdbmDaoGenerator = new TDBMDaoGenerator($this->getConfiguration(), $tdbmSchemaAnalyzer);
$this->rootPath = __DIR__.'/../';
$this->rootPath = __DIR__ . '/../';
//$this->tdbmDaoGenerator->setComposerFile($this->rootPath.'composer.json');
}

public function testDaoGeneration()
{
// Remove all previously generated files.
$this->recursiveDelete($this->rootPath.'src/Test/Dao/');
$this->recursiveDelete($this->rootPath . 'src/Test/Dao/');

$this->tdbmDaoGenerator->generateAllDaosAndBeans();

// Let's require all files to check they are valid PHP!
// Test the daoFactory
require_once $this->rootPath.'src/Test/Dao/Generated/DaoFactory.php';
require_once $this->rootPath . 'src/Test/Dao/Generated/DaoFactory.php';
// Test the others

$beanDescriptors = $this->getDummyGeneratorListener()->getBeanDescriptors();
Expand All @@ -95,10 +97,10 @@ public function testDaoGeneration()
$daoBaseName = $beanDescriptor->getBaseDaoClassName();
$beanName = $beanDescriptor->getBeanClassName();
$baseBeanName = $beanDescriptor->getBaseBeanClassName();
require_once $this->rootPath.'src/Test/Dao/Bean/Generated/'.$baseBeanName.'.php';
require_once $this->rootPath.'src/Test/Dao/Bean/'.$beanName.'.php';
require_once $this->rootPath.'src/Test/Dao/Generated/'.$daoBaseName.'.php';
require_once $this->rootPath.'src/Test/Dao/'.$daoName.'.php';
require_once $this->rootPath . 'src/Test/Dao/Bean/Generated/' . $baseBeanName . '.php';
require_once $this->rootPath . 'src/Test/Dao/Bean/' . $beanName . '.php';
require_once $this->rootPath . 'src/Test/Dao/Generated/' . $daoBaseName . '.php';
require_once $this->rootPath . 'src/Test/Dao/' . $daoName . '.php';
}

// Check that pivot tables do not generate DAOs or beans.
Expand All @@ -113,7 +115,7 @@ public function testGenerationException()
$schemaAnalyzer = new SchemaAnalyzer($schemaManager);
$tdbmSchemaAnalyzer = new TDBMSchemaAnalyzer($this->tdbmService->getConnection(), new ArrayCache(), $schemaAnalyzer);
$tdbmDaoGenerator = new TDBMDaoGenerator($configuration, $tdbmSchemaAnalyzer);
$this->rootPath = __DIR__.'/../../../../';
$this->rootPath = __DIR__ . '/../../../../';
//$tdbmDaoGenerator->setComposerFile($this->rootPath.'composer.json');

$this->expectException(NoPathFoundException::class);
Expand All @@ -126,7 +128,7 @@ public function testGenerationException()
* @param string $str Path to file or directory
* @return bool
*/
private function recursiveDelete(string $str) : bool
private function recursiveDelete(string $str): bool
{
if (is_file($str)) {
return @unlink($str);
Expand Down Expand Up @@ -1468,7 +1470,7 @@ public function testPSR2Compliance()
// executes after the command finishes
if (!$process->isSuccessful()) {
echo $process->getOutput();
$this->fail('Generated code is not PRS2 compliant');
$this->fail('Generated code is not PSR-2 compliant');
}
}

Expand Down Expand Up @@ -1517,7 +1519,7 @@ public function testTypeHintedConstructors()
$userBaseBeanReflectionConstructor = new \ReflectionMethod(UserBaseBean::class, '__construct');
$nameParam = $userBaseBeanReflectionConstructor->getParameters()[0];

$this->assertSame('string', (string) $nameParam->getType());
$this->assertSame('string', (string)$nameParam->getType());
}

/**
Expand Down Expand Up @@ -1607,4 +1609,50 @@ public function testRecursiveSave()
$leaf->setParent($intermediate);
$categoryDao->save($leaf);
}

/**
* @depends testDaoGeneration
*/
public function testBlob()
{
$fp = fopen(__FILE__, 'r');
$file = new FileBean($fp);

$fileDao = new FileDao($this->tdbmService);

$fileDao->save($file);

$loadedFile = $fileDao->getById($file->getId());

$resource = $loadedFile->getFile();
$result = fseek($resource, 0);
$this->assertSame(0, $result);
$this->assertInternalType('resource', $resource);
$firstLine = fgets($resource);
$this->assertSame("<?php\n", $firstLine);
}

/**
* @depends testBlob
*/
public function testReadBlob()
{
$fileDao = new FileDao($this->tdbmService);
$loadedFile = $fileDao->getById(1);

$resource = $loadedFile->getFile();
$this->assertInternalType('resource', $resource);
$firstLine = fgets($resource);
$this->assertSame("<?php\n", $firstLine);
}

/**
* @depends testDaoGeneration
*/
public function testBlobResourceException()
{
$this->expectException(TDBMInvalidArgumentException::class);
$this->expectExceptionMessage('Invalid argument passed to \'TheCodingMachine\\TDBM\\Test\\Dao\\Bean\\Generated\\FileBaseBean::setFile\'. Expecting a resource. Got a string.');
new FileBean('foobar');
}
}

0 comments on commit 7b934b7

Please sign in to comment.