I started writing a site and encountered this problem. I created RouteController, which serves for address bar parsing, and created an inherited class to have a binding:
protected $controller;
protected $inputMethod;
protected $outputMethod;
protected $parameters;
Well, generally speaking, the base class, which is inherited by RouteController. But when I just added "extends BaseController" to "class RouteController", it immediately started crashing and outputted the following error:
PHP Fatal error: Uncaught Error: Class 'Core\Base\Controllers\BaseController' not found in /var/www/Artemiys_Shop/Core/Base/Controllers/RouteController. php:10 Stack trace: #0 {main} thrown in /var/www/Artemiys_Shop/Core/Base/Controllers/RouteController.php on line 10
Does anyone know where I might have made a mistake or done something wrong? I've never had a problem with this before.
RouteController.php
<?php
namespace Core\Base\Controllers;
use Core\Base\Exceptions\RouteException;
use Core\Base\Settings\Settings;
use Core\Base\Settings\ShopSettings;
class RouteController extends BaseController
{
static private $_instance;
protected $routes;
private function _clone()
{
}
static public function getInstance()
{
if(self::$_instance instanceof self){
return self::$_instance;
}
return self::$_instance = new self;
}
/**
* #throws RouteException
*/
private function _construct()
{
$address_str = $_SERVER['REQUEST_URI'];
if(strpos($address_str, '/') === strlen($address_str) - 1 && strpos($address_str, '/') !== 0){
$this->redirect(rtrim($address_str, '/'), 301);
}
$path = substr($_SERVER['PHP_SELF'], 0, strpos($_SERVER['PHP_SELF'], 'index.php'));
if($path === PATH){
$this->routes = Settings::get('routes');
if(!$this->routes) throw new RouteException('Website is under maintenance');
$url = explode('/', substr($address_str, strlen(PATH)));
if($url[0] && $url[0] === $this->routes['admin']['alias']){
array_shift($url);
if($url[0] && is_dir($_SERVER['DOCUMENT_ROOT'] . PATH . $this->routes['plugins']['path'] . $url[0])){
$plugin = array_shift($url);
$pluginSettings = $this->routes['settings']['path'] . ucfirst($plugin . 'Settings');
if(file_exists($_SERVER['DOCUMENT_ROOT'] . PATH . $pluginSettings . '.php')){
$pluginSettings = str_replace('/', '\\', $pluginSettings);
$this->routes = $pluginSettings::get('routes');
}
$dir = $this->routes['plugins']['dir'] ? '/' . $this->routes['plugins']['dir'] . '/' : '/';
$dir = str_replace('//', '/', $dir);
$this->controller = $this->routes['plugins']['path'] . $plugin . $dir;
$hrUrl = $this->routes['plugins']['hrUrl'];
$route = 'plugins';
}else{
$this->controller = $this->routes['admin']['path'];
$hrUrl = $this->routes['admin']['hrUrl'];
$route = 'admin';
}
}else{
$hrUrl = $this->routes['user']['hrUrl'];
$this->controller = $this->routes['user']['path'];
$route = 'user';
}
$this->createRoute($route, $url);
if($url[1]){
$count = count($url);
$key = '';
if(!$hrUrl){
$i = 1;
}else{
$this->parameters['alias'] = $url[1];
$i = 2;
}
for( ; $i < $count; $i++){
if(!$key){
$key = $url[$i];
$this->parameters[$key] = '';
}else{
$this->parameters[$key] = $url[$i];
$key = '';
}
}
}
exit();
}else{
try{
throw new \Exception('Incorrect directory');
}
catch (\Exception $e){
exit($e->getMessage());
}
}
}
private function createRoute($var, $arr){
$route = [];
if(!empty($arr[0])){
if($this->routes[$var]['routes'][$arr[0]]){
$route = explode('/', $this->routes[$var]['routes'][$arr[0]]);
$this->controller .= ucfirst($route[0]. 'Controller');
}else{
$this->controller .= ucfirst($arr[0]. 'Controller');
}
}else{
$this->controller .= $this->routes['default']['controller'];
}
$this->inputMethod = $route[1] ? $route[1] : $this->routes['default']['inputMethod'];
$this->outputMethod = $route[2] ? $route[2] : $this->routes['default']['outputMethod'];
return;
}
}
BaseController.php
<?php
namespace Core\Base\Controllers;
use Core\Base\Exceptions\RouteException;
abstract class BaseController
{
protected $controller;
protected $inputMethod;
protected $outputMethod;
protected $parameters;
/**
* #throws RouteException
*/
public function route() {
$controller = str_replace('/', '\\', $this->controller);
try {
$object = new \ReflectionMethod($controller, 'request');
$args = [
'parameters' => $this->parameters,
'inputMethod' => $this->inputMethod,
'outputMethod' => $this->outputMethod
];
$object->invoke(new $controller, $args);
}
catch (\ReflectionException $e){
throw new RouteException($e->getMessage());
}
}
public function request($args){
}
}
Index.php
<?php
define ('VG_ACCESS', true);
header('Content-Type:text/html;charset=utf-8');
session_start();
require_once 'config.php';
require_once 'Core/Base/Settings/internal_settings.php';
use Core\Base\Exceptions\RouteException;
use Core\Base\Controllers\RouteController;
try{
RouteController::getInstance()->route();
}
catch (RouteException $e) {
exit($e->getMessage());
}
Setting.php
<?php
namespace Core\Base\Settings;
class Settings
{
static private $_instance;
private array $routes = [
'admin' => [
'alias' => 'admin',
'path' => 'Core/Admin/Controllers/',
'hrUrl' => false
],
'settings' => [
'path' => 'Core/Base/Settings/'
],
'plugins' => [
'path' => 'Core/Plugins/',
'hrUrl' => false,
'dir' => false
],
'user' => [
'path' => 'Core/User/Controllers/',
'hrUrl' => true,
'routes' => [
]
],
'default' => [
'controller' => 'IndexController',
'inputMethod' => 'inputData',
'outputMethod' => 'outputData'
]
];
And i would like to know, why is this error showing up?
Related
I'm trying to test a simple controller that authenticates a user using the LdapAdapter and using the 'ldap' array from the configuration of the Application, but phpunit is returning the following error:
Fatal error: Uncaught Error: Call to a member function get() on null in /var/www/html/app/module/Auth/src/Auth/Controller/AuthController.php:53
Stack trace:
#0 /var/www/html/app/module/Auth/test/AuthTest/Controller/AuthControllerTest.php(37): Auth\Controller\AuthController->authenticate('myuser', 'mypassword')
#1 [internal function]: AuthTest\Controller\AlbumControllerTest->testLoginAction()
#2 /var/www/html/vendor/phpunit/phpunit/src/Framework/TestCase.php(863): ReflectionMethod->invokeArgs(Object(AuthTest\Controller\AlbumControllerTest), Array)
#3 /var/www/html/vendor/phpunit/phpunit/src/Framework/TestCase.php(741): PHPUnit_Framework_TestCase->runTest()
#4 /var/www/html/vendor/phpunit/phpunit/src/Framework/TestResult.php(608): PHPUnit_Framework_TestCase->runBare()
#5 /var/www/html/vendor/phpunit/phpunit/src/Framework/TestCase.php(697): PHPUnit_Framework_TestResult->run(Object(AuthTest\Controller\AlbumControllerTest))
#6 /var/www/html/vendor/phpunit/phpunit/src/Framework/TestSuite.php(733): PHPUnit_Framework_TestCase- in /var/www/html/app/module/Auth/src/Auth/Controller/AuthController.php on line 53
My Controller is the following:
<?php
namespace Auth\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Authentication\Adapter\Ldap as AuthAdapter;
use Zend\Authentication\AuthenticationService;
use Zend\Authentication\Result;
use Auth\Form\AuthForm;
use Auth\Model\Auth;
class AuthController extends AbstractActionController
{
public function loginAction()
{
$form = new AuthForm();
$request = $this->getRequest();
if ($request->isPost()) {
$auth = new Auth();
$form->setInputFilter($auth->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid()){
$auth->exchangeArray($form->getData());
$values = $form->getData();
$result = $this->authenticate($values['username'], $values['password']);
switch($result->getCode()) {
case Result::SUCCESS:
return $this->redirect()->toRoute('home');
break;
case Result::FAILURE:
break;
}
}
}
return array('form' => $form);
}
public function authenticate($username, $password){
$options = $this->getServiceLocator()->get('Config');
$authAdapter = new AuthAdapter($options['ldap'],
'username',
'password');
$authAdapter
->setIdentity($username)
->setCredential($password);
$auth = new AuthenticationService();
$result = $auth->authenticate($authAdapter);
return $result;
}
private function debug($var){
echo '<pre>';
var_dump($var);
echo '</pre>';
exit();
}
}
The TestCase:
namespace AuthTest\Controller;
use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase;
use Zend\Authentication\AuthenticationService;
use Auth\Controller\AuthController;
class AuthControllerTest extends AbstractHttpControllerTestCase
{
protected $traceError = true;
public function setUp()
{
$this->setApplicationConfig(
include '/var/www/html/app/config/application.config.php'
);
parent::setUp();
}
public function testLoginAction()
{
#Basic Access to the page
$this->dispatch('/login');
$this->assertResponseStatusCode(200);
$data = array(
'identity' => 'myuser',
'credential' => 'mypassword',
);
$auth = new AuthController();
$auth->authenticate($data['identity'], $data['credential']);
$identity = new AuthenticationService();
$this->assertEquals($data['identity'], $identity->getIdentity());
}
}
PHPUnittest's BootStrap:
<?php
namespace AuthTest;
use Zend\Loader\AutoloaderFactory;
use Zend\Mvc\Service\ServiceManagerConfig;
use Zend\ServiceManager\ServiceManager;
use RuntimeException;
error_reporting(E_ALL | E_STRICT);
chdir(__DIR__);
/**
* Test bootstrap, for setting up autoloading
*/
class Bootstrap
{
protected static $serviceManager;
public static function init()
{
$zf2ModulePaths = array(dirname(dirname(__DIR__)));
if (($path = static::findParentPath('vendor'))) {
$zf2ModulePaths[] = $path;
}
if (($path = static::findParentPath('module')) !== $zf2ModulePaths[0]) {
$zf2ModulePaths[] = $path;
}
static::initAutoloader();
// use ModuleManager to load this module and it's dependencies
$config = array(
'module_listener_options' => array(
'module_paths' => $zf2ModulePaths,
),
'modules' => array(
'Auth'
)
);
$serviceManager = new ServiceManager(new ServiceManagerConfig());
$serviceManager->setService('ApplicationConfig', $config);
$serviceManager->get('ModuleManager')->loadModules();
static::$serviceManager = $serviceManager;
}
public static function chroot()
{
$rootPath = dirname(static::findParentPath('module'));
chdir($rootPath);
}
public static function getServiceManager()
{
return static::$serviceManager;
}
protected static function initAutoloader()
{
$vendorPath = static::findParentPath('vendor');
if (file_exists($vendorPath.'/autoload.php')) {
include $vendorPath.'/autoload.php';
}
if (! class_exists('Zend\Loader\AutoloaderFactory')) {
throw new RuntimeException(
'Unable to load ZF2. Run `php composer.phar install`'
);
}
AutoloaderFactory::factory(array(
'Zend\Loader\StandardAutoloader' => array(
'autoregister_zf' => true,
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/' . __NAMESPACE__,
),
),
));
}
protected static function findParentPath($path)
{
$dir = __DIR__;
$previousDir = '.';
while (!is_dir($dir . '/' . $path)) {
$dir = dirname($dir);
if ($previousDir === $dir) {
return false;
}
$previousDir = $dir;
}
return $dir . '/' . $path;
}
}
Bootstrap::init();
Bootstrap::chroot();
In the functional tests it works as expected, but on php unittests the error occurs in the line 53 '$options = $this->getServiceLocator()->get('Config');'.
So, how can use or set ServiceLocator to work with phpunittests?
After so much struggling on this doubt and other tests which a controller that uses an ServiceLocator, I figure that the correct way to test a controllers Action is to Mock the object or/and his methods.
I don't feel totally comfortable with this solution, but for now, this is what it is.
An other feel is that mocking an behaviour of the code is something like that breaks the DRY principle: instead of use the real code I just create a mock that half behaves like it :S
Anyway, the following code does the job for me:
$authControllerMock = $this->getMockBuilder('Auth\Controller\AuthController')
->disableOriginalConstructor()
->getMock();
$authControllerMock->expects($this->any())
->method('authenticate')
->will($this->returnValue(true);
$serviceManager = $this->getApplicationServiceLocator();
$serviceManager->setAllowOverride(true);
$serviceManager->setService('Auth\Controller\Auth', $authControllerMock);
$this->dispatch('/usermgr');
self::assertMatchedRouteName('usermgr');
self::assertControllerClass('AuthController');
self::assertControllerName('auth\controller\auth');
self::assertResponseStatusCode('200');
Hello I'm trying to make a simple configuration file for my app
my project folder is:
inside folder 'app'
-Config.php
inside root directory:
-index.php
-config.php
this is how config.php looks like:
<?php
return [
'db' => [
'hosts' => [
'local' => 'localhost',
'externo' => '1.1.1.1',
],
'name' => 'db-stats',
'user' => 'root',
'password' => 'root'
],
'mail' => [
'host' => 'smtp.gmail.com'
]
];
?>
Config.php is:
<?php
namespace Project\Helpers;
class Config
{
protected $data;
protected $default = null;
public function load($file){
$this->$data = require $file;
}
public function get($key, $default = null){
$this->$default = $default;
$segments = explode('.', $key);
$data = $this->$data;
foreach ($segments as $segment) {
if(isset($data[$segment])){
$data = $data[$segment];
}else{
$data = $this->$default;
break;
}
}
return $data;
}
public function exists($key){
return $this->get($key) !== $this->$default;
}
}
?>
and finally index.php:
<?php
use Project\Helpers\Config;
require 'app/Config.php';
$config = new Config;
$config->load('config.php');
echo $config->get('db.hosts.local');
?>
the thing is I'm getting this 2 errors when I run the page:
Notice: Undefined variable: data in
C:\xampp\htdocs\probar\app\Config.php on line 11
Fatal error: Cannot access empty property in
C:\xampp\htdocs\probar\app\Config.php on line 11
please help me what's is wrong with this???
$this->data = require $file; not $this->$data = require $file;.
And $this->default = $default; instead of $this->$default = $default;
Otherwise those would be variable variables.
<?php
namespace Project\Helpers;
class Config
{
protected $data;
protected $default = null;
public function load($file){
$this->data = require $file;
}
public function get($key, $default = null){
$this->default = $default;
$segments = explode('.', $key);
$data = $this->data;
foreach ($segments as $segment) {
if(isset($data[$segment])){
$data = $data[$segment];
}else{
$data = $this->default;
break;
}
}
return $data;
}
public function exists($key){
return $this->get($key) !== $this->default;
}
}
You have a synthax error in the class constructor. In PHP, when you access a member attribute with the -> operator, you don't have to use the $ modifier.
The correct code looks like this:
<?php
namespace Project\Helpers;
class Config
{
protected $data;
protected $default = null;
public function load($file){
$this->data = require $file;
}
public function get($key, $default = null){
$this->default = $default;
$segments = explode('.', $key);
$data = $this->data;
foreach ($segments as $segment) {
if(isset($data[$segment])){
$data = $data[$segment];
}else{
$data = $this->default;
break;
}
}
return $data;
}
public function exists($key){
return $this->get($key) !== $this->default;
}
}
I'm currently developing a router for one of my projects and I need to do the following:
For example, imagine we have this array of set routes:
$routes = [
'blog/posts' => 'Path/To/Module/Blog#posts',
'blog/view/{params} => 'Path/To/Module/Blog#view',
'api/blog/create/{params}' => 'Path/To/Module/API/Blog#create'
];
and then if we pass this URL through: http://localhost/blog/posts it will dispatch the blog/posts route - that's fine.
Now, when it comes to the routes that require parameters, all I need is a method of implementing the ability to pass the parameters through, (i.e. http://localhost/blog/posts/param1/param2/param3 and the ability to prepend api to create http://localhost/api/blog/create/ to target API calls but I'm stumped.
Here's something basic, currently routes can have a pattern, and if the application paths start with that pattern then it's a match. The rest of the path is turned into params.
<?php
class Route
{
public $name;
public $pattern;
public $class;
public $method;
public $params;
}
class Router
{
public $routes;
public function __construct(array $routes)
{
$this->routes = $routes;
}
public function resolve($app_path)
{
$matched = false;
foreach($this->routes as $route) {
if(strpos($app_path, $route->pattern) === 0) {
$matched = true;
break;
}
}
if(! $matched) throw new Exception('Could not match route.');
$param_str = str_replace($route->pattern, '', $app_path);
$params = explode('/', trim($param_str, '/'));
$params = array_filter($params);
$match = clone($route);
$match->params = $params;
return $match;
}
}
class Controller
{
public function action()
{
var_dump(func_get_args());
}
}
$route = new Route;
$route->name = 'blog-posts';
$route->pattern = '/blog/posts/';
$route->class = 'Controller';
$route->method = 'action';
$router = new Router(array($route));
$match = $router->resolve('/blog/posts/foo/bar');
// Dispatch
if($match) {
call_user_func_array(array(new $match->class, $match->method), $match->params);
}
Output:
array (size=2)
0 => string 'foo' (length=3)
1 => string 'bar' (length=3)
This is a basic version - simply just a concept version to show functionality and I wouldn't suggest using it in a production environment.
$routes = [
'blog/view' => 'Example#index',
'api/forum/create' => 'other.php'
];
$url = explode('/', $_GET['url']);
if (isset($url[0]))
{
if ($url[0] == 'api')
{
$params = array_slice($url, 3);
$url = array_slice($url, 0, 3);
}
else
{
$params = array_slice($url, 2);
$url = array_slice($url, 0, 2);
}
}
$url = implode('/', $url);
if (array_key_exists($url, $routes))
{
$path = explode('/', $routes[$url]);
unset($path[count($path)]);
$segments = end($path);
$segments = explode('#', $segments);
$controller = $segments[0];
$method = $segments[1];
require_once APP_ROOT . '/app/' . $controller . '.php';
$controller = new $controller;
call_user_func_array([$controller, $method], $params);
}
For my ZF2 form validation I need the entity manager in my custom validator. I Zend\ServiceManager\ServiceLocatorAwareInterface to get the service locator in my validator so I would be able to get the entitymanager from there.
My problem is that this Zend\ServiceManager\ServiceLocatorAwareInterface::setServiceLocator injects the "Zend\Validator\ValidatorPluginManager" and not the desired ServiceLocator.
Does anybody know how to solve this problem?
My abstract controller
abstract class AbstractController extends AbstractActionController
{
/**
* This property contains the doctrine entitymanager.
* #var Doctrine\ORM\EntityManager
*/
protected $_entityManager;
/**
* This method returns the doctrine entitymanager.
* #return \Doctrine\ORM\EntityManager
*/
public function getEntityManager()
{
var_dump(spl_object_hash($this->getServiceLocator())); echo '<br>';
if (null === $this->_entityManager)
$this->_entityManager = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default');
return $this->_entityManager;
}
}
My validator:
class EntityUnique extends AbstractValidator implements ServiceLocatorAwareInterface
{
protected $_serviceLocator;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
var_dump(spl_object_hash($serviceLocator)); echo '<br>';
$this->_serviceLocator = $serviceLocator;
}
function getServiceLocator()
{
return $this->_serviceLocator;
}
}
When executing this both var_dumps result in another object hash:
string(32) "000000005e4258390000000024a7f829"
string(32) "000000005e425a2d0000000024a7f829"
The reason for this is that in your validator you are getting the Zend\Validator\ValidatorPluginManager injected. If you want the the Service Manager call getServiceLocator on the Zend\Validator\ValidatorPluginManager.
For example look at the someMethod() method bellow:
class EntityUnique extends AbstractValidator implements ServiceLocatorAwareInterface
{
protected $_serviceLocator;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
var_dump(spl_object_hash($serviceLocator)); echo '<br>';
$this->_serviceLocator = $serviceLocator;
}
public function getServiceLocator()
{
return $this->_serviceLocator;
}
public function someMethod()
{
$validatorPluginManager = $this->getServiceLocator();
$serviceLocator = $validatorPluginManager->getServiceLocator(); // HERE
}
}
Hope this helps :)
Stoyan
I got it working in the end. I don't know if this is the best practice so if you have any feedback don't hesitate. My validator ended up like this:
<?php
namespace Flex\Validator;
use Doctrine\ORM\EntityManager;
use Zend\Stdlib\ArrayUtils;
use Zend\ServiceManager\ServiceManager;
use Zend\Validator\AbstractValidator;
class EntityUnique extends AbstractValidator
{
const EXISTS = 'exists';
protected $messageTemplates = array(
self::EXISTS => 'A %entity% entity allready exists.',
);
protected $messageVariables = array(
'entity' => '_entity',
);
protected $_context;
protected $_entity;
protected $_excludes = array();
protected $_filters = array();
protected $_property;
protected $_serviceLocator;
public function __construct($options = null)
{
if ($options instanceof \Traversable)
$options = ArrayUtils::iteratorToArray($options);
if (is_array($options))
{
if (isset($options['entity']))
$this->_entity = $options['entity'];
if (isset($options['exclude']))
{
if (!is_array($options['exclude']))
throw new \Exception('Exclude option should be an array');
if (isset($options['exclude']['property']) && isset($options['exclude']['value']))
$this->_excludes[] = array(
'property' => $options['exclude']['property'],
'value' => $options['exclude']['value'],
);
else
foreach ($options['exclude'] as $exclude)
{
if (!isset($exclude['property']) || !isset($exclude['value']))
throw new \Exception('exclude shoud contain a property and value');
$this->_excludes[] = array(
'property' => $exclude['property'],
'value' => $exclude['value'],
);
}
}
if (isset($options['filter']))
{
if (!is_array($options['filter']))
throw new \Exception('Filter option should be an array');
if (isset($options['filter']['property']) && isset($options['filter']['value']))
$this->_filters[] = array(
'property' => $options['filter']['property'],
'value' => $options['filter']['value'],
);
else
foreach ($options['filter'] as $filter)
{
if (!isset($filter['property']) || !isset($filter['value']))
throw new \Exception('Filters shoud contain a property and value');
$this->_filters[] = array(
'property' => $filter['property'],
'value' => $filter['value'],
);
}
}
if (isset($options['property']))
$this->_property = $options['property'];
if (!isset($options['serviceLocator']))
throw new \InvalidArgumentException(__CLASS__ . ' requires the option serviceLocator.');
$ServiceManagerInstance = 'Zend\ServiceManager\ServiceManager';
if (!($options['serviceLocator'] instanceof $ServiceManagerInstance))
throw new \InvalidArgumentException(__CLASS__ . ' expects the option serviceLocator to be an instance of Zend\ServiceManager\ServiceManager.');
$this->_serviceLocator = $options['serviceLocator'];
}
parent::__construct(is_array($options) ? $options : null);
}
public function isValid($value, $context = null)
{
$this->setValue($value);
$this->_context = $context;
$entityManager = $this->_serviceLocator->get('doctrine.entitymanager.orm_default');
$queryBuilder = $entityManager->createQueryBuilder();
$queryBuilder->from($this->_entity, 'e')
->select('COUNT(e)')
->where('e.' . $this->_property . ' = :value')
->setParameter('value', $this->getValue());
if (count($this->_excludes))
foreach ($this->_excludes as $key => $exclude)
{
$exclude['value'] = $this->parse($exclude['value']);
if ($exclude['value'] !== null)
$queryBuilder->andWhere('e.' . $exclude['property'] . ' = :exclude_' . $key)
->setParameter('exclude_' . $key, $exclude['value']);
else
$queryBuilder->andWhere('e.' . $exclude['property'] . ' IS NULL');
}
if (count($this->_filters))
foreach ($this->_filters as $key => $filter)
{
$filter['value'] = $this->parse($filter['value']);
if ($filter['value'] !== null)
$queryBuilder->andWhere('e.' . $filter['property'] . ' = :filter_' . $key)
->setParameter('filter_' . $key, $filter['value']);
else
$queryBuilder->andWhere('e.' . $filter['property'] . ' IS NULL');
}
$query = $queryBuilder->getQuery();
if ((integer) $query->getSingleScalarResult() !== 0)
{
$this->error(self::EXISTS);
return false;
}
return true;
}
private function parse($input, $current = null)
{
if (!is_array($input))
return $input;
if ($current === null)
$current = $this;
$currentKey = array_shift($input);
if (is_object($current) && property_exists($current, $currentKey))
$current = $current->$currentKey;
elseif (is_array($current) && array_key_exists($currentKey, $current))
$current = $current[$currentKey];
else
return null;
if (count($input))
return $this->parse($input, $current);
if (strlen($current) == 0)
return null;
return $current;
}
}
This is loaded in like this:
<?php
namespace FlexCategories\Form;
use Zend\Form\Form;
use Zend\InputFilter\InputFilterProviderInterface;
use Zend\ServiceManager\ServiceManager;
use Zend\ServiceManager\ServiceManagerAwareInterface;
class CategoryForm extends Form implements InputFilterProviderInterface, ServiceManagerAwareInterface
{
private $_serviceManager;
public function init()
{
/* */
}
public function getInputFilterSpecification()
{
return array(
'name' => array(
'filters' => array(
array(
'name' => 'Zend\Filter\StringTrim'
),
),
'required' => true,
'validators' => array(
array(
'name' => 'Flex\Validator\EntityUnique',
'options' => array(
'entity' => 'FlexCategories\Entity\Category',
'filter' => array(
array('property' => 'parent',
'value' => array('_context', 'parent')),
),
'property' => 'name',
'serviceLocator' => $this->_serviceManager,
),
),
),
),
);
}
public function setServiceManager(ServiceManager $serviceManager)
{
$this->_serviceManager = $serviceManager;
$this->init();
return $this;
}
}
The form is registered in the service_manager => invokables and is retreived within the controller thorugh the service manager.
This worked for me. I hope somebody will be able to learn from this or give me feedbacl to create an better solution.
This question already has answers here:
Call to a member function on a non-object [duplicate]
(8 answers)
Closed 9 years ago.
I am learning oop and trying to implement php standards PSR-0 & PSR-1 along the way. I have started by creating a small MVC framework based on Swiftlet.
I am trying to call functions from my base View controller within the Controller that I am calling from the url and I get 'Call to a member function set() on a non-object in'.
All the classes load ok. So in my controller I call $this->view->set('helloWorld', 'Hello world!'); but I get an error. I had some trouble trying to get the namespace structure right so maybe this is the cause?
Here is the file structure:
index.php
lib/bootstrap.php
lib/view.php
lib/controller.php
app/controllers/index.php
and here is the code for each:
index.php
<?php
namespace MVC;
// Bootstrap the application
require 'lib/Bootstrap.php';
$app = new lib\Bootstrap;
spl_autoload_register(array($app, 'autoload'));
$app->run();
$app->serve();
bootstrap.php
namespace MVC\lib;
class Bootstrap
{
protected
$action = 'index',
$controller,
$hooks = array(),
$view
;
/**
* Run the application
*/
function run()
{
... Code that gets controller and the action form the url
$this->view = new \lib\View($this, strtolower($controllerName));
// Instantiate the controller
$controllerName = 'app\Controllers\\' . basename($controllerName);
$this->controller = new $controllerName();
// Call the controller action
$this->registerHook('actionBefore');
if ( method_exists($this->controller, $this->action) ) {
$method = new \ReflectionMethod($this->controller, $this->action);
if ( $method->isPublic() && !$method->isFinal() && !$method->isConstructor() ) {
$this->controller->{$this->action}();
} else {
$this->controller->notImplemented();
}
} else {
$this->controller->notImplemented();
}
return array($this->view, $this->controller);
}
<?php
namespace MVC\lib;
class Bootstrap
{
protected
$action = 'index',
$args = array(),
$config = array(),
$controller,
$hooks = array(),
$plugins = array(),
$rootPath = '/',
$singletons = array(),
$view
;
/**
* Run the application
*/
function run()
{
// Determine the client-side path to root
if ( !empty($_SERVER['REQUEST_URI']) ) {
$this->rootPath = preg_replace('/(index\.php)?(\?.*)?$/', '', $_SERVER['REQUEST_URI']);
if ( !empty($_GET['route']) ) {
$this->rootPath = preg_replace('/' . preg_quote($_GET['route'], '/') . '$/', '', $this->rootPath);
}
}
// Extract controller name, view name, action name and arguments from URL
$controllerName = 'Index';
if ( !empty($_GET['route']) ) {
$this->args = explode('/', $_GET['route']);
if ( $this->args ) {
$controllerName = str_replace(' ', '/', ucwords(str_replace('_', ' ', str_replace('-', '', array_shift($this->args)))));
}
if ( $action = $this->args ? array_shift($this->args) : '' ) {
$this->action = str_replace('-', '', $action);
}
}
if ( !is_file('app/Controllers/'. $controllerName . '.php') ) {
$controllerName = 'Error404';
}
$this->view = new \lib\View($this, strtolower($controllerName));
// Instantiate the controller
$controllerName = 'app\Controllers\\' . basename($controllerName);
$this->controller = new $controllerName();
// Call the controller action
$this->registerHook('actionBefore');
if ( method_exists($this->controller, $this->action) ) {
$method = new \ReflectionMethod($this->controller, $this->action);
if ( $method->isPublic() && !$method->isFinal() && !$method->isConstructor() ) {
$this->controller->{$this->action}();
} else {
$this->controller->notImplemented();
}
} else {
$this->controller->notImplemented();
}
$this->registerHook('actionAfter');
return array($this->view, $this->controller);
}
<?php
namespace MVC\lib;
class Bootstrap
{
protected
$action = 'index',
$args = array(),
$config = array(),
$controller,
$hooks = array(),
$plugins = array(),
$rootPath = '/',
$singletons = array(),
$view
;
/**
* Run the application
*/
function run()
{
// Determine the client-side path to root
if ( !empty($_SERVER['REQUEST_URI']) ) {
$this->rootPath = preg_replace('/(index\.php)?(\?.*)?$/', '', $_SERVER['REQUEST_URI']);
if ( !empty($_GET['route']) ) {
$this->rootPath = preg_replace('/' . preg_quote($_GET['route'], '/') . '$/', '', $this->rootPath);
}
}
// Extract controller name, view name, action name and arguments from URL
$controllerName = 'Index';
if ( !empty($_GET['route']) ) {
$this->args = explode('/', $_GET['route']);
if ( $this->args ) {
$controllerName = str_replace(' ', '/', ucwords(str_replace('_', ' ', str_replace('-', '', array_shift($this->args)))));
}
if ( $action = $this->args ? array_shift($this->args) : '' ) {
$this->action = str_replace('-', '', $action);
}
}
if ( !is_file('app/Controllers/'. $controllerName . '.php') ) {
$controllerName = 'Error404';
}
$this->view = new \lib\View($this, strtolower($controllerName));
// Instantiate the controller
$controllerName = 'app\Controllers\\' . basename($controllerName);
$this->controller = new $controllerName();
// Call the controller action
$this->registerHook('actionBefore');
if ( method_exists($this->controller, $this->action) ) {
$method = new \ReflectionMethod($this->controller, $this->action);
if ( $method->isPublic() && !$method->isFinal() && !$method->isConstructor() ) {
$this->controller->{$this->action}();
} else {
$this->controller->notImplemented();
}
} else {
$this->controller->notImplemented();
}
$this->registerHook('actionAfter');
return array($this->view, $this->controller);
}
lib/view.php
namespace lib;
class View
{
protected
$app,
$variables = array()
;
public
$name
;
/**
* Constructor
* #param object $app
* #param string $name
*/
public function __construct($app, $name)
{
$this->app = $app;
$this->name = $name;
}
/**
* Set a view variable
* #param string $variable
* #param mixed $value
*/
public function set($variable, $value = null)
{
$this->variables[$variable] = $value;
}
and finally app/controllers/index.php
namespace app\Controllers;
class index extends \lib\Controller
{
public function test()
{
// This gets the error
$this->view->set('helloWorld', 'Hello world!');
}
}
If that is all of the code in your controller, then $this->view is not an object.
Try running the following code:
namespace app\Controllers;
class index extends \lib\Controller
{
public function test()
{
var_dump( $this->view );
exit;
$this->view->set('helloWorld', 'Hello world!');
}
}
You also should know, that in PHP, the __construct() methods are not inherited.
I must have suffered some brain injury.
Oh .. and I fail to see, why this question has mvc tag. While you are trying to write MVC-like thing, the issue itself has no relation to MVC as architectural pattern.