I am using ZF2 + Doctrine2 + PHPUNIT, when setting up Phpunit, it works fine for a basic test, however as soon as I try to run a test that invokes Doctrine2, I get:
PDOException: SQLSTATE[HY000] [1045] Access denied for user 'username'#'localhost' (using password: YES)
But, I have never specified "username" nor "localhost" nor any sort of password. In-fact, my application runs perfectly fine, and the configuration I have specifies completely different settings.
So where is PHPUnit getting those settings and how to fix it?
My global.php:
'doctrine' => array(
'connection' => array(
'orm_default' => array(
'driverClass' => 'Doctrine\DBAL\Driver\PDOMySql\Driver',
'params' => array(
'host' => '127.0.0.1',
'port' => '3306',
'user' => 'sbapp',
'password' => 'myp#ss',
'dbname' => 'root'
)
)
),
The Test:
class ApplicationControllerTest extends AbstractHttpControllerTestCase
{
protected $traceError = true;
public function setUp()
{
$this->setApplicationConfig(
include '../../../config/application.config.php'
);
parent::setUp();
}
public function testAuthActionCanBeAccessed()
{
$postData = new \stdClass();
$postData->username = "someAppUser";
$postData->password = "12345";
$postData = json_decode(json_encode($postData),true);
$this->dispatch('/auth', 'POST', $postData);
$response = $this->getResponse();
$this->assertResponseStatusCode(200);
$this->assertActionName('auth');
}
}
The relative paths used within the config on setUp when done incorrectly can seriously affect the loading of entities and so on. So I was chasing around the right path.. when calling phpunit from root..or from vendor..or from within the test folder, etc..
Solution:
Call it from project root, and leave the routes exactly as the main project are.
Related
I'm using Phinx to execute migrations across 100s of applications on multiple servers. Every application should execute same migrations.
In order to do this there is a instance of app on central server which is aware of all configs and other information needed to do bootstrap process (which is being done based on applicationId).
That central instance (let's call it adminapp) executes command and receives applicationIds through STDIN and then does a loop which bootstraps application and runs migration command.
<?php
namespace Command\Db;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use App\Command\AppCommand;
class MigrateBulkCommand extends AppCommand
{
protected function configure()
{
$this
->setName('command:blah')
->setDescription('Executes SQL migrations accross multiple applications. Expects ApplicationIDs to be passed as new line delimited string on STDIN.')
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$stdin = $this->getStdin();
if ($stdin === false) {
throw new \RuntimeException("Bulk migration command requires applicationIds to be passed to STDIN.");
}
$applicationIds = explode("\n", $stdin);
foreach($applicationIds as $applicationId) {
try {
$this->bootstrap($applicationId);
} catch (\Exception $e) {
$output->writeln(sprintf("<error>Bootstrap process failed for applicationId `%s`</error>", $applicationId));
}
$command = new \Phinx\Console\Command\Migrate();
$migrationInput = new \Symfony\Component\Console\Input\ArrayInput([
]);
$returnCode = $command->run($migrationInput, $output);
$output->writeln(sprinf("<info>Migrations for applicationId `%s` executed successfully.</info>", $applicationId));
}
}
}
Now Phinx expects it's configuration to be present in form of a config file. What I'm trying to do is reuse DB connection resource (PDO) and pass it to Phinx command Phinx\Console\Command\Migrate on the fly, together with db name.
I've seen in Phinx documentation that this is an option with PHP config file but I can't find a way to do this on the fly (during Phinx\Console\Command\Migrate class initialization).
Phinx doc suggests:
require 'app/init.php';
global $app;
$pdo = $app->getDatabase()->getPdo();
return array('environments' =>
array(
'default_database' => 'development',
'development' => array(
'name' => 'devdb',
'connection' => $pdo
)
)
);
Is there a way, without horrible hacking to pass PDO connection resource and db name to \Phinx\Console\Command\Migrate
I ended up extending Phinx Config class \Phinx\Config\Config and creating method fromArray.
$command = new \Phinx\Console\Command\Migrate();
$command->setConfig(\MyNamespace\Config::fromArray(
[
'paths' => [
'migrations' => APPLICATION_PATH . "/../db/migrations",
'seeds' => APPLICATION_PATH . "/../db/seeds"
],
'environments' => [
'default_database' => 'production',
'production' => [
'name' => $db->get('dbname'),
'adapter' => 'mysql',
'host' => $db->get('host'),
'port' => $db->get('port'),
'user' => $db->get('username'),
'pass' => $db->get('password'),
]
]
]
));
so I have a project in CakePHP. When pushing my code, Travis CI should run my test. My Test is named ToolTest and its Fixture is ToolFixture.
My .travis.yml looks as following:
language: php
php: 5.3
services:
- mysql
before_script:
- sh -c "mysql -e 'CREATE DATABASE test;'"
- chmod -R 777 project/tmp
- echo "<?php
class DATABASE_CONFIG {
public \$test = array(
'datasource' => 'Database/Mysql',
'persistent' => false,
'database' => 'test',
'host' => 'localhost',
'login' => 'travis'
);
}" > project/database.php
script:
sudo project/Console/cake test app Model/Tool --stderr
The error strack trace on travis says:
Fatal error: Uncaught exception 'MissingConnectionException' with message 'Database connection "Mysql" is missing, or could not be created.' in /home/travis/build/project/lib/Cake/Model/Datasource/Database/Mysql.php:194
Error: Database connection "Mysql" is missing, or could not be created.
I already tried '127.0.0.1' instead of localhost, same error messages. When running my test on the VM, the test passes.
What I've noticed:
If I'm not running the script command, travis is successful, so creating the db test and writing the database.php should work fine, right?
My test and fixture are pretty minimalistic.
ToolTest:
public function setUp()
{
parent::setUp();
$this->Tool = ClassRegistry::init('Tool');
}
public function testFindListById()
{
$result = $this->Tool->findListById(2);
$expected = array(
2 => 'Java'
);
$this->assertEquals($expected, $result);
}
ToolFixture:
class ToolFixture extends CakeTestFixture
{
public $useDbConfig = 'test';
public $fields = array(
'id' => 'string',
'name' => 'string'
);
public $records = array(
array(
'id' => 1,
'name' => 'HTML'
),
array(
'id' => 2,
'name' => 'Java'
)
);
}
What am I missing? I've been stuck with this problem for days..Any ideas? Glad for any help!
So I found out that I didn't write my database config in the correct folder..
Instead of
>project/database.php
It should have been
> project/Config/database.php
Jeez..
I'm setting timezone to php and mysql to internacionalize my CakePHP application.
When the server receives a request from a client, before process request, it connects to a GeoIp location server and gets the Timezone. Then I use date_default_timezone_set() to set php timezone. The problem comes up when I want to set database timezone. Once Cakephp connected, I need to execute sql query like SET time_zone='-06:00'.
In /lib/Cake/Model/Datasource/Database/Mysql.php I can see at connect() function the following code:
try {
$this->_connection = new PDO(
$dsn,
$config['login'],
$config['password'],
$flags
);
$this->connected = true;
if (!empty($config['settings'])) {
foreach ($config['settings'] as $key => $value) {
$this->_execute("SET $key=$value");
}
}
} catch (PDOException $e) {
throw new MissingConnectionException(array(
'class' => get_class($this),
'message' => $e->getMessage()
));
}
There is a $config['settings'] array that can be configured to do it. But I don't know how to fill settings array and where it's the best place to do that.
What I need is modify default datasource config on-the-fly
You can add an additional key to config array located at app/Config/database.php like this:
public $default = array(
'datasource' => 'Database/Mysql',
'persistent' => false,
'host' => 'localhost',
'login' => 'db_user',
'password' => 'db_pass',
'database' => 'db_name',
'prefix' => '',
'settings' => array(
'time_zone' => "'+01:00'", // note the quotes!
)
);
Related: CakePHP switch database (using same datasource) on the fly?
I solved it in the following way.
First of all, adding setOptions() method into DATABASE_CONFIG class as follows:
public function setOptions ($datasource, array $options){
$this->{$datasource} = array_merge($this->{$datasource}, $options);
}
Afterwards, extending ConnectionManager class to initialize it:
class ConnectionManagerCustomConfig extends ConnectionManager
{
public static function initialize(){
self::_init();
}
}
Now I initialize the class and add new options:
ConnectionManagerCustomConfig::initialize();
$configClass = ConnectionManagerCustomConfig::$config;
$configClass->setOptions('default', array(
'settings' => array(
'time_zone' => $offset
)
));
Im creating a Zend APIgility application REST service and having a problem with my fetchAll Mapper function.
I'm connecting to an IBM DB2 database running on an i Series server (AS/400) via DB2 Connect on a Windows Application Server.
My Connection is is made in my local.php as such:
return array(
'db' => array(
'driver' => 'IbmDb2',
'database' => $database,
'username' => $user,
'password' => $password,
'hostname' => $host,
'port' => $port,
'driver_options' => array(
'i5_naming' => DB2_I5_NAMING_ON,
'i5_lib' => 'LIBWEB',
),
),
);
The fetchAll() function in my Mapper class is:
public function fetchAll()
{
$select = new Select('WBRESOURCE');
$paginatorAdapter = new DbSelect($select, $this->adapter);
$collection = new ResourcesCollection($paginatorAdapter);
return $collection;
}
When I hit the DbSelect, ZF2 throws the following DB2 Connect error:
"[IBM][CLI Driver][AS] SQL0204N \"*LIBL.WBRESOURCE\" is an undefined name. SQLSTATE=42704"
Im not sure why its using *LIBL (user defined library list), since I defined the library (SCHEMA) to use as LIBWEB in my connection option.
Thanks in advance!
Rob
Try changing this section:
'driver_options' => array(
'i5_naming' => DB2_I5_NAMING_ON,
'i5_lib' => 'LIBWEB',
Change to":
'driver_options' => array(
'i5_naming' => DB2_I5_NAMING_OFF, <=== change
'i5_lib' => 'LIBWEB',
By using DB2_I5_NAMING_OFF, you should get SQL naming mode. The use of DB2 i5 naming mode will result in things like reliance on a job's library list.
See PHP: db2-connect for some info on the parameter.
I had to move an existing site to another hosting (with the same software).
Now, when second user tries to do something with entities (programatically adding, editing or removing) the site fails with the following error:
Fatal error: Class 'Entity\User' not found in /home/.../www/includes/Objects/Proxies/__CG__EntityUser.php on line 9
Here is the config:
ini_set('include_path', ROOT_PATH.'/includes');
require_once(ROOT_PATH.'/includes/Doctrine/ORM/Tools/Setup.php');
Doctrine\ORM\Tools\Setup::registerAutoloadPEAR();
use Doctrine\ORM\Tools\Setup,
Doctrine\ORM\EntityManager,
Doctrine\ORM\Configuration,
Doctrine\DBAL\Event\Listeners\MysqlSessionInit;
$cache = new \Doctrine\Common\Cache\ArrayCache;
$config = new Configuration;
$driverImpl = $config->newDefaultAnnotationDriver(ROOT_PATH.'/includes/Objects');
$driverImpl->getAllClassNames();
$config->setMetadataDriverImpl($driverImpl);
$config->setProxyDir(ROOT_PATH.'/includes/Objects/Proxies');
$config->setProxyNamespace('Objects\Proxies');
// developer mode
//$config->setAutoGenerateProxyClasses(true);
$config->setQueryCacheImpl($cache);
$em = EntityManager::create(array(
'driver' => 'pdo_mysql',
'unix_socket' => '/var/lib/mysql/mysql.sock',
'charset' => 'utf8',
'host' => DB_HOST,
'user' => DB_USER,
'password' => DB_PASS,
'dbname' => DB_NAME
), $config);
$em->getEventManager()->addEventSubscriber(new MysqlSessionInit('utf8', 'utf8_unicode_ci'));
As I said I didn't changed nothing in code...
When the site in single-user state - everything is fine..
What can it be? Thank you
Sometimes I have had to manually create the proxies with ./doctrine orm:generate-proxies (on the command line tool)
But make sure your file permissions are correct as sometimes they are trying to be auto-generated but the server won't allow it.