Error extending GearmanClient class - php

Im trying to create a class extending GearmanClient so i can centralize and use gearman across my app according to my own specifications. One of the reasons im doing my own class is to store easily failed tasks into a database so they can be later processed again.
Im getting a basic error
Warning: GearmanClient::runTasks():
_client_run_task(GEARMAN_NO_SERVERS) no servers added -> libgearman/run.cc:66 in /var/www/html/app/forecast/Forecast.php on
line 37
<?php
namespace app\service;
use helpers\Config_helper;
use \GearmanClient;
class Gearman_service extends GearmanClient
{
public $client;
private $servers = array();
private $tasks = array();
private $completedTasks = array();
private $failedTasks = array();
private $maxRetryAttempts;
public function __construct()
{
$this->client = new GearmanClient();
$this->servers = Config_helper::get_config_option('gearman_servers');
$this->maxRetryAttempts = Config_helper::get_config_option('gearman_retry_attempts');
$this->initialize();
}
protected function initialize()
{
foreach($this->servers as $key => $value):
$this->client->addServer($value[0],$value[1]);
endforeach;
}
}
I must assume something is wrong with this implementation but i would like to know why.
The Config_helper::get_config_option('gearman_servers'); is retrieving correctly my list of servers.
This is my Forecast class
<?php
namespace app\forecast;
use app\service\Gearman_service;
use helpers\Config_helper;
use helpers\Urlrequest_helper;
use app\warehouse\models\Client_time_forecast;
abstract class Forecast
{
public $coordinates = array(); # set of coordinates
public $servers = array();
public $variables = array();
public $url = array();
public $prevision;
public $client;
public $gearmanclient;
public function __construct()
{
$this->servers = Config_helper::get_config_option('forecast_servers');
$this->variables = Config_helper::get_config_option('surface_variables');
$this->prevision = Config_helper::get_config_option('forecast_prevision');
$this->gearmanclient = new Gearman_service();
}
public function storeResults()
{
$this->gearmanclient->setCompleteCallback(array($this, 'requestComplete'));
foreach($this->url as $key => $value):
$this->gearmanclient->addTask('request_forecast', serialize($value[0]));
endforeach;
$this->gearmanclient->runTasks(); // **line 37**
}
/**
* [requestComplete store request results in cassandra db]
* #param \GearmanTask $task [description]
* #return [boolean]
*/
public function requestComplete(\GearmanTask $task)
{
$persistent = new Client_time_forecast($this->client, unserialize($task->data()));
$persistent->storeData();
}
}
Anyone can share me a light on this?
Thank you!

