1
0
mirror of synced 2025-01-29 19:41:45 +03:00

Merge master into DDC-117

This commit is contained in:
Benjamin Eberlei 2010-12-28 17:27:47 +01:00
commit e7b4dca611
149 changed files with 6589 additions and 4089 deletions

9
bin/doctrine.bat Normal file
View File

@ -0,0 +1,9 @@
@echo off
if "%PHPBIN%" == "" set PHPBIN=@php_bin@
if not exist "%PHPBIN%" if "%PHP_PEAR_PHP_BIN%" neq "" goto USE_PEAR_PATH
GOTO RUN
:USE_PEAR_PATH
set PHPBIN=%PHP_PEAR_PHP_BIN%
:RUN
"%PHPBIN%" "@bin_dir@\doctrine" %*

View File

@ -1,4 +1,21 @@
<?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 LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
require_once 'Doctrine/Common/ClassLoader.php';
@ -19,7 +36,7 @@ if (file_exists($configFile)) {
}
require $configFile;
foreach ($GLOBALS as $helperSetCandidate) {
if ($helperSetCandidate instanceof \Symfony\Component\Console\Helper\HelperSet) {
$helperSet = $helperSetCandidate;
@ -30,29 +47,4 @@ if (file_exists($configFile)) {
$helperSet = ($helperSet) ?: new \Symfony\Component\Console\Helper\HelperSet();
$cli = new \Symfony\Component\Console\Application('Doctrine Command Line Interface', Doctrine\ORM\Version::VERSION);
$cli->setCatchExceptions(true);
$cli->setHelperSet($helperSet);
$cli->addCommands(array(
// DBAL Commands
new \Doctrine\DBAL\Tools\Console\Command\RunSqlCommand(),
new \Doctrine\DBAL\Tools\Console\Command\ImportCommand(),
// ORM Commands
new \Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand(),
new \Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand(),
new \Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand(),
new \Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand(),
new \Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand(),
new \Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand(),
new \Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand(),
new \Doctrine\ORM\Tools\Console\Command\ConvertDoctrine1SchemaCommand(),
new \Doctrine\ORM\Tools\Console\Command\GenerateRepositoriesCommand(),
new \Doctrine\ORM\Tools\Console\Command\GenerateEntitiesCommand(),
new \Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand(),
new \Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand(),
new \Doctrine\ORM\Tools\Console\Command\RunDqlCommand(),
new \Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand(),
));
$cli->run();
\Doctrine\ORM\Tools\Console\ConsoleRunner::run($helperSet);

View File

@ -1,10 +1,13 @@
version=2.0.0BETA2
dependencies.common=2.0.0BETA4
dependencies.dbal=2.0.0BETA4
stability=beta
build.dir=build
dist.dir=dist
report.dir=reports
log.archive.dir=logs
svn.path=/usr/bin/svn
project.pirum_dir=
project.download_dir=
test.phpunit_configuration_file=
test.phpunit_generate_coverage=0
test.pmd_reports=0

View File

@ -5,8 +5,6 @@
-->
<project name="Doctrine2" default="build" basedir=".">
<taskdef classname="NativePhpunitTask" classpath="./tests/" name="nativephpunit" />
<taskdef classname="phing.tasks.ext.d51PearPkg2Task" name="d51pearpkg2" />
<property file="build.properties" />
@ -24,6 +22,7 @@
<fileset id="bin-scripts" dir="./bin">
<include name="doctrine"/>
<include name="doctrine.php"/>
<include name="doctrine.bat"/>
</fileset>
<!--
@ -51,7 +50,7 @@
Fileset for source of the Symfony YAML and Console components.
-->
<fileset id="symfony-sources" dir="./lib/vendor">
<include name="Symfony/Components/**"/>
<include name="Symfony/Component/**"/>
</fileset>
<!--
@ -83,23 +82,24 @@
<!--
Builds ORM package, preparing it for distribution.
-->
<target name="build-orm" depends="test">
<copy todir="${build.dir}/orm">
<target name="build-orm" depends="prepare">
<exec command="grep '${version}' ${project.basedir}/lib/Doctrine/ORM/Version.php" checkreturn="true"/>
<copy todir="${build.dir}/doctrine-orm">
<fileset refid="shared-artifacts"/>
</copy>
<copy todir="${build.dir}/orm">
<copy todir="${build.dir}/doctrine-orm">
<fileset refid="common-sources"/>
<fileset refid="dbal-sources"/>
<fileset refid="orm-sources"/>
</copy>
<copy todir="${build.dir}/orm/Doctrine">
<copy todir="${build.dir}/doctrine-orm/Doctrine">
<fileset refid="symfony-sources"/>
</copy>
<copy todir="${build.dir}/orm/bin">
<copy todir="${build.dir}/doctrine-orm/bin">
<fileset refid="bin-scripts"/>
</copy>
<exec command="sed 's/${version}-DEV/${version}/' ${build.dir}/orm/Doctrine/ORM/Version.php > ${build.dir}/orm/Doctrine/ORM/Version2.php" passthru="true" />
<exec command="mv ${build.dir}/orm/Doctrine/ORM/Version2.php ${build.dir}/orm/Doctrine/ORM/Version.php" passthru="true" />
<exec command="sed 's/${version}-DEV/${version}/' ${build.dir}/doctrine-orm/Doctrine/ORM/Version.php > ${build.dir}/doctrine-orm/Doctrine/ORM/Version2.php" passthru="true" />
<exec command="mv ${build.dir}/doctrine-orm/Doctrine/ORM/Version2.php ${build.dir}/doctrine-orm/Doctrine/ORM/Version.php" passthru="true" />
</target>
<target name="build" depends="test, build-orm"/>
@ -141,8 +141,8 @@
<!--
Builds distributable PEAR packages.
-->
<target name="build-packages" depends="build">
<d51pearpkg2 baseinstalldir="/" dir="${build.dir}/orm">
<target name="build-packages" depends="build-orm">
<d51pearpkg2 baseinstalldir="/" dir="${build.dir}/doctrine-orm">
<name>DoctrineORM</name>
<summary>Doctrine Object Relational Mapper</summary>
<channel>pear.doctrine-project.org</channel>
@ -150,6 +150,7 @@
<lead user="jwage" name="Jonathan H. Wage" email="jonwage@gmail.com" />
<lead user="guilhermeblanco" name="Guilherme Blanco" email="guilhermeblanco@gmail.com" />
<lead user="romanb" name="Roman Borschel" email="roman@code-factory.org" />
<lead user="beberlei" name="Benjamin Eberlei" email="kontakt@beberlei.de" />
<license>LGPL</license>
<version release="${version}" api="${version}" />
<stability release="${stability}" api="${stability}" />
@ -157,17 +158,55 @@
<dependencies>
<php minimum_version="5.3.0" />
<pear minimum_version="1.6.0" recommended_version="1.6.1" />
<package name="DoctrineCommon" channel="pear.doctrine-project.org" minimum_version="${dependencies.common}" />
<package name="DoctrineDBAL" channel="pear.doctrine-project.org" minimum_version="${dependencies.dbal}" />
</dependencies>
<dirroles key="bin">script</dirroles>
<replacement path="bin/doctrine" type="pear-config" from="@php_bin@" to="php_bin" />
<replacement path="bin/doctrine.php" type="pear-config" from="@php_bin@" to="php_bin" />
<replacement path="bin/doctrine.php" type="pear-config" from="@bin_dir@" to="bin_dir" />
<ignore>Doctrine/Common/</ignore>
<ignore>Doctrine/DBAL/</ignore>
<release>
<install as="doctrine" name="bin/doctrine" />
<install as="doctrine.php" name="bin/doctrine.php" />
<install as="doctrine.bat" name="bin/doctrine.bat" />
</release>
<replacement path="bin/doctrine.bat" type="pear-config" from="@php_bin@" to="php_bin" />
<replacement path="bin/doctrine.bat" type="pear-config" from="@bin_dir@" to="bin_dir" />
</d51pearpkg2>
<exec command="pear package" dir="${build.dir}/orm" passthru="true" />
<exec command="mv DoctrineORM-${version}.tgz ../../dist" dir="${build.dir}/orm" passthru="true" />
<exec command="pear package" dir="${build.dir}/doctrine-orm" passthru="true" />
<exec command="mv DoctrineORM-${version}.tgz ../../dist" dir="${build.dir}/doctrine-orm" passthru="true" />
<tar destfile="dist/DoctrineORM-${version}-full.tar.gz" compression="gzip" basedir="${build.dir}">
<fileset dir="${build.dir}">
<include name="**/**" />
<exclude name="logs/" />
<exclude name="doctrine-orm/package.xml" />
</fileset>
</tar>
</target>
<target name="git-tag">
<exec command="grep '${version}' ${project.basedir}/lib/Doctrine/ORM/Version.php" checkreturn="true"/>
<exec command="git tag -a ${version}" passthru="true" />
<exec command="git push origin ${version}" passthru="true" />
</target>
<target name="pirum-release">
<exec command="sudo pirum add ${project.pirum_dir} ${project.basedir}/dist/DoctrineORM-${version}.tgz" dir="." passthru="true" />
<exec command="sudo pirum build ${project.pirum_dir}" passthru="true" />
</target>
<target name="distribute-download">
<copy file="dist/DoctrineORM-${version}-full.tar.gz" todir="${project.download_dir}" />
</target>
<target name="update-dev-version">
<exec command="grep '${version}' ${project.basedir}/lib/Doctrine/ORM/Version.php" checkreturn="true"/>
<propertyprompt propertyName="next_version" defaultValue="${version}" promptText="Enter next version string (without -DEV)" />
<exec command="sed 's/${version}-DEV/${next_version}-DEV/' ${project.basedir}/lib/Doctrine/ORM/Version.php > ${project.basedir}/lib/Doctrine/ORM/Version2.php" passthru="true" />
<exec command="mv ${project.basedir}/lib/Doctrine/ORM/Version2.php ${project.basedir}/lib/Doctrine/ORM/Version.php" passthru="true" />
<exec command="git add ${project.basedir}/lib/Doctrine/ORM/Version.php" passthru="true" />
<exec command="git commit -m 'Bump Dev Version to ${next_version}-DEV'" passthru="true" />
<exec command="git push origin master" passthru="true" />
</target>
<target name="release" depends="git-tag,build-packages,distribute-download,pirum-release,update-dev-version" />
</project>

View File

@ -63,7 +63,7 @@
<xs:element name="discriminator-column" type="orm:discriminator-column" minOccurs="0"/>
<xs:element name="discriminator-map" type="orm:discriminator-map" minOccurs="0"/>
<xs:element name="lifecycle-callbacks" type="orm:lifecycle-callbacks" minOccurs="0" maxOccurs="1" />
<xs:element name="id" type="orm:id" />
<xs:element name="id" type="orm:id" minOccurs="0" maxOccurs="1" />
<xs:element name="field" type="orm:field" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="one-to-one" type="orm:one-to-one" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="one-to-many" type="orm:one-to-many" minOccurs="0" maxOccurs="unbounded" />
@ -73,7 +73,7 @@
<xs:attribute name="name" type="xs:string" use="required" />
<xs:attribute name="table" type="xs:NMTOKEN" />
<xs:attribute name="schema" type="xs:NMTOKEN" />
<xs:attribute name="repository-class" type="xs:NMTOKEN"/>
<xs:attribute name="repository-class" type="xs:string"/>
<xs:attribute name="inheritance-type" type="orm:inheritance-type"/>
<xs:attribute name="change-tracking-policy" type="orm:change-tracking-policy" />
</xs:complexType>
@ -252,7 +252,7 @@
<xs:element name="join-table" type="orm:join-table" minOccurs="0" />
<xs:element name="order-by" type="orm:order-by" minOccurs="0" />
</xs:sequence>
<xs:attribute name="target-entity" type="xs:NMTOKEN" use="required" />
<xs:attribute name="target-entity" type="xs:string" use="required" />
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
<xs:attribute name="mapped-by" type="xs:NMTOKEN" />
<xs:attribute name="inversed-by" type="xs:NMTOKEN" />
@ -264,7 +264,7 @@
<xs:element name="cascade" type="orm:cascade-type" minOccurs="0" />
<xs:element name="order-by" type="orm:order-by" minOccurs="0" />
</xs:sequence>
<xs:attribute name="target-entity" type="xs:NMTOKEN" use="required" />
<xs:attribute name="target-entity" type="xs:string" use="required" />
<xs:attribute name="mapped-by" type="xs:NMTOKEN" use="required" />
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
<xs:attribute name="orphan-removal" type="xs:boolean" default="false" />
@ -279,7 +279,7 @@
<xs:element name="join-columns" type="orm:join-columns"/>
</xs:choice>
</xs:sequence>
<xs:attribute name="target-entity" type="xs:NMTOKEN" use="required" />
<xs:attribute name="target-entity" type="xs:string" use="required" />
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
<xs:attribute name="orphan-removal" type="xs:boolean" default="false" />
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
@ -295,11 +295,11 @@
</xs:choice>
</xs:sequence>
<xs:attribute name="field" type="xs:NMTOKEN" use="required" />
<xs:attribute name="target-entity" type="xs:NMTOKEN" use="required" />
<xs:attribute name="target-entity" type="xs:string" use="required" />
<xs:attribute name="mapped-by" type="xs:NMTOKEN" />
<xs:attribute name="inversed-by" type="xs:NMTOKEN" />
<xs:attribute name="orphan-removal" type="xs:boolean" default="false" />
<xs:attribute name="fetch" type="orm:fetch-type" default="LAZY" />
</xs:complexType>
</xs:schema>
</xs:schema>

View File

@ -482,15 +482,6 @@ abstract class AbstractQuery
*/
public function execute($params = array(), $hydrationMode = null)
{
// If there are still pending insertions in the UnitOfWork we need to flush
// in order to guarantee a correct result.
//TODO: Think this over. Its tricky. Not doing this can lead to strange results
// potentially, but doing it could result in endless loops when querying during
// a flush, i.e. inside an event listener.
if ($this->_em->getUnitOfWork()->hasPendingInsertions()) {
$this->_em->flush();
}
if ($hydrationMode !== null) {
$this->setHydrationMode($hydrationMode);
}
@ -562,9 +553,22 @@ abstract class AbstractQuery
if ($this->_resultCacheId) {
return $this->_resultCacheId;
} else {
$params = $this->_params;
foreach ($params AS $key => $value) {
if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value))) {
if ($this->_em->getUnitOfWork()->getEntityState($value) == UnitOfWork::STATE_MANAGED) {
$idValues = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
} else {
$class = $this->_em->getClassMetadata(get_class($value));
$idValues = $class->getIdentifierValues($value);
}
$params[$key] = $idValues;
}
}
$sql = $this->getSql();
ksort($this->_hints);
return md5(implode(";", (array)$sql) . var_export($this->_params, true) .
return md5(implode(";", (array)$sql) . var_export($params, true) .
var_export($this->_hints, true)."&hydrationMode=".$this->_hydrationMode);
}
}
@ -583,6 +587,8 @@ abstract class AbstractQuery
*/
public function __clone()
{
$this->free();
$this->_params = array();
$this->_paramTypes = array();
$this->_hints = array();
}
}

View File

@ -462,4 +462,25 @@ class Configuration extends \Doctrine\DBAL\Configuration
{
$this->_attributes['customHydrationModes'][$modeName] = $hydrator;
}
/**
* Set a class metadata factory.
*
* @param string $cmf
*/
public function setClassMetadataFactoryName($cmfName)
{
$this->_attributes['classMetadataFactoryName'] = $cmfName;
}
/**
* @return string
*/
public function getClassMetadataFactoryName()
{
if (!isset($this->_attributes['classMetadataFactoryName'])) {
$this->_attributes['classMetadataFactoryName'] = 'Doctrine\ORM\Mapping\ClassMetadataFactory';
}
return $this->_attributes['classMetadataFactoryName'];
}
}

View File

@ -118,8 +118,12 @@ class EntityManager
$this->conn = $conn;
$this->config = $config;
$this->eventManager = $eventManager;
$this->metadataFactory = new ClassMetadataFactory($this);
$metadataFactoryClassName = $config->getClassMetadataFactoryName();
$this->metadataFactory = new $metadataFactoryClassName;
$this->metadataFactory->setEntityManager($this);
$this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl());
$this->unitOfWork = new UnitOfWork($this);
$this->proxyFactory = new ProxyFactory($this,
$config->getProxyDir(),
@ -352,11 +356,15 @@ class EntityManager
if ($entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName)) {
return $entity;
}
if ( ! is_array($identifier)) {
$identifier = array($class->identifier[0] => $identifier);
if ($class->subClasses) {
$entity = $this->find($entityName, $identifier);
} else {
if ( ! is_array($identifier)) {
$identifier = array($class->identifier[0] => $identifier);
}
$entity = $this->proxyFactory->getProxy($class->name, $identifier);
$this->unitOfWork->registerManaged($entity, $identifier, array());
}
$entity = $this->proxyFactory->getProxy($class->name, $identifier);
$this->unitOfWork->registerManaged($entity, $identifier, array());
return $entity;
}
@ -611,6 +619,16 @@ class EntityManager
}
}
/**
* Check if the Entity manager is open or closed.
*
* @return bool
*/
public function isOpen()
{
return (!$this->closed);
}
/**
* Gets the UnitOfWork used by the EntityManager to coordinate operations.
*

View File

@ -133,7 +133,6 @@ class EntityRepository
/**
* Finds all entities in the repository.
*
* @param int $hydrationMode
* @return array The entities.
*/
public function findAll()
@ -144,8 +143,7 @@ class EntityRepository
/**
* Finds entities by a set of criteria.
*
* @param string $column
* @param string $value
* @param array $criteria
* @return array
*/
public function findBy(array $criteria)
@ -156,8 +154,7 @@ class EntityRepository
/**
* Finds a single entity by a set of criteria.
*
* @param string $column
* @param string $value
* @param array $criteria
* @return object
*/
public function findOneBy(array $criteria)
@ -188,13 +185,14 @@ class EntityRepository
);
}
if ( ! isset($arguments[0])) {
if ( !isset($arguments[0])) {
// we dont even want to allow null at this point, because we cannot (yet) transform it into IS NULL.
throw ORMException::findByRequiresParameter($method.$by);
}
$fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by));
if ($this->_class->hasField($fieldName)) {
if ($this->_class->hasField($fieldName) || $this->_class->hasAssociation($fieldName)) {
return $this->$method(array($fieldName => $arguments[0]));
} else {
throw ORMException::invalidFindByCall($this->_entityName, $fieldName, $method.$by);

View File

@ -4,6 +4,9 @@ namespace Doctrine\ORM\Event;
use Doctrine\Common\EventArgs;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Doctrine\ORM\EntityManager;
/**
* Class that holds event arguments for a loadMetadata event.
*
@ -12,16 +15,40 @@ use Doctrine\Common\EventArgs;
*/
class LoadClassMetadataEventArgs extends EventArgs
{
private $_classMetadata;
/**
* @var ClassMetadata
*/
private $classMetadata;
public function __construct(\Doctrine\ORM\Mapping\ClassMetadata $classMetadata)
/**
* @var EntityManager
*/
private $em;
/**
* @param ClassMetadataInfo $classMetadata
* @param EntityManager $em
*/
public function __construct(ClassMetadataInfo $classMetadata, EntityManager $em)
{
$this->_classMetadata = $classMetadata;
$this->classMetadata = $classMetadata;
$this->em = $em;
}
/**
* @return ClassMetadataInfo
*/
public function getClassMetadata()
{
return $this->_classMetadata;
return $this->classMetadata;
}
/**
* @return EntityManager
*/
public function getEntityManager()
{
return $this->em;
}
}

View File

@ -145,12 +145,12 @@ class ObjectHydrator extends AbstractHydrator
{
$oid = spl_object_hash($entity);
$relation = $class->associationMappings[$fieldName];
$value = $class->reflFields[$fieldName]->getValue($entity);
if ($value === null) {
$value = new ArrayCollection;
}
if ( ! $value instanceof PersistentCollection) {
$value = new PersistentCollection(
$this->_em,
@ -161,17 +161,19 @@ class ObjectHydrator extends AbstractHydrator
$class->reflFields[$fieldName]->setValue($entity, $value);
$this->_uow->setOriginalEntityProperty($oid, $fieldName, $value);
$this->_initializedCollections[$oid . $fieldName] = $value;
} else if (isset($this->_hints[Query::HINT_REFRESH])) {
// Is already PersistentCollection, but REFRESH
} else if (isset($this->_hints[Query::HINT_REFRESH]) ||
isset($this->_hints['fetched'][$class->name][$fieldName]) &&
! $value->isInitialized()) {
// Is already PersistentCollection, but either REFRESH or FETCH-JOIN and UNINITIALIZED!
$value->setDirty(false);
$value->setInitialized(true);
$value->unwrap()->clear();
$this->_initializedCollections[$oid . $fieldName] = $value;
} else {
// Is already PersistentCollection, and DONT REFRESH
// Is already PersistentCollection, and DON'T REFRESH or FETCH-JOIN!
$this->_existingCollections[$oid . $fieldName] = $value;
}
return $value;
}
@ -269,6 +271,9 @@ class ObjectHydrator extends AbstractHydrator
// It's a joined result
$parentAlias = $this->_rsm->parentAliasMap[$dqlAlias];
// we need the $path to save into the identifier map which entities were already
// seen for this parent-child relationship
$path = $parentAlias . '.' . $dqlAlias;
// Get a reference to the parent object to which the joined element belongs.
if ($this->_rsm->isMixed && isset($this->_rootAliases[$parentAlias])) {
@ -298,8 +303,8 @@ class ObjectHydrator extends AbstractHydrator
$reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField);
}
$indexExists = isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]]);
$index = $indexExists ? $this->_identifierMap[$dqlAlias][$id[$dqlAlias]] : false;
$indexExists = isset($this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]]);
$index = $indexExists ? $this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] : false;
$indexIsValid = $index !== false ? isset($reflFieldValue[$index]) : false;
if ( ! $indexExists || ! $indexIsValid) {
@ -317,11 +322,11 @@ class ObjectHydrator extends AbstractHydrator
$field = $this->_rsm->indexByMap[$dqlAlias];
$indexValue = $this->_ce[$entityName]->reflFields[$field]->getValue($element);
$reflFieldValue->hydrateSet($indexValue, $element);
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $indexValue;
$this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue;
} else {
$reflFieldValue->hydrateAdd($element);
$reflFieldValue->last();
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $reflFieldValue->key();
$this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $reflFieldValue->key();
}
// Update result pointer
$this->_resultPointers[$dqlAlias] = $element;
@ -332,6 +337,7 @@ class ObjectHydrator extends AbstractHydrator
}
} else if ( ! $reflField->getValue($parentObject)) {
$coll = new PersistentCollection($this->_em, $this->_ce[$entityName], new ArrayCollection);
$coll->setOwner($parentObject, $relation);
$reflField->setValue($parentObject, $coll);
$this->_uow->setOriginalEntityProperty($oid, $relationField, $coll);
}

View File

