Jul 152012

This is meant to be a short, easy-to-follow tutorial to help you get started with Zend Framework 2.0 and add perhaps one of the most common modules, ZfcUser. By the end of this tutorial, you’ll have a simple ZF2 application with user registration and authentication capabilities.


I’m going to assume you have the following installed:

  • PHP 5.3.3+ (with pdo-sqlite or pdo-mysql)
  • A web server and knowledge of how to set up a virtual host
  • Git

If you have PHP 5.4+ you don’t even technically need a web server to try out ZF2, as you can simply use PHP’s new built-in development web server.

A note about composer

While composer is a supported way of setting up the skeleton, I am choosing to simply use git for the sake of this tutorial. In my opinion, this keeps things simpler, and easier to debug if anyone has issues following my tutorial.

Step 1: Getting the skeleton

The first step is to clone the skeleton application:

[user@workstation workspace]$ git clone --recursive https://github.com/zendframework/ZendSkeletonApplication.git

Next, set up a vhost pointing to the public directory of the newly cloned skeleton. You should at this point be able to go to http://yourvhost/ in your browser and see the skeleton app:


If you are using PHP 5.4 and would like to use the built-in development web server instead, simply open a new terminal, cd into the public directory, and run:

[user@workstation public]$ php -S 8000

With that running, you should be able to go to http://localhost:8000/ and see the skeleton application welcome page.

Step 2: Setting up a database connection

For ZfcUser, we’ll need a database to store users. For this tutorial, I’ll cover MySQL and SQLite, though ZfcUser and Zend\Db support many other RDBMs.

Create a new file, ./config/autoload/database.local.php and put the following in it:

For MySQL:

