I've done my functional tests and now I want to run them. However, every time I run them I get sfTestFunctional class not found.
As far as I can tell the functional.php bootstrap is not autoloading the classes from the framework. Any reason why this could be?
This is my functional bootstrap
// guess current application
if (!isset($app))
{
$traces = debug_backtrace();
$caller = $traces[0];
$dirPieces = explode(DIRECTORY_SEPARATOR, dirname($caller['file']));
$app = array_pop($dirPieces);
}
require_once dirname(__FILE__).'/../../config/ProjectConfiguration.class.php';
$configuration = ProjectConfiguration::getApplicationConfiguration($app, 'test', isset($debug) ? $debug : true);
sfContext::createInstance($configuration);
// remove all cache
sfToolkit::clearDirectory(sfConfig::get('sf_app_cache_dir'));
$doctrine = new sfDoctrineDropDbTask($configuration->getEventDispatcher(), new sfAnsiColorFormatter());
$doctrine->run(array(), array("--no-confirmation","--env=test"));
$doctrine = new sfDoctrineBuildDbTask($configuration->getEventDispatcher(), new sfAnsiColorFormatter());
$doctrine->run(array(), array("--env=test"));
$doctrine = new sfDoctrineInsertSqlTask($configuration->getEventDispatcher(), new sfAnsiColorFormatter());
$doctrine->run(array(), array("--env=test"));
This is what is in my the functional tests
include(dirname(__FILE__).'/../../bootstrap/functional.php');
$browser = sfTestFunctional(new sfBrowser());
Doctrine_Core::loadData(sfConfig::get('sf_test_dir').'/fixtures/fixtures_initial.yml');
Ok. So after banging my head against the wall, I found a solution.
For some reason within the test environment custom filters are not autoloaded. The solution is to add require_once for all the custom filters to the ProjectConfiguration file. Here is the example of what I did:
if(sfConfig::get('sf_environment') == 'test' && sfConfig::get('sf_app') == 'frontend')
{
require_once sfConfig::get('sf_app_lib_dir').'/myFilter.class.php';
require_once sfConfig::get('sf_app_lib_dir').'/myotherFilter.class.php';
require_once sfConfig::get('sf_app_lib_dir').'/lovefiltersFilter.php';
require_once sfConfig::get('sf_app_lib_dir').'/eventsManagement.class.php';
require_once sfConfig::get('sf_test_dir').'/ProdPadTestFunctional.class.php';
}
I also had to add my custom testFuntional class as well. This might be more elegantly done using the autoload.yml file.
I spot the problem:
$browser = sfTestFunctional(new sfBrowser());
You should write:
$browser = new sfTestFunctional(new sfBrowser());
Related
I filled a bug but it seams I'm off :p
I just want to replace the service Symfony\Component\Translation\Reader\TranslationReader (translation.reader) with my own class. In fact I want to know how to replace any service of SF4 if I want
translation.reader::addLoader() is normally called by the framework but if I decorate with my own class addLoader is not called.
Can you tell me how I can just drop replace my own service ?
https://github.com/symfony/symfony/issues/28843
Symfony version(s) affected: 4.1.6
Description
Cannot decorate translation.reader (I want to change the default i18n file loading process)
How to reproduce
copy/adapt Symfony\Component\Translation\Reader\TranslationReader to App\Translation\Reader\TranslationReader
Follow https://symfony.com/doc/current/service_container/service_decoration.html
Modify services.yaml
Symfony\Component\Translation\Reader\TranslationReader: ~
App\Translation\Reader\TranslationReader:
decorates: Symfony\Component\Translation\Reader\TranslationReader
#translation.reader: '#App\Translation\Reader\TranslationReader'
Without the alias : the new service is ignored
With the alias : read() is trigger but not addLoader()
Here are the generated injection file getTranslationReaderService.php :
<?php
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
// Returns the private 'App\Translation\Reader\TranslationReader' shared autowired service.
include_once $this->targetDirs[3].'/vendor/symfony/translation/Reader/TranslationReaderInterface.php';
include_once $this->targetDirs[3].'/src/Translation/Reader/TranslationReader.php';
return $this->privates['App\Translation\Reader\TranslationReader'] = new \App\Translation\Reader\TranslationReader();
By default it looks like :
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
// Returns the private 'translation.reader' shared service.
include_once $this->targetDirs[3].'/vendor/symfony/translation/Reader/TranslationReaderInterface.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Reader/TranslationReader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/LoaderInterface.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/ArrayLoader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/FileLoader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/PhpFileLoader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/YamlFileLoader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/XliffFileLoader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/PoFileLoader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/MoFileLoader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/QtFileLoader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/CsvFileLoader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/IcuResFileLoader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/IcuDatFileLoader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/IniFileLoader.php';
include_once $this->targetDirs[3].'/vendor/symfony/translation/Loader/JsonFileLoader.php';
$this->privates['translation.reader'] = $instance = new \Symfony\Component\Translation\Reader\TranslationReader();
$a = ($this->privates['translation.loader.yml'] ?? $this->privates['translation.loader.yml'] = new \Symfony\Component\Translation\Loader\YamlFileLoader());
$b = ($this->privates['translation.loader.xliff'] ?? $this->privates['translation.loader.xliff'] = new \Symfony\Component\Translation\Loader\XliffFileLoader());
$instance->addLoader('php', ($this->privates['translation.loader.php'] ?? $this->privates['translation.loader.php'] = new \Symfony\Component\Translation\Loader\PhpFileLoader()));
$instance->addLoader('yaml', $a);
$instance->addLoader('yml', $a);
$instance->addLoader('xlf', $b);
$instance->addLoader('xliff', $b);
$instance->addLoader('po', ($this->privates['translation.loader.po'] ?? $this->privates['translation.loader.po'] = new \Symfony\Component\Translation\Loader\PoFileLoader()));
$instance->addLoader('mo', ($this->privates['translation.loader.mo'] ?? $this->privates['translation.loader.mo'] = new \Symfony\Component\Translation\Loader\MoFileLoader()));
$instance->addLoader('ts', ($this->privates['translation.loader.qt'] ?? $this->privates['translation.loader.qt'] = new \Symfony\Component\Translation\Loader\QtFileLoader()));
$instance->addLoader('csv', ($this->privates['translation.loader.csv'] ?? $this->privates['translation.loader.csv'] = new \Symfony\Component\Translation\Loader\CsvFileLoader()));
$instance->addLoader('res', ($this->privates['translation.loader.res'] ?? $this->privates['translation.loader.res'] = new \Symfony\Component\Translation\Loader\IcuResFileLoader()));
$instance->addLoader('dat', ($this->privates['translation.loader.dat'] ?? $this->privates['translation.loader.dat'] = new \Symfony\Component\Translation\Loader\IcuDatFileLoader()));
$instance->addLoader('ini', ($this->privates['translation.loader.ini'] ?? $this->privates['translation.loader.ini'] = new \Symfony\Component\Translation\Loader\IniFileLoader()));
$instance->addLoader('json', ($this->privates['translation.loader.json'] ?? $this->privates['translation.loader.json'] = new \Symfony\Component\Translation\Loader\JsonFileLoader()));
return $instance;
You can see that loaders are not injected when I do the decorating...
I'm not sure exactly if this is the root of your problem, but here are some remarks. Hopefully this will help you find a solution, even though I'm not actually given a full answer to your question.
1) Some translation services in Symfony are called only during the cache warmup phase. Whenever you change your config, or do a bin/console cache:clear, you'll see these classes are run, and they generate translations in your var/cache/<env>/translations/ folder.
2) You can try to make sure that in your cache, the classe loaded by var/cache/<env>/Container<...>/getTranslation_ReaderService.php is yours and not the default one like this:
$this->privates['translation.reader'] =
new \Symfony\Component\Translation\Reader\TranslationReader();
3) I also encountered a similar issue in the dev environment, where I was trying to replace Symfony\Component\Translation\Translator with my own service, and didn't manage to get my methods to be called at first. Part of the explanation was that when the Symfony Profiler is enabled, Symfony does something like this (in src<env>DebugProjectContainer.php>):
$this->services['translator'] = new \Symfony\Component\Translation\DataCollectorTranslator(
($this->privates['translator.default'] ?? $this->getTranslator_DefaultService())
);
and the DataCollectorTranslator itself is a wrapper for whichever translator it gets as its constructor argument.
I know this is not a perfect answer but hopefully this will help you find your way to a solution.
I've managed to make it work... but please feel free to comment
I had to create a TranslatorPass to add loaders to the decorating service injection file.
<?php
namespace App\Translation\DependencyInjection;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use App\Translation\Reader\TranslationReader;
class TranslatorPass implements CompilerPassInterface
{
private $readerServiceId;
private $loaderTag;
public function __construct(string $readerServiceId = TranslationReader::class, string $loaderTag = 'translation.loader')
{
$this->readerServiceId = $readerServiceId;
$this->loaderTag = $loaderTag;
}
public function process(ContainerBuilder $container)
{
$loaders = array();
$loaderRefs = array();
foreach ($container->findTaggedServiceIds($this->loaderTag, true) as $id => $attributes) {
$loaderRefs[$id] = new Reference($id);
$loaders[$id][] = $attributes[0]['alias'];
if (isset($attributes[0]['legacy-alias'])) {
$loaders[$id][] = $attributes[0]['legacy-alias'];
}
}
if ($container->hasDefinition($this->readerServiceId)) {
$definition = $container->getDefinition($this->readerServiceId);
foreach ($loaders as $id => $formats) {
foreach ($formats as $format) {
$definition->addMethodCall('addLoader', array($format, $loaderRefs[$id]));
}
}
}
}
}
I've put it in the Kernel.php
protected function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new TranslatorPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 1000);
}
then
bin/console cache:clear
et voilĂ !
I am trying to integrate the WHAnonymous API in my symfony project.
I have included it in my project using composer install and it is now in my vendor folder.
But I am not understanding how to import it into my project!
This is my manager class.
<?php
namespace AppBundle\Managers;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
class WhatsAppManager
{
private $test;
/**
* Constructor
*/
public function __construct()
{
$this->test =1;
}
public function sendMessage()
{
$username = ""; // Your number with country code, ie: 34123456789
$nickname = ""; // Your nickname, it will appear in push notifications
$debug = true; // Shows debug log
// Create a instance of WhastPort.
$w = new WhatsProt($username, $nickname, $debug);
var_dump("In send message method");
}
}
?>
I have used
require_once 'whatsprot.class.php';
and
require_once 'Whatsapp\Bundle\Chat-api\src\whatsprot.class.php';
and
use Whatsapp\Bundle\Chat-api\Whatsprot
But it is just not working.
Please tell me the right way to do it!
And is there something i should do when i am using 3rd party vendors in symfony.
I did look into the documentation of the WHanonymous but i found only snippets of code to use it and not the way to import it.
Git repo for WHAnonymous : https://github.com/WHAnonymous
The class doesn't have a namespace, but is correctly loaded by the autoload system created my composer. So you can reference to the class without any include or require directive but simply with a \ as example:
// Create a instance of WhastPort.
$w = new \WhatsProt($username, $nickname, $debug);
Hope this help
what I try to accomplish is:
Loading fresh fixtures in PHP code before a WebTestCase is executed. As far as i know this code should do its work:
// Get a kernel instance and boot it up!
static::$kernel = static::createKernel();
static::$kernel->boot();
// Get entity manager
$em = static::$kernel->getContainer()->get('doctrine_phpcr.odm.default_document_manager');
$loader = new Loader();
foreach (self::$fixturesRequired as $fixtureClass) {
$loader->addFixture(new $fixtureClass);
}
$purger = new ORMPurger($em);
$executor = new ORMExecutor($em, $purger);
$executor->execute($loader->getFixtures());
... but it fails with:
AnnounceControllerTest::testAnnounce
Argument 1 passed to Doctrine\Common\DataFixtures\Purger\ORMPurger::__construct() must be an instance of Doctrine\ORM\EntityManagerInterface, instance of Doctrine\ODM\PHPCR\DocumentManager given, called in /opt/development/cms/.../WebTestCase.php
So does anyone know what I'm missing?
Thanks a lot in advance :)
Solution to purge and load new fixtures for PHPCR:
// Get a kernel instance and boot it up!
static::$kernel = static::createKernel();
static::$kernel->boot();
// Get entity manager
$em = static::$kernel->getContainer()->get('doctrine_phpcr.odm.default_document_manager');
$loader = new Loader();
foreach (self::$fixturesRequired as $fixtureClass) {
$loader->addFixture(new $fixtureClass);
}
$purger = new PHPCRPurger($em);
$executor = new PHPCRExecutor($em, $purger);
$executor->execute($loader->getFixtures());
Thanks for the help!
Sorry about the vague title, but I am trying to find some better alternatives to having to call an Autoloader class, and the register method multiple times, to map class paths as seen below.
$ClassLoader = new Autoloader\Loader(__DIR__.'/path/to/someclass');
$ClassLoader->register();
$ClassLoader = new Autoloader\Loader(_DIR__.'/path/to/anotherclass');
$ClassLoader->register();
$ClassLoader = new Autoloader\Loader(__DIR__.'/path/to/anotherclass');
$ClassLoader->register();
$ClassLoader = new Autoloader\Loader(__DIR__.'/path/to/anotherclass');
$ClassLoader->register();
$ClassLoader = new Autoloader\Loader(__DIR__.'/path/to/anotherclass');
$ClassLoader->register();
This goes on and on for about 50 lines, and I would like to know how I can handle the autoloading classes with simple few lines solution. I can obviously inject an array, to the constructor:
$ClassLoader = new Autoloader\Loader( ['paths'=>[
'/path/to/class/',
'/path/to/anotherclass',
'/path/to/anotherclass'
]);
$ClassLoader->register();
But, I am not sure if this method is recommended at-least from OOP good practice point of view.
Perhaps this is what you are looking for.
For each directory that contains your classes run ::add.
namespace ClassLoader;
class Loader
{
protected $directories = array();
public function __construct()
{
spl_autoload_register([$this, 'load']);
}
public function add($dir)
{
$this->directories[] = rtrim($dir, '/\\');
}
private function load($class)
{
$classPath = sprintf('%s.php', str_replace('\\', '/', $class));
foreach($this->directories as $dir) {
$includePath = sprintf('%s/%s', $dir, $classPath);
if(file_exists($includePath)) {
require_once $includePath;
break;
}
}
}
}
$loader = new Loader();
$loader->add(__DIR__.'/src');
$loader->add(__DIR__.'/vendor');
use Symfony\Component\Finder\Finder;
$finder = new Finder();
// Included /var/www/test/vendor/Symfony/Component/Finder/Finder.php
// I put the Symfony components in that directory manually for this example.
print_r($finder);
It is effectively the same as with composer though, just less adaptive or performant.
For this you can use Composer: https://getcomposer.org/download/
You will get a file called composer.phar.
Place this in your project directory, then go to that directory on your command line.
Run php composer.phar init.
This will ask you a few questions which you can ignore, in the end you get a new file called composer.json
It should look something like this:
{
"autoload": {
"psr-0": { "": "src/" }
},
"require": {}
}
Add the autoload field, and replace src/ with the directory containing your classes.
Make sure that directory exists.
Then run php composer.phar install.
This will create a directory called vendor. Inside this directory is a file called autoload.php.
Include this file in the bootstrap of your project, and all classes within your source directory will automatically be loaded in.
Have you looked into spl_autoload_register function?
usage
// pre php 5.3
function my_autoloader($class) {
include 'classes/' . $class . '.class.php';
}
spl_autoload_register('my_autoloader');
// Or, using an anonymous function as of PHP 5.3.0
spl_autoload_register(function ($class) {
include 'classes/' . $class . '.class.php';
});
then place all your classes in "classes" folder and when you initialise them with the new keyword, they will be auto-included. Works with static classes also.
for example:
$myClassOb1 = new MyClass();
// will include this file: classes/MyClass.class.php
or
$email = Utils::formatEmail($emailInput);
// will include this file: classes/Utils.class.php
I have directory CLASSES with files in my project.
../classes/class.system.php
../classes/class.database.php
...
I pull out every class and include it to main index.php with this code:
// load classes
foreach (glob("classes/class.*.php") as $filename) {
require_once $filename;
}
and then I create (manually write) objects for example:
$system = new System();
$database = new Database();
...
Q: How can I automatically generate object for each class from list of files in directory CLASSES without writing them?
Thank you for your answers and code.
EDIT:
My working solution:
// load classes
foreach (glob("classes/class.*.php") as $filename) {
require_once $filename;
$t = explode(".",$filename);
$obj = strtolower($t[1]);
$class = ucfirst($t[1]);
${$obj} = new $class();
}
IF you follow a typical pattern, while creating those files like
class.<classname>.php
Then
foreach (glob("classes/class.*.php") as $filename) {
require_once $filename;
$t = explode(".",$filename);
${strtolower($t[1])}= new ucfirst($t[1])(); // automatically create the object
}
I don't think loading all classes with glob and including them if you need or not is efficient or good idea performance wise. What if you have 500 different classes ???
Why don't you take advantage of PHP auto loading see http://php.net/manual/en/language.oop5.autoload.php which would only load the class you need
Example
function __autoload($className) {
require_once "classes/class." . $className . ".php";
}
$system = new System();
$database = new Database();
// load classes and create objects
foreach (glob("classes/class.*.php") as $filename) {
require_once $filename;
$t = explode(".",$filename);
$obj = strtolower($t[1]);
$class = ucfirst($t[1]);
// create object for every class
${$obj} = new $class();
}