@ -74,11 +74,9 @@ class ClassMetadataFactory
private $initialized = false;
/**
* Creates a new factory instance that uses the given metadata driver implementation.
*
* @param $driver The metadata driver to use.
* @param EntityManager $$em
*/
public function __construct(EntityManager $em)
public function setEntityManager(EntityManager $em)
{
$this->em = $em;
}
@ -262,15 +260,19 @@ class ClassMetadataFactory
$class = $this->newClassMetadataInstance($className);
if ($parent) {
$class->setInheritanceType($parent->inheritanceType);
$class->setDiscriminatorColumn($parent->discriminatorColumn);
if (!$parent->isMappedSuperclass) {
$class->setInheritanceType($parent->inheritanceType);
$class->setDiscriminatorColumn($parent->discriminatorColumn);
}
$class->setIdGeneratorType($parent->generatorType);
$this->addInheritedFields($class, $parent);
$this->addInheritedRelations($class, $parent);
$class->setIdentifier($parent->identifier);
$class->setVersioned($parent->isVersioned);
$class->setVersionField($parent->versionField);
$class->setDiscriminatorMap($parent->discriminatorMap);
if (!$parent->isMappedSuperclass) {
$class->setDiscriminatorMap($parent->discriminatorMap);
}
$class->setLifecycleCallbacks($parent->lifecycleCallbacks);
$class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
}
@ -302,6 +304,17 @@ class ClassMetadataFactory
$this->completeIdGeneratorMapping($class);
}
if ($parent && $parent->isInheritanceTypeSingleTable()) {
$class->setPrimaryTable($parent->table);
}
$class->setParentClasses($visited);
if ($this->evm->hasListeners(Events::loadClassMetadata)) {
$eventArgs = new \Doctrine\ORM\Event\LoadClassMetadataEventArgs($class, $this->em);
$this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
}
// verify inheritance
if (!$parent && !$class->isMappedSuperclass && !$class->isInheritanceTypeNone()) {
if (count($class->discriminatorMap) == 0) {
@ -312,17 +325,6 @@ class ClassMetadataFactory
}
}
if ($parent && $parent->isInheritanceTypeSingleTable()) {
$class->setPrimaryTable($parent->table);
}
$class->setParentClasses($visited);
if ($this->evm->hasListeners(Events::loadClassMetadata)) {
$eventArgs = new \Doctrine\ORM\Event\LoadClassMetadataEventArgs($class);
$this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
}
$this->loadedMetadata[$className] = $class;
$parent = $class;
@ -379,6 +381,13 @@ class ClassMetadataFactory
private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass)
{
foreach ($parentClass->associationMappings as $field => $mapping) {
if ($parentClass->isMappedSuperclass) {
if ($mapping['type'] & ClassMetadata::TO_MANY) {
throw MappingException::illegalToManyAssocationOnMappedSuperclass($parentClass->name, $field);
}
$mapping['sourceEntity'] = $subClass->name;
}
//$subclassMapping = $mapping;
if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
$mapping['inherited'] = $parentClass->name;

View File

@ -598,9 +598,10 @@ class ClassMetadataInfo
/**
* Gets the mapping of an association.
*
* @see ClassMetadataInfo::$associationMappings
* @param string $fieldName The field name that represents the association in
* the object model.
* @return Doctrine\ORM\Mapping\AssociationMapping The mapping.
* @return array The mapping.
*/
public function getAssociationMapping($fieldName)
{
@ -669,6 +670,10 @@ class ClassMetadataInfo
// Complete id mapping
if (isset($mapping['id']) && $mapping['id'] === true) {
if ($this->versionField == $mapping['fieldName']) {
throw MappingException::cannotVersionIdField($this->name, $mapping['fieldName']);
}
if ( ! in_array($mapping['fieldName'], $this->identifier)) {
$this->identifier[] = $mapping['fieldName'];
}
@ -947,6 +952,7 @@ class ClassMetadataInfo
public function setIdentifier(array $identifier)
{
$this->identifier = $identifier;
$this->isIdentifierComposite = (count($this->identifier) > 1);
}
/**
@ -1485,6 +1491,13 @@ class ClassMetadataInfo
if ( ! isset($columnDef['fieldName'])) {
$columnDef['fieldName'] = $columnDef['name'];
}
if ( ! isset($columnDef['type'])) {
$columnDef['type'] = "string";
}
if (in_array($columnDef['type'], array("boolean", "array", "object", "datetime", "time", "date"))) {
throw MappingException::invalidDiscriminatorColumnType($this->name, $columnDef['type']);
}
$this->discriminatorColumn = $columnDef;
}
}

View File

@ -155,7 +155,7 @@ abstract class AbstractFileDriver implements Driver
if ($this->_paths) {
foreach ((array) $this->_paths as $path) {
if ( ! is_dir($path)) {
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath();
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
}
$iterator = new \RecursiveIteratorIterator(

View File

@ -179,6 +179,8 @@ class AnnotationDriver implements Driver
'type' => $discrColumnAnnot->type,
'length' => $discrColumnAnnot->length
));
} else {
$metadata->setDiscriminatorColumn(array('name' => 'dtype', 'type' => 'string', 'length' => 255));
}
// Evaluate DiscriminatorMap annotation
@ -443,7 +445,7 @@ class AnnotationDriver implements Driver
foreach ($this->_paths as $path) {
if ( ! is_dir($path)) {
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath();
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
}
$iterator = new \RecursiveIteratorIterator(

View File

@ -48,7 +48,7 @@ class DatabaseDriver implements Driver
*/
private $tables = null;
private $classes = array();
private $classToTableNames = array();
/**
* @var array
@ -73,11 +73,11 @@ class DatabaseDriver implements Driver
}
foreach ($this->_sm->listTableNames() as $tableName) {
$tables[strtolower($tableName)] = $this->_sm->listTableDetails($tableName);
$tables[$tableName] = $this->_sm->listTableDetails($tableName);
}
$this->tables = array();
foreach ($tables AS $name => $table) {
foreach ($tables AS $tableName => $table) {
/* @var $table Table */
if ($this->_sm->getDatabasePlatform()->supportsForeignKeyConstraints()) {
$foreignKeys = $table->getForeignKeys();
@ -89,21 +89,23 @@ class DatabaseDriver implements Driver
foreach ($foreignKeys AS $foreignKey) {
$allForeignKeyColumns = array_merge($allForeignKeyColumns, $foreignKey->getLocalColumns());
}
$pkColumns = $table->getPrimaryKey()->getColumns();
sort($pkColumns);
sort($allForeignKeyColumns);
if ($pkColumns == $allForeignKeyColumns) {
if (count($table->getForeignKeys()) > 2) {
throw new \InvalidArgumentException("ManyToMany table '" . $name . "' with more or less than two foreign keys are not supported by the Database Reverese Engineering Driver.");
throw new \InvalidArgumentException("ManyToMany table '" . $tableName . "' with more or less than two foreign keys are not supported by the Database Reverese Engineering Driver.");
}
$this->manyToManyTables[$name] = $table;
$this->manyToManyTables[$tableName] = $table;
} else {
$className = Inflector::classify($name);
$this->tables[$name] = $table;
$this->classes[$className] = $name;
// lower-casing is necessary because of Oracle Uppercase Tablenames,
// assumption is lower-case + underscore separated.
$className = Inflector::classify(strtolower($tableName));
$this->tables[$tableName] = $table;
$this->classToTableNames[$className] = $tableName;
}
}
}
@ -115,11 +117,11 @@ class DatabaseDriver implements Driver
{
$this->reverseEngineerMappingFromDatabase();
if (!isset($this->classes[$className])) {
if (!isset($this->classToTableNames[$className])) {
throw new \InvalidArgumentException("Unknown class " . $className);
}
$tableName = Inflector::tableize($className);
$tableName = $this->classToTableNames[$className];
$metadata->name = $className;
$metadata->table['name'] = $tableName;
@ -183,7 +185,7 @@ class DatabaseDriver implements Driver
foreach ($this->manyToManyTables AS $manyTable) {
foreach ($manyTable->getForeignKeys() AS $foreignKey) {
if ($tableName == strtolower($foreignKey->getForeignTableName())) {
if (strtolower($tableName) == strtolower($foreignKey->getForeignTableName())) {
$myFk = $foreignKey;
foreach ($manyTable->getForeignKeys() AS $foreignKey) {
if ($foreignKey != $myFk) {
@ -269,6 +271,6 @@ class DatabaseDriver implements Driver
{
$this->reverseEngineerMappingFromDatabase();
return array_keys($this->classes);
return array_keys($this->classToTableNames);
}
}

View File

@ -114,8 +114,8 @@ final class Index extends Annotation {
final class JoinTable extends Annotation {
public $name;
public $schema;
public $joinColumns;
public $inverseJoinColumns;
public $joinColumns = array();
public $inverseJoinColumns = array();
}
final class SequenceGenerator extends Annotation {
public $sequenceName;

View File

@ -88,10 +88,15 @@ class DriverChain implements Driver
public function getAllClassNames()
{
$classNames = array();
foreach ($this->_drivers AS $driver) {
$classNames = array_merge($classNames, $driver->getAllClassNames());
foreach ($this->_drivers AS $namespace => $driver) {
$driverClasses = $driver->getAllClassNames();
foreach ($driverClasses AS $className) {
if (strpos($className, $namespace) === 0) {
$classNames[] = $className;
}
}
}
return $classNames;
return array_unique($classNames);
}
/**

View File

@ -78,7 +78,7 @@ class StaticPHPDriver implements Driver
foreach ($this->_paths as $path) {
if ( ! is_dir($path)) {
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath();
throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
}
$iterator = new \RecursiveIteratorIterator(

View File

@ -87,6 +87,8 @@ class XmlDriver extends AbstractFileDriver
'type' => (string)$discrColumn['type'],
'length' => (string)$discrColumn['length']
));
} else {
$metadata->setDiscriminatorColumn(array('name' => 'dtype', 'type' => 'string', 'length' => 255));
}
// Evaluate <discriminator-map...>

View File

@ -79,6 +79,8 @@ class YamlDriver extends AbstractFileDriver
'type' => $discrColumn['type'],
'length' => $discrColumn['length']
));
} else {
$metadata->setDiscriminatorColumn(array('name' => 'dtype', 'type' => 'string', 'length' => 255));
}
// Evaluate discriminatorMap

View File

@ -68,9 +68,9 @@ class MappingException extends \Doctrine\ORM\ORMException
return new self("No mapping file found named '$fileName' for class '$entityName'.");
}
public static function mappingNotFound($fieldName)
public static function mappingNotFound($className, $fieldName)
{
return new self("No mapping found for field '$fieldName'.");
return new self("No mapping found for field '$fieldName' on class '$className'.");
}
public static function oneToManyRequiresMappedBy($fieldName)
@ -170,9 +170,16 @@ class MappingException extends \Doctrine\ORM\ORMException
);
}
public static function fileMappingDriversRequireConfiguredDirectoryPath()
public static function fileMappingDriversRequireConfiguredDirectoryPath($path = null)
{
return new self('File mapping drivers must have a valid directory path, however the given path seems to be incorrect!');
if ( ! empty($path)) {
$path = '[' . $path . ']';
}
return new self(
'File mapping drivers must have a valid directory path, ' .
'however the given path ' . $path . ' seems to be incorrect!'
);
}
/**
@ -200,6 +207,16 @@ class MappingException extends \Doctrine\ORM\ORMException
return new self("Entity class '$className' is using inheritance but no discriminator column was defined.");
}
public static function invalidDiscriminatorColumnType($className, $type)
{
return new self("Discriminator column type on entity class '$className' is not allowed to be '$type'. 'string' or 'integer' type variables are suggested!");
}
public static function cannotVersionIdField($className, $fieldName)
{
return new self("Setting Id field '$fieldName' as versionale in entity class '$className' is not supported.");
}
/**
* @param string $className
* @param string $columnName
@ -209,4 +226,9 @@ class MappingException extends \Doctrine\ORM\ORMException
{
return new self("Duplicate definition of column '".$columnName."' on entity '".$className."' in a field or discriminator column mapping.");
}
public static function illegalToManyAssocationOnMappedSuperclass($className, $field)
{
return new self("It is illegal to put a one-to-many or many-to-many association on mapped superclass '".$className."#".$field."'.");
}
}

View File

@ -78,6 +78,14 @@ class ORMException extends Exception
);
}
public static function invalidFindByInverseAssociation($entityName, $associationFieldName)
{
return new self(
"You cannot search for the association field '".$entityName."#".$associationFieldName."', ".
"because it is the inverse side of an association. Find methods only work on owning side associations."
);
}
public static function invalidResultCacheDriver() {
return new self("Invalid result cache driver; it must implement \Doctrine\Common\Cache\Cache.");
}

View File

@ -196,7 +196,7 @@ final class PersistentCollection implements Collection
* Initializes the collection by loading its contents from the database
* if the collection is not yet initialized.
*/
private function initialize()
public function initialize()
{
if ( ! $this->initialized && $this->association) {
if ($this->isDirty) {

View File

@ -33,11 +33,18 @@ use Doctrine\ORM\Mapping\ClassMetadata,
abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
{
/**
* Map from column names to class names that declare the field the column is mapped to.
* Map from column names to class metadata instances that declare the field the column is mapped to.
*
* @var array
*/
private $_declaringClassMap = array();
private $declaringClassMap = array();
/**
* Map from column names to class names that declare the field the association with join column is mapped to.
*
* @var array
*/
private $declaringJoinColumnMap = array();
/**
* {@inheritdoc}
@ -70,8 +77,8 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
unset($sqlResult[$discrColumnName]);
foreach ($sqlResult as $column => $value) {
$realColumnName = $this->_resultColumnNames[$column];
if (isset($this->_declaringClassMap[$column])) {
$class = $this->_declaringClassMap[$column];
if (isset($this->declaringClassMap[$column])) {
$class = $this->declaringClassMap[$column];
if ($class->name == $entityName || is_subclass_of($entityName, $class->name)) {
$field = $class->fieldNames[$realColumnName];
if (isset($data[$field])) {
@ -81,6 +88,10 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
->convertToPHPValue($value, $this->_platform);
}
}
} else if (isset($this->declaringJoinColumnMap[$column])) {
if ($this->declaringJoinColumnMap[$column] == $entityName || is_subclass_of($entityName, $this->declaringJoinColumnMap[$column])) {
$data[$realColumnName] = $value;
}
} else {
$data[$realColumnName] = $value;
}
@ -99,9 +110,21 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
$columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++);
if ( ! isset($this->_resultColumnNames[$columnAlias])) {
$this->_resultColumnNames[$columnAlias] = $columnName;
$this->_declaringClassMap[$columnAlias] = $class;
$this->declaringClassMap[$columnAlias] = $class;
}
return "$sql AS $columnAlias";
}
protected function getSelectJoinColumnSQL($tableAlias, $joinColumnName, $className)
{
$columnAlias = $joinColumnName . $this->_sqlAliasCounter++;
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! isset($this->_resultColumnNames[$resultColumnName])) {
$this->_resultColumnNames[$resultColumnName] = $joinColumnName;
$this->declaringJoinColumnMap[$resultColumnName] = $className;
}
return $tableAlias . ".$joinColumnName AS $columnAlias";
}
}

View File

@ -251,6 +251,8 @@ class BasicEntityPersister
$sql = "SELECT " . $versionFieldColumnName . " FROM " . $class->getQuotedTableName($this->_platform)
. " WHERE " . implode(' = ? AND ', $identifier) . " = ?";
$value = $this->_conn->fetchColumn($sql, array_values((array)$id));
$value = Type::getType($class->fieldMappings[$versionField]['type'])->convertToPHPValue($value, $this->_platform);
$this->_class->setFieldValue($entity, $versionField, $value);
}
@ -273,7 +275,15 @@ class BasicEntityPersister
$updateData = $this->_prepareUpdateData($entity);
$tableName = $this->_class->table['name'];
if (isset($updateData[$tableName]) && $updateData[$tableName]) {
$this->_updateTable($entity, $tableName, $updateData[$tableName], $this->_class->isVersioned);
$this->_updateTable(
$entity, $this->_class->getQuotedTableName($this->_platform),
$updateData[$tableName], $this->_class->isVersioned
);
if ($this->_class->isVersioned) {
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity);
$this->_assignDefaultVersionValue($this->_class, $entity, $id);
}
}
}
@ -282,11 +292,11 @@ class BasicEntityPersister
* The UPDATE can optionally be versioned, which requires the entity to have a version field.
*
* @param object $entity The entity object being updated.
* @param string $tableName The name of the table to apply the UPDATE on.
* @param string $quotedTableName The quoted name of the table to apply the UPDATE on.
* @param array $updateData The map of columns to update (column => value).
* @param boolean $versioned Whether the UPDATE should be versioned.
*/
protected final function _updateTable($entity, $tableName, array $updateData, $versioned = false)
protected final function _updateTable($entity, $quotedTableName, array $updateData, $versioned = false)
{
$set = $params = $types = array();
@ -317,7 +327,7 @@ class BasicEntityPersister
if ($versioned) {
$versionField = $this->_class->versionField;
$versionFieldType = $this->_class->getTypeOfField($versionField);
$versionFieldType = $this->_class->fieldMappings[$versionField]['type'];
$versionColumn = $this->_class->getQuotedColumnName($versionField, $this->_platform);
if ($versionFieldType == Type::INTEGER) {
$set[] = $versionColumn . ' = ' . $versionColumn . ' + 1';
@ -329,7 +339,7 @@ class BasicEntityPersister
$types[] = $this->_class->fieldMappings[$versionField]['type'];
}
$sql = "UPDATE $tableName SET " . implode(', ', $set)
$sql = "UPDATE $quotedTableName SET " . implode(', ', $set)
. ' WHERE ' . implode(' = ? AND ', $where) . ' = ?';
$result = $this->_conn->executeUpdate($sql, $params, $types);
@ -393,17 +403,7 @@ class BasicEntityPersister
$this->deleteJoinTableRecords($identifier);
$id = array_combine($this->_class->getIdentifierColumnNames(), $identifier);
$this->_conn->delete($this->_class->table['name'], $id);
}
/**
* Gets the ClassMetadata instance of the entity class this persister is used for.
*
* @return Doctrine\ORM\Mapping\ClassMetadata
*/
public function getClassMetadata()
{
return $this->_class;
$this->_conn->delete($this->_class->getQuotedTableName($this->_platform), $id);
}
/**
@ -660,7 +660,7 @@ class BasicEntityPersister
if ($found = $this->_em->getUnitOfWork()->tryGetById($joinColumnValues, $targetClass->rootEntityName)) {
$this->_class->reflFields[$field]->setValue($entity, $found);
// Complete inverse side, if necessary.
if ($assoc['inversedBy']) {
if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) {
$inverseAssoc = $targetClass->associationMappings[$assoc['inversedBy']];
$targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($found, $entity);
}
@ -681,6 +681,9 @@ class BasicEntityPersister
}
} else if ($value instanceof PersistentCollection && $value->isInitialized()) {
$value->setInitialized(false);
// no matter if dirty or non-dirty entities are already loaded, smoke them out!
// the beauty of it being, they are still in the identity map
$value->unwrap()->clear();
$newData[$field] = $value;
}
}
@ -848,8 +851,8 @@ class BasicEntityPersister
}
return 'SELECT ' . $this->_getSelectColumnListSQL()
. ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' '
. $this->_getSQLTableAlias($this->_class->name)
. $this->_platform->appendLockHint(' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' '
. $this->_getSQLTableAlias($this->_class->name), $lockMode)
. $joinSql
. ($conditionSql ? ' WHERE ' . $conditionSql : '')
. $orderBySql
@ -1086,7 +1089,7 @@ class BasicEntityPersister
}
$sql = 'SELECT 1 '
. $this->getLockTablesSql()
. $this->_platform->appendLockHint($this->getLockTablesSql(), $lockMode)
. ($conditionSql ? ' WHERE ' . $conditionSql : '') . ' ' . $lockSql;
$params = array_values($criteria);
$this->_conn->executeQuery($sql, $params);
@ -1128,9 +1131,16 @@ class BasicEntityPersister
}
$conditionSql .= $this->_class->getQuotedColumnName($field, $this->_platform);
} else if (isset($this->_class->associationMappings[$field])) {
// TODO: Inherited?
// TODO: Composite Keys as Foreign Key PK? That would be super ugly! And should probably be disallowed ;)
$conditionSql .= $this->_getSQLTableAlias($this->_class->name) . '.';
if (!$this->_class->associationMappings[$field]['isOwningSide']) {
throw ORMException::invalidFindByInverseAssociation($this->_class->name, $field);
}
if (isset($this->_class->associationMappings[$field]['inherited'])) {
$conditionSql .= $this->_getSQLTableAlias($this->_class->associationMappings[$field]['inherited']) . '.';
} else {
$conditionSql .= $this->_getSQLTableAlias($this->_class->name) . '.';
}
$conditionSql .= $this->_class->associationMappings[$field]['joinColumns'][0]['name'];
} else if ($assoc !== null) {

View File

@ -35,9 +35,18 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
/**
* Map that maps column names to the table names that own them.
* This is mainly a temporary cache, used during a single request.
*
* @var array
*/
private $_owningTableMap = array();
/**
* Map of table to quoted table names.
*
* @var array
*/
private $_quotedTableMap = array();
/**
* {@inheritdoc}
*/
@ -74,18 +83,16 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
*/
public function getOwningTable($fieldName)
{
if ( ! isset($this->_owningTableMap[$fieldName])) {
if (!isset($this->_owningTableMap[$fieldName])) {
if (isset($this->_class->associationMappings[$fieldName]['inherited'])) {
$this->_owningTableMap[$fieldName] = $this->_em->getClassMetadata(
$this->_class->associationMappings[$fieldName]['inherited']
)->table['name'];
$cm = $this->_em->getClassMetadata($this->_class->associationMappings[$fieldName]['inherited']);
} else if (isset($this->_class->fieldMappings[$fieldName]['inherited'])) {
$this->_owningTableMap[$fieldName] = $this->_em->getClassMetadata(
$this->_class->fieldMappings[$fieldName]['inherited']
)->table['name'];
$cm = $this->_em->getClassMetadata($this->_class->fieldMappings[$fieldName]['inherited']);
} else {
$this->_owningTableMap[$fieldName] = $this->_class->table['name'];
$cm = $this->_class;
}
$this->_owningTableMap[$fieldName] = $cm->table['name'];
$this->_quotedTableMap[$cm->table['name']] = $cm->getQuotedTableName($this->_platform);
}
return $this->_owningTableMap[$fieldName];
@ -186,17 +193,21 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
$updateData = $this->_prepareUpdateData($entity);
if ($isVersioned = $this->_class->isVersioned) {
$versionedTable = $this->_getVersionedClassMetadata()->table['name'];
$versionedClass = $this->_getVersionedClassMetadata();
$versionedTable = $versionedClass->table['name'];
}
if ($updateData) {
foreach ($updateData as $tableName => $data) {
$this->_updateTable($entity, $tableName, $data, $isVersioned && $versionedTable == $tableName);
$this->_updateTable($entity, $this->_quotedTableMap[$tableName], $data, $isVersioned && $versionedTable == $tableName);
}
// Make sure the table with the version column is updated even if no columns on that
// table were affected.
if ($isVersioned && ! isset($updateData[$versionedTable])) {
$this->_updateTable($entity, $versionedTable, array(), true);
$this->_updateTable($entity, $versionedClass->getQuotedTableName($this->_platform), array(), true);
$id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity);
$this->_assignDefaultVersionValue($this->_class, $entity, $id);
}
}
}
@ -252,12 +263,10 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
$this->_getSQLTableAlias($assoc2['inherited'])
: $baseTableAlias;
foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) {
$columnAlias = $srcColumn . $this->_sqlAliasCounter++;
$columnList .= ", $tableAlias.$srcColumn AS $columnAlias";
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! isset($this->_resultColumnNames[$resultColumnName])) {
$this->_resultColumnNames[$resultColumnName] = $srcColumn;
}
if ($columnList != '') $columnList .= ', ';
$columnList .= $this->getSelectJoinColumnSQL($tableAlias, $srcColumn,
isset($assoc2['inherited']) ? $assoc2['inherited'] : $this->_class->name
);
}
}
}
@ -307,12 +316,10 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
if ($assoc2['isOwningSide'] && $assoc2['type'] & ClassMetadata::TO_ONE
&& ! isset($assoc2['inherited'])) {
foreach ($assoc2['targetToSourceKeyColumns'] as $srcColumn) {
$columnAlias = $srcColumn . $this->_sqlAliasCounter++;
$columnList .= ', ' . $tableAlias . ".$srcColumn AS $columnAlias";
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! isset($this->_resultColumnNames[$resultColumnName])) {
$this->_resultColumnNames[$resultColumnName] = $srcColumn;
}
if ($columnList != '') $columnList .= ', ';
$columnList .= $this->getSelectJoinColumnSQL($tableAlias, $srcColumn,
isset($assoc2['inherited']) ? $assoc2['inherited'] : $subClass->name
);
}
}
}
@ -354,6 +361,7 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
*/
public function getLockTablesSql()
{
$idColumns = $this->_class->getIdentifierColumnNames();
$baseTableAlias = $this->_getSQLTableAlias($this->_class->name);
// INNER JOIN parent tables

View File

@ -63,12 +63,10 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
foreach ($subClass->associationMappings as $assoc) {
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE && ! isset($assoc['inherited'])) {
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
$columnAlias = $srcColumn . $this->_sqlAliasCounter++;
$columnList .= ', ' . $tableAlias . ".$srcColumn AS $columnAlias";
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
if ( ! isset($this->_resultColumnNames[$resultColumnName])) {
$this->_resultColumnNames[$resultColumnName] = $srcColumn;
}
if ($columnList != '') $columnList .= ', ';
$columnList .= $this->getSelectJoinColumnSQL($tableAlias, $srcColumn,
isset($assoc['inherited']) ? $assoc['inherited'] : $this->_class->name
);
}
}
}

View File

@ -77,9 +77,11 @@ class ProxyFactory
$proxyClassName = str_replace('\\', '', $className) . 'Proxy';
$fqn = $this->_proxyNamespace . '\\' . $proxyClassName;
if ($this->_autoGenerate && ! class_exists($fqn, false)) {
if (! class_exists($fqn, false)) {
$fileName = $this->_proxyDir . DIRECTORY_SEPARATOR . $proxyClassName . '.php';
$this->_generateProxyClass($this->_em->getClassMetadata($className), $proxyClassName, $fileName, self::$_proxyClassTemplate);
if ($this->_autoGenerate) {
$this->_generateProxyClass($this->_em->getClassMetadata($className), $proxyClassName, $fileName, self::$_proxyClassTemplate);
}
require $fileName;
}

View File

@ -242,10 +242,14 @@ final class Query extends AbstractQuery
}
if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value))) {
//TODO: Check that $value is MANAGED?
$values = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
if ($this->_em->getUnitOfWork()->getEntityState($value) == UnitOfWork::STATE_MANAGED) {
$idValues = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
} else {
$class = $this->_em->getClassMetadata(get_class($value));
$idValues = $class->getIdentifierValues($value);
}
$sqlPositions = $paramMappings[$key];
$sqlParams += array_combine((array)$sqlPositions, $values);
$sqlParams += array_combine((array)$sqlPositions, $idValues);
} else {
foreach ($paramMappings[$key] as $position) {
$sqlParams[$position] = $value;
@ -544,4 +548,15 @@ final class Query extends AbstractQuery
'&hydrationMode='.$this->_hydrationMode.'DOCTRINE_QUERY_CACHE_SALT'
);
}
/**
* Cleanup Query resource when clone is called.
*
* @return void
*/
public function __clone()
{
parent::__clone();
$this->_state = self::STATE_DIRTY;
}
}

View File

@ -41,8 +41,9 @@ class LengthFunction extends FunctionNode
*/
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
//TODO: Use platform to get SQL
return 'LENGTH(' . $sqlWalker->walkStringPrimary($this->stringPrimary) . ')';
return $sqlWalker->getConnection()->getDatabasePlatform()->getLengthExpression(
$sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary)
);
}
/**

View File

@ -41,8 +41,9 @@ class LowerFunction extends FunctionNode
*/
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
//TODO: Use platform to get SQL
return 'LOWER(' . $sqlWalker->walkStringPrimary($this->stringPrimary) . ')';
return $sqlWalker->getConnection()->getDatabasePlatform()->getLowerExpression(
$sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary)
);
}
/**

View File

@ -42,12 +42,10 @@ class ModFunction extends FunctionNode
*/
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
//TODO: Use platform to get SQL
return 'MOD('
. $sqlWalker->walkSimpleArithmeticExpression($this->firstSimpleArithmeticExpression)
. ', '
. $sqlWalker->walkSimpleArithmeticExpression($this->secondSimpleArithmeticExpression)
. ')';
return $sqlWalker->getConnection()->getDatabasePlatform()->getModExpression(
$sqlWalker->walkSimpleArithmeticExpression($this->firstSimpleArithmeticExpression),
$sqlWalker->walkSimpleArithmeticExpression($this->secondSimpleArithmeticExpression)
);
}
/**

View File

@ -41,8 +41,9 @@ class UpperFunction extends FunctionNode
*/
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
//TODO: Use platform to get SQL
return 'UPPER(' . $sqlWalker->walkStringPrimary($this->stringPrimary) . ')';
return $sqlWalker->getConnection()->getDatabasePlatform()->getUpperExpression(
$sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary)
);
}
/**

View File

@ -125,6 +125,11 @@ class Parser
*/
private $_customOutputWalker;
/**
* @var array
*/
private $_identVariableExpressions = array();
/**
* Creates a new query parser object.
*
@ -272,6 +277,9 @@ class Parser
{
$AST = $this->getAST();
$this->fixIdentificationVariableOrder($AST);
$this->assertSelectEntityRootAliasRequirement();
if (($customWalkers = $this->_query->getHint(Query::HINT_CUSTOM_TREE_WALKERS)) !== false) {
$this->_customTreeWalkers = $customWalkers;
}
@ -312,6 +320,46 @@ class Parser
return $this->_parserResult;
}
private function assertSelectEntityRootAliasRequirement()
{
if ( count($this->_identVariableExpressions) > 0) {
$foundRootEntity = false;
foreach ($this->_identVariableExpressions AS $dqlAlias => $expr) {
if (isset($this->_queryComponents[$dqlAlias]) && $this->_queryComponents[$dqlAlias]['parent'] === null) {
$foundRootEntity = true;
}
}
if (!$foundRootEntity) {
$this->semanticalError('Cannot select entity through identification variables without choosing at least one root entity alias.');
}
}
}
/**
* Fix order of identification variables.
*
* They have to appear in the select clause in the same order as the
* declarations (from ... x join ... y join ... z ...) appear in the query
* as the hydration process relies on that order for proper operation.
*
* @param AST\SelectStatement|AST\DeleteStatement|AST\UpdateStatement $AST
* @return void
*/
private function fixIdentificationVariableOrder($AST)
{
if ( count($this->_identVariableExpressions) > 1) {
foreach ($this->_queryComponents as $dqlAlias => $qComp) {
if (isset($this->_identVariableExpressions[$dqlAlias])) {
$expr = $this->_identVariableExpressions[$dqlAlias];
$key = array_search($expr, $AST->selectClause->selectExpressions);
unset($AST->selectClause->selectExpressions[$key]);
$AST->selectClause->selectExpressions[] = $expr;
}
}
}
}
/**
* Generates a new syntax error.
@ -1628,6 +1676,7 @@ class Parser
public function SelectExpression()
{
$expression = null;
$identVariable = null;
$fieldAliasIdentificationVariable = null;
$peek = $this->_lexer->glimpse();
@ -1639,7 +1688,7 @@ class Parser
$expression = $this->ScalarExpression();
} else {
$supportsAlias = false;
$expression = $this->IdentificationVariable();
$expression = $identVariable = $this->IdentificationVariable();
}
} else if ($this->_lexer->lookahead['value'] == '(') {
if ($peek['type'] == Lexer::T_SELECT) {
@ -1666,6 +1715,7 @@ class Parser
} else if ($this->_lexer->lookahead['type'] == Lexer::T_PARTIAL) {
$supportsAlias = false;
$expression = $this->PartialObjectExpression();
$identVariable = $expression->identificationVariable;
} else if ($this->_lexer->lookahead['type'] == Lexer::T_INTEGER ||
$this->_lexer->lookahead['type'] == Lexer::T_FLOAT) {
// Shortcut: ScalarExpression => SimpleArithmeticExpression
@ -1694,7 +1744,11 @@ class Parser
}
}
return new AST\SelectExpression($expression, $fieldAliasIdentificationVariable);
$expr = new AST\SelectExpression($expression, $fieldAliasIdentificationVariable);
if (!$supportsAlias) {
$this->_identVariableExpressions[$identVariable] = $expr;
}
return $expr;
}
/**

View File

@ -689,7 +689,7 @@ class SqlWalker implements TreeWalker
$sql = $this->_scalarResultAliasMap[$columnName];
}
return $sql . ' ' . strtoupper($orderByItem->type);;
return $sql . ' ' . strtoupper($orderByItem->type);
}
/**

View File

@ -888,6 +888,40 @@ class QueryBuilder
. (isset($options['post']) ? $options['post'] : '');
}
/**
* Reset DQL parts
*
* @param array $parts
* @return QueryBuilder
*/
public function resetDQLParts($parts = null)
{
if (is_null($parts)) {
$parts = array_keys($this->_dqlParts);
}
foreach ($parts as $part) {
$this->resetDQLPart($part);
}
return $this;
}
/**
* Reset single DQL part
*
* @param string $part
* @return QueryBuilder;
*/
public function resetDQLPart($part)
{
if (is_array($this->_dqlParts[$part])) {
$this->_dqlParts[$part] = array();
} else {
$this->_dqlParts[$part] = null;
}
$this->_state = self::STATE_DIRTY;
return $this;
}
/**
* Gets a string representation of this QueryBuilder which corresponds to
* the final DQL query being constructed.
@ -898,4 +932,24 @@ class QueryBuilder
{
return $this->getDQL();
}
/**
* Deep clone of all expression objects in the DQL parts.
*
* @return void
*/
public function __clone()
{
foreach ($this->_dqlParts AS $part => $elements) {
if (is_array($this->_dqlParts[$part])) {
foreach ($this->_dqlParts[$part] AS $idx => $element) {
if (is_object($element)) {
$this->_dqlParts[$part][$idx] = clone $element;
}
}
} else if (\is_object($elements)) {
$this->_dqlParts[$part] = clone $elements;
}
}
}
}

View File

@ -49,19 +49,19 @@ class ResultCommand extends Console\Command\Command
->setDescription('Clear result cache of the various cache drivers.')
->setDefinition(array(
new InputOption(
'id', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY,
'id', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'ID(s) of the cache entry to delete (accepts * wildcards).', array()
),
new InputOption(
'regex', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY,
'regex', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Delete cache entries that match the given regular expression(s).', array()
),
new InputOption(
'prefix', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY,
'prefix', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Delete cache entries that have the given prefix(es).', array()
),
new InputOption(
'suffix', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY,
'suffix', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Delete cache entries that have the given suffix(es).', array()
),
))

View File

@ -112,16 +112,16 @@ class ConvertDoctrine1SchemaCommand extends Console\Command\Command
'The path to generate your Doctrine 2.X mapping information.'
),
new InputOption(
'from', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY,
'from', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Optional paths of Doctrine 1.X schema information.',
array()
),
new InputOption(
'extend', null, InputOption::PARAMETER_OPTIONAL,
'extend', null, InputOption::VALUE_OPTIONAL,
'Defines a base class to be extended by generated entity classes.'
),
new InputOption(
'num-spaces', null, InputOption::PARAMETER_OPTIONAL,
'num-spaces', null, InputOption::VALUE_OPTIONAL,
'Defines the number of indentation spaces', 4
)
))

View File

@ -53,7 +53,7 @@ class ConvertMappingCommand extends Console\Command\Command
->setDescription('Convert mapping information between supported formats.')
->setDefinition(array(
new InputOption(
'filter', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY,
'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'A string pattern used to match entities that should be processed.'
),
new InputArgument(
@ -67,11 +67,11 @@ class ConvertMappingCommand extends Console\Command\Command
'from-database', null, null, 'Whether or not to convert mapping information from existing database.'
),
new InputOption(
'extend', null, InputOption::PARAMETER_OPTIONAL,
'extend', null, InputOption::VALUE_OPTIONAL,
'Defines a base class to be extended by generated entity classes.'
),
new InputOption(
'num-spaces', null, InputOption::PARAMETER_OPTIONAL,
'num-spaces', null, InputOption::VALUE_OPTIONAL,
'Defines the number of indentation spaces', 4
)
))
@ -96,7 +96,8 @@ EOT
);
}
$cmf = new DisconnectedClassMetadataFactory($em);
$cmf = new DisconnectedClassMetadataFactory();
$cmf->setEntityManager($em);
$metadata = $cmf->getAllMetadata();
$metadata = MetadataFilter::filter($metadata, $input->getOption('filter'));

