Review the route dynamically Symfony2 - php

Hello I need get the route and review if the have access to this, reviewing on the database but a level really abstract and automatic. By now i am making that on this form:
$accesos = MenuQuery::create()
->useAccesoMenuQuery()
->usePerfilQuery()
->usePerfilUsuarioQuery()
->filterByUsuarioId($this->getUser()->getId())
->endUse()
->endUse()
->endUse()
->orderBy('menu.orden')
->groupBy('menu.id')
->find();
$permiso = false;
foreach ($accesos as $acceso) {
if (($acceso->getDireccion() == $ruta) || ($permiso)) {
$permiso = true;
break;
}
}
return $permiso;
}
I am using this php function for make that. But i need to make that on the firewalls of symfony2 or another form but abstract.

One way, and if the routes are generic, set up ACL in the security.yml where you can specify that for example ^/admin/.* require ROLE_ADMIN.
If you have special roles, for example, ACCOUNT_ADMIN where it's depending on what is the selected account, then you need to write your own VOTER, where you can decide that at a specific point, is the rights are enough or not.

The form that I did it was using a Event Listener of Symfon2 Here I leave to yours my own code.
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
class SoporteListener {
private $container;
private $acceso = 0;
public function __construct(ContainerInterface $container) {
$this->container = $container;
}
public function onKernelRequest(GetResponseEvent $event) {
$request = $this->container->get('request');
$routeName = $request->get('_route');
$securityContext = $this->container->get('security.context');
if (($securityContext->isGranted('ROLE_USUARIO')) &&
($routeName != 'done_login') &&
($routeName != 'done_logout')) {
$usuario = $this->container->get('security.context')->getToken()->getUser();
$permisos = MenuQuery::create()
->useAccesoMenuQuery()
->usePerfilQuery()
->usePerfilUsuarioQuery()
->filterByUsuarioId($usuario->getId())
->endUse()
->endUse()
->endUse()
->groupBy('menu.id')
->find();
foreach ($permisos as $permiso) {
if (($permiso->getDireccion() == $routeName)) {
$this->acceso = 1;
break;
}
}
if ($this->acceso == 0) {
$event->setResponse($this->container->get('templating')->renderResponse('::error.html.twig', array('error' => 'Permiso denegado')));
} else {
return;
}
} else {
return;
}
}
}

Related

How to redirect from a EventSubscriber in Symfony 5

I'm attempting to write a custom auth checker for Symfony5. This is to run on selected Controllers, all of which have an AuthenticationControllerInterface which includes numerous other bits of relevant code.
I am attempting to use an EventSubscriber bound to the ControllerEvent. This checks for this interface and identifies correctly the relevant controllers.
Please see the below for context:
class BearerTokenSubscriber implements EventSubscriberInterface
{
public function onKernelController(ControllerEvent $event)
{
$controller = $event->getController();
// load the controller
if (is_array($controller)) {
$controller = $controller[0];
if ($controller instanceof AuthenticationControllerInterface) {
if(
// my assorted auth functionality: works as expected
) {
//... where my question lies:
}
}
}
}
}
public static function getSubscribedEvents()
{
return [
KernelEvents::CONTROLLER => 'onKernelController',
];
}
}
At "where my question lies": is the point I want to do a redirect... in the following ways (in order of preference):
returning a specific controller method (I have a 403 pre-configured).
redirecting to another URL (with a 403)
Thanks in advance.
You can use ControllerEvent::setController($myController) to provide a controller of your choice once your conditions are met:
class TestControllerEventSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
$events = [];
$events[KernelEvents::CONTROLLER] = ['onKernelController', 0];
return $events;
}
public function onKernelController(ControllerEvent $event): void
{
$controller = $this->getControllerObjectFromEvent($event);
// Check if your conditions are met
if ($controller instanceof AuthenticationControllerInterface && $whateverElse) {
$event->stopPropagation();
// Set your custom controller that produces a 403 response
$event->setController(static function () {
return new Response(null, 403);
});
}
}
private function getControllerObjectFromEvent(ControllerEvent $event): ?object
{
$controller = $event->getController();
if (true === is_object($controller)) {
return (object) $controller;
}
if (false === is_array($controller)) {
return null;
}
foreach ($controller as $value) {
if (true === is_object($value)) {
return $value;
}
}
return null;
}
}
The specific answer in my case was:
if (/* some test logic */) {
$event->stopPropagation();
$event->setController(
static function () use ($controller) {
return $controller->some403ErrorResponse();
}
);
}
Many thanks to Jeroen who pointed me in the correct direction.
Have marked his as correct also.