return array(
    'service_manager' => array(
        'factories' => array(
            'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory',
    'db' => array(
        'driver'    => 'pdo',
        'dsn'       => 'mysql:dbname=CHANGEME;host=localhost',
        'username'  => 'CHANGEME',
        'password'  => 'CHANGEME',

Or for SQLite:

return array(
    'service_manager' => array(
        'factories' => array(
            'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory',
    'db' => array(
        'driver'   => 'pdo',
        'dsn'      => 'sqlite:' . getcwd() . '/data/users.db',

(For more information, see my post on sharing a database connection across modules in Zend Framework 2.)

Step 3: Installing ZfcUser

Next, we’re going to install ZfcUser. First cd into the vendor directory, then run the following:

[user@workstation vendor]$ git clone https://github.com/ZF-Commons/ZfcUser.git
[user@workstation vendor]$ git clone https://github.com/ZF-Commons/ZfcBase.git

Note: ZfcBase is simply a dependency of ZfcUser — it contains some common classes which are useful for many modules.

Now enable the modules by editing ./config/application.config.php and adding them to the modules array:

return array(
    'modules' => array(
    'module_listener_options' => array(
        'config_glob_paths'    => array(
        'module_paths' => array(

Now we need to import the ZfcUser db schema. If you’re using SQLite, this is a simple one-line command you run from the root of the project:

[user@workstation skeleton]$ cat vendor/ZfcUser/data/schema.sqlite.sql | sqlite3 data/users.db

(If anyone knows how to do that in Windows, let me know in the comments!)

If you’re using MySQL, import the ./vendor/ZfcUser/data/schema.sql file into the DB you configured earlier.

That should be it! Try going to http://youvhost/user and you should be greeted with a login screen:


You now have a basic ZF2 application with simple user registration and authentication capabilities!


Check out some of the awesome ZfcUser extension modules already available:

  • BjyAuthorize – Adds ACL support for ZfcUser using Zend\Acl.
  • CdliTwoStageSignup – Adds an e-mail address verification step to the signup process.
  • CdliUserProfile – An extensible user profile system.
  • CdliAutogenUsername – Allows for auto-generated usernames upon registration.
  • ScnSocialAuth – Adds social network authentication to ZfcUser for sites like Google, Facebook, Twitter, Yahoo!, etc.
  • EdpGithub – Adds Github authentication to ZfcUser. (ScnSocialAuth will eventually support Github and this will simply remain a Github API integration module).

If you’re asking yourself “what’s next?”, take a few minutes to check out Rob Allen’s excellent ZF2 tutorial and/or come chat with us in #zftalk.2 on Freenode if you have any questions.

Update: Rafael Jaques has kindly translated this post to Portuguese.

  66 Responses to “Getting started with the ZF2 skeleton and ZfcUser”

  1. Is this tutorial working with ZF2 beta5? I actually have beta 5, and I get the following error message: PHP Fatal error: Class ZfcUser\Module contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Zend\ModuleManager\Feature\ServiceProviderInterface::getServiceConfiguration) in C:\ZZZ\vendor\ZfcUser\src\ZfcUser\Module.php on line 130. Thanks :)

    • I can confirm this tutorial has been working since around beta5. It’s possible that you cloned ZF2 and/or ZfcUser at a bad time and there just happened to be a bug. Such is life with beta software!

      Edit: Sorry for the delay, I’m still getting used to responding to blog comments!

      • hi when tried to put this zfcuser module in application using composer my doctrine module gets removed and when installing doctrine zfcuser get removed how can i solve this how can i use both of this please help

        thank you

  2. Hi , I modified user_entity_class with my own User class but this one have many other fields that default class haven’t. So how can i manage that? Do i have to install CdliUserProfile? (i see this module have a ‘field_settings’ param)


    • Your user entity must implement \ZfcUser\Entity\UserInterface and in your config/autoload/zfcuser.global.php define the user_entity_class to be your own entity rather than the default one.

  3. Hello,

    (If anyone knows how to do that in Windows, let me know in the comments!) To run linux style commands on windows, simply install cygwin (cygwin.com)


    • Thanks Hardie. I was going to recommend cygwin, but wasn’t sure if it came with a version of SQLite that would work out of the box without further instruction. Now that I’m thinking about it, you may simply be able to run type vendor/ZfcUser/data/schema.sqlite.sql | sqlite3 data/users.db, assuming you have SQLite installed on Windows.

      • I would actually suggest just using the version of bash that is installed when you install git on a windows machine. Then I would put the client you get from http://www.sqlite.org/sqlite-shell-win32-x86-3071500.zip in your path somewhere (git’s bin folder works nicely), then just run your command.

        I’ve found that git’s bash can be handy for all kinds of stuff like this… if you want just that little bit of unix flavor in your windows install without the full on invasion from cygwin.

  4. how to control acces to my app.

    redirect to registration form (using ZfcUser module ) from other modules if user is not loged

    Thank you

  5. Can’t get it to work correctly http://myhost/user gives me 404 “The requested URL could not be matched by routing.” error.

    i’ve double-checked my application.config.php and all is ok there, even tried to override default routing settings from /user to /member and still nothing.

  6. Hi,

    My goal is to authenticate the user against LDAP first and then against Db if LDAP fails. So I wrote a LDAP auth adapter and “registered” it in the AdapterChain via my own AdapterChainServiceFactory like this :

    class AdapterChainServiceFactory implements FactoryInterface
        public function createService(ServiceLocatorInterface $serviceLocator)
            $chain = new AdapterChain;
            $ldapAdapter = $serviceLocator->get('UnicaenUser\Authentication\Adapter\Ldap');
            $dbAdapter = $serviceLocator->get('ZfcUser\Authentication\Adapter\Db');
            $chain->getEventManager()->attach('authenticate', array($ldapAdapter, 'authenticate'), 2);
            $chain->getEventManager()->attach('authenticate', array($dbAdapter,   'authenticate'), 1);
            return $chain;

    Looking at ZfcUser code, I see that my LDAP auth adapter can return a Response instance in order to “return early” from the controller auth process. Here is an excerpt of the “authenticate” method in my LDAP auth adapter doing this :

        public function authenticate(AuthEvent $e)
            // Success!
            $userObject = new \UnicaenUser\Model\Ldap\PeopleAdapter($userObject);
            $storage = $this->getStorage()->read();
            $storage['identity'] = $e->getIdentity();
              ->setMessages(array('Authentication successful.'));
            // return HTTP response
            /* @var $cpm \Zend\Mvc\Controller\PluginManager */
            $cpm = $this->getServiceManager()->get('controllerpluginmanager');
            /* @var $f \Zend\Mvc\Controller\Plugin\Redirect */
            $f = $cpm->get('redirect');
            $response = $f->toRoute('home');
            return $response;

    Unfortunately, it seems to me that the user controller “authenticateAction” method returns too early after receiving the Response from the adapter : it should call $auth = $this->zfcUserAuthentication()->getAuthService()->authenticate($adapter); immediately after $result = $adapter->prepareForAuthentication($request); in order to populate auth service storage with authenticated user identity. If not, authenticated user identity provided by an other auth adapter (like my LDAP one) won’t be persisted by auth service and ZfcUser view helpers won’t be able to retrieve it (for example).

    Am i wrong ?

    Thank you. Regards. Bertrand

  7. Hello,

    This is great!, really work in mine. I’m just new user of zf2 and I have some questions. How can I make the login as a default view? is there a way to make it? please guide.

    Thanks, Eugene

    • In \module\Application\config\module.config.php Change 'controller' => 'Application\Controller\Index', to 'controller' => 'zfcuser',

  8. Hi, i was trying to set up zfcUser with Doctrine 2 ORM but i was unsuccessful. There isn’t any tut on zfcUser git. Can You give my some advice on this topic? Thanks:)

  9. Hi Evan,

    thank you at first for your tutorial. I followed it with much excitement!

    Anyhow, i am encountering a problem with the database connection. 1) I can’t register a new user via the website because this gives me the error:

    File: /opt/ZendFramework-2.0.2/library/Zend/Db/Adapter/Driver/Pdo/Statement.php:220 Message: Statement could not be executed


    0 /var/www/zf2-tutorial/vendor/ZfcBase/src/ZfcBase/Mapper/AbstractDbMapper.php(141): Zend\Db\Adapter\Driver\Pdo\Statement->execute()

    1 /var/www/zf2-tutorial/vendor/ZfcUser/src/ZfcUser/Mapper/User.php(48): ZfcBase\Mapper\AbstractDbMapper->insert(Object(ZfcUser\Entity\User), NULL, NULL)

    2 /var/www/zf2-tutorial/vendor/ZfcUser/src/ZfcUser/Service/User.php(86): ZfcUser\Mapper\User->insert(Object(ZfcUser\Entity\User))

    3 /var/www/zf2-tutorial/vendor/ZfcUser/src/ZfcUser/Controller/UserController.php(182): ZfcUser\Service\User->register(Array)

    4 /opt/ZendFramework-2.0.2/library/Zend/Mvc/Controller/AbstractActionController.php(87): ZfcUser\Controller\UserController->registerAction()

    Somehow it tries to insert the Object(ZfcUser\Entity\User, NULL, NULL into the database. Are those two NULL parameters what they should be like? The AbstractDbMapper requires it like this though. Can you give a short explanation to this?

    greetings, Robert

    • What database are you using? I had some problems with postgres not accepting null to insert into auto_increment key. I had to change the extract method in the UserHydrator, not to set the ID = null value:

      public function extract($object)
              if (!$object instanceof UserEntityInterface) {
                  throw new Exception\InvalidArgumentException('$object must be an instance of ZfcUser\Entity\UserInterface');
              /* @var $object UserInterface*/
              $data = parent::extract($object);
              unset($data['id']);                                                      //<===add this line
              //$data = $this->mapField('id', 'user_id', $data);       //<===comment or remove this line
              return $data;

  10. Evan,

    Is there an easy way to change the table names so they work better with my own naming conventions?


    • Hi,

      Trying to do the same myself, so if the is an easy and preferred way to do this, i would like to know the answer too :)

  11. Hi Evan,

    is there any possibility to clear all Sessions before a new login attempt? I am storing several informations in a Session and I want that each user starts with a clean session after login. I did not find a possibility to hook into the login process e.g. using an event

  12. Works great! Tested on 14.12.2012

  13. hi when tied to put this zfcuser module in application using composer my doctrine module gets removed and when installing doctrine zfcuser get removed how can i solve this how can i use both of this please help i am in desperate need

    thank you

  14. hi when tried to put this zfcuser module in application using composer my doctrine module gets removed and when installing doctrine zfcuser get removed how can i solve this how can i use both of this please help i am in desperate need

    thank you

  15. Hi Evan,

    i tries to understand for what cases this code are intend:

    namespace ZfcUser\Authentication\Adapter; ... class Db extends AbstractAdapter implements ServiceManagerAwareInterface { ... public function authenticate(AuthEvent $e) { if ($this->isSatisfied()) { $storage = $this->getStorage()->read(); $e->setIdentity($storage['identity']) ->setCode(AuthenticationResult::SUCCESS) ->setMessages(array('Authentication successful.')); return; } ...

    thank you

  16. Thanks for the example Evan! Is there any convenient way to override the password encryption? I have an existing DB table with MD5′ed passwords that my new Zend app needs to refer to.

    Thanks again!

  17. Hi, all is going perfectly here and ofcourse every one is sharing facts, that’s really fine, keep up writing.

  18. hi friend I need your help I going your tutorial but I have an error it says,

    Fatal error: Class ‘ZfcBase\EventManager\EventProvider’ not found in H:\www\local.zend.com\vendor\ZfcUser\src\ZfcUser\Authentication\Adapter\AdapterChain.php on line 12

    why if I am sure copy of your code correctly please help me thankyou, I hope your answers, sorry for my english

  19. Hi guys,

    I need help, zfcuser run correctly, but now, I want to verify if an email exists into my database. Is it posible with ZfcUser? How? I don’t want to request directly my db into controller.

    Thanks !!

    Bad english, I know.

  20. Question! How do you accomplish this:

    If you’re using MySQL, import the ./vendor/ZfcUser/data/schema.sql file into the DB you configured earlier.

    Thanks! :D

    • This depends on what database you are using. I use phpmyadmin and create the database that way. Once in the database, I run the SQL located in the ./vendor/ZfcUser/data/schema.sql (MySQL for me) and it creates the table.

      How ever you do it depends on what your set up is.

  21. My spouse and I stumbled over here by a different website and thought I may as well check things out.

    I like what I see so now i’m following you. Look forward to checking out your web page for a second time.

  22. It is working fine and perfect me

  23. Hi… I am new to Zend Framework 2. I have followed the above steps but I am getting the following error…..

    Not Found The requested URL /user/public/index.php was not found on this server.

    • Have you ever gotten a ZF2 basic website to work? It seems to be that you are not deploying correctly. Can you provide details on your environment?

      Like for example, I am running locally on MAC OSX. I develop with ZendStudio and deploy to a local copy of the free version of ZendServer 6.3.

  24. Hi How we can implements Zend Auth Library with Bcrypt .

    zfcuser module already use Bcrypt method for authentication but we want to use this is zend Authentication library standalone . You help will appreciate me.


  25. Challenging disks and solid-state drives are utilized for internal storage.

  26. Did you ever receive those solid foods are so overweight in table salt that the economic value of the atomic number 19 is nil. If yes, tieding has relevant manufacture golf links numbered for leveling more of the latest Gages on nuptials and Getting Married Resources. How did the exact same armed service for $50!

  27. Bluenile.com joins the ranks of more than 100 big-name retailers like Barnes & Noble.com, Office Max, BassPro Shops, and Circuit City that have made Quixtar’s Partner Store program one of the most successful affiliate programs on the Internet. Since Quixtar’s affiliate program was developed in 1999, IBOs have generated more than $300 million in sales for Partner Stores, on top of more than $3 billion in sales they’ve generated at

    christian louboutin wedding collection louboutin handbags

    These types running footwear are getting in which to only be some sort of celebration with regards to liberation are generally well as well as typically the ability to help you end up with whatever you prefer via lifestyle without the type of need with regards to having to finally create compromises or else to positively attain to help you settle towards the second most excellent every an individual with regards to the actual instances. Most likely the most valuable together with use ability being observed here combined with patterns which really are superior combined with louboutin heels that happens to be increasing along with as yet almost certainly the most important. The very kind in which it youll certainly not falter immediately subsequently, after probably sporting them designed for your favorite total day! There are numerous hunted into decide to purchase the type of superb boots or shoes with all the well-appointed taste, it would getting more appropriate there are numerous click the particular louboutin wedges sale. This is exactly the exact ideal site when you need to pay money for some people superb alternatives with regards to Christian Louboutin Sandals.

    Please visit it:http://www.christianlouboutinsaleforsale.com/best-price-for-christian-louboutin-louis-rhinestones-sneakers-blue-outlet-4304.html

  28. Good site you’ve got here.. It’s difficult to find good quality writing like yours nowadays. I seriously appreciate individuals like you! Take care!!

  29. Ahaa, its fastidious conversation regarding this post at this place at this web site, I have read all that, so now me also commenting here.

  30. I just couldn’t leave your web site before suggesting that I actually enjoyed the usual info a person provide for your guests?

    Is going to be again steadily in order to check out new posts

  31. Greate post. Keep posting such kind of information on your page. Im really impressed by it. Hey there, You have done a fantastic job. I’ll definitely digg it and for my part recommend to my friends. I am confident they will be benefited from this website.

  32. Yes! Finally something about specifics.

  33. Simply want to say your article is as astounding. The clearness in your submit is just great and that i can think you’re an expert on this subject. Fine with your permission allow me to clutch your feed to keep up to date with impending post. Thank you one million and please continue the rewarding work.

  34. You can certainly see your enthusiasm in the work you write. The arena hopes for even more passionate writers such as you who aren’t afraid to mention how they believe. All the time go after your heart.

  35. I drop a comment whenever I appreciate a post on a site or if I have something to valuable to contribute to the conversation.

    It’s caused by the fire displayed in the post I browsed.


    p>And after this article Getting started with the ZF2 skeleton and ZfcUser

  36. It’s going to be end of mine day, but before ending I am reading this enormous post to increase my know-how.

  37. Hi there to all, the contents present at this web site are genuinely awesome for people experience, well, keep up the nice work fellows.

    Feel free to visit my web blog: ルイ ヴィトン ボストンバッグ 豪華

  38. <

    p>I think that everything said was very logical. However, think on this, suppose you were to write a killer post title? I ain’t saying your content isn’t solid, but what if you added a title that makes people desire more? I mean Getting started with the ZF2 skeleton and ZfcUser

  39. Just a minor note here: the #zftalk.2 channel is now officially #zftalk

    I was looking for how to redirect nicely if a user is not authenticated. Say I have a module, that in its entirity should be closed for non authenticated users and I would use the dispatch event to deny the user based on the namespace key, how would I redirect? (probably change the event params and dispatch again, but an example would be so nice).

    Anyway, a nice post. You seem to be getting a lot of spam lately. Please take a few minutes to remove those comments, it would make the comment list so much more valueable :)

    Keep up the good work!

  40. This is often a quite useful chemistry web page.

  41. I have to thank you for the efforts you’ve put in penning this site. I am hoping to view the same high-grade blog posts from you in the future as well. In fact, your creative writing abilities has inspired me to get my own site now ;)

  42. Thank you Evan ! This is really helpful for my homework at the moment, Thanks for pushing.

  43. One pan-seared Portobello mushroom (10 cm or 4 inches in diameter) topped with julienne cucumbers and juicy cherry tomatoes. The 11 Nutritional Principles for Optimal Health and Fat Loss. What follows is my 10 truths about losing body fat.

  44. Hello Sir,

    I am getting the error.

    Fatal error: Class ‘ZfcBase\EventManager\EventProvider’ not found in D:\xampp\htdocs\myscript\zend_first\module\ZfcUser\src\ZfcUser\Authentication\Adapter\AdapterChain.php on line 13

    Thanks in advance.

  45. Hello, I think your site might be having browser compatibility issues. When I look at your website in Ie, it looks fine but when opening in Internet Explorer, it has some overlapping. I just wanted to give you a quick heads up! Other then that, wonderful blog!

  46. Great tutorial.. I got this up and running in 22 min with little ZF2 experience. Thanks for the article Evan!

    PS: you need a spam filter on your comments!

  47. This doesn’t work. I installed everything as it should, worked ok. When i point to user/login i get A ’404 error occurred’ Page not found.

  48. http://oemie.x10.mx/ cheap electronics components for sale , most style electronics components can be find on our web , even many hard to find components , buy electronics components at our web is best choose . go to our web find what you need and ask for a quote now .

 Leave a Reply



You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre user="" computer="" color="" escaped="">