View File

@ -49,7 +49,7 @@ class EnsureProductionSettingsCommand extends Console\Command\Command
->setDescription('Verify that Doctrine is properly configured for a production environment.')
->setDefinition(array(
new InputOption(
'complete', null, InputOption::PARAMETER_NONE,
'complete', null, InputOption::VALUE_NONE,
'Flag to also inspect database connection existance.'
)
))

View File

@ -52,34 +52,34 @@ class GenerateEntitiesCommand extends Console\Command\Command
->setDescription('Generate entity classes and method stubs from your mapping information.')
->setDefinition(array(
new InputOption(
'filter', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY,
'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'A string pattern used to match entities that should be processed.'
),
new InputArgument(
'dest-path', InputArgument::REQUIRED, 'The path to generate your entity classes.'
),
new InputOption(
'generate-annotations', null, InputOption::PARAMETER_OPTIONAL,
'generate-annotations', null, InputOption::VALUE_OPTIONAL,
'Flag to define if generator should generate annotation metadata on entities.', false
),
new InputOption(
'generate-methods', null, InputOption::PARAMETER_OPTIONAL,
'generate-methods', null, InputOption::VALUE_OPTIONAL,
'Flag to define if generator should generate stub methods on entities.', true
),
new InputOption(
'regenerate-entities', null, InputOption::PARAMETER_OPTIONAL,
'regenerate-entities', null, InputOption::VALUE_OPTIONAL,
'Flag to define if generator should regenerate entity if it exists.', false
),
new InputOption(
'update-entities', null, InputOption::PARAMETER_OPTIONAL,
'update-entities', null, InputOption::VALUE_OPTIONAL,
'Flag to define if generator should only update entity if it exists.', true
),
new InputOption(
'extend', null, InputOption::PARAMETER_OPTIONAL,
'extend', null, InputOption::VALUE_OPTIONAL,
'Defines a base class to be extended by generated entity classes.'
),
new InputOption(
'num-spaces', null, InputOption::PARAMETER_OPTIONAL,
'num-spaces', null, InputOption::VALUE_OPTIONAL,
'Defines the number of indentation spaces', 4
)
))
@ -96,7 +96,8 @@ EOT
{
$em = $this->getHelper('em')->getEntityManager();
$cmf = new DisconnectedClassMetadataFactory($em);
$cmf = new DisconnectedClassMetadataFactory();
$cmf->setEntityManager($em);
$metadatas = $cmf->getAllMetadata();
$metadatas = MetadataFilter::filter($metadatas, $input->getOption('filter'));

View File

@ -50,7 +50,7 @@ class GenerateProxiesCommand extends Console\Command\Command
->setDescription('Generates proxy classes for entity classes.')
->setDefinition(array(
new InputOption(
'filter', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY,
'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'A string pattern used to match entities that should be processed.'
),
new InputArgument(

View File

@ -51,7 +51,7 @@ class GenerateRepositoriesCommand extends Console\Command\Command
->setDescription('Generate repository classes from your mapping information.')
->setDefinition(array(
new InputOption(
'filter', null, InputOption::PARAMETER_REQUIRED | InputOption::PARAMETER_IS_ARRAY,
'filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'A string pattern used to match entities that should be processed.'
),
new InputArgument(

View File

@ -50,20 +50,20 @@ class RunDqlCommand extends Console\Command\Command
->setDefinition(array(
new InputArgument('dql', InputArgument::REQUIRED, 'The DQL to execute.'),
new InputOption(
'hydrate', null, InputOption::PARAMETER_REQUIRED,
'hydrate', null, InputOption::VALUE_REQUIRED,
'Hydration mode of result set. Should be either: object, array, scalar or single-scalar.',
'object'
),
new InputOption(
'first-result', null, InputOption::PARAMETER_REQUIRED,
'first-result', null, InputOption::VALUE_REQUIRED,
'The first result in the result set.'
),
new InputOption(
'max-result', null, InputOption::PARAMETER_REQUIRED,
'max-result', null, InputOption::VALUE_REQUIRED,
'The maximum number of results in the result set.'
),
new InputOption(
'depth', null, InputOption::PARAMETER_REQUIRED,
'depth', null, InputOption::VALUE_REQUIRED,
'Dumping depth of Entity graph.', 7
)
))
@ -114,7 +114,7 @@ EOT
throw new \LogicException("Option 'max-result' must contains an integer value");
}
$query->setMaxResult((int) $maxResult);
$query->setMaxResults((int) $maxResult);
}
$resultSet = $query->execute(array(), constant($hydrationMode));

View File

@ -53,7 +53,7 @@ class CreateCommand extends AbstractCommand
)
->setDefinition(array(
new InputOption(
'dump-sql', null, InputOption::PARAMETER_NONE,
'dump-sql', null, InputOption::VALUE_NONE,
'Instead of try to apply generated SQLs into EntityManager Storage Connection, output them.'
)
))
@ -65,6 +65,8 @@ EOT
protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas)
{
$output->write('ATTENTION: This operation should not be executed in an production enviroment.' . PHP_EOL . PHP_EOL);
if ($input->getOption('dump-sql') === true) {
$sqls = $schemaTool->getCreateSchemaSql($metadatas);
$output->write(implode(';' . PHP_EOL, $sqls) . PHP_EOL);

View File

@ -49,13 +49,21 @@ class DropCommand extends AbstractCommand
$this
->setName('orm:schema-tool:drop')
->setDescription(
'Processes the schema and either drop the database schema of EntityManager Storage Connection or generate the SQL output.'
'Drop the complete database schema of EntityManager Storage Connection or generate the corresponding SQL output.'
)
->setDefinition(array(
new InputOption(
'dump-sql', null, InputOption::PARAMETER_NONE,
'dump-sql', null, InputOption::VALUE_NONE,
'Instead of try to apply generated SQLs into EntityManager Storage Connection, output them.'
)
),
new InputOption(
'force', null, InputOption::VALUE_NONE,
"Don't ask for the deletion of the database, but force the operation to run."
),
new InputOption(
'full-database', null, InputOption::VALUE_NONE,
'Instead of using the Class Metadata to detect the database table schema, drop ALL assets that the database contains.'
),
))
->setHelp(<<<EOT
Processes the schema and either drop the database schema of EntityManager Storage Connection or generate the SQL output.
@ -66,13 +74,38 @@ EOT
protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas)
{
$isFullDatabaseDrop = ($input->getOption('full-database'));
if ($input->getOption('dump-sql') === true) {
$sqls = $schemaTool->getDropSchemaSql($metadatas);
if ($isFullDatabaseDrop) {
$sqls = $schemaTool->getDropDatabaseSQL();
} else {
$sqls = $schemaTool->getDropSchemaSQL($metadatas);
}
$output->write(implode(';' . PHP_EOL, $sqls) . PHP_EOL);
} else {
} else if ($input->getOption('force') === true) {
$output->write('Dropping database schema...' . PHP_EOL);
$schemaTool->dropSchema($metadatas);
if ($isFullDatabaseDrop) {
$schemaTool->dropDatabase();
} else {
$schemaTool->dropSchema($metadatas);
}
$output->write('Database schema dropped successfully!' . PHP_EOL);
} else {
$output->write('ATTENTION: This operation should not be executed in an production enviroment.' . PHP_EOL . PHP_EOL);
if ($isFullDatabaseDrop) {
$sqls = $schemaTool->getDropDatabaseSQL();
} else {
$sqls = $schemaTool->getDropSchemaSQL($metadatas);
}
if (count($sqls)) {
$output->write('Schema-Tool would execute ' . count($sqls) . ' queries to drop the database.' . PHP_EOL);
$output->write('Please run the operation with --force to execute these queries or use --dump-sql to see them.' . PHP_EOL);
} else {
$output->write('Nothing to drop. The database is empty!' . PHP_EOL);
}
}
}
}

View File

@ -53,13 +53,17 @@ class UpdateCommand extends AbstractCommand
)
->setDefinition(array(
new InputOption(
'complete', null, InputOption::PARAMETER_NONE,
'complete', null, InputOption::VALUE_NONE,
'If defined, all assets of the database which are not relevant to the current metadata will be dropped.'
),
new InputOption(
'dump-sql', null, InputOption::PARAMETER_NONE,
'dump-sql', null, InputOption::VALUE_NONE,
'Instead of try to apply generated SQLs into EntityManager Storage Connection, output them.'
)
),
new InputOption(
'force', null, InputOption::VALUE_NONE,
"Don't ask for the incremental update of the database, but force the operation to run."
),
))
->setHelp(<<<EOT
Processes the schema and either update the database schema of EntityManager Storage Connection or generate the SQL output.
@ -72,15 +76,28 @@ EOT
protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas)
{
// Defining if update is complete or not (--complete not defined means $saveMode = true)
$saveMode = ($input->getOption('complete') === true);
$saveMode = ($input->getOption('complete') !== true);
if ($input->getOption('dump-sql') === true) {
$sqls = $schemaTool->getUpdateSchemaSql($metadatas, $saveMode);
$output->write(implode(';' . PHP_EOL, $sqls) . PHP_EOL);
} else {
} else if ($input->getOption('force') === true) {
$output->write('Updating database schema...' . PHP_EOL);
$schemaTool->updateSchema($metadatas, $saveMode);
$output->write('Database schema updated successfully!' . PHP_EOL);
} else {
$output->write('ATTENTION: This operation should not be executed in an production enviroment.' . PHP_EOL);
$output->write('Use the incremental update to detect changes during development and use' . PHP_EOL);
$output->write('this SQL DDL to manually update your database in production.' . PHP_EOL . PHP_EOL);
$sqls = $schemaTool->getUpdateSchemaSql($metadatas, $saveMode);
if (count($sqls)) {
$output->write('Schema-Tool would execute ' . count($sqls) . ' queries to update the database.' . PHP_EOL);
$output->write('Please run the operation with --force to execute these queries or use --dump-sql to see them.' . PHP_EOL);
} else {
$output->write('Nothing to update. The database is in sync with the current entity metadata.' . PHP_EOL);
}
}
}
}

View File

@ -0,0 +1,69 @@
<?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 LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Tools\Console;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Helper\HelperSet;
class ConsoleRunner
{
/**
* Run console with the given helperset.
*
* @param \Symfony\Component\Console\Helper\HelperSet $helperSet
* @return void
*/
static public function run(HelperSet $helperSet)
{
$cli = new Application('Doctrine Command Line Interface', \Doctrine\ORM\Version::VERSION);
$cli->setCatchExceptions(true);
$cli->setHelperSet($helperSet);
self::addCommands($cli);
$cli->run();
}
/**
* @param Application $cli
*/
static public function addCommands(Application $cli)
{
$cli->addCommands(array(
// DBAL Commands
new \Doctrine\DBAL\Tools\Console\Command\RunSqlCommand(),
new \Doctrine\DBAL\Tools\Console\Command\ImportCommand(),
// ORM Commands
new \Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand(),
new \Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand(),
new \Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand(),
new \Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand(),
new \Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand(),
new \Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand(),
new \Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand(),
new \Doctrine\ORM\Tools\Console\Command\ConvertDoctrine1SchemaCommand(),
new \Doctrine\ORM\Tools\Console\Command\GenerateRepositoriesCommand(),
new \Doctrine\ORM\Tools\Console\Command\GenerateEntitiesCommand(),
new \Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand(),
new \Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand(),
new \Doctrine\ORM\Tools\Console\Command\RunDqlCommand(),
new \Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand(),
));
}
}

View File

@ -188,7 +188,6 @@ class ConvertDoctrine1Schema
$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO);
} else if (isset($column['sequence'])) {
$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE);
$metadata->setSequenceGeneratorDefinition($definition);
$definition = array(
'sequenceName' => is_array($column['sequence']) ? $column['sequence']['name']:$column['sequence']
);
@ -198,6 +197,7 @@ class ConvertDoctrine1Schema
if (isset($column['sequence']['value'])) {
$definition['initialValue'] = $column['sequence']['value'];
}
$metadata->setSequenceGeneratorDefinition($definition);
}
return $fieldMapping;
}

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* 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
@ -33,7 +31,6 @@ use Doctrine\ORM\Mapping\ClassMetadataInfo;
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision$
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
@ -46,7 +43,13 @@ class DisconnectedClassMetadataFactory extends ClassMetadataFactory
*/
protected function newClassMetadataInstance($className)
{
return new ClassMetadataInfo($className);
$metadata = new ClassMetadataInfo($className);
if (strpos($className, "\\") !== false) {
$metadata->namespace = strrev(substr( strrev($className), strpos(strrev($className), "\\")+1 ));
} else {
$metadata->namespace = "";
}
return $metadata;
}
/**

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* 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
@ -82,7 +80,7 @@ class EntityGenerator
private static $_classTemplate =
'<?php
<namespace><use>
<namespace>
<entityAnnotation>
<entityClassName>
@ -189,7 +187,6 @@ public function <methodName>()
{
$placeHolders = array(
'<namespace>',
'<use>',
'<entityAnnotation>',
'<entityClassName>',
'<entityBody>'
@ -197,7 +194,6 @@ public function <methodName>()
$replacements = array(
$this->_generateEntityNamespace($metadata),
$this->_generateEntityUse($metadata),
$this->_generateEntityDocBlock($metadata),
$this->_generateEntityClassName($metadata),
$this->_generateEntityBody($metadata)
@ -222,7 +218,7 @@ public function <methodName>()
$body = str_replace('<spaces>', $this->_spaces, $body);
$last = strrpos($currentCode, '}');
return substr($currentCode, 0, $last) . $body . "\n}";
return substr($currentCode, 0, $last) . $body . (strlen($body) > 0 ? "\n" : ''). "}";
}
/**
@ -309,13 +305,6 @@ public function <methodName>()
}
}
private function _generateEntityUse(ClassMetadataInfo $metadata)
{
if ($this->_extendsClass()) {
return "\n\nuse " . $this->_getClassToExtendNamespace() . ";\n";
}
}
private function _generateEntityClassName(ClassMetadataInfo $metadata)
{
return 'class ' . $this->_getClassName($metadata) .
@ -379,14 +368,7 @@ public function <methodName>()
{
$refl = new \ReflectionClass($this->_getClassToExtend());
return $refl->getName();
}
private function _getClassToExtendNamespace()
{
$refl = new \ReflectionClass($this->_getClassToExtend());
return $refl->getNamespaceName() ? $refl->getNamespaceName():$refl->getShortName();
return '\\' . $refl->getName();
}
private function _getClassName(ClassMetadataInfo $metadata)

View File

@ -46,11 +46,14 @@ class XmlExporter extends AbstractExporter
*/
public function exportClassMetadata(ClassMetadataInfo $metadata)
{
$xml = new \SimpleXmlElement("<?xml version=\"1.0\" encoding=\"utf-8\"?><doctrine-mapping/>");
$xml = new \SimpleXmlElement("<?xml version=\"1.0\" encoding=\"utf-8\"?><doctrine-mapping ".
"xmlns=\"http://doctrine-project.org/schemas/orm/doctrine-mapping\" " .
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ".
"xsi:schemaLocation=\"http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd\" />");
$xml->addAttribute('xmlns', 'http://doctrine-project.org/schemas/orm/doctrine-mapping');
/*$xml->addAttribute('xmlns', 'http://doctrine-project.org/schemas/orm/doctrine-mapping');
$xml->addAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
$xml->addAttribute('xsi:schemaLocation', 'http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd');
$xml->addAttribute('xsi:schemaLocation', 'http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd');*/
if ($metadata->isMappedSuperclass) {
$root = $xml->addChild('mapped-superclass');
@ -109,7 +112,7 @@ class XmlExporter extends AbstractExporter
foreach ($metadata->table['uniqueConstraints'] as $unique) {
$uniqueConstraintXml = $uniqueConstraintsXml->addChild('unique-constraint');
$uniqueConstraintXml->addAttribute('name', $name);
$uniqueConstraintXml->addAttribute('name', $unique['name']);
$uniqueConstraintXml->addAttribute('columns', implode(',', $unique['columns']));
}
}
@ -128,6 +131,21 @@ class XmlExporter extends AbstractExporter
$id[$metadata->getSingleIdentifierFieldName()]['generator']['strategy'] = $idGeneratorType;
}
if ($id) {
foreach ($id as $field) {
$idXml = $root->addChild('id');
$idXml->addAttribute('name', $field['fieldName']);
$idXml->addAttribute('type', $field['type']);
if (isset($field['columnName'])) {
$idXml->addAttribute('column', $field['columnName']);
}
if ($idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
$generatorXml = $idXml->addChild('generator');
$generatorXml->addAttribute('strategy', $idGeneratorType);
}
}
}
if ($fields) {
foreach ($fields as $field) {
$fieldXml = $root->addChild('field');
@ -163,21 +181,6 @@ class XmlExporter extends AbstractExporter
}
}
if ($id) {
foreach ($id as $field) {
$idXml = $root->addChild('id');
$idXml->addAttribute('name', $field['fieldName']);
$idXml->addAttribute('type', $field['type']);
if (isset($field['columnName'])) {
$idXml->addAttribute('column', $field['columnName']);
}
if ($idGeneratorType = $this->_getIdGeneratorTypeString($metadata->generatorType)) {
$generatorXml = $idXml->addChild('generator');
$generatorXml->addAttribute('strategy', $idGeneratorType);
}
}
}
foreach ($metadata->associationMappings as $name => $associationMapping) {
if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_ONE) {
$associationMappingXml = $root->addChild('one-to-one');
@ -305,47 +308,16 @@ class XmlExporter extends AbstractExporter
}
/**
* Code originally taken from
* http://recurser.com/articles/2007/04/05/format-xml-with-php/
*
* @param string $simpleXml
* @param \SimpleXMLElement $simpleXml
* @return string $xml
*/
private function _asXml($simpleXml)
{
$xml = $simpleXml->asXml();
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->loadXML($simpleXml->asXML());
$dom->formatOutput = true;
// add marker linefeeds to aid the pretty-tokeniser (adds a linefeed between all tag-end boundaries)
$xml = preg_replace('/(>)(<)(\/*)/', "$1\n$2$3", $xml);
// now indent the tags
$token = strtok($xml, "\n");
$result = ''; // holds formatted version as it is built
$pad = 0; // initial indent
$matches = array(); // returns from preg_matches()
// test for the various tag states
while ($token !== false) {
// 1. open and closing tags on same line - no change
if (preg_match('/.+<\/\w[^>]*>$/', $token, $matches)) {
$indent = 0;
// 2. closing tag - outdent now
} else if (preg_match('/^<\/\w/', $token, $matches)) {
$pad = $pad - 4;
// 3. opening tag - don't pad this one, only subsequent tags
} elseif (preg_match('/^<\w[^>]*[^\/]>.*$/', $token, $matches)) {
$indent = 4;
// 4. no indentation needed
} else {
$indent = 0;
}
// pad the line with the required number of leading spaces
$line = str_pad($token, strlen($token)+$pad, ' ', STR_PAD_LEFT);
$result .= $line . "\n"; // add to the cumulative result, with linefeed
$token = strtok("\n"); // get the next token
$pad += $indent; // update the pad size for subsequent lines
}
$result = $dom->saveXML();
return $result;
}
}

View File