As suspected the cause of the problem is that you are mixing inheritance and composition. You extended the GearmanClient class and at the same time you are creating a new instance of the GearmanClient class in the constructor and configuring this new instance in method initialize.
class Gearman_service extends GearmanClient
{
public $client;
// other properties
public function __construct()
{
$this->client = new GearmanClient();
// more code
$this->initialize();
}
You could change the line 37 and all other calls to GermanClient public methods to call the instance initiated in constructor and do not extend GearmanClient class.
$this->gearmanclient->client->runTasks();
However it would be better to change visibility of the property Gearman_service::client to private and implement GeamanClient class public interface.
class Gearman_service extends GearmanClient
{
private $client;
// constructor etc
public function addTask($name, $workload, $context = null, $unique = "")
{
return $this->client->addTask($name, $workload, $context, $unique);
}
If you do so the line 37 should stay as it is.
Alternatively you could opt for inheritance. In that case you would need to remove public property client, do not create a new instance of the GeamanClient class in the constructor and change initialize methods.
protected function initialize()
{
foreach($this->servers as $key => $value):
$this->addServer($value[0],$value[1]);
endforeach;
}
In this case as well you don't need to change the line 37 nor any other calls GeamanClient class public methods.

Related

Send interface to constructor PHP

I have downloaded an example for a payment connection. No i am trying to use it, but the constructor want's to get the interface when i declare ClassName
But i have no idea how to do that. I tried
$interface = CallbackInterface::class;
$interface = CallbackInterface();
$interface = CallbackInterface;
And many more , but i can't figure it out. Only thing i know is to implement an interface with a class. Maybe a noob question, but i've searched almost a day with no success.
$config = new Config('string1', 'string2');
$pay = new ClassName($config, $interface);
interface CallbackInterface
{
public function Key($sIdentifier, $sTransactionKey);
public function tSuccess($sTransactionKey);
}
class ClassName
{
public function __construct(Config $oConfig, CallbackInterface $oCallbacks)
{
$this->oConfig = $oConfig;
$this->oCallbacks = $oCallbacks;
}
}
you should be looking for a solution along these lines
// Create a class that implements the interface (e.g. MyClass)
// MyClass implements the interface functions: Key and tSuccess
// MyClass can now be injected as type CallbackInterface into the __construct() of class ClassName
Class MyClass implements CallbackInterface
{
public function Key($sIdentifier, $sTransactionKey)
{
// your implementation here
}
public function tSuccess($sTransactionKey)
{
// your implementation here
}
}
interface CallbackInterface
{
public function Key($sIdentifier, $sTransactionKey);
public function tSuccess($sTransactionKey);
}
class ClassName
{
public function __construct(Config $oConfig, CallbackInterface $oCallbacks)
{
$this->oConfig = $oConfig;
$this->oCallbacks = $oCallbacks;
}
}
$config = new Config('string1', 'string2');
$interface = new MyClass(); // you've now instantiated an object of type CallbackInterface
$pay = new ClassName($config, $interface);

Symfony2, acess/use the custom class in another custom class

How to load/access a custom class in the custom class in Symfony 2 not using service container?
If i try to use the custom logger as shown below i am getting the error:
Catchable Fatal Error: Argument 1 passed to MeetingBundle\Components\LogWriter::__construct() must implement interface Symfony\Component\HttpKernel\Log\LoggerInterface, none given, called in C:\Bitnami\wampstack-5.6.20-0\apache2\htdocs\sym\just2\src\MeetingBundle\Components\Serializer\Diff\EventDiff.php on line 23 and defined
Stack Trace:
in src\MeetingBundle\Components\LogWriter.php at line 12 -
private $logger;
public function __construct( LoggerInterface $logger ) // here they show the error
{
$this->logger = $logger;
}
src\MeetingBundle\Components\LogWriter.php
<?php
namespace MeetingBundle\Components;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
class LogWriter
{
private $logger;
public function __construct( LoggerInterface $logger )
{
$this->logger = $logger;
}
public function log($msg)
{
$this->logger->log($msg);
}
}
src\MeetingBundle\Components\Serializer\Diff\Diff.php
<?php
namespace MeetingBundle\Components\Serializer\Diff;
//use MeetingBundle\Components\LogWriter;
class Diff
{
private $diffCluesArr;
private $name;
private $logWriter;
public function __construct ($logger) {
$this->name= "";
$this->diffCluesArr = [];
$this->logWriter = $logger;
//$this->logWriter = new $logWriter; //changed here
}
public function array_diff_str_o ( $arr1, $arr2, $str ) {
$this->logWriter("<br> In Diff function array_diff_str_o ");
//...
src\MeetingBundle\Components\Serializer\Diff\EventDiff.php
<?php
namespace MeetingBundle\Components\Serializer\Diff;
use MeetingBundle\Components\Serializer\Diff\Diff;
use MeetingBundle\Components\LogWriter;
/**
* Event normalizer
*/
class EventDiff extends Diff
{
private $diffCluesArr;
private $name;
private $logw;
// does not work
// public function __construct (LogWriter $logger) {
// $this->logw= $logger;
// parent::__construct($this->logw);
public function __construct () {
$this->logw = new LogWriter();
parent::__construct($this->logw);
$this->logw("<br> In constructor of EventDiff");
$this->name= "event";
$this->diffCluesArr = array(
//1 means compare normally
//2 means compare the values of the keys
'id' => 1,
// ..
app\config\services.yml
services:
meeting.logw:
class: MeetingBundle\Components\LogWriter
arguments: ["#logger"]
meeting.diff.diff:
class: 'MeetingBundle\Components\Serializer\Diff\Diff'
arguments: ["#meeting.logw"]
meeting.diff.event:
class: 'MeetingBundle\Components\Serializer\Diff\EventDiff'
parent: meeting.logw
#the same error: parent: meeting.diff.diff
src\MeetingBundle\Controller\EventMapController.php
//..
$diffentiator = $this->get('meeting.diff.event');
$diffentiator->array_diff_str_o( $arrEventOld, $arrEventNew, $msg );
//..
//**** THE OLD VERSION OF THE QUESTION
If i try to use the custom logger as shown below i am getting the error:
Catchable Fatal Error: Argument 1 passed to MeetingBundle\Components\Serializer\Diff\Diff::__construct() must be an instance of MeetingBundle\Components\Serializer\Diff\LoggerInterface, none given, called in C:\Bitnami\wampstack-5.6.20-0\apache2\htdocs\sym\just2\src\MeetingBundle\Components\Serializer\Diff\EventDiff.php on line 16 and defined
Where is the mistake? The code is as follow:
just2\src\MeetingBundle\Components\LogWriter.php
<?php
namespace MeetingBundle\Components;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
class LogWriter
{
private $logger;
public function __construct(LoggerInterface $logger) // the place of error
{
$this->logger = $logger;
}
public function log($msg)
{
$this->logger->log($msg);
}
}
just2\src\MeetingBundle\Components\Serializer\Diff\Diff.php
<?php
namespace MeetingBundle\Components\Serializer\Diff;
use MeetingBundle\Components\LogWriter;
class Diff
{
private $logWriter;
public function __construct (LoggerInterface $logger) {
// the first mistake
$this->logWriter = $logger;
}
public function array_diff_str_o ( $arr1, $arr2, $str ) {
$this->logWriter("<br> In Diff function array_diff_str_o ");
//..
}
// src\MeetingBundle\Components\Serializer\Diff\EventDiff.php
<?php
namespace MeetingBundle\Components\Serializer\Diff;
use MeetingBundle\Components\Serializer\Diff\Diff;
/** Provides clue how to calculate the differences between entities instances */
class EventDiff extends Diff
{
private $diffCluesArr;
private $name;
public function __construct () {
parent::__construct();
$this->logWriter("<br> In constructor of EventDiff");
$this->name= "event";
$this->diffCluesArr = array(
//1 means compare normally
//2 means compare the values of the keys
'id' => 1,
//...
just2\src\MeetingBundle\Controller\EventMapController.php
/** * #Route("/edit/{id}", name="event_jsMap_edit")
* #Method("GET|POST")
* #Template("MeetingBundle:Event:ev_jsMap_edit.html.twig")
*/
public function editAction($id, Request $request)
{
...
$diffentiator = $this->get('meeting.diff.event');
$diffentiator->array_diff_str_o( $arrEventOld, $arrEventNew, $msg );
...
I also made logwrite to be a service, but maybe it is not necessary and i do not want it to be a service. I would like to use it as a individual class not as a part of a service container:
app\config\services.yml
services:
events.logger:
class: MeetingBundle\Components\LogWriter
arguments: ["#logger"]
meeting.diff.event:
class: 'MeetingBundle\Components\Serializer\Diff\EventDiff'
#class Diff is not a service. Class EventDiff extends from Diff.
The problem is most likely in the way you instantiate Diff in EventDiff class as the error message suggests. LogWriter seems allright.
Do you define EventDiff as a service as well with correct dependencies?
Edit: In EventDiff you're calling parent::__construct(); without any parameter. However the parent Diff class takes one parameter. You probably want to inject a service to EventDiff that'll be passed to its parent in the constructor.
You could Manage Common Dependencies with Parent Services. Try defining the service defining the parent attribute in the following manner:
services:
events.logger:
class: MeetingBundle\Components\LogWriter
arguments: ["#logger"]
meeting.diff.event:
class: 'MeetingBundle\Components\Serializer\Diff\EventDiff'
parent: events.logger
Hope this help

Testing Private Methods Not Working

Here is my Test Class;
<?php
namespace stats\Test;
use stats\Baseball;
class BaseballTest extends \PHPUnit_Framework_TestCase
{
public function setUp() {
$this->instance = new Baseball();
}
public function tearDown() {
unset($this->instance);
}
public function testOps() {
$obp = .363;
$slg = .469;
$ops = $this->instance->calc_ops($obp, $slg); //line 23
$expectedops = $obp + $slg;
$this->assertEquals($expectedops, $ops);
}
}
And this is my Baseball Class;
<?php
namespace stats;
class Baseball
{
private function calc_ops($slg,$obp)
{
return $slg + $obp;
}
}
And I keep getting this error when I run my tests;
Fatal error: Call to private method stats\Baseball::calc_ops() from context 'stats\Test\BaseballTest' in /media/sf_sandbox/phpunit/stats/Test/BaseballTest.php on line 23
This is only a tutorial I am following.. But it's not working so it's frustrating because I am following it exactly.
You can't test private method, you can use a workaround and invoke it via reflection as described in this article.
This is a working example based on the article:
class BaseballTest extends \PHPUnit_Framework_TestCase
{
public function setUp() {
$this->instance = new Baseball();
}
public function tearDown() {
unset($this->instance);
}
public function testOps() {
$obp = .363;
$slg = .469;
// $ops = $this->instance->calc_ops($obp, $slg); //line 23
$ops = $this->invokeMethod($this->instance, 'calc_ops', array($obp, $slg));
$expectedops = $obp + $slg;
$this->assertEquals($expectedops, $ops);
}
/**
* Call protected/private method of a class.
*
* #param object &$object Instantiated object that we will run method on.
* #param string $methodName Method name to call
* #param array $parameters Array of parameters to pass into method.
*
* #return mixed Method return.
*/
public function invokeMethod(&$object, $methodName, array $parameters = array())
{
$reflection = new \ReflectionClass(get_class($object));
$method = $reflection->getMethod($methodName);
$method->setAccessible(true);
return $method->invokeArgs($object, $parameters);
}
Public – The method is publicly available and can be accessed by all subclasses.
Protected – the method / function / property is available to the parent class and all inheriting classes or we call them subclasses or child classes.
Private – the method is private and only available to the parent class / base class.
You can only test private methods within the class and call that public method that using the private method.
class Baseball
{
public function testMethod()
{
$a = 1;
$b = 2;
return $this->calc_ops($a, $b);
}
private function calc_ops($slg,$obp)
{
return $slg + $obp;
}
}

Instance Class inside construct

I need to instance 2 or more classes on top of my controller class, so I can use them with $this->filter or $this->logger_instance inside any method of my controller class. Right now it is not letting me, I get an error. (I do not want to extend the class if possible.) Is it okay to instance in construct if it is possible.
Parse error: syntax error, unexpected T_NEW in controller.php
(I am in the process of transferring my coding habits from procedural to OOP so I am really bad at it.)
class ID_Controller
{
public $input;
public $register_attempt = 2;
public $maximum_attempts = 3;
public $log_data_attempts = 2;
public $query_log_file_path;
public $sql_filtering = true;
public $xss_filtering = true;
public $sql_register_attempt = 3;
public $xss_register_attempt = 6;
public $filter = new ID_Algorithm;
public $logger_instance = new ID_Logger;
function __construct()
{
}
}
Why not try initializing these classes through the __construct() method?
/*
If the two classes are located in seperate files, be sure to require them:
*/
require("ID_Algorithm Page");
require("ID_Lodder Page");
class ID_Controller {
/* previous lines here */
/*
Comment out the next two lines and initiate them within the construct class
*/
// public $filer = new ID_Algorithm;
// public $logger_instance;
public $filer;
public $logger_instance
public function __construct(){
$this->filter = new ID_Algorithm;
$this->logger_instance = new ID_Logger;
}
}
Then when calling:
$Class = new ID_Controller();
this will set the necessary internal pointers correctly.

Doctrine2 strange persist exception

I'm having strange problems when trying to persist a class of User that has a reference to many UserProperties. Note that a UserProperty will be managed by a cascade:persist.
UserProperties itself has a reference to a Property.
When creating a new User with a new UserProperty (which itself has a reference to an existing Property) it throws strange (strange as in i didn't expect it) error:
InvalidArgumentException: A new entity was found through the relationship 'UserProperty#property' that was not configured to cascade persist operations for entity
User:
class User extends IdentifiableObject {
// … other vars
/**
* #OneToMany(targetEntity="UserProperty", mappedBy="user", cascade={"persist", "remove"}, orphanRemoval=true)
*/
private $userProperties = null;
public function __construct() {
$this->userProperties = new ArrayCollection();
}
// … other methods
public function getUserProperties() {
return $this->userProperties;
}
public function setUserProperties($userProperties) {
$this->userProperties = $userProperties;
}
public function addUserProperty(UserProperty $userProperty) {
$userProperty->setUser($this);
$this->userProperties[] = $userProperty;
}
}
UserProperty:
class UserProperty extends IdentifiableObject {
/**
* #OneToOne(targetEntity="Property")
* #JoinColumn(name="propertyID")
*/
private $property;
public function getProperty() {
return $this->property;
}
public function setProperty($property) {
$this->property = $property;
}
}
Property class has no references to either class.
And finally my testClass using PHPUnit:
class UserDaoTest extends PHPUnit_Framework_TestCase {
private static $userDao;
private static $propertyDao;
public static function setUpBeforeClass() {
//this will make the EntityManager called inside our DAOImpl point to our test database...
define('__DBNAME__', 'db_test');
createCleanTestDatabase();
self::$userDao = new UserDaoImpl();
self::$propertyDao = new PropertyDaoImpl();
}
public function testEntityClassVariable() {
$this->assertEquals("User", self::$userDao->getEntityClass());
}
public function testPersistUserWithoutProperties() {
$user = new User();
$user->setUserName("tester1");
$user->setUserType(1);
self::$userDao->persist($user);
self::$userDao->flush();
$this->assertEquals(1, count(self::$userDao->findAll()));
}
public function testPersistUserWithProperties() {
$user = new User();
$user->setUserName("tester2");
$user->setUserType(1);
$property = new Property();
$property->setName("propertyName");
$property->setType(1);
self::$propertyDao->persist($property);
self::$propertyDao->flush();
$userProperty = new UserProperty();
$userProperty->setProperty($property);
$userProperty->setValue("test");
$user->addUserProperty($userProperty);
self::$userDao->persist($user);
self::$userDao->flush();
$this->assertEquals(2, count(self::$userDao->findAll()));
$userInDB = self::$userDao->find($user);
$this->assertNotNull($userInDB);
$this->assertEquals(1, count($userInDB->getUserProperties()));
}
}
The strange thing is that the Property is indeed created in the Database.
Also the test works perfectly fine IF i use the userDao->persist to save the Property (instead of the propertyDao...
Any help would be appreciated, thanks in advance!
The problem was that i was using a different entityManager in each dao so effectively having a different UnitOfWork for each DAO. When i made the entity a singleton so that each DAO had the same reference to it.

Categories