Shall I create a separate Zend Application for the user backend of a web application?
My main concern is that I have to have a separate Zend_Auth on both the public website (for clients to login) and for employees to manage the site.
Since it appears to me that I can't use multiple Zend_Auth instances in one application this would be the only solution.
The next concern would be that the two Zend_Auth sessions will collide since they run on the same webspace?
Cheers
Actually, Benjamin Cremer's solution won't work, because Zend_Auth_Admin extends a Singleton implementation, so its getInstance() would yield a Zend_Auth instance, not a Zend_Auth_Admin one.
I myself was confronted with this situation, and seeing that the ZF people (at least in ZF1) see authetication as a single entry-point in an application (they could've made it so that Zend_Auth could contain multiple instances, using LSB in php etc.), made a minor modification to Benjamin Cremer's code - you must also override the getInstance():
<?php
class AdminAuth extends Zend_Auth
{
/**
* #var AdminAuth
*/
static protected $_adminInstance;
/**
* #return Zend_Auth_Storage_Interface
*/
public function getStorage()
{
if (null === $this->_storage) {
$this->setStorage(new Zend_Auth_Storage_Session('Zend_Auth_Admin'));
}
return $this->_storage;
}
/**
* Singleton pattern implementation.
*
* #return AdminAuth
*/
public static function getInstance()
{
if (null === self::$_adminInstance) {
self::$_adminInstance = new self();
}
return self::$_adminInstance;
}
}
Zend_Auth implements the Singleton Pattern so there can only exist one instance of this class.
To distinguish whether the current identity is an admin or an user you could use an isAdmin-Flag, or even better implement the Zend_Acl_Role_Interface.
If it is really required by your application to have two Auth-Sessions at the same time (one for a User, on for an Admin) you could 'copy' the Zend_Auth class by extending it and adjust the session storage.
<?php
class Zend_Auth_Admin extends Zend_Auth
{
/**
* Returns the persistent storage handler
*
* Session storage is used by default unless a different storage adapter has been set.
*
* #return Zend_Auth_Storage_Interface
*/
public function getStorage()
{
if (null === $this->_storage) {
$namespace = 'Zend_Auth_Admin'; // default is 'Zend_Auth'
/**
* #see Zend_Auth_Storage_Session
*/
require_once 'Zend/Auth/Storage/Session.php';
$this->setStorage(new Zend_Auth_Storage_Session($namespace));
}
return $this->_storage;
}
}
So you can use two distinct Auth objects for your Session handling
Zend_Auth::getInstance(); // instance for users
Zend_Auth_Admin::getInstance(); // instance for admins
Related
Before I dive into reinventing the wheel, I'd first like to check if ZF2 supports, either out-of-the-box or with a 3rd party library, this particular use case where admins log in as other user, or assume their identity.
If not, as I'm not familiar with ZF2 internal design, how would I go into implementing this, with the only constraint being that the system is already built, so I can't change components (controllers, auth services, etc) into supporting it.
My first thought would be to make a mechanism to switch the logged user information stored in the session storage, with the one whose identity I want to assume. Then, write to the session, under a different namespace, the original user information (admin) so that it can be reverted.
Going by this approach, I am expecting components like Zend\Authentication\AuthenticationService return the user whose identity I'm assuming. So, in every call I make to $this->identity()->getId() (identity being a controller plugin for AuthenticationService, that returns the User) in other controllers, the business logic will work normally.
Having said this, the questions would be:
Is there a solution already for this?
Is my approach correct in assuming that by overwriting the session storage I can assume other user ID and expect ZF2 components to work accordingly, or is there any considerations regarding ZF2 internal design/infrastructure I haven't taken in consideration that I should?
Maybe there's a better way to do this?
I think you would need to create your own AuthenticationAdaptor.
class AdminUserLoginAsUser implements \Zend\Authentication\Adapter\AdapterInterface
{
/**
* #var User
*/
private $userToLoginAs;
/**
* #var AdminUser
*/
private $adminUser;
public function __construct(User $userToLoginAs, AdminUser $adminUser)
{
$this->userToLoginAs = $userToLoginAs;
$this->adminUser = $adminUser;
}
/**
* Performs an authentication attempt
*
* #return \Zend\Authentication\Result
* #throws \Zend\Authentication\Adapter\Exception\ExceptionInterface If authentication cannot be performed
*/
public function authenticate()
{
return new \Zend\Authentication\Result(
Result::SUCCESS, $this->user, [
'You have assumed control of user.',
]
);
}
}
The above class will allow you to login as another user when used with Zend's AuthenticationService class.
You will need some way of using Zend's AuthenticationService class and I would recommend using an AuthManager that wraps around the AuthenticationService.
/**
* The AuthManager service is responsible for user's login/logout and simple access
* filtering. The access filtering feature checks whether the current visitor
* is allowed to see the given page or not.
*/
class AuthManager
{
/**
* Authentication service.
* #var \Zend\Authentication\AuthenticationService
*/
private $authService;
/**
* Session manager.
* #var Zend\Session\SessionManager
*/
private $sessionManager;
/**
* Contents of the 'access_filter' config key.
* #var array
*/
private $config;
/**
* Constructs the service.
*/
public function __construct($authService, $sessionManager, $config)
{
$this->authService = $authService;
$this->sessionManager = $sessionManager;
$this->config = $config;
}
/**
* Performs a login attempt. If $rememberMe argument is true, it forces the session
* to last for one month (otherwise the session expires on one hour).
*/
public function login($email, $password, $rememberMe)
{
// Check if user has already logged in. If so, do not allow to log in
// twice.
if ($this->authService->getIdentity()!=null) {
throw new \Exception('Already logged in');
}
// Authenticate with login/password.
$authAdapter = $this->authService->getAdapter();
$authAdapter->setEmail($email);
$authAdapter->setPassword($password);
$result = $this->authService->authenticate();
// If user wants to "remember him", we will make session to expire in
// one month. By default session expires in 1 hour (as specified in our
// config/global.php file).
if ($result->getCode()==Result::SUCCESS && $rememberMe) {
// Session cookie will expire in 1 month (30 days).
$this->sessionManager->rememberMe(60*60*24*30);
}
return $result;
}
public function loginAsUser($user)
{
// Check if user has already logged in. If so, do not allow to log in
// twice.
if ($this->authService->getIdentity() !== null) {
throw new \Exception('Not logged in.');
}
// First need to logout of current user
$this->authService->clearIdentity();
$authAdapter = $this->authService->setAdapter(new AdminUserLoginAsUser($user, $this->authService->getIdentity()));
return $this->authService->authenticate();
}
/**
* Performs user logout.
*/
public function logout()
{
// Allow to log out only when user is logged in.
if ($this->authService->getIdentity()==null) {
throw new \Exception('The user is not logged in');
}
// Remove identity from session.
$this->authService->clearIdentity();
}
}
To see how to plug it all together I would recommend looking at the following resources:
https://olegkrivtsov.github.io/using-zend-framework-3-book/html/en/User_Management__Authentication_and_Access_Filtering.html
https://github.com/olegkrivtsov/using-zf3-book-samples/tree/master/userdemo/module/User
The resources are for zf3 but I think the authenticating of users and managing authentication is very similar to zf2.
Sorry for perhaps not the most accurate title. I'm having trouble figuring out what this would even be called.
I'm somewhat new to OOP with php, as most of my time is spent with procedural programming. In an effort to better learn these concepts, I'm taking an existing application and rewriting portions of it using OOP. The below example is how I set up the base class, and then I extended the base class into several smaller classes for easier maintainability. Below, you can see how I extended the base class to create a user class. Please note, that my class definitions are in separate files, but I have a working autoloader that automatically registers them
class EventScheduler{
function __construct(){
// set up database connections here
}
}
class User extends EventScheduler{
private function getUserProfile($username){
// return an array here representing
// details of passed username from database
}
public function getUserType($username){
return $this->getUserProfile($username)['user_type'];
}
}
What I'd like to be able to do is reference the User class from inside the base class, like this:
$eventApp = new EventScheduler();
$userType = $eventApp->User->getUserProfile("nameHere");
What I'm currently doing is this:
$eventApp = new EventScheduler();
//do some stuff here using the base class
$users = new User();
$userType = $users->getUserProfile("nameHere");
But as I add more child classes, I don't want to have to instantiate every extended class like I did there, I'd like to have them all grouped under the base object, rather than having each extended class in it's own object.
What you want to do is
$users = new User();
//do some stuff here using the base class
// ie $users->someFunctionFromEventScheduler();
$userType = $users->getUserProfile("nameHere");
This is a good reason to start reading up on common design patterns in OOP. There are plenty of good resources for this online and a quick google search will yield plenty of results and examples mostly hosted on github.
The specific pattern I believe you are looking for is the mediator pattern (Mediator pattern example in PHP). Rather than extending a class, as you are doing in your example, the mediator pattern is useful when you want an instance of a class that has access to many other classes that can all communicate with each other through one base class. The premise is that 'One good friend is better than many acquaintances.`.
An example for you: (interfaces are incredibly useful here as they define specific characteristics that are required in each of the classes)
/**
* Interface Mediator
*/
interface Mediator {
/**
* #param string $key
* #param Mediated $mediated
* #return void
*/
public function attach($key, Mediated $mediated);
/**
* #param $key
* #return Mediated
*/
public function getAttached($key);
}
/**
* Interface Mediated
*/
interface Mediated {
/**
* #param Mediator $mediator
* #return void
*/
public function setMediator(Mediator $mediator);
/**
* #return Mediator
*/
public function getMediator();
}
Now we need a base mediator class, I'll use your event scheduler example. Notice that it implements the Mediator interface and must, as a result implement the methods the interface requires.
/**
* Class EventScheduler
*/
class EventScheduler implements Mediator {
/**
* A collection of mediated instances.
*
* #var array
*/
protected $mediated = [];
/**
* #param string $key
* #param Mediated $mediated
* #return void
*/
public function attach($key, Mediated $mediated)
{
// So upon attaching a mediated instance we can build the two
// way binding in one place using the key as the identifier.
// First we set $this on the mediated instance.
$mediated->setMediator($this);
// Then we add this instance to our mediated array inside this instance
$this->mediated[$key] = $mediated;
}
/**
* #param $key
* #return Mediated
*/
public function getAttached($key)
{
return $this->mediated[$key];
}
}
Now we can setup a mediated instance. That can be attached to the mediator. Notice it implements the Mediated interface.
/**
* Class User
*/
class User implements Mediated {
/**
* #var Mediator
*/
protected $mediator;
/**
* #param Mediator $mediator
* #return void
*/
public function setMediator(Mediator $mediator)
{
$this->mediator = $mediator;
}
/**
* #return Mediator
*/
public function getMediator()
{
return $this->mediator;
}
}
You can create as many of the mediated instances as you like and attach them to the Mediator instance. Bear in mind that this isn't a specific mediator, in that many instances can be attached, in most cases it's better to be explicit with which classes can be attached rather than allowing dynamic registration by a key.
$scheduler = new EventScheduler();
$user = new User();
$scheduler->attach('user', $user);
// Now we know that we can get the mediator from the User class
$user->getMediator();
// We can also get the User from the mediator itself.
$scheduler->getAttached('user');
As you attach more classes you'll notice that each of them can use their mediator to get instances of the other attached classes, this is where the concept of one good friend comes from.
This is just an example and not fully featured for brevity, but should give you a good idea why using common design patterns will help you enormously and is a very good place to start when learning OOP if you want to develop good habits.
I'm using symfony2 and in my application for student accommodation, i'm creating dynamic subdomains for each university, i'm configuring the virtual host with the wildcard subdomain entry so any subdomain would be valid.
How do i check if a subdomain is registered and belongs to a university, and how do i differentiate it from a randomly typed non user registered subdomain efficiently?
if i go with a database query then every random access from curious users would result in a lot of db queries, and the use of hosts file would be too slow (not the best practice)
Please suggest an efficient way to do this using php or symfony or any other techniques you guys know of
(additional info) there will be a 'free trial' option so that would result in a lot of subdomains, as anyone and every one would start a free trial, a very good example of what i'm trying to achieve woudld be this StudyStays
-thanks
You could cache all each of the subdomain requests (using something like Doctrine cache as a wrapper for whatever caching system you use) so that each subsequent check would only need to check the cache rather than the database.
Also when adding/removing/updating your subdomain object you could update the cache value to keep it all up to date.
app/config/config.yml
Set your provider for Doctrine Cache Bundle, for more info see the docs (you would need to add the Doctrine Cache Bundle to your vendors and AppKernel, obviously).
doctrine_cache:
providers:
acme_subdomain:
type: filesystem # apc, array, redis, etc
namespace: acme_subdomain
Acme\YourBundle\Registry\SubdomainRegistry
Create a registry that can check for the subdomain state and update the cache when required. This example stores the state as a string rather than a boolean as (as far as I know) a "not found" key will return a false rather than a null.
use Doctrine\Common\Cache\Cache;
use Doctrine\Common\Persistence\ObjectManager;
class SubdomainRegistry implements SubdomainRegistry
{
const REGISTERED = 'registered';
const UNREGISTERED = 'unregistered';
/**
* #param ObjectManager
*/
protected $manager;
/**
* #param Cache
*/
protected $cache;
public function __construct(ObjectManager $manager, Cache $cache)
{
$this->manager = $manager;
$this->cache = $cache;
}
/**
* #param string $subdomain
* #return boolean
*/
public function isSubdomainRegistered($subdomain)
{
// If subdomain is not in cache update cache
if (!$this->cache->has($subdomain)) {
$this->updateRegistry($subdomain);
}
return self::REGISTERED === $this->cache->get($subdomain);
}
/**
* #param string $subdomain
* #return boolean
*/
public function registerSubdomain($subdomain)
{
$this->cache->set($subdomain, self::REGISTERED);
return $this;
}
/**
* #param string $subdomain
* #return boolean
*/
public function unregisterSubdomain($subdomain)
{
$this->cache->set($subdomain, self::UNREGISTERED);
return $this;
}
/**
* #param string $subdomain
* #return null
*/
private function updateRegistry($subdomain)
{
$object = $this->manager->findOneBy(array('subdomain' => $subdomain);
// $object->isActive() assume you are storing all subdomains after cancelling
// and setting it to inactive. You could put your own real logic here
if (null === $object || !$object->isActive()) {
$this->unregisterSubdomain($subdomain);
return null;
}
$this->registerSubdomain($subdomain);
}
Then when you are registering or unregistering your subdomain you could add a call to the registry in the method.
For example...
$subdomain = new Subdomain();
// Subdomain as a property of subdomain seems weird to me
// but as I can't immediately think of anything better I'll go with it
$subdomain->setSubdomain('acme');
// .. subdomain details
$this->manager->persist($subdomain);
$this->manager->flush();
$this->registry->registerSubdomain($subdomain->getSubdomain());
I'm developing a quite complex logistics management system which will keep growing into several other ERP related modules. Therefore, I am trying to have as much of the SRP and Open/Close Principles in place for ease of extension and domain based management.
Therefore, I decided to use Laravel and the following pattern (not sure if this has a name or not):
I will use the PRODUCT object for my example.
An object/entity/domain has a Class
class ProductService {}
This class has a Service Provider which is included in the providers array and is also autoloaded:
ProductServiceServiceProvider
The service provider instantiate (makes) the ProductRepository which is an interface.
The interface currently has a MySQL (and some Eloquent) called EloquentProductRepository implementation(s) and a ProductRepositoryServiceProvider binds the implementation which is also loaded and in the providers array.
Now a product has many different attributes and relationships with other domains and because the other domains (or entities) need to be fully detached and again abiding with the above principle (SRP etc..) I decided to also have the same structure for them as i do for the product...I know some might think that this is too much but we need to have the system very extendable and to be honest I like to be organised and have a uniform pattern (it doesn't take that much more time and saves me a lot later).
My question is this. The ProductService which handles all the business logic of the Product and makes the "Product" what it is will have several dependencies injected on creation of it's instance through the constructor.
This is what it has at the moment:
namespace Ecommerce\Services\Product;
use Ecommerce\Repositories\Product\ProductRepository;
use Ecommerce\Services\ShopEntity\ShopEntityDescriptionService;
use Content\Services\Entity\EntitySeoService;
use Content\Services\Entity\EntitySlugService;
use Ecommerce\Services\Tax\TaxService;
use Ecommerce\Services\Product\ProductAttributeService;
use Ecommerce\Services\Product\ProductCustomAttributeService;
use Ecommerce\Services\Product\ProductVolumeDiscountService;
use Ecommerce\Services\Product\ProductWeightAttributeService;
use Ecommerce\Services\Product\ProductDimensionAttributeService;
/**
* Class ProductService
* #package Ecommerce\Services\Product
*/
class ProductService {
/**
* #var ProductRepository
*/
protected $productRepo;
/**
* #var ShopEntityDescriptionService
*/
protected $entityDescription;
/**
* #var EntitySeoService
*/
protected $entitySeo;
/**
* #var EntitySlugService
*/
protected $entitySlug;
/**
* #var TaxService
*/
protected $tax;
/**
* #var ProductAttributeService
*/
protected $attribute;
/**
* #var ProductCustomAttributeService
*/
protected $customAttribute;
/**
* #var ProductVolumeDiscountService
*/
protected $volumeDiscount;
/**
* #var ProductDimensionAttributeService
*/
protected $dimension;
/**
* #var ProductWeightAttributeService
*/
protected $weight;
/**
* #var int
*/
protected $entityType = 3;
public function __construct(ProductRepository $productRepo, ShopEntityDescriptionService $entityDescription, EntitySeoService $entitySeo, EntitySlugService $entitySlug, TaxService $tax, ProductAttributeService $attribute, ProductCustomAttributeService $customAttribute, ProductVolumeDiscountService $volumeDiscount, ProductDimensionAttributeService $dimension, ProductWeightAttributeService $weight)
{
$this->productRepo = $productRepo;
$this->entityDescription = $entityDescription;
$this->entitySeo = $entitySeo;
$this->entitySlug = $entitySlug;
$this->tax = $tax;
$this->attribute = $attribute;
$this->customAttribute = $customAttribute;
$this->volumeDiscount = $volumeDiscount;
$this->dimension = $dimension;
$this->weight = $weight;
}
`
Is it bad practice to have as much arguments passed to the constructor in PHP (please ignore the long names of the services as these might change when the ERP namespaces have been decided upon)?
As answered by Ben below, in this case it is not. My question was not related to OOP but more to performance etc.. The reason being is that this particular class ProductService is what web deves would do with a controller, i.e. they would probably (and against principles) add all DB relationships in one ProductController which handles repository services (db etc..) and attaches relationships and then it suddenly becomes your business logic.
In my application (and I see most applications this way), the web layer is just another layer. MVC takes care of the web layer and sometimes other Apis too but I will not have any logic except related to views and JS frameworks in my MVC. All of this is in my software.
In conclusion: I know that this is a very SOLID design, the dependencies are injected and they really are dependencies (i.e. a product must have tax and a product does have weight etc..) and they can easily be swapped with other classes thanks to the interfaces and ServiceProviders. Now thanks to the answers, I also know that it is Okay to inject so many dependencies in constructor.
I will eventually write an article about the design patterns which I use and why I use them in different scenarios so follow me if you're interested in such.
Thanks everyone
Generally, no, It's not a bad practice, in most cases. But in your case, as said in the comments by #zerkms, it looks like your class is depending on a lot of dependencies, and you should look into it, and think on how to minimize the dependencies, but if you're actually using them and they should be there, I don't see a problem at all.
However, you should be using a Dependency Injection Container (DIC).
An dependency injection container, is basically a tool which creates the class by the namespace you provide, and it creates the instance including all the dependencies. You can also share objects, so it won't create a new instance of it while creating the dependencies.
I suggest you to ue Auryn DIC
Usage:
$provider = new Provider();
$class = $provider->make("My\\App\MyClass");
What happens here is this:
namespace My\App;
use Dependencies\DependencyOne,
Dependencies\DependencyTwo,
Dependencies\DependencyThree;
class MyClass {
public function __construct(DependencyOne $one, Dependency $two, DependencyThree $three) {
// .....
}
}
Basically, the Provider#make(namespace) creates an instance of the given namespace, and creates the needed instances of it's consturctor's parameters and all parameter's constructors parameters and so on.
I need to cache some info about a user who is logged in (such as security groups, name, username, etc.)
Currently I have a separate class to achieve just this, lets call it CurrentUserHelper. Given a user object, it will cache the appropriate data and save store info in the $_SESSION variable.
The issue is I'm finding a bunch of classes relying just on CurrentUserHelper because they only need a couple of common fields. In fact, most of the functions have the same name as my User class. There's a couple of functions in CurrentUserHelper, such as getSecurityGroupsNames(), that contains a cache of all security group names, but there is no reason this function name could not be in the user class also.
Instead, should I create a CachedUser class and pass this around? This class can extend User, but then I can override getName(), getSecurityGroups(), etc, and returned the cached data, and not preform db requests to get the data.
The downside of passing around a CachedUser object is that this kind of hides the fact the data isn't really up to date if a constructor/function is accepting a type User. I also need to find way to handle merging the entity with Doctrine 2, and making sure entities associating themselves with a CachedUser won't break. If I decide to cache some temporary data (such as # of page views since logged in), this property shouldn't be part of the User class, it's more about the current user's session.
If I continue using the CurrentUserHelper class, maybe I should create an interface and have both CurrentUserHelper and User for the common functionality the two classes would share?
Preface: Extension isn't the best way for these sorts of things.. I'm sure you've heard composition over inheritance shouted at you over and over again. In fact, you can even gain inheritance without using extends!
This sounds like a good use-case for the decorator pattern. Basically, you wrap your existing object with another one that implements the same interface, so it has the same methods as the inner object, but this object's method adds the extra stuff around the method call to the inner object, like caching, for example.
Imagine you have a UserRepository:
/**
* Represents an object capable of providing a user from persistence
*/
interface UserProvider
{
/**
* #param int $id
*
* #return User
*/
public function findUser($id);
}
/**
* Our object that already does the stuff we want to do, without caching
*/
class UserRepository implements UserProvider
{
/**
* #var DbAbstraction
*/
protected $db;
/**
* #var UserMapper
*/
protected $mapper;
/**
* #param DbAbstraction $db
* #param UserMapper $mapper
*/
public function __construct(DbAbstraction $db, UserMapper $mapper)
{
$this->db = $db;
$this->mapper = $mapper;
}
/**
* {#inheritDoc}
*/
public function findUser($id)
{
$data = $this->db->find(['id' => $id]);
/** Data mapper pattern - map data to the User object **/
$user = $this->mapper->map($data);
return $user;
}
}
The above is a really simple example. It'll retrieve the user data from it's persistence (a database, filesystem, whatever), map that data to an actual User object, then return it.
Great, so now we want to add caching. Where should we do this, within the UserRepository? No, because it's not the responsibility of the repository to perform caching, even if you Dependency Inject a caching object (or even a logger!).. this is where the decorator pattern would come in useful.
/**
* Decorates the UserRepository to provide caching
*/
class CachedUserRepository implements UserProvider
{
/**
* #var UserRepository
*/
protected $repo;
/**
* #var CachingImpl
*/
protected $cache;
/**
* #param UserRepository $repo
*/
public function __construct(UserRepository $repo, CachingImpl $cache)
{
$this->repo = $repo;
$this->cache = $cache;
}
/**
* {#inheritDoc}
*
* So, because this class also implements UserProvider, it has to
* have the same method in the interface. We FORWARD the call to
* the ACTUAL user provider, but put caching AROUND it...
*/
public function findUser($id)
{
/** Has this been cached? **/
if ($this->cache->hasKey($id))
{
/**
* Returns your user object, or maps data or whatever
*/
return $this->cache->get($id);
}
/** Hasn't been cached, forward the call to our user repository **/
$user = $this->repo->findUser($id);
/** Store the user in the cache for next time **/
$this->cache->add($id, $user);
return $user;
}
}
Very simply, we've wrapped the original object and method call with some additional caching functionality. The cool thing about this is that, not only can you switch out this cached version for the non-cached version at any time (because they both rely on the same interface), but you can remove the caching completely as a result, just by changing how you instantiate this object (you could take a look at the factory pattern for that, and even decide which factory (abstract factory?) depending on a configuration variable).