@ -89,11 +89,6 @@ class YamlExporter extends AbstractExporter
$ids = array();
foreach ($fieldMappings as $name => $fieldMapping) {
if (isset($fieldMapping['length'])) {
$fieldMapping['type'] = $fieldMapping['type'] . '(' . $fieldMapping['length'] . ')';
unset($fieldMapping['length']);
}
$fieldMapping['column'] = $fieldMapping['columnName'];
unset(
$fieldMapping['columnName'],

View File

@ -1,7 +1,5 @@
<?php
/*
* $Id$
*
* 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
@ -94,6 +92,22 @@ class SchemaTool
return $schema->toSql($this->_platform);
}
/**
* Some instances of ClassMetadata don't need to be processed in the SchemaTool context. This method detects them.
*
* @param ClassMetadata $class
* @param array $processedClasses
* @return bool
*/
private function processingNotRequired($class, array $processedClasses)
{
return (
isset($processedClasses[$class->name]) ||
$class->isMappedSuperclass ||
($class->isInheritanceTypeSingleTable() && $class->name != $class->rootEntityName)
);
}
/**
* From a given set of metadata classes this method creates a Schema instance.
*
@ -112,19 +126,12 @@ class SchemaTool
$evm = $this->_em->getEventManager();
foreach ($classes as $class) {
if (isset($processedClasses[$class->name]) || $class->isMappedSuperclass) {
if ($this->processingNotRequired($class, $processedClasses)) {
continue;
}
$table = $schema->createTable($class->getQuotedTableName($this->_platform));
// TODO: Remove
/**if ($class->isIdGeneratorIdentity()) {
$table->setIdGeneratorType(\Doctrine\DBAL\Schema\Table::ID_IDENTITY);
} else if ($class->isIdGeneratorSequence()) {
$table->setIdGeneratorType(\Doctrine\DBAL\Schema\Table::ID_SEQUENCE);
}*/
$columns = array(); // table columns
if ($class->isInheritanceTypeSingleTable()) {
@ -171,16 +178,14 @@ class SchemaTool
$idMapping = $class->fieldMappings[$class->identifier[0]];
$this->_gatherColumn($class, $idMapping, $table);
$columnName = $class->getQuotedColumnName($class->identifier[0], $this->_platform);
// TODO: This seems rather hackish, can we optimize it?
$table->getColumn($class->identifier[0])->setAutoincrement(false);
$pkColumns[] = $columnName;
// TODO: REMOVE
/*if ($table->isIdGeneratorIdentity()) {
$table->setIdGeneratorType(\Doctrine\DBAL\Schema\Table::ID_NONE);
}*/
// Add a FK constraint on the ID column
$table->addUnnamedForeignKeyConstraint(
$this->_em->getClassMetadata($class->rootEntityName)->getQuotedTableName($this->_platform),
$this->_em->getClassMetadata($class->rootEntityName)->getTableName(),
array($columnName), array($columnName), array('onDelete' => 'CASCADE')
);
}
@ -318,6 +323,9 @@ class SchemaTool
$options = array();
$options['length'] = isset($mapping['length']) ? $mapping['length'] : null;
$options['notnull'] = isset($mapping['nullable']) ? ! $mapping['nullable'] : true;
if ($class->isInheritanceTypeSingleTable() && count($class->parentClasses) > 0) {
$options['notnull'] = false;
}
$options['platformOptions'] = array();
$options['platformOptions']['version'] = $class->isVersioned && $class->versionField == $mapping['fieldName'] ? true : false;
@ -345,6 +353,9 @@ class SchemaTool
if ($class->isIdGeneratorIdentity() && $class->getIdentifierFieldNames() == array($mapping['fieldName'])) {
$options['autoincrement'] = true;
}
if ($class->isInheritanceTypeJoined() && $class->name != $class->rootEntityName) {
$options['autoincrement'] = false;
}
if ($table->hasColumn($columnName)) {
// required in some inheritance scenarios
@ -459,6 +470,12 @@ class SchemaTool
if (isset($joinColumn['nullable'])) {
$columnOptions['notnull'] = !$joinColumn['nullable'];
}
if ($fieldMapping['type'] == "string") {
$columnOptions['length'] = $fieldMapping['length'];
} else if ($fieldMapping['type'] == "decimal") {
$columnOptions['scale'] = $fieldMapping['scale'];
$columnOptions['precision'] = $fieldMapping['precision'];
}
$theJoinTable->addColumn(
$columnName, $class->getTypeOfColumn($joinColumn['referencedColumnName']), $columnOptions
@ -479,7 +496,7 @@ class SchemaTool
}
$theJoinTable->addUnnamedForeignKeyConstraint(
$class->getQuotedTableName($this->_platform), $localColumns, $foreignColumns, $fkOptions
$class->getTableName(), $localColumns, $foreignColumns, $fkOptions
);
}
@ -494,7 +511,26 @@ class SchemaTool
*/
public function dropSchema(array $classes)
{
$dropSchemaSql = $this->getDropSchemaSql($classes);
$dropSchemaSql = $this->getDropSchemaSQL($classes);
$conn = $this->_em->getConnection();
foreach ($dropSchemaSql as $sql) {
try {
$conn->executeQuery($sql);
} catch(\Exception $e) {
}
}
}
/**
* Drops all elements in the database of the current connection.
*
* @return void
*/
public function dropDatabase()
{
$dropSchemaSql = $this->getDropDatabaseSQL();
$conn = $this->_em->getConnection();
foreach ($dropSchemaSql as $sql) {
@ -503,12 +539,11 @@ class SchemaTool
}
/**
* Gets the SQL needed to drop the database schema for the given classes.
* Gets the SQL needed to drop the database schema for the connections database.
*
* @param array $classes
* @return array
*/
public function getDropSchemaSql(array $classes)
public function getDropDatabaseSQL()
{
$sm = $this->_em->getConnection()->getSchemaManager();
$schema = $sm->createSchema();
@ -520,39 +555,31 @@ class SchemaTool
}
/**
* Drop all tables of the database connection.
*
* @param array $classes
* @return array
*/
private function _getDropSchemaTablesDatabaseMode($classes)
public function getDropSchemaSQL(array $classes)
{
$conn = $this->_em->getConnection();
$sm = $this->_em->getConnection()->getSchemaManager();
$sql = array();
$orderedTables = array();
$sm = $conn->getSchemaManager();
/* @var $sm \Doctrine\DBAL\Schema\AbstractSchemaManager */
$allTables = $sm->listTables();
$orderedTables = $this->_getDropSchemaTablesMetadataMode($classes);
foreach($allTables AS $tableName) {
if(!in_array($tableName, $orderedTables)) {
$orderedTables[] = $tableName;
foreach ($classes AS $class) {
if ($class->isIdGeneratorSequence() && !$class->isMappedSuperclass && $class->name == $class->rootEntityName && $this->_platform->supportsSequences()) {
$sql[] = $this->_platform->getDropSequenceSQL($class->sequenceGeneratorDefinition['sequenceName']);
}
}
return $orderedTables;
}
private function _getDropSchemaTablesMetadataMode(array $classes)
{
$orderedTables = array();
$commitOrder = $this->_getCommitOrder($classes);
$associationTables = $this->_getAssociationTables($commitOrder);
// Drop association tables first
foreach ($associationTables as $associationTable) {
$orderedTables[] = $associationTable;
if (!in_array($associationTable, $orderedTables)) {
$orderedTables[] = $associationTable;
}
}
// Drop tables in reverse commit order
@ -564,17 +591,27 @@ class SchemaTool
continue;
}
$orderedTables[] = $class->getTableName();
if (!in_array($class->getTableName(), $orderedTables)) {
$orderedTables[] = $class->getTableName();
}
}
//TODO: Drop other schema elements, like sequences etc.
$dropTablesSql = array();
foreach ($orderedTables AS $tableName) {
/* @var $sm \Doctrine\DBAL\Schema\AbstractSchemaManager */
$foreignKeys = $sm->listTableForeignKeys($tableName);
foreach ($foreignKeys AS $foreignKey) {
$sql[] = $this->_platform->getDropForeignKeySQL($foreignKey, $tableName);
}
$dropTablesSql[] = $this->_platform->getDropTableSQL($tableName);
}
return $orderedTables;
return array_merge($sql, $dropTablesSql);
}
/**
* Updates the database schema of the given classes by comparing the ClassMetadata
* instances to the current database schema that is inspected.
* ins$tableNametances to the current database schema that is inspected.
*
* @param array $classes
* @return void
@ -645,7 +682,7 @@ class SchemaTool
foreach ($classes as $class) {
foreach ($class->associationMappings as $assoc) {
if ($assoc['isOwningSide'] && $assoc['type'] == ClassMetadata::MANY_TO_MANY) {
$associationTables[] = $assoc->joinTable['name'];
$associationTables[] = $assoc['joinTable']['name'];
}
}
}

View File

@ -573,7 +573,7 @@ class UnitOfWork implements PropertyChangedListener
$this->computeChangeSet($targetClass, $entry);
} else if ($state == self::STATE_REMOVED) {
return new InvalidArgumentException("Removed entity detected during flush: "
. self::objToStr($removedEntity).". Remove deleted entities from associations.");
. self::objToStr($entry).". Remove deleted entities from associations.");
} else if ($state == self::STATE_DETACHED) {
// Can actually not happen right now as we assume STATE_NEW,
// so the exception will be raised from the DBAL layer (constraint violation).
@ -1437,7 +1437,16 @@ class UnitOfWork implements PropertyChangedListener
$prop->setValue($managedCopy, $managedCol);
$this->originalEntityData[$oid][$name] = $managedCol;
}
$managedCol->setInitialized($assoc2['isCascadeMerge']);
if ($assoc2['isCascadeMerge']) {
$managedCol->initialize();
if (!$managedCol->isEmpty()) {
$managedCol->unwrap()->clear();
$managedCol->setDirty(true);
if ($assoc2['isOwningSide'] && $assoc2['type'] == ClassMetadata::MANY_TO_MANY && $class->isChangeTrackingNotify()) {
$this->scheduleForDirtyCheck($managedCopy);
}
}
}
}
}
if ($class->isChangeTrackingNotify()) {
@ -1456,7 +1465,7 @@ class UnitOfWork implements PropertyChangedListener
if ($assoc['type'] & ClassMetadata::TO_ONE) {
$prevClass->reflFields[$assocField]->setValue($prevManagedCopy, $managedCopy);
} else {
$prevClass->reflFields[$assocField]->getValue($prevManagedCopy)->unwrap()->add($managedCopy);
$prevClass->reflFields[$assocField]->getValue($prevManagedCopy)->add($managedCopy);
if ($assoc['type'] == ClassMetadata::ONE_TO_MANY) {
$class->reflFields[$assoc['mappedBy']]->setValue($managedCopy, $prevManagedCopy);
}
@ -1500,7 +1509,9 @@ class UnitOfWork implements PropertyChangedListener
switch ($this->getEntityState($entity, self::STATE_DETACHED)) {
case self::STATE_MANAGED:
$this->removeFromIdentityMap($entity);
if ($this->isInIdentityMap($entity)) {
$this->removeFromIdentityMap($entity);
}
unset($this->entityInsertions[$oid], $this->entityUpdates[$oid],
$this->entityDeletions[$oid], $this->entityIdentifiers[$oid],
$this->entityStates[$oid], $this->originalEntityData[$oid]);
@ -1889,7 +1900,7 @@ class UnitOfWork implements PropertyChangedListener
if ($assoc['isOwningSide']) {
$associatedId = array();
foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
$joinColumnValue = $data[$srcColumn];
$joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null;
if ($joinColumnValue !== null) {
$associatedId[$targetClass->fieldNames[$targetColumn]] = $joinColumnValue;
}
@ -1937,14 +1948,12 @@ class UnitOfWork implements PropertyChangedListener
}
} else {
// Inject collection
$reflField = $class->reflFields[$field];
$pColl = new PersistentCollection(
$this->em, $targetClass,
//TODO: getValue might be superfluous once DDC-79 is implemented.
$reflField->getValue($entity) ?: new ArrayCollection
);
$pColl = new PersistentCollection($this->em, $targetClass, new ArrayCollection);
$pColl->setOwner($entity, $assoc);
$reflField = $class->reflFields[$field];
$reflField->setValue($entity, $pColl);
if ($assoc['fetch'] == ClassMetadata::FETCH_LAZY) {
$pColl->setInitialized(false);
} else {

View File

@ -36,7 +36,7 @@ class Version
/**
* Current Doctrine Version
*/
const VERSION = '2.0.0BETA4-DEV';
const VERSION = '2.1.0-DEV';
/**
* Compares a Doctrine version with the current one.
@ -52,4 +52,4 @@ class Version
return version_compare($version, $currentVersion);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@ use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Application;
/*
* This file is part of the symfony framework.
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
@ -21,510 +21,492 @@ use Symfony\Component\Console\Application;
/**
* Base class for all commands.
*
* @package symfony
* @subpackage console
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class Command
{
protected $name;
protected $namespace;
protected $aliases;
protected $definition;
protected $help;
protected $application;
protected $description;
protected $ignoreValidationErrors;
protected $formatter;
protected $applicationDefinitionMerged;
protected $code;
protected $name;
protected $namespace;
protected $aliases;
protected $definition;
protected $help;
protected $application;
protected $description;
protected $ignoreValidationErrors;
protected $applicationDefinitionMerged;
protected $code;
/**
* Constructor.
*
* @param string $name The name of the command
*/
public function __construct($name = null)
{
$this->definition = new InputDefinition();
$this->ignoreValidationErrors = false;
$this->applicationDefinitionMerged = false;
$this->aliases = array();
if (null !== $name)
/**
* Constructor.
*
* @param string $name The name of the command
*
* @throws \LogicException When the command name is empty
*/
public function __construct($name = null)
{
$this->setName($name);
$this->definition = new InputDefinition();
$this->ignoreValidationErrors = false;
$this->applicationDefinitionMerged = false;
$this->aliases = array();
if (null !== $name) {
$this->setName($name);
}
$this->configure();
if (!$this->name) {
throw new \LogicException('The command name cannot be empty.');
}
}
$this->configure();
if (!$this->name)
/**
* Sets the application instance for this command.
*
* @param Application $application An Application instance
*/
public function setApplication(Application $application = null)
{
throw new \LogicException('The command name cannot be empty.');
}
}
/**
* Sets the application instance for this command.
*
* @param Application $application An Application instance
*/
public function setApplication(Application $application = null)
{
$this->application = $application;
}
/**
* Configures the current command.
*/
protected function configure()
{
}
/**
* Executes the current command.
*
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
*
* @return integer 0 if everything went fine, or an error code
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
throw new \LogicException('You must override the execute() method in the concrete command class.');
}
/**
* Interacts with the user.
*
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
*/
protected function interact(InputInterface $input, OutputInterface $output)
{
}
/**
* Initializes the command just after the input has been validated.
*
* This is mainly useful when a lot of commands extends one main command
* where some things need to be initialized based on the input arguments and options.
*
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
*/
protected function initialize(InputInterface $input, OutputInterface $output)
{
}
/**
* Runs the command.
*
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
*/
public function run(InputInterface $input, OutputInterface $output)
{
// add the application arguments and options
$this->mergeApplicationDefinition();
// bind the input against the command specific arguments/options
try
{
$input->bind($this->definition);
}
catch (\Exception $e)
{
if (!$this->ignoreValidationErrors)
{
throw $e;
}
$this->application = $application;
}
$this->initialize($input, $output);
if ($input->isInteractive())
/**
* Configures the current command.
*/
protected function configure()
{
$this->interact($input, $output);
}
$input->validate();
if ($this->code)
/**
* Executes the current command.
*
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
*
* @return integer 0 if everything went fine, or an error code
*
* @throws \LogicException When this abstract class is not implemented
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
return call_user_func($this->code, $input, $output);
}
else
{
return $this->execute($input, $output);
}
}
/**
* Sets the code to execute when running this command.
*
* @param \Closure $code A \Closure
*
* @return Command The current instance
*/
public function setCode(\Closure $code)
{
$this->code = $code;
return $this;
}
/**
* Merges the application definition with the command definition.
*/
protected function mergeApplicationDefinition()
{
if (null === $this->application || true === $this->applicationDefinitionMerged)
{
return;
throw new \LogicException('You must override the execute() method in the concrete command class.');
}
$this->definition->setArguments(array_merge(
$this->application->getDefinition()->getArguments(),
$this->definition->getArguments()
));
$this->definition->addOptions($this->application->getDefinition()->getOptions());
$this->applicationDefinitionMerged = true;
}
/**
* Sets an array of argument and option instances.
*
* @param array|Definition $definition An array of argument and option instances or a definition instance
*
* @return Command The current instance
*/
public function setDefinition($definition)
{
if ($definition instanceof InputDefinition)
/**
* Interacts with the user.
*
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
*/
protected function interact(InputInterface $input, OutputInterface $output)
{
$this->definition = $definition;
}
else
{
$this->definition->setDefinition($definition);
}
$this->applicationDefinitionMerged = false;
return $this;
}
/**
* Gets the InputDefinition attached to this Command.
*
* @return InputDefinition $definition An InputDefinition instance
*/
public function getDefinition()
{
return $this->definition;
}
/**
* Adds an argument.
*
* @param string $name The argument name
* @param integer $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL
* @param string $description A description text
* @param mixed $default The default value (for InputArgument::OPTIONAL mode only)
*
* @return Command The current instance
*/
public function addArgument($name, $mode = null, $description = '', $default = null)
{
$this->definition->addArgument(new InputArgument($name, $mode, $description, $default));
return $this;
}
/**
* Adds an option.
*
* @param string $name The option name
* @param string $shortcut The shortcut (can be null)
* @param integer $mode The option mode: self::PARAMETER_REQUIRED, self::PARAMETER_NONE or self::PARAMETER_OPTIONAL
* @param string $description A description text
* @param mixed $default The default value (must be null for self::PARAMETER_REQUIRED or self::PARAMETER_NONE)
*
* @return Command The current instance
*/
public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null)
{
$this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default));
return $this;
}
/**
* Sets the name of the command.
*
* This method can set both the namespace and the name if
* you separate them by a colon (:)
*
* $command->setName('foo:bar');
*
* @param string $name The command name
*
* @return Command The current instance
*/
public function setName($name)
{
if (false !== $pos = strrpos($name, ':'))
/**
* Initializes the command just after the input has been validated.
*
* This is mainly useful when a lot of commands extends one main command
* where some things need to be initialized based on the input arguments and options.
*
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
*/
protected function initialize(InputInterface $input, OutputInterface $output)
{
$namespace = substr($name, 0, $pos);
$name = substr($name, $pos + 1);
}
else
{
$namespace = $this->namespace;
}
if (!$name)
/**
* Runs the command.
*
* @param InputInterface $input An InputInterface instance
* @param OutputInterface $output An OutputInterface instance
*/
public function run(InputInterface $input, OutputInterface $output)
{
throw new \InvalidArgumentException('A command name cannot be empty');
// add the application arguments and options
$this->mergeApplicationDefinition();
// bind the input against the command specific arguments/options
try {
$input->bind($this->definition);
} catch (\Exception $e) {
if (!$this->ignoreValidationErrors) {
throw $e;
}
}
$this->initialize($input, $output);
if ($input->isInteractive()) {
$this->interact($input, $output);
}
$input->validate();
if ($this->code) {
return call_user_func($this->code, $input, $output);
} else {
return $this->execute($input, $output);
}
}
$this->namespace = $namespace;
$this->name = $name;
return $this;
}
/**
* Returns the command namespace.
*
* @return string The command namespace
*/
public function getNamespace()
{
return $this->namespace;
}
/**
* Returns the command name
*
* @return string The command name
*/
public function getName()
{
return $this->name;
}
/**
* Returns the fully qualified command name.
*
* @return string The fully qualified command name
*/
public function getFullName()
{
return $this->getNamespace() ? $this->getNamespace().':'.$this->getName() : $this->getName();
}
/**
* Sets the description for the command.
*
* @param string $description The description for the command
*
* @return Command The current instance
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Returns the description for the command.
*
* @return string The description for the command
*/
public function getDescription()
{
return $this->description;
}
/**
* Sets the help for the command.
*
* @param string $help The help for the command
*
* @return Command The current instance
*/
public function setHelp($help)
{
$this->help = $help;
return $this;
}
/**
* Returns the help for the command.
*
* @return string The help for the command
*/
public function getHelp()
{
return $this->help;
}
/**
* Returns the processed help for the command replacing the %command.name% and
* %command.full_name% patterns with the real values dynamically.
*
* @return string The processed help for the command
*/
public function getProcessedHelp()
{
$name = $this->namespace.':'.$this->name;
$placeholders = array(
'%command.name%',
'%command.full_name%'
);
$replacements = array(
$name,
$_SERVER['PHP_SELF'].' '.$name
);
return str_replace($placeholders, $replacements, $this->getHelp());
}
/**
* Sets the aliases for the command.
*
* @param array $aliases An array of aliases for the command
*
* @return Command The current instance
*/
public function setAliases($aliases)
{
$this->aliases = $aliases;
return $this;
}
/**
* Returns the aliases for the command.
*
* @return array An array of aliases for the command
*/
public function getAliases()
{
return $this->aliases;
}
/**
* Returns the synopsis for the command.
*
* @return string The synopsis
*/
public function getSynopsis()
{
return sprintf('%s %s', $this->getFullName(), $this->definition->getSynopsis());
}
/**
* Gets a helper instance by name.
*
* @param string $name The helper name
*
* @return mixed The helper value
*
* @throws \InvalidArgumentException if the helper is not defined
*/
protected function getHelper($name)
{
return $this->application->getHelperSet()->get($name);
}
/**
* Gets a helper instance by name.
*
* @param string $name The helper name
*
* @return mixed The helper value
*
* @throws \InvalidArgumentException if the helper is not defined
*/
public function __get($name)
{
return $this->application->getHelperSet()->get($name);
}
/**
* Returns a text representation of the command.
*
* @return string A string representing the command
*/
public function asText()
{
$messages = array(
'<comment>Usage:</comment>',
' '.$this->getSynopsis(),
'',
);
if ($this->getAliases())
/**
* Sets the code to execute when running this command.
*
* @param \Closure $code A \Closure
*
* @return Command The current instance
*/
public function setCode(\Closure $code)
{
$messages[] = '<comment>Aliases:</comment> <info>'.implode(', ', $this->getAliases()).'</info>';
$this->code = $code;
return $this;
}
$messages[] = $this->definition->asText();
if ($help = $this->getProcessedHelp())
/**
* Merges the application definition with the command definition.
*/
protected function mergeApplicationDefinition()
{
$messages[] = '<comment>Help:</comment>';
$messages[] = ' '.implode("\n ", explode("\n", $help))."\n";
if (null === $this->application || true === $this->applicationDefinitionMerged) {
return;
}
$this->definition->setArguments(array_merge(
$this->application->getDefinition()->getArguments(),
$this->definition->getArguments()
));
$this->definition->addOptions($this->application->getDefinition()->getOptions());
$this->applicationDefinitionMerged = true;
}
return implode("\n", $messages);
}
/**
* Returns an XML representation of the command.
*
* @param Boolean $asDom Whether to return a DOM or an XML string
*
* @return string|DOMDocument An XML string representing the command
*/
public function asXml($asDom = false)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->formatOutput = true;
$dom->appendChild($commandXML = $dom->createElement('command'));
$commandXML->setAttribute('id', $this->getFullName());
$commandXML->setAttribute('namespace', $this->getNamespace() ? $this->getNamespace() : '_global');
$commandXML->setAttribute('name', $this->getName());
$commandXML->appendChild($usageXML = $dom->createElement('usage'));
$usageXML->appendChild($dom->createTextNode(sprintf($this->getSynopsis(), '')));
$commandXML->appendChild($descriptionXML = $dom->createElement('description'));
$descriptionXML->appendChild($dom->createTextNode(implode("\n ", explode("\n", $this->getDescription()))));
$commandXML->appendChild($helpXML = $dom->createElement('help'));
$help = $this->help;
$helpXML->appendChild($dom->createTextNode(implode("\n ", explode("\n", $help))));
$commandXML->appendChild($aliasesXML = $dom->createElement('aliases'));
foreach ($this->getAliases() as $alias)
/**
* Sets an array of argument and option instances.
*
* @param array|Definition $definition An array of argument and option instances or a definition instance
*
* @return Command The current instance
*/
public function setDefinition($definition)
{
$aliasesXML->appendChild($aliasXML = $dom->createElement('alias'));
$aliasXML->appendChild($dom->createTextNode($alias));
if ($definition instanceof InputDefinition) {
$this->definition = $definition;
} else {
$this->definition->setDefinition($definition);
}
$this->applicationDefinitionMerged = false;
return $this;
}
$definition = $this->definition->asXml(true);
$commandXML->appendChild($dom->importNode($definition->getElementsByTagName('arguments')->item(0), true));
$commandXML->appendChild($dom->importNode($definition->getElementsByTagName('options')->item(0), true));
/**
* Gets the InputDefinition attached to this Command.
*
* @return InputDefinition An InputDefinition instance
*/
public function getDefinition()
{
return $this->definition;
}
return $asDom ? $dom : $dom->saveXml();
}
/**
* Adds an argument.
*
* @param string $name The argument name
* @param integer $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL
* @param string $description A description text
* @param mixed $default The default value (for InputArgument::OPTIONAL mode only)
*
* @return Command The current instance
*/
public function addArgument($name, $mode = null, $description = '', $default = null)
{
$this->definition->addArgument(new InputArgument($name, $mode, $description, $default));
return $this;
}
/**
* Adds an option.
*
* @param string $name The option name
* @param string $shortcut The shortcut (can be null)
* @param integer $mode The option mode: One of the InputOption::VALUE_* constants
* @param string $description A description text
* @param mixed $default The default value (must be null for InputOption::VALUE_REQUIRED or self::VALUE_NONE)
*
* @return Command The current instance
*/
public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null)
{
$this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default));
return $this;
}
/**
* Sets the name of the command.
*
* This method can set both the namespace and the name if
* you separate them by a colon (:)
*
* $command->setName('foo:bar');
*
* @param string $name The command name
*
* @return Command The current instance
*
* @throws \InvalidArgumentException When command name given is empty
*/
public function setName($name)
{
if (false !== $pos = strrpos($name, ':')) {
$namespace = substr($name, 0, $pos);
$name = substr($name, $pos + 1);
} else {
$namespace = $this->namespace;
}
if (!$name) {
throw new \InvalidArgumentException('A command name cannot be empty.');
}
$this->namespace = $namespace;
$this->name = $name;
return $this;
}
/**
* Returns the command namespace.
*
* @return string The command namespace
*/
public function getNamespace()
{
return $this->namespace;
}
/**
* Returns the command name
*
* @return string The command name
*/
public function getName()
{
return $this->name;
}
/**
* Returns the fully qualified command name.
*
* @return string The fully qualified command name
*/
public function getFullName()
{
return $this->getNamespace() ? $this->getNamespace().':'.$this->getName() : $this->getName();
}
/**
* Sets the description for the command.
*
* @param string $description The description for the command
*
* @return Command The current instance
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Returns the description for the command.
*
* @return string The description for the command
*/
public function getDescription()
{
return $this->description;
}
/**
* Sets the help for the command.
*
* @param string $help The help for the command
*
* @return Command The current instance
*/
public function setHelp($help)
{
$this->help = $help;
return $this;
}
/**
* Returns the help for the command.
*
* @return string The help for the command
*/
public function getHelp()
{
return $this->help;
}
/**
* Returns the processed help for the command replacing the %command.name% and
* %command.full_name% patterns with the real values dynamically.
*
* @return string The processed help for the command
*/
public function getProcessedHelp()
{
$name = $this->namespace.':'.$this->name;
$placeholders = array(
'%command.name%',
'%command.full_name%'
);
$replacements = array(
$name,
$_SERVER['PHP_SELF'].' '.$name
);
return str_replace($placeholders, $replacements, $this->getHelp());
}
/**
* Sets the aliases for the command.
*
* @param array $aliases An array of aliases for the command
*
* @return Command The current instance
*/
public function setAliases($aliases)
{
$this->aliases = $aliases;
return $this;
}
/**
* Returns the aliases for the command.
*
* @return array An array of aliases for the command
*/
public function getAliases()
{
return $this->aliases;
}
/**
* Returns the synopsis for the command.
*
* @return string The synopsis
*/
public function getSynopsis()
{
return sprintf('%s %s', $this->getFullName(), $this->definition->getSynopsis());
}
/**
* Gets a helper instance by name.
*
* @param string $name The helper name
*
* @return mixed The helper value
*
* @throws \InvalidArgumentException if the helper is not defined
*/
protected function getHelper($name)
{
return $this->application->getHelperSet()->get($name);
}
/**
* Gets a helper instance by name.
*
* @param string $name The helper name
*
* @return mixed The helper value
*
* @throws \InvalidArgumentException if the helper is not defined
*/
public function __get($name)
{
return $this->application->getHelperSet()->get($name);
}
/**
* Returns a text representation of the command.
*
* @return string A string representing the command
*/
public function asText()
{
$messages = array(
'<comment>Usage:</comment>',
' '.$this->getSynopsis(),
'',
);
if ($this->getAliases()) {
$messages[] = '<comment>Aliases:</comment> <info>'.implode(', ', $this->getAliases()).'</info>';
}
$messages[] = $this->definition->asText();
if ($help = $this->getProcessedHelp()) {
$messages[] = '<comment>Help:</comment>';
$messages[] = ' '.implode("\n ", explode("\n", $help))."\n";
}
return implode("\n", $messages);
}
/**
* Returns an XML representation of the command.
*
* @param Boolean $asDom Whether to return a DOM or an XML string
*
* @return string|DOMDocument An XML string representing the command
*/
public function asXml($asDom = false)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->formatOutput = true;
$dom->appendChild($commandXML = $dom->createElement('command'));
$commandXML->setAttribute('id', $this->getFullName());
$commandXML->setAttribute('namespace', $this->getNamespace() ? $this->getNamespace() : '_global');
$commandXML->setAttribute('name', $this->getName());
$commandXML->appendChild($usageXML = $dom->createElement('usage'));
$usageXML->appendChild($dom->createTextNode(sprintf($this->getSynopsis(), '')));
$commandXML->appendChild($descriptionXML = $dom->createElement('description'));
$descriptionXML->appendChild($dom->createTextNode(implode("\n ", explode("\n", $this->getDescription()))));
$commandXML->appendChild($helpXML = $dom->createElement('help'));
$help = $this->help;
$helpXML->appendChild($dom->createTextNode(implode("\n ", explode("\n", $help))));
$commandXML->appendChild($aliasesXML = $dom->createElement('aliases'));
foreach ($this->getAliases() as $alias) {
$aliasesXML->appendChild($aliasXML = $dom->createElement('alias'));
$aliasXML->appendChild($dom->createTextNode($alias));
}
$definition = $this->definition->asXml(true);
$commandXML->appendChild($dom->importNode($definition->getElementsByTagName('arguments')->item(0), true));
$commandXML->appendChild($dom->importNode($definition->getElementsByTagName('options')->item(0), true));
return $asDom ? $dom : $dom->saveXml();
}
}

View File

@ -10,7 +10,7 @@ use Symfony\Component\Console\Output\Output;
use Symfony\Component\Console\Command\Command;
/*
* This file is part of the symfony framework.
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
@ -21,63 +21,57 @@ use Symfony\Component\Console\Command\Command;
/**
* HelpCommand displays the help for a given command.
*
* @package symfony
* @subpackage console
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class HelpCommand extends Command
{
protected $command;
protected $command;
/**
* @see Command
*/
protected function configure()
{
$this->ignoreValidationErrors = true;
/**
* @see Command
*/
protected function configure()
{
$this->ignoreValidationErrors = true;
$this
->setDefinition(array(
new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'),
new InputOption('xml', null, InputOption::PARAMETER_NONE, 'To output help as XML'),
))
->setName('help')
->setAliases(array('?'))
->setDescription('Displays help for a command')
->setHelp(<<<EOF
$this
->setDefinition(array(
new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'),
new InputOption('xml', null, InputOption::VALUE_NONE, 'To output help as XML'),
))
->setName('help')
->setAliases(array('?'))
->setDescription('Displays help for a command')
->setHelp(<<<EOF
The <info>help</info> command displays help for a given command:
<info>./symfony help test:all</info>
<info>./symfony help list</info>
You can also output the help as XML by using the <comment>--xml</comment> option:
<info>./symfony help --xml test:all</info>
<info>./symfony help --xml list</info>
EOF
);
}
public function setCommand(Command $command)
{
$this->command = $command;
}
/**
* @see Command
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
if (null === $this->command)
{
$this->command = $this->application->getCommand($input->getArgument('command_name'));
);
}
if ($input->getOption('xml'))
public function setCommand(Command $command)
{
$output->writeln($this->command->asXml(), Output::OUTPUT_RAW);
$this->command = $command;
}
else
/**
* @see Command
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln($this->command->asText());
if (null === $this->command) {
$this->command = $this->application->get($input->getArgument('command_name'));
}
if ($input->getOption('xml')) {
$output->writeln($this->command->asXml(), Output::OUTPUT_RAW);
} else {
$output->writeln($this->command->asText());
}
}
}
}

View File

@ -10,7 +10,7 @@ use Symfony\Component\Console\Output\Output;
use Symfony\Component\Console\Command\Command;
/*
* This file is part of the symfony framework.
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
@ -21,25 +21,23 @@ use Symfony\Component\Console\Command\Command;
/**
* ListCommand displays the list of all available commands for the application.
*
* @package symfony
* @subpackage console
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class ListCommand extends Command
{
/**
* @see Command
*/
protected function configure()
{
$this
->setDefinition(array(
new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'),
new InputOption('xml', null, InputOption::PARAMETER_NONE, 'To output help as XML'),
))
->setName('list')
->setDescription('Lists commands')
->setHelp(<<<EOF
/**
* @see Command
*/
protected function configure()
{
$this
->setDefinition(array(
new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'),
new InputOption('xml', null, InputOption::VALUE_NONE, 'To output help as XML'),
))
->setName('list')
->setDescription('Lists commands')
->setHelp(<<<EOF
The <info>list</info> command lists all commands:
<info>./symfony list</info>
@ -52,21 +50,18 @@ You can also output the information as XML by using the <comment>--xml</comment>
<info>./symfony list --xml</info>
EOF
);
}
);
}
/**
* @see Command
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
if ($input->getOption('xml'))
/**
* @see Command
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln($this->application->asXml($input->getArgument('namespace')), Output::OUTPUT_RAW);
if ($input->getOption('xml')) {
$output->writeln($this->application->asXml($input->getArgument('namespace')), Output::OUTPUT_RAW);
} else {
$output->writeln($this->application->asText($input->getArgument('namespace')));
}
}
else
{
$output->writeln($this->application->asText($input->getArgument('namespace')));
}
}
}

View File

@ -5,7 +5,7 @@ namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Output\OutputInterface;
/*
* This file is part of the symfony framework.
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
@ -16,104 +16,95 @@ use Symfony\Component\Console\Output\OutputInterface;
/**
* The Dialog class provides helpers to interact with the user.
*
* @package symfony
* @subpackage console
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class DialogHelper extends Helper
{
/**
* Asks a question to the user.
*
* @param OutputInterface $output
* @param string|array $question The question to ask
* @param string $default The default answer if none is given by the user
*
* @param string The user answer
*/
public function ask(OutputInterface $output, $question, $default = null)
{
// @codeCoverageIgnoreStart
$output->writeln($question);
$ret = trim(fgets(STDIN));
return $ret ? $ret : $default;
// @codeCoverageIgnoreEnd
}
/**
* Asks a confirmation to the user.
*
* The question will be asked until the user answer by nothing, yes, or no.
*
* @param OutputInterface $output
* @param string|array $question The question to ask
* @param Boolean $default The default answer if the user enters nothing
*
* @param Boolean true if the user has confirmed, false otherwise
*/
public function askConfirmation(OutputInterface $output, $question, $default = true)
{
// @codeCoverageIgnoreStart
$answer = 'z';
while ($answer && !in_array(strtolower($answer[0]), array('y', 'n')))
/**
* Asks a question to the user.
*
* @param OutputInterface $output
* @param string|array $question The question to ask
* @param string $default The default answer if none is given by the user
*
* @return string The user answer
*/
public function ask(OutputInterface $output, $question, $default = null)
{
$answer = $this->ask($output, $question);
// @codeCoverageIgnoreStart
$output->writeln($question);
$ret = trim(fgets(STDIN));
return $ret ? $ret : $default;
// @codeCoverageIgnoreEnd
}
if (false === $default)
/**
* Asks a confirmation to the user.
*
* The question will be asked until the user answer by nothing, yes, or no.
*
* @param OutputInterface $output
* @param string|array $question The question to ask
* @param Boolean $default The default answer if the user enters nothing
*
* @return Boolean true if the user has confirmed, false otherwise
*/
public function askConfirmation(OutputInterface $output, $question, $default = true)
{
return $answer && 'y' == strtolower($answer[0]);
}
else
{
return !$answer || 'y' == strtolower($answer[0]);
}
// @codeCoverageIgnoreEnd
}
// @codeCoverageIgnoreStart
$answer = 'z';
while ($answer && !in_array(strtolower($answer[0]), array('y', 'n'))) {
$answer = $this->ask($output, $question);
}
/**
* Asks for a value and validates the response.
*
* @param OutputInterface $output
* @param string|array $question
* @param Closure $validator
* @param integer $attempts Max number of times to ask before giving up (false by default, which means infinite)
*
* @return mixed
*/
public function askAndValidate(OutputInterface $output, $question, \Closure $validator, $attempts = false)
{
// @codeCoverageIgnoreStart
$error = null;
while (false === $attempts || $attempts--)
{
if (null !== $error)
{
$output->writeln($this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error'));
}
$value = $this->ask($output, $question, null);
try
{
return $validator($value);
}
catch (\Exception $error)
{
}
if (false === $default) {
return $answer && 'y' == strtolower($answer[0]);
} else {
return !$answer || 'y' == strtolower($answer[0]);
}
// @codeCoverageIgnoreEnd
}
throw $error;
// @codeCoverageIgnoreEnd
}
/**
* Asks for a value and validates the response.
*
* @param OutputInterface $output
* @param string|array $question
* @param Closure $validator
* @param integer $attempts Max number of times to ask before giving up (false by default, which means infinite)
*
* @return mixed
*
* @throws \Exception When any of the validator returns an error
*/
public function askAndValidate(OutputInterface $output, $question, \Closure $validator, $attempts = false)
{
// @codeCoverageIgnoreStart
$error = null;
while (false === $attempts || $attempts--) {
if (null !== $error) {
$output->writeln($this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error'));
}
/**
* Returns the helper's canonical name
*/
public function getName()
{
return 'dialog';
}
$value = $this->ask($output, $question, null);
try {
return $validator($value);
} catch (\Exception $error) {
}
}
throw $error;
// @codeCoverageIgnoreEnd
}
/**
* Returns the helper's canonical name
*/
public function getName()
{
return 'dialog';
}
}

View File

@ -3,7 +3,7 @@
namespace Symfony\Component\Console\Helper;
/*
* This file is part of the symfony framework.
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
@ -14,76 +14,69 @@ namespace Symfony\Component\Console\Helper;
/**
* The Formatter class provides helpers to format messages.
*
* @package symfony
* @subpackage console
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class FormatterHelper extends Helper
{
/**
* Formats a message within a section.
*
* @param string $section The section name
* @param string $message The message
* @param string $style The style to apply to the section
*/
public function formatSection($section, $message, $style = 'info')
{
return sprintf("<%s>[%s]</%s> %s", $style, $section, $style, $message);
}
/**
* Formats a message as a block of text.
*
* @param string|array $messages The message to write in the block
* @param string $style The style to apply to the whole block
* @param Boolean $large Whether to return a large block
*
* @return string The formatter message
*/
public function formatBlock($messages, $style, $large = false)
{
if (!is_array($messages))
/**
* Formats a message within a section.
*
* @param string $section The section name
* @param string $message The message
* @param string $style The style to apply to the section
*/
public function formatSection($section, $message, $style = 'info')
{
$messages = array($messages);
return sprintf('<%s>[%s]</%s> %s', $style, $section, $style, $message);
}
$len = 0;
$lines = array();
foreach ($messages as $message)
/**
* Formats a message as a block of text.
*
* @param string|array $messages The message to write in the block
* @param string $style The style to apply to the whole block
* @param Boolean $large Whether to return a large block
*
* @return string The formatter message
*/
public function formatBlock($messages, $style, $large = false)
{
$lines[] = sprintf($large ? ' %s ' : ' %s ', $message);
$len = max($this->strlen($message) + ($large ? 4 : 2), $len);
if (!is_array($messages)) {
$messages = array($messages);
}
$len = 0;
$lines = array();
foreach ($messages as $message) {
$lines[] = sprintf($large ? ' %s ' : ' %s ', $message);
$len = max($this->strlen($message) + ($large ? 4 : 2), $len);
}
$messages = $large ? array(str_repeat(' ', $len)) : array();
foreach ($lines as $line) {
$messages[] = $line.str_repeat(' ', $len - $this->strlen($line));
}
if ($large) {
$messages[] = str_repeat(' ', $len);
}
foreach ($messages as &$message) {
$message = sprintf('<%s>%s</%s>', $style, $message, $style);
}
return implode("\n", $messages);
}
$messages = $large ? array(str_repeat(' ', $len)) : array();
foreach ($lines as $line)
protected function strlen($string)
{
$messages[] = $line.str_repeat(' ', $len - $this->strlen($line));
}
if ($large)
{
$messages[] = str_repeat(' ', $len);
return function_exists('mb_strlen') ? mb_strlen($string) : strlen($string);
}
foreach ($messages as &$message)
/**
* Returns the helper's canonical name
*/
public function getName()
{
$message = sprintf('<%s>%s</%s>', $style, $message, $style);
return 'formatter';
}
return implode("\n", $messages);
}
protected function strlen($string)
{
return function_exists('mb_strlen') ? mb_strlen($string) : strlen($string);
}
/**
* Returns the helper's canonical name
*/
public function getName()
{
return 'formatter';
}
}

View File

@ -3,7 +3,7 @@
namespace Symfony\Component\Console\Helper;
/*
* This file is part of the symfony framework.
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
@ -14,32 +14,29 @@ namespace Symfony\Component\Console\Helper;
/**
* Helper is the base class for all helper classes.
*
* @package symfony
* @subpackage console
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
abstract class Helper implements HelperInterface
{
protected
$helperSet = null;
protected $helperSet = null;
/**
* Sets the helper set associated with this helper.
*
* @param HelperSet $helperSet A HelperSet instance
*/
public function setHelperSet(HelperSet $helperSet = null)
{
$this->helperSet = $helperSet;
}
/**
* Sets the helper set associated with this helper.
*
* @param HelperSet $helperSet A HelperSet instance
*/
public function setHelperSet(HelperSet $helperSet = null)
{
$this->helperSet = $helperSet;
}
/**
* Gets the helper set associated with this helper.
*
* @return HelperSet A HelperSet instance
*/
public function getHelperSet()
{
return $this->helperSet;
}
/**
* Gets the helper set associated with this helper.
*
* @return HelperSet A HelperSet instance
*/
public function getHelperSet()
{
return $this->helperSet;
}
}

View File

@ -3,7 +3,7 @@
namespace Symfony\Component\Console\Helper;
/*
* This file is part of the symfony framework.
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
@ -14,30 +14,28 @@ namespace Symfony\Component\Console\Helper;
/**
* HelperInterface is the interface all helpers must implement.
*
* @package symfony
* @subpackage console
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
interface HelperInterface
{
/**
* Sets the helper set associated with this helper.
*
* @param HelperSet $helperSet A HelperSet instance
*/
function setHelperSet(HelperSet $helperSet = null);
/**
* Sets the helper set associated with this helper.
*
* @param HelperSet $helperSet A HelperSet instance
*/
function setHelperSet(HelperSet $helperSet = null);
/**
* Gets the helper set associated with this helper.
*
* @return HelperSet A HelperSet instance
*/
function getHelperSet();
/**
* Gets the helper set associated with this helper.
*
* @return HelperSet A HelperSet instance
*/
function getHelperSet();
/**
* Returns the canonical name of this helper.
*
* @return string The canonical name
*/
function getName();
/**
* Returns the canonical name of this helper.
*
* @return string The canonical name
*/
function getName();
}