simplify nested if statements

I'm implementing a search functionality and based on the query parameter i use a different class to search.
class Search {
public function getResults()
{
if (request('type') == 'thread') {
$results = app(SearchThreads::class)->query();
} elseif (request('type') == 'profile_post') {
$results = app(SearchProfilePosts::class)->query();
} elseif (request()->missing('type')) {
$results = app(SearchAllPosts::class)->query();
}
}
Now when i want to search threads i have the following code.
class SearchThreads{
public function query()
{
$searchQuery = request('q');
$onlyTitle = request()->boolean('only_title');
if (isset($searchQuery)) {
if ($onlyTitle) {
$query = Thread::search($searchQuery);
} else {
$query = Threads::search($searchQuery);
}
} else {
if ($onlyTitle) {
$query = Activity::ofThreads();
} else {
$query = Activity::ofThreadsAndReplies();
}
}
}
}
To explain the code.
If the user enters a search word ( $searchQuery) then use Algolia to search, otherwise make a database query directly.
If the user enters a search word
Use the Thread index if the user has checked the onlyTitle checkbox
Use the Threads index if the user hasn't checked the onlyTitle checkbox
If the user doesn't enter a search word
Get all the threads if the user has checked the onlyTitle checkbox
Get all the threads and replies if the user hasn't checked the onlyTitle checkbox
Is there a pattern to simplify the nested if statements or should i just create a separate class for the cases where
a user has entered a search word
a user hasn't entered a search word
And inside each of those classes to check if the user has checked the onlyTitle checkbox
I would refactor this code to this:
Leave the request parameter to unify the search methods in an interface.
interface SearchInterface
{
public function search(\Illuminate\Http\Request $request);
}
class Search {
protected $strategy;
public function __construct($search)
{
$this->strategy = $search;
}
public function getResults(\Illuminate\Http\Request $request)
{
return $this->strategy->search($request);
}
}
class SearchFactory
{
private \Illuminate\Contracts\Container\Container $container;
public function __construct(\Illuminate\Contracts\Container\Container $container)
{
$this->container = $container;
}
public function algoliaFromRequest(\Illuminate\Http\Request $request): Search
{
$type = $request['type'];
$onlyTitle = $request->boolean('only_title');
if ($type === 'thread' && !$onlyTitle) {
return $this->container->get(Threads::class);
}
if ($type === 'profile_post' && !$onlyTitle) {
return $this->container->get(ProfilePosts::class);
}
if (empty($type) && !$onlyTitle) {
return $this->container->get(AllPosts::class);
}
if ($onlyTitle) {
return $this->container->get(Thread::class);
}
throw new UnexpectedValueException();
}
public function fromRequest(\Illuminate\Http\Request $request): Search
{
if ($request->missing('q')) {
return $this->databaseFromRequest($request);
}
return $this->algoliaFromRequest($request);
}
public function databaseFromRequest(\Illuminate\Http\Request $request): Search
{
$type = $request['type'];
$onlyTitle = $request->boolean('only_title');
if ($type === 'thread' && !$onlyTitle) {
return $this->container->get(DatabaseSearchThreads::class);
}
if ($type === 'profile_post' && !$onlyTitle) {
return $this->container->get(DatabaseSearchProfilePosts::class);
}
if ($type === 'thread' && $onlyTitle) {
return $this->container->get(DatabaseSearchThread::class);
}
if ($request->missing('type')) {
return $this->container->get(DatabaseSearchAllPosts::class);
}
throw new InvalidArgumentException();
}
}
final class SearchController
{
private SearchFactory $factory;
public function __construct(SearchFactory $factory)
{
$this->factory = $factory;
}
public function listResults(\Illuminate\Http\Request $request)
{
return $this->factory->fromRequest($request)->getResults($request);
}
}
The takeaway from this is it is very important to not involve the request in the constructors. This way you can create instances without a request in the application lifecycle. This is good for caching, testability and modularity. I also don't like the app and request methods as they pull variables out of thin air, reducing testability and performance.
class Search
{
public function __construct(){
$this->strategy = app(SearchFactory::class)->create();
}
public function getResults()
{
return $this->strategy->search();
}
}
class SearchFactory
{
public function create()
{
if (request()->missing('q')) {
return app(DatabaseSearch::class);
} else {
return app(AlgoliaSearch::class);
}
}
}
class AlgoliaSearch implements SearchInterface
{
public function __construct()
{
$this->strategy = app(AlgoliaSearchFactory::class)->create();
}
public function search()
{
$this->strategy->search();
}
}
class AlgoliaSearchFactory
{
public function create()
{
if (request('type') == 'thread') {
return app(Threads::class);
} elseif (request('type') == 'profile_post') {
return app(ProfilePosts::class);
} elseif (request()->missing('type')) {
return app(AllPosts::class);
} elseif (request()->boolean('only_title')) {
return app(Thread::class);
}
}
}
Where the classes created in the AlgoliaSearchFactory are algolia aggregators so the search method can be called on any of those classes.
Would something like this make it cleaner or even worse ?
Right now i have strategies that have strategies which sounds too much to me.
I have tried to implement a good solution for you, but I had to make some assumptions about the code.
I decoupled the request from the constructor logic and gave the search interface a request parameter. This makes the intention clearer than just pulling the Request from thin air with the request function.
final class SearchFactory
{
private ContainerInterface $container;
/**
* I am not a big fan of using the container to locate the dependencies.
* If possible I would implement the construction logic inside the methods.
* The only object you would then pass into the constructor are basic building blocks,
* independent from the HTTP request (e.g. PDO, AlgoliaClient etc.)
*/
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
private function databaseSearch(): DatabaseSearch
{
return // databaseSearch construction logic
}
public function thread(): AlgoliaSearch
{
return // thread construction logic
}
public function threads(): AlgoliaSearch
{
return // threads construction logic
}
public function profilePost(): AlgoliaSearch
{
return // thread construction logic
}
public function onlyTitle(): AlgoliaSearch
{
return // thread construction logic
}
public function fromRequest(Request $request): SearchInterface
{
if ($request->missing('q')) {
return $this->databaseSearch();
}
// Fancy solution to reduce if statements in exchange for legibility :)
// Note: this is only a viable solution if you have done correct http validation IMO
$camelCaseType = Str::camel($request->get('type'));
if (!method_exists($this, $camelCaseType)) {
// Throw a relevent error here
}
return $this->$camelCaseType();
}
}
// According to the code you provided, algoliasearch seems an unnecessary wrapper class, which receives a search interface, just to call another search interface. If this is the only reason for its existence, I would remove it
final class AlgoliaSearch implements SearchInterface {
private SearchInterface $search;
public function __construct(SearchInterface $search) {
$this->search = $search;
}
public function search(Request $request): SearchInterface {
return $this->search->search($request);
}
}
I am also not sure about the point of the Search class. If it only effectively renames the search methods to getResults, I am not sure what the point is. Which is why I omitted it.
I had to write all this to make the problem understandable.
The SearchFactory takes all the required parameters and based on these parameters, it calls either AlgoliaSearchFactory or DatabaseSearchFactory to produce the final object that will be returned.
class SearchFactory
{
protected $type;
protected $searchQuery;
protected $onlyTitle;
protected $algoliaSearchFactory;
protected $databaseSearchFactory;
public function __construct(
$type,
$searchQuery,
$onlyTitle,
DatabaseSearchFactory $databaseSearchFactory,
AlgoliaSearchFactory $algoliaSearchFactory
) {
$this->type = $type;
$this->searchQuery = $searchQuery;
$this->onlyTitle = $onlyTitle;
$this->databaseSearchFactory = $databaseSearchFactory;
$this->algoliaSearchFactory = $algoliaSearchFactory;
}
public function create()
{
if (isset($this->searchQuery)) {
return $this->algoliaSearchFactory->create($this->type, $this->onlyTitle);
} else {
return $this->databaseSearchFactory->create($this->type, $this->onlyTitle);
}
}
}
The DatabaseSearchFactory based on the $type and the onlyTitle parameters that are passed from the SearchFactory returns an object which is the final object that needs to be used in order to get the results.
class DatabaseSearchFactory
{
public function create($type, $onlyTitle)
{
if ($type == 'thread' && !$onlyTitle) {
return app(DatabaseSearchThreads::class);
} elseif ($type == 'profile_post' && !$onlyTitle) {
return app(DatabaseSearchProfilePosts::class);
} elseif ($type == 'thread' && $onlyTitle) {
return app(DatabaseSearchThread::class);
} elseif (is_null($type)) {
return app(DatabaseSearchAllPosts::class);
}
}
}
Same logic with DatabaseSearchFactory
class AlgoliaSearchFactory
{
public function create($type, $onlyTitle)
{
if ($type == 'thread' && !$onlyTitle) {
return app(Threads::class);
} elseif ($type == 'profile_post' && !$onlyTitle) {
return app(ProfilePosts::class);
} elseif (empty($type) && !$onlyTitle) {
return app(AllPosts::class);
} elseif ($onlyTitle) {
return app(Thread::class);
}
}
}
The objects that are created by AlgoliaSearchFactory have a method search which needs a $searchQuery value
interface AlgoliaSearchInterface
{
public function search($searchQuery);
}
The objects that are created by DatabaseSearchFactory have a search method that doesn't need any parameters.
interface DatabaseSearchInterface
{
public function search();
}
The class Search now takes as a parameter the final object that is produced by SearchFactory which can either implement AlgoliaSearchInterface or DatabaseSearchInterface that's why I haven't type hinted
The getResults method now has to find out the type of the search variable ( which interface it implements ) in order to either pass the $searchQuery as a parameter or not.
And that is how a controller can use the Search class to get the results.
class Search
{
protected $strategy;
public function __construct($search)
{
$this->strategy = $search;
}
public function getResults()
{
if(isset(request('q')))
{
$results = $this->strategy->search(request('q'));
}
else
{
$results = $this->strategy->search();
}
}
}
class SearchController(Search $search)
{
$results = $search->getResults();
}
According to all of #Transitive suggestions this is what I came up with. The only thing that I cannot solve is how to call search in the getResults method without having an if statement.

Dirty Methods in Rails equivalent in PHP Codeignitor

I am new to PHP & Codeigniter but it was needed some kind of implementation in PHP.
Following are dirty methods are provided in rails framework by default, here person is model object representing row inside persons table.
person.name = 'Bob'
person.changed? # => true
person.name_changed? # => true
person.name_changed?(from: nil, to: "Bob") # => true
person.name_was # => nil
person.name_change # => [nil, "Bob"]
person.name = 'Bill'
person.name_change # => [nil, "Bill"]
I am interested in to & from specially, Please suggest whether it is possible with any way.
If you would consider Laravel's elquent framework you have a great deal of that functionality already.
Laravel Eloquent update just if changes have been made
It holds an array of the "original" values in the Model, and if any of them have been changed it will commit them to the database.
They also come with a lot of events you can plug into(beforeSave, afterSave, beforeCreate, afterCreate, validation rules, etc...) and they can be extended easily. It might be the closest compatible thing I can imagine you're looking for.
This is however not codeigniter, it relies on a different framework. If you're not dead set on codeigniter you might consider switching to a framework like Laravel or OctoberCMS depending on your needs.
Edit because you're stuck with codeigniter
You might wish to use a library like this one: https://github.com/yidas/codeigniter-model
It is then very easy to extend with some custom caching mechanisms.
The code below is something you could use as a basis for your own model implementation.
It has a very rudementary logic basis, but allows you to check on the dirty status and roll back any changes made to the model.
Note this this is very rudementary, and might even contain a few errors because I have not run this code. it's more a proof of concept to help you create a model that suits your needs.
class User extends CI_Model
{
public $table = 'users';
public $primaryKey = 'id';
public $attributes;
public $original;
public $dirty = [];
public $exists = false;
function __construct()
{
parent::Model();
}
public static function find($model_id)
{
$static = new static;
$query = $static->db->query("SELECT * FROM ' . $static->getTable() . ' WHERE ' . $this->getKeyName() . ' = ?", [$model_id]);
if($result->num_rows()) {
$static->fill($query->row_array());
$static->exists = true;
}
else {
return null;
}
return $static;
}
public function getKeyName()
{
return $this->primaryKey;
}
public function getKey()
{
return $this->getAttribute($this->getKeyName());
}
public function getTable()
{
return $this->table;
}
public function fill($attributes)
{
if(is_null($this->original)) {
$this->original = $attributes;
$this->dirty = $attributes;
}
else {
foreach($attributes as $key => $value) {
if($this->original[$key] != $value) {
$this->dirty[$key] = $value;
}
}
}
$this->attributes = $attributes;
}
public function reset()
{
$this->dirty = [];
$this->attributes = $this->original;
}
public function getAttribute($attribute_name)
{
return array_key_exists($attribute_name, $this->attributes) ? $this->attributes[$attribute_name] : null;
}
public function __get($key)
{
return $this->getAttribute($key);
}
public function __set($key, $value)
{
$this->setAttribute($key, $value);
}
public function setAttribute($key, $value)
{
if(array_key_exists($key, $this->original)) {
if($this->original[$key] !== $value) {
$this->dirty[$key] = $value;
}
}
else {
$this->original[$key] = $value;
$this->dirty[$key] = $value;
}
$this->attributes[$key] = $value;
}
public function getDirty()
{
return $this->dirty;
}
public function isDirty()
{
return (bool)count($this->dirty);
}
public function save()
{
if($this->isDirty()) {
if($this->exists)
{
$this->db->where($this->getKeyName(), $this->getKey());
$this->db->update($this->getTable(), $this->getDirty());
$this->dirty = [];
$this->original = $this->attributes;
}
else
{
$this->db->insert($this->getTable(), $this->getDirty());
$this->dirty = [];
$this->original = $this->attributes;
$this->attributes[$this->getKeyName()] = $this->db->insert_id();
$this->original[$this->getKeyName()] = $this->getKey();
$this->exists = true;
}
}
}
}
if($user = User::find(1)) {
$user->name = "Johnny Bravo";
$user->save();
}

phalconphp Access controllerName that triggered a event

How could i reference the controllerName and actionName that triggered the beforeExecuteRoute from the event itself?
<?php
use Phalcon\Events\Manager as EventsManager;
//Create a events manager
$eventManager = new EventsManager();
//Listen all the application events
$eventManager->attach('micro', function($event, $app) {
if ($event->getType() == 'beforeExecuteRoute') {
//how to get controller name to handle acl stuff
}
});
From the documentation - http://docs.phalconphp.com/en/latest/api/Phalcon_Mvc_Dispatcher.html
getModuleName () - Gets the module where the controller class is
getControllerName () - Gets last dispatched controller name
getActionName () - Gets the lastest dispatched action name
Example:
<?php
use Phalcon\Events\Manager as EventsManager;
//Create a events manager
$eventManager = new EventsManager();
//Listen all the application events
$eventManager->attach('micro', function($event, $app) {
if ($event->getType() == 'beforeExecuteRoute') {
$controllerName = $app->getControllerName();
$moduleName = $app->getModuleName();
$actionName = $app->getActionName();
}
});
This way you can get the route in string format to parse it:
$router->getMatchedRoute()->getPattern();
Hope this help. I found no other way to do this.
If you don't have a dispatcher you must be getting those values from the router. I'm not very familiar with specifics of micro apps, but from looking at the docs it must be something like that.
<?php
use Phalcon\Events\Manager as EventsManager;
//Create a events manager
$eventManager = new EventsManager();
//Listen all the application events
$eventManager->attach('micro', function($event, $app) {
if ($event->getType() == 'beforeExecuteRoute') {
//how to get controller name to handle acl stuff
DI::getDefault()->get('router')->getControllerName();
DI::getDefault()->get('router')->getActionName();
// or
$app->getRouter()->getControllerName();
$app->getRouter()->getActionName();
}
});
Does this work?
I actually ran into a similar confusion, but the other answers didn't help, and Phalcon's official documentation didn't find the right way to get it, so if you're using Micro, it's hard to find an effective answer on the web.So I used my reflection to understand Phalcon's before, and finally came up with the following answer, which I hope will help you. Good luck!
<?php
$eventsManager = new \Phalcon\Events\Manager();
$eventsManager->attach(
'micro:beforeExecuteRoute',
function (\Phalcon\Events\Event $event, $app) {
$controllerName = $event->getSource()->getActiveHandler()[0]->getDefinition();
$actionName = $event->getSource()->getActiveHandler()[1];
}
);
$app = new \Phalcon\Mvc\Micro($di);
$app->setEventsManager($eventsManager);
If you want to implement the ACL with the before event, the following AclAnnotation code may help:
<?php
class AclAnnotation
{
protected $private = true;
protected $roles = [];
protected $component = [];
protected $access = [];
public function __construct($event, $app)
{
$controllerName = $event->getSource()->getActiveHandler()[0]->getDefinition();
$actionName = $event->getSource()->getActiveHandler()[1];
$reflector = $app->annotations->get($controllerName);
$annotations = $reflector->getClassAnnotations();
if (!$annotations) {
throw new ErrorException('The permission configuration is abnormal. Please check the resource permission comments.');
return;
}
if (
$annotations->has('Private')
&& ($annotation = $annotations->get('Private'))->numberArguments() > 0
) {
$this->private = $annotation->getArguments('Private')[0];
}
if (
$annotations->has('Roles')
&& ($annotation = $annotations->get('Roles'))->numberArguments() > 0
) {
$this->roles = $annotation->getArguments('Roles');
}
if (
$annotations->has('Components')
&& ($annotation = $annotations->get('Components'))->numberArguments() > 0
) {
$this->components = $annotation->getArguments('Components');
}
$annotations = $app->annotations->getMethod($controllerName, $actionName);
if (
$annotations->has('Access')
&& ($annotation = $annotations->get('Access'))->numberArguments() > 0
) {
$this->access = $annotation->getArguments('Access');
}
}
public function isPrivate()
{
return $this->private;
}
public function getRoles()
{
return $this->roles;
}
public function getComponents()
{
return $this->components;
}
public function getAccess()
{
return $this->access;
}
}
Create an event listener in index.php:
<?php
$eventsManager = new \Phalcon\Events\Manager();
$eventsManager->attach(
'micro:beforeExecuteRoute',
function (\Phalcon\Events\Event $event, $app) {
$aclAnnotation = new AclAnnotation($event, $app);
if (!$aclAnnotation->isPrivate()) {
return true;
}
$acl = $app->currentACL;
// Verify that you have access permission for the interface
if (!$acl->isAllowed($aclAnnotation->getRoles(), $aclAnnotation->getComponents(), $aclAnnotation->getAccess())) {
throw new \ErrorException('You do not have access to the resource', 40001);
return false;
}
return true;
}
);
$app = new \Phalcon\Mvc\Micro($di);
$app->setEventsManager($eventsManager);
As for the use of annotations, you can refer to here:
https://docs.phalcon.io/5.0/en/annotations

PHPUnit how to mock newly created model

How I'm stuck with writing a test for the following code. I want to mock the $userModel but how can I add this to the test?
class PC_Validate_UserEmailDoesNotExist extends Zend_Validate_Abstract
{
public function isValid($email, $context = NULL)
{
$userModel = new Application_Model_User();
$user = $userModel->findByEmailReseller($email, $context['reseller']);
if ($user == NULL) {
return TRUE;
} else {
return FALSE;
}
}
}
Update: the Solution
I did change my class to the following to get it testable, it now uses dependency injection. More information about dependency injection you van find out here
I now call the class like this:
new PC_Validate_UserEmailDoesNotExist(new Application_Model_User()
The refactored class
class PC_Validate_UserEmailDoesNotExist extends Zend_Validate_Abstract
{
protected $_userModel;
public function __construct($model)
{
$this->_userModel = $model;
}
public function isValid($email, $context = NULL)
{
if ($this->_userModel->findByEmailReseller($email, $context['reseller']) == NULL) {
return TRUE;
} else {
return FALSE;
}
}
}
The unit test
class PC_Validate_UserEmailDoesNotExistTest extends BaseControllerTestCase
{
protected $_userModelMock;
public function setUp()
{
parent::setUp();
$this->_userModelMock = $this->getMock('Application_Model_User', array('findByEmailReseller'));
}
public function testIsValid()
{
$this->_userModelMock->expects($this->once())
->method('findByEmailReseller')
->will($this->returnValue(NULL));
$validate = new PC_Validate_UserEmailDoesNotExist($this->_userModelMock);
$this->assertTrue(
$validate->isValid('jef#test.com', NULL),
'The email shouldn\'t exist'
);
}
public function testIsNotValid()
{
$userStub = new \Entities\User();
$this->_userModelMock->expects($this->once())
->method('findByEmailReseller')
->will($this->returnValue($userStub));
$validate = new PC_Validate_UserEmailDoesNotExist($this->_userModelMock);
$this->assertFalse(
$validate->isValid('jef#test.com', NULL),
'The email should exist'
);
}
}
Short answer: you cant, because you hardcoded the dependency into the method.
There is three workarounds for this:
1) Make the used classname configurable, so you can do something like:
$className = $this->userModelClassName;
$userModel = new $className();
or 2) Add a third param to the method signature that allows passing in the dependency
public function isValid($email, $context = NULL, $userModel = NULL)
{
if($userModel === NULL)
{
$userModel = new Application_Model_User();
}
// ...
}
or 3) use set_new_overload() as described in
http://sebastian-bergmann.de/archives/885-Stubbing-Hard-Coded-Dependencies.html
http://github.com/sebastianbergmann/php-test-helpers
Note: the Test-Helper extension is superseded by https://github.com/krakjoe/uopz

Categories