View File

@ -5,7 +5,7 @@ namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Command\Command;
/*
* This file is part of the symfony framework.
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
@ -16,89 +16,87 @@ use Symfony\Component\Console\Command\Command;
/**
* HelperSet represents a set of helpers to be used with a command.
*
* @package symfony
* @subpackage console
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class HelperSet
{
protected
$helpers = array(),
$command = null;
protected $helpers;
protected $command;
public function __construct(array $helpers = array())
{
foreach ($helpers as $alias => $helper)
/**
* @param Helper[] $helpers An array of helper.
*/
public function __construct(array $helpers = array())
{
$this->set($helper, is_int($alias) ? null : $alias);
}
}
/**
* Sets a helper.
*
* @param HelperInterface $value The helper instance
* @param string $alias An alias
*/
public function set(HelperInterface $helper, $alias = null)
{
$this->helpers[$helper->getName()] = $helper;
if (null !== $alias)
{
$this->helpers[$alias] = $helper;
$this->helpers = array();
foreach ($helpers as $alias => $helper) {
$this->set($helper, is_int($alias) ? null : $alias);
}
}
$helper->setHelperSet($this);
}
/**
* Returns true if the helper if defined.
*
* @param string $name The helper name
*
* @return Boolean true if the helper is defined, false otherwise
*/
public function has($name)
{
return isset($this->helpers[$name]);
}
/**
* Gets a helper value.
*
* @param string $name The helper name
*
* @return HelperInterface The helper instance
*
* @throws \InvalidArgumentException if the helper is not defined
*/
public function get($name)
{
if (!$this->has($name))
/**
* Sets a helper.
*
* @param HelperInterface $value The helper instance
* @param string $alias An alias
*/
public function set(HelperInterface $helper, $alias = null)
{
throw new \InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name));
$this->helpers[$helper->getName()] = $helper;
if (null !== $alias) {
$this->helpers[$alias] = $helper;
}
$helper->setHelperSet($this);
}
return $this->helpers[$name];
}
/**
* Returns true if the helper if defined.
*
* @param string $name The helper name
*
* @return Boolean true if the helper is defined, false otherwise
*/
public function has($name)
{
return isset($this->helpers[$name]);
}
/**
* Sets the command associated with this helper set.
*
* @param Command $command A Command instance
*/
public function setCommand(Command $command = null)
{
$this->command = $command;
}
/**
* Gets a helper value.
*
* @param string $name The helper name
*
* @return HelperInterface The helper instance
*
* @throws \InvalidArgumentException if the helper is not defined
*/
public function get($name)
{
if (!$this->has($name)) {
throw new \InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name));
}
/**
* Gets the command associated with this helper set.
*
* @return Command A Command instance
*/
public function getCommand()
{
return $this->command;
}
return $this->helpers[$name];
}
/**
* Sets the command associated with this helper set.
*
* @param Command $command A Command instance
*/
public function setCommand(Command $command = null)
{
$this->command = $command;
}
/**
* Gets the command associated with this helper set.
*
* @return Command A Command instance
*/
public function getCommand()
{
return $this->command;
}
}

View File

@ -3,7 +3,7 @@
namespace Symfony\Component\Console\Input;
/*
* This file is part of the symfony framework.
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
@ -20,7 +20,7 @@ namespace Symfony\Component\Console\Input;
*
* By default, the `$_SERVER['argv']` array is used for the input values.
*
* This can be overriden by explicitly passing the input values in the constructor:
* This can be overridden by explicitly passing the input values in the constructor:
*
* $input = new ArgvInput($_SERVER['argv']);
*
@ -31,254 +31,225 @@ namespace Symfony\Component\Console\Input;
* the same rules as the argv one. It's almost always better to use the
* `StringInput` when you want to provide your own input.
*
* @package symfony
* @subpackage console
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*
* @see http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html
* @see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02
*/
class ArgvInput extends Input
{
protected $tokens;
protected $parsed;
protected $tokens;
protected $parsed;
/**
* Constructor.
*
* @param array $argv An array of parameters from the CLI (in the argv format)
* @param InputDefinition $definition A InputDefinition instance
*/
public function __construct(array $argv = null, InputDefinition $definition = null)
{
if (null === $argv)
/**
* Constructor.
*
* @param array $argv An array of parameters from the CLI (in the argv format)
* @param InputDefinition $definition A InputDefinition instance
*/
public function __construct(array $argv = null, InputDefinition $definition = null)
{
$argv = $_SERVER['argv'];
if (null === $argv) {
$argv = $_SERVER['argv'];
}
// strip the program name
array_shift($argv);
$this->tokens = $argv;
parent::__construct($definition);
}
// strip the program name
array_shift($argv);
$this->tokens = $argv;
parent::__construct($definition);
}
/**
* Processes command line arguments.
*/
protected function parse()
{
$this->parsed = $this->tokens;
while (null !== ($token = array_shift($this->parsed)))
/**
* Processes command line arguments.
*/
protected function parse()
{
if ('--' === substr($token, 0, 2))
{
$this->parseLongOption($token);
}
elseif ('-' === $token[0])
{
$this->parseShortOption($token);
}
else
{
$this->parseArgument($token);
}
}
}
/**
* Parses a short option.
*
* @param string $token The current token.
*/
protected function parseShortOption($token)
{
$name = substr($token, 1);
if (strlen($name) > 1)
{
if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptParameter())
{
// an option with a value (with no space)
$this->addShortOption($name[0], substr($name, 1));
}
else
{
$this->parseShortOptionSet($name);
}
}
else
{
$this->addShortOption($name, null);
}
}
/**
* Parses a short option set.
*
* @param string $token The current token
*/
protected function parseShortOptionSet($name)
{
$len = strlen($name);
for ($i = 0; $i < $len; $i++)
{
if (!$this->definition->hasShortcut($name[$i]))
{
throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i]));
}
$option = $this->definition->getOptionForShortcut($name[$i]);
if ($option->acceptParameter())
{
$this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1));
break;
}
else
{
$this->addLongOption($option->getName(), true);
}
}
}
/**
* Parses a long option.
*
* @param string $token The current token
*/
protected function parseLongOption($token)
{
$name = substr($token, 2);
if (false !== $pos = strpos($name, '='))
{
$this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1));
}
else
{
$this->addLongOption($name, null);
}
}
/**
* Parses an argument.
*
* @param string $token The current token
*/
protected function parseArgument($token)
{
if (!$this->definition->hasArgument(count($this->arguments)))
{
throw new \RuntimeException('Too many arguments.');
$this->parsed = $this->tokens;
while (null !== $token = array_shift($this->parsed)) {
if ('--' === substr($token, 0, 2)) {
$this->parseLongOption($token);
} elseif ('-' === $token[0]) {
$this->parseShortOption($token);
} else {
$this->parseArgument($token);
}
}
}
$this->arguments[$this->definition->getArgument(count($this->arguments))->getName()] = $token;
}
/**
* Adds a short option value.
*
* @param string $shortcut The short option key
* @param mixed $value The value for the option
*/
protected function addShortOption($shortcut, $value)
{
if (!$this->definition->hasShortcut($shortcut))
/**
* Parses a short option.
*
* @param string $token The current token.
*/
protected function parseShortOption($token)
{
throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));
$name = substr($token, 1);
if (strlen($name) > 1) {
if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) {
// an option with a value (with no space)
$this->addShortOption($name[0], substr($name, 1));
} else {
$this->parseShortOptionSet($name);
}
} else {
$this->addShortOption($name, null);
}
}
$this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
}
/**
* Adds a long option value.
*
* @param string $name The long option key
* @param mixed $value The value for the option
*/
protected function addLongOption($name, $value)
{
if (!$this->definition->hasOption($name))
/**
* Parses a short option set.
*
* @param string $token The current token
*
* @throws \RuntimeException When option given doesn't exist
*/
protected function parseShortOptionSet($name)
{
throw new \RuntimeException(sprintf('The "--%s" option does not exist.', $name));
$len = strlen($name);
for ($i = 0; $i < $len; $i++) {
if (!$this->definition->hasShortcut($name[$i])) {
throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i]));
}
$option = $this->definition->getOptionForShortcut($name[$i]);
if ($option->acceptValue()) {
$this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1));
break;
} else {
$this->addLongOption($option->getName(), true);
}
}
}
$option = $this->definition->getOption($name);
if (null === $value && $option->acceptParameter())
/**
* Parses a long option.
*
* @param string $token The current token
*/
protected function parseLongOption($token)
{
// if option accepts an optional or mandatory argument
// let's see if there is one provided
$next = array_shift($this->parsed);
if ('-' !== $next[0])
{
$value = $next;
}
else
{
array_unshift($this->parsed, $next);
}
$name = substr($token, 2);
if (false !== $pos = strpos($name, '=')) {
$this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1));
} else {
$this->addLongOption($name, null);
}
}
if (null === $value)
/**
* Parses an argument.
*
* @param string $token The current token
*
* @throws \RuntimeException When too many arguments are given
*/
protected function parseArgument($token)
{
if ($option->isParameterRequired())
{
throw new \RuntimeException(sprintf('The "--%s" option requires a value.', $name));
}
if (!$this->definition->hasArgument(count($this->arguments))) {
throw new \RuntimeException('Too many arguments.');
}
$value = $option->isParameterOptional() ? $option->getDefault() : true;
$this->arguments[$this->definition->getArgument(count($this->arguments))->getName()] = $token;
}
$this->options[$name] = $value;
}
/**
* Returns the first argument from the raw parameters (not parsed).
*
* @return string The value of the first argument or null otherwise
*/
public function getFirstArgument()
{
foreach ($this->tokens as $token)
/**
* Adds a short option value.
*
* @param string $shortcut The short option key
* @param mixed $value The value for the option
*
* @throws \RuntimeException When option given doesn't exist
*/
protected function addShortOption($shortcut, $value)
{
if ($token && '-' === $token[0])
{
continue;
}
if (!$this->definition->hasShortcut($shortcut)) {
throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));
}
return $token;
}
}
/**
* Returns true if the raw parameters (not parsed) contains a value.
*
* This method is to be used to introspect the input parameters
* before it has been validated. It must be used carefully.
*
* @param string|array $values The value(s) to look for in the raw parameters (can be an array)
*
* @return Boolean true if the value is contained in the raw parameters
*/
public function hasParameterOption($values)
{
if (!is_array($values))
{
$values = array($values);
$this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
}
foreach ($this->tokens as $v)
/**
* Adds a long option value.
*
* @param string $name The long option key
* @param mixed $value The value for the option
*
* @throws \RuntimeException When option given doesn't exist
*/
protected function addLongOption($name, $value)
{
if (in_array($v, $values))
{
return true;
}
if (!$this->definition->hasOption($name)) {
throw new \RuntimeException(sprintf('The "--%s" option does not exist.', $name));
}
$option = $this->definition->getOption($name);
if (null === $value && $option->acceptValue()) {
// if option accepts an optional or mandatory argument
// let's see if there is one provided
$next = array_shift($this->parsed);
if ('-' !== $next[0]) {
$value = $next;
} else {
array_unshift($this->parsed, $next);
}
}
if (null === $value) {
if ($option->isValueRequired()) {
throw new \RuntimeException(sprintf('The "--%s" option requires a value.', $name));
}
$value = $option->isValueOptional() ? $option->getDefault() : true;
}
$this->options[$name] = $value;
}
return false;
}
/**
* Returns the first argument from the raw parameters (not parsed).
*
* @return string The value of the first argument or null otherwise
*/
public function getFirstArgument()
{
foreach ($this->tokens as $token) {
if ($token && '-' === $token[0]) {
continue;
}
return $token;
}
}
/**
* Returns true if the raw parameters (not parsed) contains a value.
*
* This method is to be used to introspect the input parameters
* before it has been validated. It must be used carefully.
*
* @param string|array $values The value(s) to look for in the raw parameters (can be an array)
*
* @return Boolean true if the value is contained in the raw parameters
*/
public function hasParameterOption($values)
{
if (!is_array($values)) {
$values = array($values);
}
foreach ($this->tokens as $v) {
if (in_array($v, $values)) {
return true;
}
}
return false;
}
}

View File

@ -3,7 +3,7 @@
namespace Symfony\Component\Console\Input;
/*
* This file is part of the symfony framework.
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
@ -18,157 +18,145 @@ namespace Symfony\Component\Console\Input;
*
* $input = new ArrayInput(array('name' => 'foo', '--bar' => 'foobar'));
*
* @package symfony
* @subpackage console
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class ArrayInput extends Input
{
protected $parameters;
protected $parameters;
/**
* Constructor.
*
* @param array $param An array of parameters
* @param InputDefinition $definition A InputDefinition instance
*/
public function __construct(array $parameters, InputDefinition $definition = null)
{
$this->parameters = $parameters;
parent::__construct($definition);
}
/**
* Returns the first argument from the raw parameters (not parsed).
*
* @return string The value of the first argument or null otherwise
*/
public function getFirstArgument()
{
foreach ($this->parameters as $key => $value)
/**
* Constructor.
*
* @param array $param An array of parameters
* @param InputDefinition $definition A InputDefinition instance
*/
public function __construct(array $parameters, InputDefinition $definition = null)
{
if ($key && '-' === $key[0])
{
continue;
}
$this->parameters = $parameters;
return $value;
}
}
/**
* Returns true if the raw parameters (not parsed) contains a value.
*
* This method is to be used to introspect the input parameters
* before it has been validated. It must be used carefully.
*
* @param string|array $value The values to look for in the raw parameters (can be an array)
*
* @return Boolean true if the value is contained in the raw parameters
*/
public function hasParameterOption($values)
{
if (!is_array($values))
{
$values = array($values);
parent::__construct($definition);
}
foreach ($this->parameters as $k => $v)
/**
* Returns the first argument from the raw parameters (not parsed).
*
* @return string The value of the first argument or null otherwise
*/
public function getFirstArgument()
{
if (!is_int($k))
{
$v = $k;
}
foreach ($this->parameters as $key => $value) {
if ($key && '-' === $key[0]) {
continue;
}
if (in_array($v, $values))
{
return true;
}
return $value;
}
}
return false;
}
/**
* Processes command line arguments.
*/
protected function parse()
{
foreach ($this->parameters as $key => $value)
/**
* Returns true if the raw parameters (not parsed) contains a value.
*
* This method is to be used to introspect the input parameters
* before it has been validated. It must be used carefully.
*
* @param string|array $value The values to look for in the raw parameters (can be an array)
*
* @return Boolean true if the value is contained in the raw parameters
*/
public function hasParameterOption($values)
{
if ('--' === substr($key, 0, 2))
{
$this->addLongOption(substr($key, 2), $value);
}
elseif ('-' === $key[0])
{
$this->addShortOption(substr($key, 1), $value);
}
else
{
$this->addArgument($key, $value);
}
}
}
if (!is_array($values)) {
$values = array($values);
}
/**
* Adds a short option value.
*
* @param string $shortcut The short option key
* @param mixed $value The value for the option
*/
protected function addShortOption($shortcut, $value)
{
if (!$this->definition->hasShortcut($shortcut))
{
throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));
foreach ($this->parameters as $k => $v) {
if (!is_int($k)) {
$v = $k;
}
if (in_array($v, $values)) {
return true;
}
}
return false;
}
$this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
}
/**
* Adds a long option value.
*
* @param string $name The long option key
* @param mixed $value The value for the option
*/
protected function addLongOption($name, $value)
{
if (!$this->definition->hasOption($name))
/**
* Processes command line arguments.
*/
protected function parse()
{
throw new \RuntimeException(sprintf('The "--%s" option does not exist.', $name));
foreach ($this->parameters as $key => $value) {
if ('--' === substr($key, 0, 2)) {
$this->addLongOption(substr($key, 2), $value);
} elseif ('-' === $key[0]) {
$this->addShortOption(substr($key, 1), $value);
} else {
$this->addArgument($key, $value);
}
}
}
$option = $this->definition->getOption($name);
if (null === $value)
/**
* Adds a short option value.
*
* @param string $shortcut The short option key
* @param mixed $value The value for the option
*
* @throws \RuntimeException When option given doesn't exist
*/
protected function addShortOption($shortcut, $value)
{
if ($option->isParameterRequired())
{
throw new \RuntimeException(sprintf('The "--%s" option requires a value.', $name));
}
if (!$this->definition->hasShortcut($shortcut)) {
throw new \InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut));
}
$value = $option->isParameterOptional() ? $option->getDefault() : true;
$this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
}
$this->options[$name] = $value;
}
/**
* Adds an argument value.
*
* @param string $name The argument name
* @param mixed $value The value for the argument
*/
protected function addArgument($name, $value)
{
if (!$this->definition->hasArgument($name))
/**
* Adds a long option value.
*
* @param string $name The long option key
* @param mixed $value The value for the option
*
* @throws \InvalidArgumentException When option given doesn't exist
* @throws \InvalidArgumentException When a required value is missing
*/
protected function addLongOption($name, $value)
{
throw new \RuntimeException(sprintf('The "%s" argument does not exist.', $name));
if (!$this->definition->hasOption($name)) {
throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name));
}
$option = $this->definition->getOption($name);
if (null === $value) {
if ($option->isValueRequired()) {
throw new \InvalidArgumentException(sprintf('The "--%s" option requires a value.', $name));
}
$value = $option->isValueOptional() ? $option->getDefault() : true;
}
$this->options[$name] = $value;
}
$this->arguments[$name] = $value;
}
/**
* Adds an argument value.
*
* @param string $name The argument name
* @param mixed $value The value for the argument
*
* @throws \InvalidArgumentException When argument given doesn't exist
*/
protected function addArgument($name, $value)
{
if (!$this->definition->hasArgument($name)) {
throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}
$this->arguments[$name] = $value;
}
}

View File

@ -3,7 +3,7 @@
namespace Symfony\Component\Console\Input;
/*
* This file is part of the symfony framework.
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
@ -20,179 +20,180 @@ namespace Symfony\Component\Console\Input;
* * `StringInput`: The input is provided as a string
* * `ArrayInput`: The input is provided as an array
*
* @package symfony
* @subpackage console
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
abstract class Input implements InputInterface
{
protected $definition;
protected $options;
protected $arguments;
protected $interactive = true;
protected $definition;
protected $options;
protected $arguments;
protected $interactive = true;
/**
* Constructor.
*
* @param InputDefinition $definition A InputDefinition instance
*/
public function __construct(InputDefinition $definition = null)
{
if (null === $definition)
/**
* Constructor.
*
* @param InputDefinition $definition A InputDefinition instance
*/
public function __construct(InputDefinition $definition = null)
{
$this->definition = new InputDefinition();
}
else
{
$this->bind($definition);
$this->validate();
}
}
/**
* Binds the current Input instance with the given arguments and options.
*
* @param InputDefinition $definition A InputDefinition instance
*/
public function bind(InputDefinition $definition)
{
$this->arguments = array();
$this->options = array();
$this->definition = $definition;
$this->parse();
}
/**
* Processes command line arguments.
*/
abstract protected function parse();
public function validate()
{
if (count($this->arguments) < $this->definition->getArgumentRequiredCount())
{
throw new \RuntimeException('Not enough arguments.');
}
}
public function isInteractive()
{
return $this->interactive;
}
public function setInteractive($interactive)
{
$this->interactive = (Boolean) $interactive;
}
/**
* Returns the argument values.
*
* @return array An array of argument values
*/
public function getArguments()
{
return array_merge($this->definition->getArgumentDefaults(), $this->arguments);
}
/**
* Returns the argument value for a given argument name.
*
* @param string $name The argument name
*
* @return mixed The argument value
*/
public function getArgument($name)
{
if (!$this->definition->hasArgument($name))
{
throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
if (null === $definition) {
$this->definition = new InputDefinition();
} else {
$this->bind($definition);
$this->validate();
}
}
return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)->getDefault();
}
/**
* Sets an argument value by name.
*
* @param string $name The argument name
* @param string $value The argument value
*/
public function setArgument($name, $value)
{
if (!$this->definition->hasArgument($name))
/**
* Binds the current Input instance with the given arguments and options.
*
* @param InputDefinition $definition A InputDefinition instance
*/
public function bind(InputDefinition $definition)
{
throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
$this->arguments = array();
$this->options = array();
$this->definition = $definition;
$this->parse();
}
$this->arguments[$name] = $value;
}
/**
* Processes command line arguments.
*/
abstract protected function parse();
/**
* Returns true if an InputArgument object exists by name or position.
*
* @param string|integer $name The InputArgument name or position
*
* @return Boolean true if the InputArgument object exists, false otherwise
*/
public function hasArgument($name)
{
return $this->definition->hasArgument($name);
}
/**
* Returns the options values.
*
* @return array An array of option values
*/
public function getOptions()
{
return array_merge($this->definition->getOptionDefaults(), $this->options);
}
/**
* Returns the option value for a given option name.
*
* @param string $name The option name
*
* @return mixed The option value
*/
public function getOption($name)
{
if (!$this->definition->hasOption($name))
/**
* @throws \RuntimeException When not enough arguments are given
*/
public function validate()
{
throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
if (count($this->arguments) < $this->definition->getArgumentRequiredCount()) {
throw new \RuntimeException('Not enough arguments.');
}
}
return isset($this->options[$name]) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
}
/**
* Sets an option value by name.
*
* @param string $name The option name
* @param string $value The option value
*/
public function setOption($name, $value)
{
if (!$this->definition->hasOption($name))
public function isInteractive()
{
throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
return $this->interactive;
}
$this->options[$name] = $value;
}
public function setInteractive($interactive)
{
$this->interactive = (Boolean) $interactive;
}
/**
* Returns true if an InputOption object exists by name.
*
* @param string $name The InputOption name
*
* @return Boolean true if the InputOption object exists, false otherwise
*/
public function hasOption($name)
{
return $this->definition->hasOption($name);
}
/**
* Returns the argument values.
*
* @return array An array of argument values
*/
public function getArguments()
{
return array_merge($this->definition->getArgumentDefaults(), $this->arguments);
}
/**
* Returns the argument value for a given argument name.
*
* @param string $name The argument name
*
* @return mixed The argument value
*
* @throws \InvalidArgumentException When argument given doesn't exist
*/
public function getArgument($name)
{
if (!$this->definition->hasArgument($name)) {
throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}
return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)->getDefault();
}
/**
* Sets an argument value by name.
*
* @param string $name The argument name
* @param string $value The argument value
*
* @throws \InvalidArgumentException When argument given doesn't exist
*/
public function setArgument($name, $value)
{
if (!$this->definition->hasArgument($name)) {
throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}
$this->arguments[$name] = $value;
}
/**
* Returns true if an InputArgument object exists by name or position.
*
* @param string|integer $name The InputArgument name or position
*
* @return Boolean true if the InputArgument object exists, false otherwise
*/
public function hasArgument($name)
{
return $this->definition->hasArgument($name);
}
/**
* Returns the options values.
*
* @return array An array of option values
*/
public function getOptions()
{
return array_merge($this->definition->getOptionDefaults(), $this->options);
}
/**
* Returns the option value for a given option name.
*
* @param string $name The option name
*
* @return mixed The option value
*
* @throws \InvalidArgumentException When option given doesn't exist
*/
public function getOption($name)
{
if (!$this->definition->hasOption($name)) {
throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
}
return isset($this->options[$name]) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
}
/**
* Sets an option value by name.
*
* @param string $name The option name
* @param string $value The option value
*
* @throws \InvalidArgumentException When option given doesn't exist
*/
public function setOption($name, $value)
{
if (!$this->definition->hasOption($name)) {
throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
}
$this->options[$name] = $value;
}
/**
* Returns true if an InputOption object exists by name.
*
* @param string $name The InputOption name
*
* @return Boolean true if the InputOption object exists, false otherwise
*/
public function hasOption($name)
{
return $this->definition->hasOption($name);
}
}

View File

@ -3,7 +3,7 @@
namespace Symfony\Component\Console\Input;
/*
* This file is part of the symfony framework.
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
@ -14,121 +14,115 @@ namespace Symfony\Component\Console\Input;
/**
* Represents a command line argument.
*
* @package symfony
* @subpackage console
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class InputArgument
{
const REQUIRED = 1;
const OPTIONAL = 2;
const IS_ARRAY = 4;
const REQUIRED = 1;
const OPTIONAL = 2;
const IS_ARRAY = 4;
protected $name;
protected $mode;
protected $default;
protected $description;
protected $name;
protected $mode;
protected $default;
protected $description;
/**
* Constructor.
*
* @param string $name The argument name
* @param integer $mode The argument mode: self::REQUIRED or self::OPTIONAL
* @param string $description A description text
* @param mixed $default The default value (for self::OPTIONAL mode only)
*/
public function __construct($name, $mode = null, $description = '', $default = null)
{
if (null === $mode)
/**
* Constructor.
*
* @param string $name The argument name
* @param integer $mode The argument mode: self::REQUIRED or self::OPTIONAL
* @param string $description A description text
* @param mixed $default The default value (for self::OPTIONAL mode only)
*
* @throws \InvalidArgumentException When argument mode is not valid
*/
public function __construct($name, $mode = null, $description = '', $default = null)
{
$mode = self::OPTIONAL;
}
else if (is_string($mode) || $mode > 7)
{
throw new \InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode));
if (null === $mode) {
$mode = self::OPTIONAL;
} else if (is_string($mode) || $mode > 7) {
throw new \InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode));
}
$this->name = $name;
$this->mode = $mode;
$this->description = $description;
$this->setDefault($default);
}
$this->name = $name;
$this->mode = $mode;
$this->description = $description;
$this->setDefault($default);
}
/**
* Returns the argument name.
*
* @return string The argument name
*/
public function getName()
{
return $this->name;
}
/**
* Returns true if the argument is required.
*
* @return Boolean true if parameter mode is self::REQUIRED, false otherwise
*/
public function isRequired()
{
return self::REQUIRED === (self::REQUIRED & $this->mode);
}
/**
* Returns true if the argument can take multiple values.
*
* @return Boolean true if mode is self::IS_ARRAY, false otherwise
*/
public function isArray()
{
return self::IS_ARRAY === (self::IS_ARRAY & $this->mode);
}
/**
* Sets the default value.
*
* @param mixed $default The default value
*/
public function setDefault($default = null)
{
if (self::REQUIRED === $this->mode && null !== $default)
/**
* Returns the argument name.
*
* @return string The argument name
*/
public function getName()
{
throw new \LogicException('Cannot set a default value except for Parameter::OPTIONAL mode.');
return $this->name;
}
if ($this->isArray())
/**
* Returns true if the argument is required.
*
* @return Boolean true if parameter mode is self::REQUIRED, false otherwise
*/
public function isRequired()
{
if (null === $default)
{
$default = array();
}
else if (!is_array($default))
{
throw new \LogicException('A default value for an array argument must be an array.');
}
return self::REQUIRED === (self::REQUIRED & $this->mode);
}
$this->default = $default;
}
/**
* Returns true if the argument can take multiple values.
*
* @return Boolean true if mode is self::IS_ARRAY, false otherwise
*/
public function isArray()
{
return self::IS_ARRAY === (self::IS_ARRAY & $this->mode);
}
/**
* Returns the default value.
*
* @return mixed The default value
*/
public function getDefault()
{
return $this->default;
}
/**
* Sets the default value.
*
* @param mixed $default The default value
*
* @throws \LogicException When incorrect default value is given
*/
public function setDefault($default = null)
{
if (self::REQUIRED === $this->mode && null !== $default) {
throw new \LogicException('Cannot set a default value except for Parameter::OPTIONAL mode.');
}
/**
* Returns the description text.
*
* @return string The description text
*/
public function getDescription()
{
return $this->description;
}
if ($this->isArray()) {
if (null === $default) {
$default = array();
} else if (!is_array($default)) {
throw new \LogicException('A default value for an array argument must be an array.');
}
}
$this->default = $default;
}
/**
* Returns the default value.
*
* @return mixed The default value
*/
public function getDefault()
{
return $this->default;
}
/**
* Returns the description text.
*
* @return string The description text
*/
public function getDescription()
{
return $this->description;
}
}

View File

@ -3,7 +3,7 @@
namespace Symfony\Component\Console\Input;
/*
* This file is part of the symfony framework.
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
@ -18,490 +18,454 @@ namespace Symfony\Component\Console\Input;
*
* $definition = new InputDefinition(array(
* new InputArgument('name', InputArgument::REQUIRED),
* new InputOption('foo', 'f', InputOption::PARAMETER_REQUIRED),
* new InputOption('foo', 'f', InputOption::VALUE_REQUIRED),
* ));
*
* @package symfony
* @subpackage console
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class InputDefinition
{
protected $arguments;
protected $requiredCount;
protected $hasAnArrayArgument = false;
protected $hasOptional;
protected $options;
protected $shortcuts;
protected $arguments;
protected $requiredCount;
protected $hasAnArrayArgument = false;
protected $hasOptional;
protected $options;
protected $shortcuts;
/**
* Constructor.
*
* @param array $definition An array of InputArgument and InputOption instance
*/
public function __construct(array $definition = array())
{
$this->setDefinition($definition);
}
public function setDefinition(array $definition)
{
$arguments = array();
$options = array();
foreach ($definition as $item)
/**
* Constructor.
*
* @param array $definition An array of InputArgument and InputOption instance
*/
public function __construct(array $definition = array())
{
if ($item instanceof InputOption)
{
$options[] = $item;
}
else
{
$arguments[] = $item;
}
$this->setDefinition($definition);
}
$this->setArguments($arguments);
$this->setOptions($options);
}
/**
* Sets the InputArgument objects.
*
* @param array $arguments An array of InputArgument objects
*/
public function setArguments($arguments = array())
{
$this->arguments = array();
$this->requiredCount = 0;
$this->hasOptional = false;
$this->addArguments($arguments);
}
/**
* Add an array of InputArgument objects.
*
* @param array $arguments An array of InputArgument objects
*/
public function addArguments($arguments = array())
{
if (null !== $arguments)
public function setDefinition(array $definition)
{
foreach ($arguments as $argument)
{
$this->addArgument($argument);
}
}
}
/**
* Add an InputArgument object.
*
* @param InputArgument $argument An InputArgument object
*/
public function addArgument(InputArgument $argument)
{
if (isset($this->arguments[$argument->getName()]))
{
throw new \LogicException(sprintf('An argument with name "%s" already exist.', $argument->getName()));
}
if ($this->hasAnArrayArgument)
{
throw new \LogicException('Cannot add an argument after an array argument.');
}
if ($argument->isRequired() && $this->hasOptional)
{
throw new \LogicException('Cannot add a required argument after an optional one.');
}
if ($argument->isArray())
{
$this->hasAnArrayArgument = true;
}
if ($argument->isRequired())
{
++$this->requiredCount;
}
else
{
$this->hasOptional = true;
}
$this->arguments[$argument->getName()] = $argument;
}
/**
* Returns an InputArgument by name or by position.
*
* @param string|integer $name The InputArgument name or position
*
* @return InputArgument An InputArgument object
*/
public function getArgument($name)
{
$arguments = is_int($name) ? array_values($this->arguments) : $this->arguments;
if (!$this->hasArgument($name))
{
throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}
return $arguments[$name];
}
/**
* Returns true if an InputArgument object exists by name or position.
*
* @param string|integer $name The InputArgument name or position
*
* @return Boolean true if the InputArgument object exists, false otherwise
*/
public function hasArgument($name)
{
$arguments = is_int($name) ? array_values($this->arguments) : $this->arguments;
return isset($arguments[$name]);
}
/**
* Gets the array of InputArgument objects.
*
* @return array An array of InputArgument objects
*/
public function getArguments()
{
return $this->arguments;
}
/**
* Returns the number of InputArguments.
*
* @return integer The number of InputArguments
*/
public function getArgumentCount()
{
return $this->hasAnArrayArgument ? PHP_INT_MAX : count($this->arguments);
}
/**
* Returns the number of required InputArguments.
*
* @return integer The number of required InputArguments
*/
public function getArgumentRequiredCount()
{
return $this->requiredCount;
}
/**
* Gets the default values.
*
* @return array An array of default values
*/
public function getArgumentDefaults()
{
$values = array();
foreach ($this->arguments as $argument)
{
$values[$argument->getName()] = $argument->getDefault();
}
return $values;
}
/**
* Sets the InputOption objects.
*
* @param array $options An array of InputOption objects
*/
public function setOptions($options = array())
{
$this->options = array();
$this->shortcuts = array();
$this->addOptions($options);
}
/**
* Add an array of InputOption objects.
*
* @param array $options An array of InputOption objects
*/
public function addOptions($options = array())
{
foreach ($options as $option)
{
$this->addOption($option);
}
}
/**
* Add an InputOption object.
*
* @param InputOption $option An InputOption object
*/
public function addOption(InputOption $option)
{
if (isset($this->options[$option->getName()]))
{
throw new \LogicException(sprintf('An option named "%s" already exist.', $option->getName()));
}
else if (isset($this->shortcuts[$option->getShortcut()]))
{
throw new \LogicException(sprintf('An option with shortcut "%s" already exist.', $option->getShortcut()));
}
$this->options[$option->getName()] = $option;
if ($option->getShortcut())
{
$this->shortcuts[$option->getShortcut()] = $option->getName();
}
}
/**
* Returns an InputOption by name.
*
* @param string $name The InputOption name
*
* @return InputOption A InputOption object
*/
public function getOption($name)
{
if (!$this->hasOption($name))
{
throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name));
}
return $this->options[$name];
}
/**
* Returns true if an InputOption object exists by name.
*
* @param string $name The InputOption name
*
* @return Boolean true if the InputOption object exists, false otherwise
*/
public function hasOption($name)
{
return isset($this->options[$name]);
}
/**
* Gets the array of InputOption objects.
*
* @return array An array of InputOption objects
*/
public function getOptions()
{
return $this->options;
}
/**
* Returns true if an InputOption object exists by shortcut.
*
* @param string $name The InputOption shortcut
*
* @return Boolean true if the InputOption object exists, false otherwise
*/
public function hasShortcut($name)
{
return isset($this->shortcuts[$name]);
}
/**
* Gets an InputOption by shortcut.
*
* @return InputOption An InputOption object
*/
public function getOptionForShortcut($shortcut)
{
return $this->getOption($this->shortcutToName($shortcut));
}
/**
* Gets an array of default values.
*
* @return array An array of all default values
*/
public function getOptionDefaults()
{
$values = array();
foreach ($this->options as $option)
{
$values[$option->getName()] = $option->getDefault();
}
return $values;
}
/**
* Returns the InputOption name given a shortcut.
*
* @param string $shortcut The shortcut
*
* @return string The InputOption name
*/
protected function shortcutToName($shortcut)
{
if (!isset($this->shortcuts[$shortcut]))
{
throw new \InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut));
}
return $this->shortcuts[$shortcut];
}
/**
* Gets the synopsis.
*
* @return string The synopsis
*/
public function getSynopsis()
{
$elements = array();
foreach ($this->getOptions() as $option)
{
$shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : '';
$elements[] = sprintf('['.($option->isParameterRequired() ? '%s--%s="..."' : ($option->isParameterOptional() ? '%s--%s[="..."]' : '%s--%s')).']', $shortcut, $option->getName());
}
foreach ($this->getArguments() as $argument)
{
$elements[] = sprintf($argument->isRequired() ? '%s' : '[%s]', $argument->getName().($argument->isArray() ? '1' : ''));
if ($argument->isArray())
{
$elements[] = sprintf('... [%sN]', $argument->getName());
}
}
return implode(' ', $elements);
}
/**
* Returns a textual representation of the InputDefinition.
*
* @return string A string representing the InputDefinition
*/
public function asText()
{
// find the largest option or argument name
$max = 0;
foreach ($this->getOptions() as $option)
{
$max = strlen($option->getName()) + 2 > $max ? strlen($option->getName()) + 2 : $max;
}
foreach ($this->getArguments() as $argument)
{
$max = strlen($argument->getName()) > $max ? strlen($argument->getName()) : $max;
}
++$max;
$text = array();
if ($this->getArguments())
{
$text[] = '<comment>Arguments:</comment>';
foreach ($this->getArguments() as $argument)
{
if (null !== $argument->getDefault() && (!is_array($argument->getDefault()) || count($argument->getDefault())))
{
$default = sprintf('<comment> (default: %s)</comment>', is_array($argument->getDefault()) ? str_replace("\n", '', var_export($argument->getDefault(), true)): $argument->getDefault());
}
else
{
$default = '';
$arguments = array();
$options = array();
foreach ($definition as $item) {
if ($item instanceof InputOption) {
$options[] = $item;
} else {
$arguments[] = $item;
}
}
$text[] = sprintf(" <info>%-${max}s</info> %s%s", $argument->getName(), $argument->getDescription(), $default);
}
$text[] = '';
$this->setArguments($arguments);
$this->setOptions($options);
}
if ($this->getOptions())
/**
* Sets the InputArgument objects.
*
* @param array $arguments An array of InputArgument objects
*/
public function setArguments($arguments = array())
{
$text[] = '<comment>Options:</comment>';
$this->arguments = array();
$this->requiredCount = 0;
$this->hasOptional = false;
$this->hasAnArrayArgument = false;
$this->addArguments($arguments);
}
foreach ($this->getOptions() as $option)
{
if ($option->acceptParameter() && null !== $option->getDefault() && (!is_array($option->getDefault()) || count($option->getDefault())))
{
$default = sprintf('<comment> (default: %s)</comment>', is_array($option->getDefault()) ? str_replace("\n", '', print_r($option->getDefault(), true)): $option->getDefault());
/**
* Add an array of InputArgument objects.
*
* @param InputArgument[] $arguments An array of InputArgument objects
*/
public function addArguments($arguments = array())
{
if (null !== $arguments) {
foreach ($arguments as $argument) {
$this->addArgument($argument);
}
}
else
{
$default = '';
}
/**
* Add an InputArgument object.
*
* @param InputArgument $argument An InputArgument object
*
* @throws \LogicException When incorrect argument is given
*/
public function addArgument(InputArgument $argument)
{
if (isset($this->arguments[$argument->getName()])) {
throw new \LogicException(sprintf('An argument with name "%s" already exist.', $argument->getName()));
}
$multiple = $option->isArray() ? '<comment> (multiple values allowed)</comment>' : '';
$text[] = sprintf(' %-'.$max.'s %s%s%s%s', '<info>--'.$option->getName().'</info>', $option->getShortcut() ? sprintf('(-%s) ', $option->getShortcut()) : '', $option->getDescription(), $default, $multiple);
}
$text[] = '';
}
return implode("\n", $text);
}
/**
* Returns an XML representation of the InputDefinition.
*
* @param Boolean $asDom Whether to return a DOM or an XML string
*
* @return string|DOMDocument An XML string representing the InputDefinition
*/
public function asXml($asDom = false)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->formatOutput = true;
$dom->appendChild($definitionXML = $dom->createElement('definition'));
$definitionXML->appendChild($argumentsXML = $dom->createElement('arguments'));
foreach ($this->getArguments() as $argument)
{
$argumentsXML->appendChild($argumentXML = $dom->createElement('argument'));
$argumentXML->setAttribute('name', $argument->getName());
$argumentXML->setAttribute('is_required', $argument->isRequired() ? 1 : 0);
$argumentXML->setAttribute('is_array', $argument->isArray() ? 1 : 0);
$argumentXML->appendChild($descriptionXML = $dom->createElement('description'));
$descriptionXML->appendChild($dom->createTextNode($argument->getDescription()));
$argumentXML->appendChild($defaultsXML = $dom->createElement('defaults'));
$defaults = is_array($argument->getDefault()) ? $argument->getDefault() : ($argument->getDefault() ? array($argument->getDefault()) : array());
foreach ($defaults as $default)
{
$defaultsXML->appendChild($defaultXML = $dom->createElement('default'));
$defaultXML->appendChild($dom->createTextNode($default));
}
}
$definitionXML->appendChild($optionsXML = $dom->createElement('options'));
foreach ($this->getOptions() as $option)
{
$optionsXML->appendChild($optionXML = $dom->createElement('option'));
$optionXML->setAttribute('name', '--'.$option->getName());
$optionXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : '');
$optionXML->setAttribute('accept_parameter', $option->acceptParameter() ? 1 : 0);
$optionXML->setAttribute('is_parameter_required', $option->isParameterRequired() ? 1 : 0);
$optionXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0);
$optionXML->appendChild($descriptionXML = $dom->createElement('description'));
$descriptionXML->appendChild($dom->createTextNode($option->getDescription()));
if ($option->acceptParameter())
{
$optionXML->appendChild($defaultsXML = $dom->createElement('defaults'));
$defaults = is_array($option->getDefault()) ? $option->getDefault() : ($option->getDefault() ? array($option->getDefault()) : array());
foreach ($defaults as $default)
{
$defaultsXML->appendChild($defaultXML = $dom->createElement('default'));
$defaultXML->appendChild($dom->createTextNode($default));
if ($this->hasAnArrayArgument) {
throw new \LogicException('Cannot add an argument after an array argument.');
}
}
if ($argument->isRequired() && $this->hasOptional) {
throw new \LogicException('Cannot add a required argument after an optional one.');
}
if ($argument->isArray()) {
$this->hasAnArrayArgument = true;
}
if ($argument->isRequired()) {
++$this->requiredCount;
} else {
$this->hasOptional = true;
}
$this->arguments[$argument->getName()] = $argument;
}
return $asDom ? $dom : $dom->saveXml();
}
/**
* Returns an InputArgument by name or by position.
*
* @param string|integer $name The InputArgument name or position
*
* @return InputArgument An InputArgument object
*
* @throws \InvalidArgumentException When argument given doesn't exist
*/
public function getArgument($name)
{
$arguments = is_int($name) ? array_values($this->arguments) : $this->arguments;
if (!$this->hasArgument($name)) {
throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}
return $arguments[$name];
}
/**
* Returns true if an InputArgument object exists by name or position.
*
* @param string|integer $name The InputArgument name or position
*
* @return Boolean true if the InputArgument object exists, false otherwise
*/
public function hasArgument($name)
{
$arguments = is_int($name) ? array_values($this->arguments) : $this->arguments;
return isset($arguments[$name]);
}
/**
* Gets the array of InputArgument objects.
*
* @return array An array of InputArgument objects
*/
public function getArguments()
{
return $this->arguments;
}
/**
* Returns the number of InputArguments.
*
* @return integer The number of InputArguments
*/
public function getArgumentCount()
{
return $this->hasAnArrayArgument ? PHP_INT_MAX : count($this->arguments);
}
/**
* Returns the number of required InputArguments.
*
* @return integer The number of required InputArguments
*/
public function getArgumentRequiredCount()
{
return $this->requiredCount;
}
/**
* Gets the default values.
*
* @return array An array of default values
*/
public function getArgumentDefaults()
{
$values = array();
foreach ($this->arguments as $argument) {
$values[$argument->getName()] = $argument->getDefault();
}
return $values;
}
/**
* Sets the InputOption objects.
*
* @param array $options An array of InputOption objects
*/
public function setOptions($options = array())
{
$this->options = array();
$this->shortcuts = array();
$this->addOptions($options);
}
/**
* Add an array of InputOption objects.
*
* @param InputOption[] $options An array of InputOption objects
*/
public function addOptions($options = array())
{
foreach ($options as $option) {
$this->addOption($option);
}
}
/**
* Add an InputOption object.
*
* @param InputOption $option An InputOption object
*
* @throws \LogicException When option given already exist
*/
public function addOption(InputOption $option)
{
if (isset($this->options[$option->getName()])) {
throw new \LogicException(sprintf('An option named "%s" already exist.', $option->getName()));
} else if (isset($this->shortcuts[$option->getShortcut()])) {
throw new \LogicException(sprintf('An option with shortcut "%s" already exist.', $option->getShortcut()));
}
$this->options[$option->getName()] = $option;
if ($option->getShortcut()) {
$this->shortcuts[$option->getShortcut()] = $option->getName();
}
}
/**
* Returns an InputOption by name.
*
* @param string $name The InputOption name
*
* @return InputOption A InputOption object
*/
public function getOption($name)
{
if (!$this->hasOption($name)) {
throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name));
}
return $this->options[$name];
}
/**
* Returns true if an InputOption object exists by name.
*
* @param string $name The InputOption name
*
* @return Boolean true if the InputOption object exists, false otherwise
*/
public function hasOption($name)
{
return isset($this->options[$name]);
}
/**
* Gets the array of InputOption objects.
*
* @return array An array of InputOption objects
*/
public function getOptions()
{
return $this->options;
}
/**
* Returns true if an InputOption object exists by shortcut.
*
* @param string $name The InputOption shortcut
*
* @return Boolean true if the InputOption object exists, false otherwise
*/
public function hasShortcut($name)
{
return isset($this->shortcuts[$name]);
}
/**
* Gets an InputOption by shortcut.
*
* @return InputOption An InputOption object
*/
public function getOptionForShortcut($shortcut)
{
return $this->getOption($this->shortcutToName($shortcut));
}
/**
* Gets an array of default values.
*
* @return array An array of all default values
*/
public function getOptionDefaults()
{
$values = array();
foreach ($this->options as $option) {
$values[$option->getName()] = $option->getDefault();
}
return $values;
}
/**
* Returns the InputOption name given a shortcut.
*
* @param string $shortcut The shortcut
*
* @return string The InputOption name
*
* @throws \InvalidArgumentException When option given does not exist
*/
protected function shortcutToName($shortcut)
{
if (!isset($this->shortcuts[$shortcut])) {
throw new \InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut));
}
return $this->shortcuts[$shortcut];
}
/**
* Gets the synopsis.
*
* @return string The synopsis
*/
public function getSynopsis()
{
$elements = array();
foreach ($this->getOptions() as $option) {
$shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : '';
$elements[] = sprintf('['.($option->isValueRequired() ? '%s--%s="..."' : ($option->isValueOptional() ? '%s--%s[="..."]' : '%s--%s')).']', $shortcut, $option->getName());
}
foreach ($this->getArguments() as $argument) {
$elements[] = sprintf($argument->isRequired() ? '%s' : '[%s]', $argument->getName().($argument->isArray() ? '1' : ''));
if ($argument->isArray()) {
$elements[] = sprintf('... [%sN]', $argument->getName());
}
}
return implode(' ', $elements);
}
/**
* Returns a textual representation of the InputDefinition.
*
* @return string A string representing the InputDefinition
*/
public function asText()
{
// find the largest option or argument name
$max = 0;
foreach ($this->getOptions() as $option) {
$max = strlen($option->getName()) + 2 > $max ? strlen($option->getName()) + 2 : $max;
}
foreach ($this->getArguments() as $argument) {
$max = strlen($argument->getName()) > $max ? strlen($argument->getName()) : $max;
}
++$max;
$text = array();
if ($this->getArguments()) {
$text[] = '<comment>Arguments:</comment>';
foreach ($this->getArguments() as $argument) {
if (null !== $argument->getDefault() && (!is_array($argument->getDefault()) || count($argument->getDefault()))) {
$default = sprintf('<comment> (default: %s)</comment>', is_array($argument->getDefault()) ? str_replace("\n", '', var_export($argument->getDefault(), true)): $argument->getDefault());
} else {
$default = '';
}
$text[] = sprintf(" <info>%-${max}s</info> %s%s", $argument->getName(), $argument->getDescription(), $default);
}
$text[] = '';
}
if ($this->getOptions()) {
$text[] = '<comment>Options:</comment>';
foreach ($this->getOptions() as $option) {
if ($option->acceptValue() && null !== $option->getDefault() && (!is_array($option->getDefault()) || count($option->getDefault()))) {
$default = sprintf('<comment> (default: %s)</comment>', is_array($option->getDefault()) ? str_replace("\n", '', print_r($option->getDefault(), true)): $option->getDefault());
} else {
$default = '';
}
$multiple = $option->isArray() ? '<comment> (multiple values allowed)</comment>' : '';
$text[] = sprintf(' %-'.$max.'s %s%s%s%s', '<info>--'.$option->getName().'</info>', $option->getShortcut() ? sprintf('(-%s) ', $option->getShortcut()) : '', $option->getDescription(), $default, $multiple);
}
$text[] = '';
}
return implode("\n", $text);
}
/**
* Returns an XML representation of the InputDefinition.
*
* @param Boolean $asDom Whether to return a DOM or an XML string
*
* @return string|DOMDocument An XML string representing the InputDefinition
*/
public function asXml($asDom = false)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->formatOutput = true;
$dom->appendChild($definitionXML = $dom->createElement('definition'));
$definitionXML->appendChild($argumentsXML = $dom->createElement('arguments'));
foreach ($this->getArguments() as $argument) {
$argumentsXML->appendChild($argumentXML = $dom->createElement('argument'));
$argumentXML->setAttribute('name', $argument->getName());
$argumentXML->setAttribute('is_required', $argument->isRequired() ? 1 : 0);
$argumentXML->setAttribute('is_array', $argument->isArray() ? 1 : 0);
$argumentXML->appendChild($descriptionXML = $dom->createElement('description'));
$descriptionXML->appendChild($dom->createTextNode($argument->getDescription()));
$argumentXML->appendChild($defaultsXML = $dom->createElement('defaults'));
$defaults = is_array($argument->getDefault()) ? $argument->getDefault() : ($argument->getDefault() ? array($argument->getDefault()) : array());
foreach ($defaults as $default) {
$defaultsXML->appendChild($defaultXML = $dom->createElement('default'));
$defaultXML->appendChild($dom->createTextNode($default));
}
}
$definitionXML->appendChild($optionsXML = $dom->createElement('options'));
foreach ($this->getOptions() as $option) {
$optionsXML->appendChild($optionXML = $dom->createElement('option'));
$optionXML->setAttribute('name', '--'.$option->getName());
$optionXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : '');
$optionXML->setAttribute('accept_value', $option->acceptValue() ? 1 : 0);
$optionXML->setAttribute('is_value_required', $option->isValueRequired() ? 1 : 0);
$optionXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0);
$optionXML->appendChild($descriptionXML = $dom->createElement('description'));
$descriptionXML->appendChild($dom->createTextNode($option->getDescription()));
if ($option->acceptValue()) {
$optionXML->appendChild($defaultsXML = $dom->createElement('defaults'));
$defaults = is_array($option->getDefault()) ? $option->getDefault() : ($option->getDefault() ? array($option->getDefault()) : array());
foreach ($defaults as $default) {
$defaultsXML->appendChild($defaultXML = $dom->createElement('default'));
$defaultXML->appendChild($dom->createTextNode($default));
}
}
}
return $asDom ? $dom : $dom->saveXml();
}
}

View File

@ -3,7 +3,7 @@
namespace Symfony\Component\Console\Input;
/*
* This file is part of the symfony framework.
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
@ -14,45 +14,43 @@ namespace Symfony\Component\Console\Input;
/**
* InputInterface is the interface implemented by all input classes.
*
* @package symfony
* @subpackage console
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
interface InputInterface
{
/**
* Returns the first argument from the raw parameters (not parsed).
*
* @return string The value of the first argument or null otherwise
*/
function getFirstArgument();
/**
* Returns the first argument from the raw parameters (not parsed).
*
* @return string The value of the first argument or null otherwise
*/
function getFirstArgument();
/**
* Returns true if the raw parameters (not parsed) contains a value.
*
* This method is to be used to introspect the input parameters
* before it has been validated. It must be used carefully.
*
* @param string $value The value to look for in the raw parameters
*
* @return Boolean true if the value is contained in the raw parameters
*/
function hasParameterOption($value);
/**
* Returns true if the raw parameters (not parsed) contains a value.
*
* This method is to be used to introspect the input parameters
* before it has been validated. It must be used carefully.
*
* @param string $value The value to look for in the raw parameters
*
* @return Boolean true if the value is contained in the raw parameters
*/
function hasParameterOption($value);
/**
* Binds the current Input instance with the given arguments and options.
*
* @param InputDefinition $definition A InputDefinition instance
*/
function bind(InputDefinition $definition);
/**
* Binds the current Input instance with the given arguments and options.
*
* @param InputDefinition $definition A InputDefinition instance
*/
function bind(InputDefinition $definition);
function validate();
function validate();
function getArguments();
function getArguments();
function getArgument($name);
function getArgument($name);
function getOptions();
function getOptions();
function getOption($name);
function getOption($name);
}

View File

@ -3,7 +3,7 @@
namespace Symfony\Component\Console\Input;
/*
* This file is part of the symfony framework.
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
@ -14,178 +14,165 @@ namespace Symfony\Component\Console\Input;
/**
* Represents a command line option.
*
* @package symfony
* @subpackage console
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class InputOption
{
const PARAMETER_NONE = 1;
const PARAMETER_REQUIRED = 2;
const PARAMETER_OPTIONAL = 4;
const PARAMETER_IS_ARRAY = 8;
const VALUE_NONE = 1;
const VALUE_REQUIRED = 2;
const VALUE_OPTIONAL = 4;
const VALUE_IS_ARRAY = 8;
protected $name;
protected $shortcut;
protected $mode;
protected $default;
protected $description;
protected $name;
protected $shortcut;
protected $mode;
protected $default;
protected $description;
/**
* Constructor.
*
* @param string $name The option name
* @param string $shortcut The shortcut (can be null)
* @param integer $mode The option mode: self::PARAMETER_REQUIRED, self::PARAMETER_NONE or self::PARAMETER_OPTIONAL
* @param string $description A description text
* @param mixed $default The default value (must be null for self::PARAMETER_REQUIRED or self::PARAMETER_NONE)
*/
public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null)
{
if ('--' === substr($name, 0, 2))
/**
* Constructor.
*
* @param string $name The option name
* @param string $shortcut The shortcut (can be null)
* @param integer $mode The option mode: One of the VALUE_* constants
* @param string $description A description text
* @param mixed $default The default value (must be null for self::VALUE_REQUIRED or self::VALUE_NONE)
*
* @throws \InvalidArgumentException If option mode is invalid or incompatible
*/
public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null)
{
$name = substr($name, 2);
if ('--' === substr($name, 0, 2)) {
$name = substr($name, 2);
}
if (empty($shortcut)) {
$shortcut = null;
}
if (null !== $shortcut) {
if ('-' === $shortcut[0]) {
$shortcut = substr($shortcut, 1);
}
}
if (null === $mode) {
$mode = self::VALUE_NONE;
} else if (!is_int($mode) || $mode > 15) {
throw new \InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode));
}
$this->name = $name;
$this->shortcut = $shortcut;
$this->mode = $mode;
$this->description = $description;
if ($this->isArray() && !$this->acceptValue()) {
throw new \InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.');
}
$this->setDefault($default);
}
if (empty($shortcut))
/**
* Returns the shortcut.
*
* @return string The shortcut
*/
public function getShortcut()
{
$shortcut = null;
return $this->shortcut;
}
if (null !== $shortcut)
/**
* Returns the name.
*
* @return string The name
*/
public function getName()
{
if ('-' === $shortcut[0])
{
$shortcut = substr($shortcut, 1);
}
return $this->name;
}
if (null === $mode)
/**
* Returns true if the option accepts a value.
*
* @return Boolean true if value mode is not self::VALUE_NONE, false otherwise
*/
public function acceptValue()
{
$mode = self::PARAMETER_NONE;
}
else if (!is_int($mode) || $mode > 15)
{
throw new \InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode));
return $this->isValueRequired() || $this->isValueOptional();
}
$this->name = $name;
$this->shortcut = $shortcut;
$this->mode = $mode;
$this->description = $description;
if ($this->isArray() && !$this->acceptParameter())
/**
* Returns true if the option requires a value.
*
* @return Boolean true if value mode is self::VALUE_REQUIRED, false otherwise
*/
public function isValueRequired()
{
throw new \InvalidArgumentException('Impossible to have an option mode PARAMETER_IS_ARRAY if the option does not accept a parameter.');
return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode);
}
$this->setDefault($default);
}
/**
* Returns the shortcut.
*
* @return string The shortcut
*/
public function getShortcut()
{
return $this->shortcut;
}
/**
* Returns the name.
*
* @return string The name
*/
public function getName()
{
return $this->name;
}
/**
* Returns true if the option accept a parameter.
*
* @return Boolean true if parameter mode is not self::PARAMETER_NONE, false otherwise
*/
public function acceptParameter()
{
return $this->isParameterRequired() || $this->isParameterOptional();
}
/**
* Returns true if the option requires a parameter.
*
* @return Boolean true if parameter mode is self::PARAMETER_REQUIRED, false otherwise
*/
public function isParameterRequired()
{
return self::PARAMETER_REQUIRED === (self::PARAMETER_REQUIRED & $this->mode);
}
/**
* Returns true if the option takes an optional parameter.
*
* @return Boolean true if parameter mode is self::PARAMETER_OPTIONAL, false otherwise
*/
public function isParameterOptional()
{
return self::PARAMETER_OPTIONAL === (self::PARAMETER_OPTIONAL & $this->mode);
}
/**
* Returns true if the option can take multiple values.
*
* @return Boolean true if mode is self::PARAMETER_IS_ARRAY, false otherwise
*/
public function isArray()
{
return self::PARAMETER_IS_ARRAY === (self::PARAMETER_IS_ARRAY & $this->mode);
}
/**
* Sets the default value.
*
* @param mixed $default The default value
*/
public function setDefault($default = null)
{
if (self::PARAMETER_NONE === (self::PARAMETER_NONE & $this->mode) && null !== $default)
/**
* Returns true if the option takes an optional value.
*
* @return Boolean true if value mode is self::VALUE_OPTIONAL, false otherwise
*/
public function isValueOptional()
{
throw new \LogicException('Cannot set a default value when using Option::PARAMETER_NONE mode.');
return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode);
}
if ($this->isArray())
/**
* Returns true if the option can take multiple values.
*
* @return Boolean true if mode is self::VALUE_IS_ARRAY, false otherwise
*/
public function isArray()
{
if (null === $default)
{
$default = array();
}
elseif (!is_array($default))
{
throw new \LogicException('A default value for an array option must be an array.');
}
return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode);
}
$this->default = $this->acceptParameter() ? $default : false;
}
/**
* Sets the default value.
*
* @param mixed $default The default value
*/
public function setDefault($default = null)
{
if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) {
throw new \LogicException('Cannot set a default value when using Option::VALUE_NONE mode.');
}
/**
* Returns the default value.
*
* @return mixed The default value
*/
public function getDefault()
{
return $this->default;
}
if ($this->isArray()) {
if (null === $default) {
$default = array();
} elseif (!is_array($default)) {
throw new \LogicException('A default value for an array option must be an array.');
}
}
/**
* Returns the description text.
*
* @return string The description text
*/
public function getDescription()
{
return $this->description;
}
$this->default = $this->acceptValue() ? $default : false;
}
/**
* Returns the default value.
*
* @return mixed The default value
*/
public function getDefault()
{
return $this->default;
}
/**
* Returns the description text.
*
* @return string The description text
*/
public function getDescription()
{
return $this->description;
}
}

View File

@ -3,7 +3,7 @@
namespace Symfony\Component\Console\Input;
/*
* This file is part of the symfony framework.
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
@ -18,63 +18,54 @@ namespace Symfony\Component\Console\Input;
*
* $input = new StringInput('foo --bar="foobar"');
*
* @package symfony
* @subpackage console
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class StringInput extends ArgvInput
{
const REGEX_STRING = '([^ ]+?)(?: |(?<!\\\\)"|(?<!\\\\)\'|$)';
const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')';
const REGEX_STRING = '([^ ]+?)(?: |(?<!\\\\)"|(?<!\\\\)\'|$)';
const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')';
/**
* Constructor.
*
* @param string $input An array of parameters from the CLI (in the argv format)
* @param InputDefinition $definition A InputDefinition instance
*/
public function __construct($input, InputDefinition $definition = null)
{
parent::__construct(array(), $definition);
$this->tokens = $this->tokenize($input);
}
protected function tokenize($input)
{
$input = preg_replace('/(\r\n|\r|\n|\t)/', ' ', $input);
$tokens = array();
$length = strlen($input);
$cursor = 0;
while ($cursor < $length)
/**
* Constructor.
*
* @param string $input An array of parameters from the CLI (in the argv format)
* @param InputDefinition $definition A InputDefinition instance
*/
public function __construct($input, InputDefinition $definition = null)
{
if (preg_match('/\s+/A', $input, $match, null, $cursor))
{
}
elseif (preg_match('/([^="\' ]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, null, $cursor))
{
$tokens[] = $match[1].$match[2].stripcslashes(str_replace(array('"\'', '\'"', '\'\'', '""'), '', substr($match[3], 1, strlen($match[3]) - 2)));
}
elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, null, $cursor))
{
$tokens[] = stripcslashes(substr($match[0], 1, strlen($match[0]) - 2));
}
elseif (preg_match('/'.self::REGEX_STRING.'/A', $input, $match, null, $cursor))
{
$tokens[] = stripcslashes($match[1]);
}
else
{
// should never happen
// @codeCoverageIgnoreStart
throw new \InvalidArgumentException(sprintf('Unable to parse input near "... %s ..."', substr($input, $cursor, 10)));
// @codeCoverageIgnoreEnd
}
parent::__construct(array(), $definition);
$cursor += strlen($match[0]);
$this->tokens = $this->tokenize($input);
}
return $tokens;
}
/**
* @throws \InvalidArgumentException When unable to parse input (should never happen)
*/
protected function tokenize($input)
{
$input = preg_replace('/(\r\n|\r|\n|\t)/', ' ', $input);
$tokens = array();
$length = strlen($input);
$cursor = 0;
while ($cursor < $length) {
if (preg_match('/\s+/A', $input, $match, null, $cursor)) {
} elseif (preg_match('/([^="\' ]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, null, $cursor)) {
$tokens[] = $match[1].$match[2].stripcslashes(str_replace(array('"\'', '\'"', '\'\'', '""'), '', substr($match[3], 1, strlen($match[3]) - 2)));
} elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, null, $cursor)) {
$tokens[] = stripcslashes(substr($match[0], 1, strlen($match[0]) - 2));
} elseif (preg_match('/'.self::REGEX_STRING.'/A', $input, $match, null, $cursor)) {
$tokens[] = stripcslashes($match[1]);
} else {
// should never happen
// @codeCoverageIgnoreStart
throw new \InvalidArgumentException(sprintf('Unable to parse input near "... %s ..."', substr($input, $cursor, 10)));
// @codeCoverageIgnoreEnd
}
$cursor += strlen($match[0]);
}
return $tokens;
}
}

View File

@ -3,7 +3,7 @@
namespace Symfony\Component\Console\Output;
/*
* This file is part of the symfony framework.
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
@ -22,20 +22,18 @@ namespace Symfony\Component\Console\Output;
*
* $output = new StreamOutput(fopen('php://stdout', 'w'));
*
* @package symfony
* @subpackage console
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class ConsoleOutput extends StreamOutput
{
/**
* Constructor.
*
* @param integer $verbosity The verbosity level (self::VERBOSITY_QUIET, self::VERBOSITY_NORMAL, self::VERBOSITY_VERBOSE)
* @param Boolean $decorated Whether to decorate messages or not (null for auto-guessing)
*/
public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null)
{
parent::__construct(fopen('php://stdout', 'w'), $verbosity, $decorated);
}
/**
* Constructor.
*
* @param integer $verbosity The verbosity level (self::VERBOSITY_QUIET, self::VERBOSITY_NORMAL, self::VERBOSITY_VERBOSE)
* @param Boolean $decorated Whether to decorate messages or not (null for auto-guessing)
*/
public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null)
{
parent::__construct(fopen('php://stdout', 'w'), $verbosity, $decorated);
}
}

View File

@ -3,7 +3,7 @@
namespace Symfony\Component\Console\Output;
/*
* This file is part of the symfony framework.
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
@ -16,19 +16,17 @@ namespace Symfony\Component\Console\Output;
*
* $output = new NullOutput();
*
* @package symfony
* @subpackage console
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class NullOutput extends Output
{
/**
* Writes a message to the output.
*
* @param string $message A message to write to the output
* @param Boolean $newline Whether to add a newline or not
*/
public function doWrite($message, $newline)
{
}
/**
* Writes a message to the output.
*
* @param string $message A message to write to the output
* @param Boolean $newline Whether to add a newline or not
*/
public function doWrite($message, $newline)
{
}
}

View File

@ -3,7 +3,7 @@
namespace Symfony\Component\Console\Output;
/*
* This file is part of the symfony framework.
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
@ -20,211 +20,212 @@ namespace Symfony\Component\Console\Output;
* * verbose: -v (more output - debug)
* * quiet: -q (no output)
*
* @package symfony
* @subpackage console
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
abstract class Output implements OutputInterface
{
const VERBOSITY_QUIET = 0;
const VERBOSITY_NORMAL = 1;
const VERBOSITY_VERBOSE = 2;
const VERBOSITY_QUIET = 0;
const VERBOSITY_NORMAL = 1;
const VERBOSITY_VERBOSE = 2;
const OUTPUT_NORMAL = 0;
const OUTPUT_RAW = 1;
const OUTPUT_PLAIN = 2;
const OUTPUT_NORMAL = 0;
const OUTPUT_RAW = 1;
const OUTPUT_PLAIN = 2;
protected $verbosity;
protected $decorated;
protected $verbosity;
protected $decorated;
static protected $styles = array(
'error' => array('bg' => 'red', 'fg' => 'white'),
'info' => array('fg' => 'green'),
'comment' => array('fg' => 'yellow'),
'question' => array('bg' => 'cyan', 'fg' => 'black'),
);
static protected $options = array('bold' => 1, 'underscore' => 4, 'blink' => 5, 'reverse' => 7, 'conceal' => 8);
static protected $foreground = array('black' => 30, 'red' => 31, 'green' => 32, 'yellow' => 33, 'blue' => 34, 'magenta' => 35, 'cyan' => 36, 'white' => 37);
static protected $background = array('black' => 40, 'red' => 41, 'green' => 42, 'yellow' => 43, 'blue' => 44, 'magenta' => 45, 'cyan' => 46, 'white' => 47);
static protected $styles = array(
'error' => array('bg' => 'red', 'fg' => 'white'),
'info' => array('fg' => 'green'),
'comment' => array('fg' => 'yellow'),
'question' => array('bg' => 'cyan', 'fg' => 'black'),
);
static protected $options = array('bold' => 1, 'underscore' => 4, 'blink' => 5, 'reverse' => 7, 'conceal' => 8);
static protected $foreground = array('black' => 30, 'red' => 31, 'green' => 32, 'yellow' => 33, 'blue' => 34, 'magenta' => 35, 'cyan' => 36, 'white' => 37);
static protected $background = array('black' => 40, 'red' => 41, 'green' => 42, 'yellow' => 43, 'blue' => 44, 'magenta' => 45, 'cyan' => 46, 'white' => 47);
/**
* Constructor.
*
* @param integer $verbosity The verbosity level (self::VERBOSITY_QUIET, self::VERBOSITY_NORMAL, self::VERBOSITY_VERBOSE)
* @param Boolean $decorated Whether to decorate messages or not (null for auto-guessing)
*/
public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null)
{
$this->decorated = (Boolean) $decorated;
$this->verbosity = null === $verbosity ? self::VERBOSITY_NORMAL : $verbosity;
}
/**
* Sets a new style.
*
* @param string $name The style name
* @param array $options An array of options
*/
static public function setStyle($name, $options = array())
{
static::$styles[strtolower($name)] = $options;
}
/**
* Sets the decorated flag.
*
* @param Boolean $decorated Whether to decorated the messages or not
*/
public function setDecorated($decorated)
{
$this->decorated = (Boolean) $decorated;
}
/**
* Gets the decorated flag.
*
* @return Boolean true if the output will decorate messages, false otherwise
*/
public function isDecorated()
{
return $this->decorated;
}
/**
* Sets the verbosity of the output.
*
* @param integer $level The level of verbosity
*/
public function setVerbosity($level)
{
$this->verbosity = (int) $level;
}
/**
* Gets the current verbosity of the output.
*
* @return integer The current level of verbosity
*/
public function getVerbosity()
{
return $this->verbosity;
}
/**
* Writes a message to the output and adds a newline at the end.
*
* @param string|array $messages The message as an array of lines of a single string
* @param integer $type The type of output
*/
public function writeln($messages, $type = 0)
{
$this->write($messages, true, $type);
}
/**
* Writes a message to the output.
*
* @param string|array $messages The message as an array of lines of a single string
* @param Boolean $newline Whether to add a newline or not
* @param integer $type The type of output
*/
public function write($messages, $newline = false, $type = 0)
{
if (self::VERBOSITY_QUIET === $this->verbosity)
/**
* Constructor.
*
* @param integer $verbosity The verbosity level (self::VERBOSITY_QUIET, self::VERBOSITY_NORMAL, self::VERBOSITY_VERBOSE)
* @param Boolean $decorated Whether to decorate messages or not (null for auto-guessing)
*/
public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null)
{
return;
$this->decorated = (Boolean) $decorated;
$this->verbosity = null === $verbosity ? self::VERBOSITY_NORMAL : $verbosity;
}
if (!is_array($messages))
/**
* Sets a new style.
*
* @param string $name The style name
* @param array $options An array of options
*/
static public function setStyle($name, $options = array())
{
$messages = array($messages);
static::$styles[strtolower($name)] = $options;
}
foreach ($messages as $message)
/**
* Sets the decorated flag.
*
* @param Boolean $decorated Whether to decorated the messages or not
*/
public function setDecorated($decorated)
{
switch ($type)
{
case Output::OUTPUT_NORMAL:
$message = $this->format($message);
break;
case Output::OUTPUT_RAW:
break;
case Output::OUTPUT_PLAIN:
$message = strip_tags($this->format($message));
break;
default:
throw new \InvalidArgumentException(sprintf('Unknown output type given (%s)', $type));
}
$this->doWrite($message, $newline);
}
}
/**
* Writes a message to the output.
*
* @param string $message A message to write to the output
* @param Boolean $newline Whether to add a newline or not
*/
abstract public function doWrite($message, $newline);
/**
* Formats a message according to the given styles.
*
* @param string $message The message to style
*
* @return string The styled message
*/
protected function format($message)
{
$message = preg_replace_callback('#<([a-z][a-z0-9\-_]+)>#i', array($this, 'replaceStartStyle'), $message);
return preg_replace_callback('#</([a-z][a-z0-9\-_]+)>#i', array($this, 'replaceEndStyle'), $message);
}
protected function replaceStartStyle($match)
{
if (!$this->decorated)
{
return '';
$this->decorated = (Boolean) $decorated;
}
if (!isset(static::$styles[strtolower($match[1])]))
/**
* Gets the decorated flag.
*
* @return Boolean true if the output will decorate messages, false otherwise
*/
public function isDecorated()
{
throw new \InvalidArgumentException(sprintf('Unknown style "%s".', $match[1]));
return $this->decorated;
}
$parameters = static::$styles[strtolower($match[1])];
$codes = array();
if (isset($parameters['fg']))
/**
* Sets the verbosity of the output.
*
* @param integer $level The level of verbosity
*/
public function setVerbosity($level)
{
$codes[] = static::$foreground[$parameters['fg']];
$this->verbosity = (int) $level;
}
if (isset($parameters['bg']))
/**
* Gets the current verbosity of the output.
*
* @return integer The current level of verbosity
*/
public function getVerbosity()
{
$codes[] = static::$background[$parameters['bg']];
return $this->verbosity;
}
foreach (static::$options as $option => $value)
/**
* Writes a message to the output and adds a newline at the end.
*
* @param string|array $messages The message as an array of lines of a single string
* @param integer $type The type of output
*/
public function writeln($messages, $type = 0)
{
if (isset($parameters[$option]) && $parameters[$option])
{
$codes[] = $value;
}
$this->write($messages, true, $type);
}
return "\033[".implode(';', $codes)."m";
}
protected function replaceEndStyle($match)
{
if (!$this->decorated)
/**
* Writes a message to the output.
*
* @param string|array $messages The message as an array of lines of a single string
* @param Boolean $newline Whether to add a newline or not
* @param integer $type The type of output
*
* @throws \InvalidArgumentException When unknown output type is given
*/
public function write($messages, $newline = false, $type = 0)
{
return '';
if (self::VERBOSITY_QUIET === $this->verbosity) {
return;
}
if (!is_array($messages)) {
$messages = array($messages);
}
foreach ($messages as $message) {
switch ($type) {
case Output::OUTPUT_NORMAL:
$message = $this->format($message);
break;
case Output::OUTPUT_RAW:
break;
case Output::OUTPUT_PLAIN:
$message = strip_tags($this->format($message));
break;
default:
throw new \InvalidArgumentException(sprintf('Unknown output type given (%s)', $type));
}
$this->doWrite($message, $newline);
}
}
return "\033[0m";
}
/**
* Writes a message to the output.
*
* @param string $message A message to write to the output
* @param Boolean $newline Whether to add a newline or not
*/
abstract public function doWrite($message, $newline);
/**
* Formats a message according to the given styles.
*
* @param string $message The message to style
*
* @return string The styled message
*/
protected function format($message)
{
$message = preg_replace_callback('#<([a-z][a-z0-9\-_=;]+)>#i', array($this, 'replaceStartStyle'), $message);
return preg_replace_callback('#</([a-z][a-z0-9\-_]*)?>#i', array($this, 'replaceEndStyle'), $message);
}
/**
* @throws \InvalidArgumentException When style is unknown
*/
protected function replaceStartStyle($match)
{
if (!$this->decorated) {
return '';
}
if (isset(static::$styles[strtolower($match[1])])) {
$parameters = static::$styles[strtolower($match[1])];
} else {
// bg=blue;fg=red
if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', strtolower($match[1]), $matches, PREG_SET_ORDER)) {
throw new \InvalidArgumentException(sprintf('Unknown style "%s".', $match[1]));
}
$parameters = array();
foreach ($matches as $match) {
$parameters[$match[1]] = $match[2];
}
}
$codes = array();
if (isset($parameters['fg'])) {
$codes[] = static::$foreground[$parameters['fg']];
}
if (isset($parameters['bg'])) {
$codes[] = static::$background[$parameters['bg']];
}
foreach (static::$options as $option => $value) {
if (isset($parameters[$option]) && $parameters[$option]) {
$codes[] = $value;
}
}
return "\033[".implode(';', $codes).'m';
}
protected function replaceEndStyle($match)
{
if (!$this->decorated) {
return '';
}
return "\033[0m";
}
}

View File

@ -3,7 +3,7 @@
namespace Symfony\Component\Console\Output;
/*
* This file is part of the symfony framework.
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
@ -14,31 +14,32 @@ namespace Symfony\Component\Console\Output;
/**
* OutputInterface is the interface implemented by all Output classes.
*
* @package symfony
* @subpackage console
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
interface OutputInterface
{
/**
* Writes a message to the output.
*
* @param string|array $messages The message as an array of lines of a single string
* @param integer $type The type of output
*/
public function write($messages, $type = 0);
/**
* Writes a message to the output.
*
* @param string|array $messages The message as an array of lines of a single string
* @param Boolean $newline Whether to add a newline or not
* @param integer $type The type of output
*
* @throws \InvalidArgumentException When unknown output type is given
*/
function write($messages, $newline = false, $type = 0);
/**
* Sets the verbosity of the output.
*
* @param integer $level The level of verbosity
*/
public function setVerbosity($level);
/**
* Sets the verbosity of the output.
*
* @param integer $level The level of verbosity
*/
function setVerbosity($level);
/**
* Sets the decorated flag.
*
* @param Boolean $decorated Whether to decorated the messages or not
*/
public function setDecorated($decorated);
/**
* Sets the decorated flag.
*
* @param Boolean $decorated Whether to decorated the messages or not
*/
function setDecorated($decorated);
}

View File

@ -3,7 +3,7 @@
namespace Symfony\Component\Console\Output;
/*
* This file is part of the symfony framework.
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
@ -22,88 +22,84 @@ namespace Symfony\Component\Console\Output;
*
* $output = new StreamOutput(fopen('/path/to/output.log', 'a', false));
*
* @package symfony
* @subpackage console
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class StreamOutput extends Output
{
protected $stream;
protected $stream;
/**
* Constructor.
*
* @param mixed $stream A stream resource
* @param integer $verbosity The verbosity level (self::VERBOSITY_QUIET, self::VERBOSITY_NORMAL, self::VERBOSITY_VERBOSE)
* @param Boolean $decorated Whether to decorate messages or not (null for auto-guessing)
*/
public function __construct($stream, $verbosity = self::VERBOSITY_NORMAL, $decorated = null)
{
if (!is_resource($stream) || 'stream' !== get_resource_type($stream))
/**
* Constructor.
*
* @param mixed $stream A stream resource
* @param integer $verbosity The verbosity level (self::VERBOSITY_QUIET, self::VERBOSITY_NORMAL, self::VERBOSITY_VERBOSE)
* @param Boolean $decorated Whether to decorate messages or not (null for auto-guessing)
*
* @throws \InvalidArgumentException When first argument is not a real stream
*/
public function __construct($stream, $verbosity = self::VERBOSITY_NORMAL, $decorated = null)
{
throw new \InvalidArgumentException('The StreamOutput class needs a stream as its first argument.');
if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) {
throw new \InvalidArgumentException('The StreamOutput class needs a stream as its first argument.');
}
$this->stream = $stream;
if (null === $decorated) {
$decorated = $this->hasColorSupport($decorated);
}
parent::__construct($verbosity, $decorated);
}
$this->stream = $stream;
if (null === $decorated)
/**
* Gets the stream attached to this StreamOutput instance.
*
* @return resource A stream resource
*/
public function getStream()
{
$decorated = $this->hasColorSupport($decorated);
return $this->stream;
}
parent::__construct($verbosity, $decorated);
}
/**
* Gets the stream attached to this StreamOutput instance.
*
* @return resource A stream resource
*/
public function getStream()
{
return $this->stream;
}
/**
* Writes a message to the output.
*
* @param string $message A message to write to the output
* @param Boolean $newline Whether to add a newline or not
*/
public function doWrite($message, $newline)
{
if (false === @fwrite($this->stream, $message.($newline ? PHP_EOL : '')))
/**
* Writes a message to the output.
*
* @param string $message A message to write to the output
* @param Boolean $newline Whether to add a newline or not
*
* @throws \RuntimeException When unable to write output (should never happen)
*/
public function doWrite($message, $newline)
{
// @codeCoverageIgnoreStart
// should never happen
throw new \RuntimeException('Unable to write output.');
// @codeCoverageIgnoreEnd
if (false === @fwrite($this->stream, $message.($newline ? PHP_EOL : ''))) {
// @codeCoverageIgnoreStart
// should never happen
throw new \RuntimeException('Unable to write output.');
// @codeCoverageIgnoreEnd
}
flush();
}
flush();
}
/**
* Returns true if the stream supports colorization.
*
* Colorization is disabled if not supported by the stream:
*
* - windows without ansicon
* - non tty consoles
*
* @return Boolean true if the stream supports colorization, false otherwise
*/
protected function hasColorSupport()
{
// @codeCoverageIgnoreStart
if (DIRECTORY_SEPARATOR == '\\')
/**
* Returns true if the stream supports colorization.
*
* Colorization is disabled if not supported by the stream:
*
* - windows without ansicon
* - non tty consoles
*
* @return Boolean true if the stream supports colorization, false otherwise
*/
protected function hasColorSupport()
{
return false !== getenv('ANSICON');
// @codeCoverageIgnoreStart
if (DIRECTORY_SEPARATOR == '\\') {
return false !== getenv('ANSICON');
} else {
return function_exists('posix_isatty') && @posix_isatty($this->stream);
}
// @codeCoverageIgnoreEnd
}
else
{
return function_exists('posix_isatty') && @posix_isatty($this->stream);
}
// @codeCoverageIgnoreEnd
}
}

View File

@ -7,7 +7,7 @@ use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\ConsoleOutput;
/*
* This file is part of the symfony framework.
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
@ -21,118 +21,108 @@ use Symfony\Component\Console\Output\ConsoleOutput;
* This class only works with a PHP compiled with readline support
* (either --with-readline or --with-libedit)
*
* @package symfony
* @subpackage cli
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class Shell
{
protected $application;
protected $history;
protected $output;
protected $application;
protected $history;
protected $output;
/**
* Constructor.
*
* If there is no readline support for the current PHP executable
* a \RuntimeException exception is thrown.
*
* @param Application $application An application instance
*/
public function __construct(Application $application)
{
if (!function_exists('readline'))
/**
* Constructor.
*
* If there is no readline support for the current PHP executable
* a \RuntimeException exception is thrown.
*
* @param Application $application An application instance
*
* @throws \RuntimeException When Readline extension is not enabled
*/
public function __construct(Application $application)
{
throw new \RuntimeException('Unable to start the shell as the Readline extension is not enabled.');
if (!function_exists('readline')) {
throw new \RuntimeException('Unable to start the shell as the Readline extension is not enabled.');
}
$this->application = $application;
$this->history = getenv('HOME').'/.history_'.$application->getName();
$this->output = new ConsoleOutput();
}
$this->application = $application;
$this->history = getenv('HOME').'/.history_'.$application->getName();
$this->output = new ConsoleOutput();
}
/**
* Runs the shell.
*/
public function run()
{
$this->application->setAutoExit(false);
$this->application->setCatchExceptions(true);
readline_read_history($this->history);
readline_completion_function(array($this, 'autocompleter'));
$this->output->writeln($this->getHeader());
while (true)
/**
* Runs the shell.
*/
public function run()
{
$command = readline($this->application->getName().' > ');
$this->application->setAutoExit(false);
$this->application->setCatchExceptions(true);
if (false === $command)
{
$this->output->writeln("\n");
readline_read_history($this->history);
readline_completion_function(array($this, 'autocompleter'));
break;
}
$this->output->writeln($this->getHeader());
while (true) {
$command = readline($this->application->getName().' > ');
readline_add_history($command);
readline_write_history($this->history);
if (false === $command) {
$this->output->writeln("\n");
if (0 !== $ret = $this->application->run(new StringInput($command), $this->output))
{
$this->output->writeln(sprintf('<error>The command terminated with an error status (%s)</error>', $ret));
}
}
}
break;
}
/**
* Tries to return autocompletion for the current entered text.
*
* @param string $text The last segment of the entered text
* @param integer $position The current position
*/
protected function autocompleter($text, $position)
{
$info = readline_info();
$text = substr($info['line_buffer'], 0, $info['end']);
readline_add_history($command);
readline_write_history($this->history);
if ($info['point'] !== $info['end'])
{
return true;
if (0 !== $ret = $this->application->run(new StringInput($command), $this->output)) {
$this->output->writeln(sprintf('<error>The command terminated with an error status (%s)</error>', $ret));
}
}
}
// task name?
if (false === strpos($text, ' ') || !$text)
/**
* Tries to return autocompletion for the current entered text.
*
* @param string $text The last segment of the entered text
* @param integer $position The current position
*/
protected function autocompleter($text, $position)
{
return array_keys($this->application->getCommands());
$info = readline_info();
$text = substr($info['line_buffer'], 0, $info['end']);
if ($info['point'] !== $info['end']) {
return true;
}
// task name?
if (false === strpos($text, ' ') || !$text) {
return array_keys($this->application->all());
}
// options and arguments?
try {
$command = $this->application->findCommand(substr($text, 0, strpos($text, ' ')));
} catch (\Exception $e) {
return true;
}
$list = array('--help');
foreach ($command->getDefinition()->getOptions() as $option) {
$list[] = '--'.$option->getName();
}
return $list;
}
// options and arguments?
try
/**
* Returns the shell header.
*
* @return string The header string
*/
protected function getHeader()
{
$command = $this->application->findCommand(substr($text, 0, strpos($text, ' ')));
}
catch (\Exception $e)
{
return true;
}
$list = array('--help');
foreach ($command->getDefinition()->getOptions() as $option)
{
$list[] = '--'.$option->getName();
}
return $list;
}
/**
* Returns the shell header.
*
* @return string The header string
*/
protected function getHeader()
{
return <<<EOF
return <<<EOF
Welcome to the <info>{$this->application->getName()}</info> shell (<comment>{$this->application->getVersion()}</comment>).
@ -142,5 +132,5 @@ or <comment>list</comment> to get a list available commands.
To exit the shell, type <comment>^D</comment>.
EOF;
}
}
}

View File

@ -6,87 +6,96 @@ use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\StreamOutput;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class ApplicationTester
{
protected $application;
protected $display;
protected $input;
protected $output;
protected $application;
protected $display;
protected $input;
protected $output;
/**
* Constructor.
*
* @param Application $application A Application instance to test.
*/
public function __construct(Application $application)
{
$this->application = $application;
}
/**
* Executes the application.
*
* Available options:
*
* * interactive: Sets the input interactive flag
* * decorated: Sets the output decorated flag
* * verbosity: Sets the output verbosity flag
*
* @param array $input An array of arguments and options
* @param array $options An array of options
*/
public function run(array $input, $options = array())
{
$this->input = new ArrayInput($input);
if (isset($options['interactive']))
/**
* Constructor.
*
* @param Application $application A Application instance to test.
*/
public function __construct(Application $application)
{
$this->input->setInteractive($options['interactive']);
$this->application = $application;
}
$this->output = new StreamOutput(fopen('php://memory', 'w', false));
if (isset($options['decorated']))
/**
* Executes the application.
*
* Available options:
*
* * interactive: Sets the input interactive flag
* * decorated: Sets the output decorated flag
* * verbosity: Sets the output verbosity flag
*
* @param array $input An array of arguments and options
* @param array $options An array of options
*/
public function run(array $input, $options = array())
{
$this->output->setDecorated($options['decorated']);
}
if (isset($options['verbosity']))
{
$this->output->setVerbosity($options['verbosity']);
$this->input = new ArrayInput($input);
if (isset($options['interactive'])) {
$this->input->setInteractive($options['interactive']);
}
$this->output = new StreamOutput(fopen('php://memory', 'w', false));
if (isset($options['decorated'])) {
$this->output->setDecorated($options['decorated']);
}
if (isset($options['verbosity'])) {
$this->output->setVerbosity($options['verbosity']);
}
$ret = $this->application->run($this->input, $this->output);
rewind($this->output->getStream());
return $this->display = stream_get_contents($this->output->getStream());
}
$ret = $this->application->run($this->input, $this->output);
/**
* Gets the display returned by the last execution of the application.
*
* @return string The display
*/
public function getDisplay()
{
return $this->display;
}
rewind($this->output->getStream());
/**
* Gets the input instance used by the last execution of the application.
*
* @return InputInterface The current input instance
*/
public function getInput()
{
return $this->input;
}
return $this->display = stream_get_contents($this->output->getStream());
}
/**
* Gets the display returned by the last execution of the application.
*
* @return string The display
*/
public function getDisplay()
{
return $this->display;
}
/**
* Gets the input instance used by the last execution of the application.
*
* @return InputInterface The current input instance
*/
public function getInput()
{
return $this->input;
}
/**
* Gets the output instance used by the last execution of the application.
*
* @return OutputInterface The current output instance
*/
public function getOutput()
{
return $this->output;
}
/**
* Gets the output instance used by the last execution of the application.
*
* @return OutputInterface The current output instance
*/
public function getOutput()
{
return $this->output;
}
}

View File

@ -6,87 +6,96 @@ use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\StreamOutput;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class CommandTester
{
protected $command;
protected $display;
protected $input;
protected $output;
protected $command;
protected $display;
protected $input;
protected $output;
/**
* Constructor.
*
* @param Command $command A Command instance to test.
*/
public function __construct(Command $command)
{
$this->command = $command;
}
/**
* Executes the command.
*
* Available options:
*
* * interactive: Sets the input interactive flag
* * decorated: Sets the output decorated flag
* * verbosity: Sets the output verbosity flag
*
* @param array $input An array of arguments and options
* @param array $options An array of options
*/
public function execute(array $input, array $options = array())
{
$this->input = new ArrayInput(array_merge($input, array('command' => $this->command->getFullName())));
if (isset($options['interactive']))
/**
* Constructor.
*
* @param Command $command A Command instance to test.
*/
public function __construct(Command $command)
{
$this->input->setInteractive($options['interactive']);
$this->command = $command;
}
$this->output = new StreamOutput(fopen('php://memory', 'w', false));
if (isset($options['decorated']))
/**
* Executes the command.
*
* Available options:
*
* * interactive: Sets the input interactive flag
* * decorated: Sets the output decorated flag
* * verbosity: Sets the output verbosity flag
*
* @param array $input An array of arguments and options
* @param array $options An array of options
*/
public function execute(array $input, array $options = array())
{
$this->output->setDecorated($options['decorated']);
}
if (isset($options['verbosity']))
{
$this->output->setVerbosity($options['verbosity']);
$this->input = new ArrayInput($input);
if (isset($options['interactive'])) {
$this->input->setInteractive($options['interactive']);
}
$this->output = new StreamOutput(fopen('php://memory', 'w', false));
if (isset($options['decorated'])) {
$this->output->setDecorated($options['decorated']);
}
if (isset($options['verbosity'])) {
$this->output->setVerbosity($options['verbosity']);
}
$ret = $this->command->run($this->input, $this->output);
rewind($this->output->getStream());
return $this->display = stream_get_contents($this->output->getStream());
}
$ret = $this->command->run($this->input, $this->output);
/**
* Gets the display returned by the last execution of the command.
*
* @return string The display
*/
public function getDisplay()
{
return $this->display;
}
rewind($this->output->getStream());
/**
* Gets the input instance used by the last execution of the command.
*
* @return InputInterface The current input instance
*/
public function getInput()
{
return $this->input;
}
return $this->display = stream_get_contents($this->output->getStream());
}
/**
* Gets the display returned by the last execution of the command.
*
* @return string The display
*/
public function getDisplay()
{
return $this->display;
}
/**
* Gets the input instance used by the last execution of the command.
*
* @return InputInterface The current input instance
*/
public function getInput()
{
return $this->input;
}
/**
* Gets the output instance used by the last execution of the command.
*
* @return OutputInterface The current output instance
*/
public function getOutput()
{
return $this->output;
}
/**
* Gets the output instance used by the last execution of the command.
*
* @return OutputInterface The current output instance
*/
public function getOutput()
{
return $this->output;
}
}

@ -1 +1 @@
Subproject commit 3b5123434e979c7adfd42061484b5a8f10a3431b
Subproject commit 9eb66b7cf90919ff64281f7e476530fd879cefe4

@ -1 +1 @@
Subproject commit ce1e5f0f9c4f2e8894e1d2036ab11cc0597dd0a4
Subproject commit e97fbbf755ea7bc5d575d71778501a73f72a4ecb

View File

@ -8,7 +8,7 @@ namespace Doctrine\Tests\Models\Company;
class CompanyFixContract extends CompanyContract
{
/**
* @column(type="integer", nullable=true)
* @column(type="integer")
* @var int
*/
private $fixPrice = 0;

View File

@ -7,13 +7,13 @@ namespace Doctrine\Tests\Models\Company;
class CompanyFlexContract extends CompanyContract
{
/**
* @column(type="integer", nullable=true)
* @column(type="integer")
* @var int
*/
private $hoursWorked = 0;
/**
* @column(type="integer", nullable=true)
* @column(type="integer")
* @var int
*/
private $pricePerHour = 0;

View File

@ -8,7 +8,7 @@ namespace Doctrine\Tests\Models\Company;
class CompanyFlexUltraContract extends CompanyFlexContract
{
/**
* @column(type="integer", nullable=true)
* @column(type="integer")
* @var int
*/
private $maxPrice = 0;

View File

@ -0,0 +1,64 @@
<?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 LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Tests\Models\DirectoryTree;
/**
* @MappedSuperclass
*/
abstract class AbstractContentItem
{
/**
* @Id @Column(type="integer") @GeneratedValue
*/
private $id;
/**
* @ManyToOne(targetEntity="Directory")
*/
protected $parentDirectory;
/** @column(type="string") */
protected $name;
public function __construct(Directory $parentDir = null)
{
$this->parentDirectory = $parentDir;
}
public function getId()
{
return $this->id;
}
public function setName($name)
{
$this->name = $name;
}
public function getName()
{
return $this->name;
}
public function getParent()
{
return $this->parentDirectory;
}
}

View File

@ -0,0 +1,41 @@
<?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 LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Tests\Models\DirectoryTree;
/**
* @Entity
*/
class Directory extends AbstractContentItem
{
/**
* @Column(type="string")
*/
protected $path;
public function setPath($path)
{
$this->path = $path;
}
public function getPath()
{
return $this->path;
}
}

View File

@ -0,0 +1,46 @@
<?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 LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Tests\Models\DirectoryTree;
/**
* @Entity
* @Table(name="`file`")
*/
class File extends AbstractContentItem
{
/** @Column(type="string") */
protected $extension = "html";
public function __construct(Directory $parent = null)
{
parent::__construct($parent);
}
public function getExtension()
{
return $this->extension;
}
public function setExtension($ext)
{
$this->extension = $ext;
}
}

View File

@ -73,8 +73,8 @@ class ECommerceCategory
public function removeProduct(ECommerceProduct $product)
{
$removed = $this->products->removeElement($product);
if ($removed !== null) {
$removed->removeCategory($this);
if ($removed) {
$product->removeCategory($this);
}
}
@ -114,8 +114,8 @@ class ECommerceCategory
public function removeChild(ECommerceCategory $child)
{
$removed = $this->children->removeElement($child);
if ($removed !== null) {
$removed->removeParent();
if ($removed) {
$child->removeParent();
}
}

View File

@ -112,11 +112,10 @@ class ECommerceProduct
public function removeFeature(ECommerceFeature $feature)
{
$removed = $this->features->removeElement($feature);
if ($removed !== null) {
$removed->removeProduct();
return true;
if ($removed) {
$feature->removeProduct();
}
return false;
return $removed;
}
public function addCategory(ECommerceCategory $category)
@ -130,8 +129,8 @@ class ECommerceProduct
public function removeCategory(ECommerceCategory $category)
{
$removed = $this->categories->removeElement($category);
if ($removed !== null) {
$removed->removeProduct($this);
if ($removed) {
$category->removeProduct($this);
}
}

View File

@ -14,6 +14,16 @@ class EntityManagerTest extends \Doctrine\Tests\OrmTestCase
$this->_em = $this->_getTestEntityManager();
}
/**
* @group DDC-899
*/
public function testIsOpen()
{
$this->assertTrue($this->_em->isOpen());
$this->_em->close();
$this->assertFalse($this->_em->isOpen());
}
public function testGetConnection()
{
$this->assertType('\Doctrine\DBAL\Connection', $this->_em->getConnection());

View File

@ -3,6 +3,7 @@
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\ORM\Tools\SchemaTool;
use Doctrine\ORM\Query;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsPhonenumber;
use Doctrine\Tests\Models\CMS\CmsAddress;
@ -263,7 +264,111 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->refresh($user);
$this->assertEquals('developer', $user->status);
}
/**
* @group DDC-833
*/
public function testRefreshResetsCollection()
{
$user = new CmsUser;
$user->name = 'Guilherme';
$user->username = 'gblanco';
$user->status = 'developer';
// Add a phonenumber
$ph1 = new CmsPhonenumber;
$ph1->phonenumber = "12345";
$user->addPhonenumber($ph1);
// Add a phonenumber
$ph2 = new CmsPhonenumber;
$ph2->phonenumber = "54321";
$this->_em->persist($user);
$this->_em->persist($ph1);
$this->_em->persist($ph2);
$this->_em->flush();
$user->addPhonenumber($ph2);
$this->assertEquals(2, count($user->phonenumbers));
$this->_em->refresh($user);
$this->assertEquals(1, count($user->phonenumbers));
}
/**
* @group DDC-833
*/
public function testDqlRefreshResetsCollection()
{
$user = new CmsUser;
$user->name = 'Guilherme';
$user->username = 'gblanco';
$user->status = 'developer';
// Add a phonenumber
$ph1 = new CmsPhonenumber;
$ph1->phonenumber = "12345";
$user->addPhonenumber($ph1);
// Add a phonenumber
$ph2 = new CmsPhonenumber;
$ph2->phonenumber = "54321";
$this->_em->persist($user);
$this->_em->persist($ph1);
$this->_em->persist($ph2);
$this->_em->flush();
$user->addPhonenumber($ph2);
$this->assertEquals(2, count($user->phonenumbers));
$dql = "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1";
$user = $this->_em->createQuery($dql)
->setParameter(1, $user->id)
->setHint(Query::HINT_REFRESH, true)
->getSingleResult();
$this->assertEquals(1, count($user->phonenumbers));
}
/**
* @group DDC-833
*/
public function testCreateEntityOfProxy()
{
$user = new CmsUser;
$user->name = 'Guilherme';
$user->username = 'gblanco';
$user->status = 'developer';
// Add a phonenumber
$ph1 = new CmsPhonenumber;
$ph1->phonenumber = "12345";
$user->addPhonenumber($ph1);
// Add a phonenumber
$ph2 = new CmsPhonenumber;
$ph2->phonenumber = "54321";
$this->_em->persist($user);
$this->_em->persist($ph1);
$this->_em->persist($ph2);
$this->_em->flush();
$this->_em->clear();
$userId = $user->id;
$user = $this->_em->getReference('Doctrine\Tests\Models\CMS\CmsUser', $user->id);
$dql = "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1";
$user = $this->_em->createQuery($dql)
->setParameter(1, $userId)
->getSingleResult();
$this->assertEquals(1, count($user->phonenumbers));
}
public function testAddToCollectionDoesNotInitialize()
{
$user = new CmsUser;

View File

@ -353,4 +353,61 @@ class ClassTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertEquals($manager->getId(), $dqlManager->getId());
$this->assertEquals($person->getId(), $dqlManager->getSpouse()->getId());
}
/**
* @group DDC-817
*/
public function testFindByAssociation()
{
$manager = new CompanyManager();
$manager->setName('gblanco');
$manager->setSalary(1234);
$manager->setTitle('Awesome!');
$manager->setDepartment('IT');
$person = new CompanyPerson();
$person->setName('spouse');
$manager->setSpouse($person);
$this->_em->persist($manager);
$this->_em->persist($person);
$this->_em->flush();
$this->_em->clear();
$repos = $this->_em->getRepository('Doctrine\Tests\Models\Company\CompanyManager');
$pmanager = $repos->findOneBy(array('spouse' => $person->getId()));
$this->assertEquals($manager->getId(), $pmanager->getId());
$repos = $this->_em->getRepository('Doctrine\Tests\Models\Company\CompanyPerson');
$pmanager = $repos->findOneBy(array('spouse' => $person->getId()));
$this->assertEquals($manager->getId(), $pmanager->getId());
}
/**
* @group DDC-834
*/
public function testGetReferenceEntityWithSubclasses()
{
$manager = new CompanyManager();
$manager->setName('gblanco');
$manager->setSalary(1234);
$manager->setTitle('Awesome!');
$manager->setDepartment('IT');
$this->_em->persist($manager);
$this->_em->flush();
$this->_em->clear();
$ref = $this->_em->getReference('Doctrine\Tests\Models\Company\CompanyPerson', $manager->getId());
$this->assertNotType('Doctrine\ORM\Proxy\Proxy', $ref, "Cannot Request a proxy from a class that has subclasses.");
$this->assertType('Doctrine\Tests\Models\Company\CompanyPerson', $ref);
$this->assertType('Doctrine\Tests\Models\Company\CompanyEmployee', $ref, "Direct fetch of the reference has to load the child class Emplyoee directly.");
$this->_em->clear();
$ref = $this->_em->getReference('Doctrine\Tests\Models\Company\CompanyManager', $manager->getId());
$this->assertType('Doctrine\ORM\Proxy\Proxy', $ref, "A proxy can be generated only if no subclasses exists for the requested reference.");
}
}

View File

@ -54,7 +54,7 @@ class DetachedEntityTest extends \Doctrine\Tests\OrmFunctionalTestCase
$user->status = 'developer';
$ph1 = new CmsPhonenumber;
$ph1->phonenumber = 1234;
$ph1->phonenumber = "1234";
$user->addPhonenumber($ph1);
$this->_em->persist($user);
@ -68,24 +68,36 @@ class DetachedEntityTest extends \Doctrine\Tests\OrmFunctionalTestCase
unset($user);
$user = unserialize($serialized);
$this->assertEquals(1, count($user->getPhonenumbers()), "Pre-Condition: 1 Phonenumber");
$ph2 = new CmsPhonenumber;
$ph2->phonenumber = 56789;
$ph2->phonenumber = "56789";
$user->addPhonenumber($ph2);
$this->assertEquals(2, count($user->getPhonenumbers()));
$oldPhonenumbers = $user->getPhonenumbers();
$this->assertEquals(2, count($oldPhonenumbers), "Pre-Condition: 2 Phonenumbers");
$this->assertFalse($this->_em->contains($user));
$this->_em->persist($ph2);
// Merge back in
$user = $this->_em->merge($user); // merge cascaded to phonenumbers
$this->assertType('Doctrine\Tests\Models\CMS\CmsUser', $user->phonenumbers[0]->user);
$this->assertType('Doctrine\Tests\Models\CMS\CmsUser', $user->phonenumbers[1]->user);
$im = $this->_em->getUnitOfWork()->getIdentityMap();
$this->_em->flush();
$this->assertTrue($this->_em->contains($user));
$this->assertEquals(2, count($user->getPhonenumbers()));
$this->assertTrue($this->_em->contains($user), "Failed to assert that merged user is contained inside EntityManager persistence context.");
$phonenumbers = $user->getPhonenumbers();
$this->assertTrue($this->_em->contains($phonenumbers[0]));
$this->assertTrue($this->_em->contains($phonenumbers[1]));
$this->assertNotSame($oldPhonenumbers, $phonenumbers, "Merge should replace the Detached Collection with a new PersistentCollection.");
$this->assertEquals(2, count($phonenumbers), "Failed to assert that two phonenumbers are contained in the merged users phonenumber collection.");
$this->assertType('Doctrine\Tests\Models\CMS\CmsPhonenumber', $phonenumbers[1]);
$this->assertTrue($this->_em->contains($phonenumbers[1]), "Failed to assert that second phonenumber in collection is contained inside EntityManager persistence context.");
$this->assertType('Doctrine\Tests\Models\CMS\CmsPhonenumber', $phonenumbers[0]);
$this->assertTrue($this->_em->getUnitOfWork()->isInIdentityMap($phonenumbers[0]));
$this->assertTrue($this->_em->contains($phonenumbers[0]), "Failed to assert that first phonenumber in collection is contained inside EntityManager persistence context.");
}
/**
@ -105,8 +117,8 @@ class DetachedEntityTest extends \Doctrine\Tests\OrmFunctionalTestCase
} catch (\Exception $expected) {}
}
public function testUninitializedLazyAssociationsAreIgnoredOnMerge() {
//$this->_em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger);
public function testUninitializedLazyAssociationsAreIgnoredOnMerge()
{
$user = new CmsUser;
$user->name = 'Guilherme';
$user->username = 'gblanco';
@ -136,5 +148,49 @@ class DetachedEntityTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertFalse($managedAddress2->user === $detachedAddress2->user);
$this->assertFalse($managedAddress2->user->__isInitialized__);
}
/**
* @group DDC-822
*/
public function testUseDetachedEntityAsQueryParameter()
{
$user = new CmsUser;
$user->name = 'Guilherme';
$user->username = 'gblanco';
$user->status = 'developer';
$this->_em->persist($user);
$this->_em->flush();
$this->_em->detach($user);
$dql = "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1";
$query = $this->_em->createQuery($dql);
$query->setParameter(1, $user);
$newUser = $query->getSingleResult();
$this->assertType('Doctrine\Tests\Models\CMS\CmsUser', $newUser);
$this->assertEquals('gblanco', $newUser->username);
}
/**
* @group DDC-920
*/
public function testDetachManagedUnpersistedEntity()
{
$user = new CmsUser;
$user->name = 'Guilherme';
$user->username = 'gblanco';
$user->status = 'developer';
$this->_em->persist($user);
$this->_em->detach($user);
$this->_em->flush();
$this->assertFalse($this->_em->contains($user));
$this->assertFalse($this->_em->getUnitOfWork()->isInIdentityMap($user));
}
}

View File

@ -4,6 +4,7 @@ namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsPhonenumber;
use Doctrine\Tests\Models\CMS\CmsAddress;
require_once __DIR__ . '/../../TestInit.php';
@ -19,7 +20,8 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
parent::setUp();
}
public function testBasicFinders() {
public function loadFixture()
{
$user = new CmsUser;
$user->name = 'Roman';
$user->username = 'romanb';
@ -38,35 +40,58 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
unset($user2);
$this->_em->clear();
return $user1Id;
}
public function testBasicFind()
{
$user1Id = $this->loadFixture();
$repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser');
$user = $repos->find($user1Id);
$this->assertTrue($user instanceof CmsUser);
$this->assertEquals('Roman', $user->name);
$this->assertEquals('freak', $user->status);
}
$this->_em->clear();
public function testFindByField()
{
$user1Id = $this->loadFixture();
$repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser');
$users = $repos->findBy(array('status' => 'dev'));
$this->assertEquals(1, count($users));
$this->assertTrue($users[0] instanceof CmsUser);
$this->assertEquals('Guilherme', $users[0]->name);
$this->assertEquals('dev', $users[0]->status);
}
$this->_em->clear();
public function testFindFieldByMagicCall()
{
$user1Id = $this->loadFixture();
$repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser');
$users = $repos->findByStatus('dev');
$this->assertEquals(1, count($users));
$this->assertTrue($users[0] instanceof CmsUser);
$this->assertEquals('Guilherme', $users[0]->name);
$this->assertEquals('dev', $users[0]->status);
$this->_em->clear();
}
public function testFindAll()
{
$user1Id = $this->loadFixture();
$repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser');
$users = $repos->findAll();
$this->assertEquals(2, count($users));
}
$this->_em->clear();
public function testFindByAlias()
{
$user1Id = $this->loadFixture();
$repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser');
$this->_em->getConfiguration()->addEntityNamespace('CMS', 'Doctrine\Tests\Models\CMS');
@ -74,8 +99,12 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$users = $repos->findAll();
$this->assertEquals(2, count($users));
}
public function tearDown()
{
$this->_em->getConfiguration()->setEntityNamespaces(array());
parent::tearDown();
}
/**
@ -150,5 +179,116 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->setExpectedException('Doctrine\ORM\OptimisticLockException');
$this->_em->find('Doctrine\Tests\Models\Cms\CmsUser', $userId, \Doctrine\DBAL\LockMode::OPTIMISTIC);
}
/**
* @group DDC-819
*/
public function testFindMagicCallByNullValue()
{
$this->loadFixture();
$repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser');
$this->setExpectedException('Doctrine\ORM\ORMException');
$users = $repos->findByStatus(null);
}
/**
* @group DDC-819
*/
public function testInvalidMagicCall()
{
$this->setExpectedException('BadMethodCallException');
$repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser');
$repos->foo();
}
public function loadAssociatedFixture()
{
$address = new CmsAddress();
$address->city = "Berlin";
$address->country = "Germany";
$address->street = "Foostreet";
$address->zip = "12345";
$user = new CmsUser();
$user->name = 'Roman';
$user->username = 'romanb';
$user->status = 'freak';
$user->setAddress($address);
$this->_em->persist($user);
$this->_em->persist($address);
$this->_em->flush();
$this->_em->clear();
return array($user->id, $address->id);
}
/**
* @group DDC-817
*/
public function testFindByAssociationKey_ExceptionOnInverseSide()
{
list($userId, $addressId) = $this->loadAssociatedFixture();
$repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser');
$this->setExpectedException('Doctrine\ORM\ORMException', "You cannot search for the association field 'Doctrine\Tests\Models\CMS\CmsUser#address', because it is the inverse side of an association. Find methods only work on owning side associations.");
$user = $repos->findBy(array('address' => $addressId));
}
/**
* @group DDC-817
*/
public function testFindOneByAssociationKey()
{
list($userId, $addressId) = $this->loadAssociatedFixture();
$repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsAddress');
$address = $repos->findOneBy(array('user' => $userId));
$this->assertType('Doctrine\Tests\Models\CMS\CmsAddress', $address);
$this->assertEquals($addressId, $address->id);
}
/**
* @group DDC-817
*/
public function testFindByAssociationKey()
{
list($userId, $addressId) = $this->loadAssociatedFixture();
$repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsAddress');
$addresses = $repos->findBy(array('user' => $userId));
$this->assertContainsOnly('Doctrine\Tests\Models\CMS\CmsAddress', $addresses);
$this->assertEquals(1, count($addresses));
$this->assertEquals($addressId, $addresses[0]->id);
}
/**
* @group DDC-817
*/
public function testFindAssociationByMagicCall()
{
list($userId, $addressId) = $this->loadAssociatedFixture();
$repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsAddress');
$addresses = $repos->findByUser($userId);
$this->assertContainsOnly('Doctrine\Tests\Models\CMS\CmsAddress', $addresses);
$this->assertEquals(1, count($addresses));
$this->assertEquals($addressId, $addresses[0]->id);
}
/**
* @group DDC-817
*/
public function testFindOneAssociationByMagicCall()
{
list($userId, $addressId) = $this->loadAssociatedFixture();
$repos = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsAddress');
$address = $repos->findOneByUser($userId);
$this->assertType('Doctrine\Tests\Models\CMS\CmsAddress', $address);
$this->assertEquals($addressId, $address->id);
}
}

View File

@ -100,6 +100,20 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase
}
}
public function testMultipleFlushesDoIncrementalUpdates()
{
$test = new OptimisticStandard();
for ($i = 0; $i < 5; $i++) {
$test->name = 'test' . $i;
$this->_em->persist($test);
$this->_em->flush();
$this->assertType('int', $test->getVersion());
$this->assertEquals($i + 1, $test->getVersion());
}
}
public function testStandardInsertSetsInitialVersionValue()
{
$test = new OptimisticStandard();
@ -107,6 +121,7 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->persist($test);
$this->_em->flush();
$this->assertType('int', $test->getVersion());
$this->assertEquals(1, $test->getVersion());
return $test;
@ -139,10 +154,13 @@ class OptimisticTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
$test = new OptimisticTimestamp();
$test->name = 'Testing';
$this->assertNull($test->version, "Pre-Condition");
$this->_em->persist($test);
$this->_em->flush();
$this->assertTrue(strtotime($test->version) > 0);
$this->assertType('DateTime', $test->version);
return $test;
}

View File

@ -247,6 +247,34 @@ class ManyToManyBasicAssociationTest extends \Doctrine\Tests\OrmFunctionalTestCa
$this->assertEquals(0, count($newUser->getGroups()));
}
/**
* @group DDC-839
*/
public function testWorkWithDqlHydratedEmptyCollection()
{
$user = $this->addCmsUserGblancoWithGroups(0);
$group = new CmsGroup();
$group->name = "Developers0";
$this->_em->persist($group);
$this->_em->flush();
$this->_em->clear();
$newUser = $this->_em->createQuery('SELECT u, g FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.groups g WHERE u.id = ?1')
->setParameter(1, $user->getId())
->getSingleResult();
$this->assertEquals(0, count($newUser->groups));
$this->assertType('array', $newUser->groups->getMapping());
$newUser->addGroup($group);
$this->_em->flush();
$this->_em->clear();
$newUser = $this->_em->find(get_class($user), $user->getId());
$this->assertEquals(1, count($newUser->groups));
}
/**
* @param int $groupCount
* @return CmsUser

View File

@ -12,100 +12,35 @@ require_once __DIR__ . '/../../TestInit.php';
class MappedSuperclassTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp() {
$this->useModelSet('directorytree');
parent::setUp();
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\EntitySubClass'),
));
} catch (\Exception $e) {
// Swallow all exceptions. We do not test the schema tool here.
}
}
public function testCRUD()
{
$e = new EntitySubClass;
$e->setId(1);
$e->setName('Roman');
$e->setMapped1(42);
$e->setMapped2('bar');
$this->_em->persist($e);
$root = new \Doctrine\Tests\Models\DirectoryTree\Directory();
$root->setName('Root');
$root->setPath('/root');
$directory = new \Doctrine\Tests\Models\DirectoryTree\Directory($root);
$directory->setName('TestA');
$directory->setPath('/root/dir');
$file = new \Doctrine\Tests\Models\DirectoryTree\File($directory);
$file->setName('test-b.html');
$this->_em->persist($root);
$this->_em->persist($directory);
$this->_em->persist($file);
$this->_em->flush();
$this->_em->clear();
$e2 = $this->_em->find('Doctrine\Tests\ORM\Functional\EntitySubClass', 1);
$this->assertEquals(1, $e2->getId());
$this->assertEquals('Roman', $e2->getName());
$this->assertNull($e2->getMappedRelated1());
$this->assertEquals(42, $e2->getMapped1());
$this->assertEquals('bar', $e2->getMapped2());
$cleanFile = $this->_em->find(get_class($file), $file->getId());
$this->assertType('Doctrine\Tests\Models\DirectoryTree\Directory', $cleanFile->getParent());
$this->assertEquals($directory->getId(), $cleanFile->getParent()->getId());
$this->assertType('Doctrine\Tests\Models\DirectoryTree\Directory', $cleanFile->getParent()->getParent());
$this->assertEquals($root->getId(), $cleanFile->getParent()->getParent()->getId());
}
}
/** @MappedSuperclass */
class MappedSuperclassBase {
/** @Column(type="integer") */
private $mapped1;
/** @Column(type="string") */
private $mapped2;
/**
* @OneToOne(targetEntity="MappedSuperclassRelated1")
* @JoinColumn(name="related1_id", referencedColumnName="id")
*/
private $mappedRelated1;
private $transient;
public function setMapped1($val) {
$this->mapped1 = $val;
}
public function getMapped1() {
return $this->mapped1;
}
public function setMapped2($val) {
$this->mapped2 = $val;
}
public function getMapped2() {
return $this->mapped2;
}
public function getMappedRelated1() {
return $this->mappedRelated1;
}
}
/** @Entity */
class MappedSuperclassRelated1 {
/** @Id @Column(type="integer") */
private $id;
/** @Column(type="string") */
private $name;
}
/** @Entity */
class EntitySubClass extends MappedSuperclassBase {
/** @Id @Column(type="integer") */
private $id;
/** @Column(type="string") */
private $name;
public function setName($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
public function setId($id) {
$this->id = $id;
}
public function getId() {
return $this->id;
}
}

Some files were not shown because too many files have changed in this diff Show More