Here am getting an error like Fatal error: Cannot declare class ABC, because the name is already in use in while listing all controller names and functions in project.In order to get i got an library from surroundings and placed it.library looks like this
<?php
if (!defined('BASEPATH'))
exit('No direct script access allowed');
/***
* File: (Codeigniterapp)/libraries/Controllerlist.php
*
* A simple library to list al your controllers with their methods.
* This library will return an array with controllers and methods
*
* The library will scan the "controller" directory and (in case of) one (1) subdirectory level deep
* for controllers
*
* Usage in one of your controllers:
*
* $this->load->library('controllerlist');
* print_r($this->controllerlist->getControllers());
*
* #author Peter Prins
*/
class ControllerList {
/**
* Codeigniter reference
*/
private $CI;
/**
* Array that will hold the controller names and methods
*/
private $aControllers;
// Construct
function __construct() {
// Get Codeigniter instance
$this->CI = get_instance();
// Get all controllers
$this->setControllers();
}
/**
* Return all controllers and their methods
* #return array
*/
public function getControllers() {
return $this->aControllers;
}
/**
* Set the array holding the controller name and methods
*/
public function setControllerMethods($p_sControllerName, $p_aControllerMethods) {
$this->aControllers[$p_sControllerName] = $p_aControllerMethods;
}
/**
* Search and set controller and methods.
*/
private function setControllers() {
// Loop through the controller directory
foreach(glob(APPPATH . 'controllers/*') as $controller) {
// if the value in the loop is a directory loop through that directory
if(is_dir($controller)) {
// Get name of directory
$dirname = basename($controller, 'EXT');
// Loop through the subdirectory
foreach(glob(APPPATH . 'controllers/'.$dirname.'/*') as $subdircontroller) {
// Get the name of the subdir
$subdircontrollername = basename($subdircontroller, EXT);
// Load the controller file in memory if it's not load already
if(!class_exists($subdircontrollername)) {
$this->CI->load->file($subdircontroller);
}
// Add the controllername to the array with its methods
$aMethods = get_class_methods($subdircontrollername);
$aUserMethods = array();
foreach($aMethods as $method) {
if($method != '__construct' && $method != 'get_instance' && $method != $subdircontrollername) {
$aUserMethods[] = $method;
}
}
$this->setControllerMethods($subdircontrollername, $aUserMethods);
}
}
else if(pathinfo($controller, PATHINFO_EXTENSION) == "php"){
// value is no directory get controller name
$controllername = basename($controller, 'EXT');
// Load the class in memory (if it's not loaded already)
if(!class_exists($controllername)) {
var_dump($controller);
$this->CI->load->file($controller);
}
// Add controller and methods to the array
$aMethods = get_class_methods($controllername);
//var_dump($aMethods);
$aUserMethods = array();
if(is_array($aMethods)){
foreach($aMethods as $method) {
if($method != '__construct' && $method != 'get_instance' && $method != $controllername) {
$aUserMethods[] = $method;
}
}
}
$this->setControllerMethods($controllername, $aUserMethods);
}
}
}
}
// EOF
Here am getting error in the line $this->CI->load->file($controller);.when i call this library in my controller am getting error what i mentioned in title.Is it a problem of php version,Here it is telling that controller class name is already called so it cannot be call again.
Related
I'm using PHPCS to check my class name are valid with the rule PEAR.NamingConventions.ValidClassName
It works fine on class declaration:
class My_Class {} // Valid
class My_class {} // Invalid - expected behaviour
Is there any rule to check class name at instantiation?
new My_class() // Should complain about invalid class name
My_class::someMethod() // Should complain too
I ended up writing my own sniff.
It checks class name when it finds a new or a static call with ::
<?php
namespace MySniffs\Sniffs\Classes;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class MySniffs_Sniffs_Classes_ConsistentClassNameCaseSniff implements Sniff
{
/**
* Returns the token types that this sniff is interested in.
*
* #return array(int)
*/
public function register() {
return array(T_NEW, T_DOUBLE_COLON);
}//end register()
/**
* Processes this sniff, when one of its tokens is encountered.
*
* #param \PHP_CodeSniffer\Files\File $phpcsFile The current file being checked.
* #param int $stackPtr The position of the current token in the
* stack passed in $tokens.
*
* #return void
*/
public function process(File $phpcsFile, $stackPtr) {
$tokens = $phpcsFile->getTokens();
$token = $tokens[$stackPtr];
if ($token['type'] === 'T_NEW') {
$className = $phpcsFile->findNext(T_STRING, $stackPtr);
} elseif ($token['type'] === 'T_DOUBLE_COLON') {
$className = $phpcsFile->findPrevious(T_STRING, $stackPtr);
}
$name = trim($tokens[$className]['content']);
$words = explode('_', $name);
foreach ($words as $word) {
if (!preg_match("/^[A-Z]/", $word)) {
$phpcsFile->addError(
'Invalid class name case : ' . $name,
$stackPtr,
'ClassNameCase',
array('message' => 'Boom')
);
}
}
}//end process()
}
Im working on a script that i download online but when i install codeigniter it brings this.
Severity: Warning
Message: Declaration of MY_Lang::line($line = '') should be compatible with CI_Lang::line($line, $log_errors = true)
Filename: core/MY_Lang.php
Line Number: 41
And here's my Controller
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class MY_Lang extends CI_Lang {
// --------------------------------------------------------------------
/**
* Load a language file
*
* #access public
* #param mixed the name of the language file to be loaded. Can be an array
* #param string the language (english, etc.)
* #param bool return loaded array of translations
* #param bool add suffix to $langfile
* #param string alternative path to look for language file
* #return mixed
*/
function load($langfile = '', $idiom = '', $return = FALSE, $add_suffix = TRUE, $log_errors = TRUE, $alt_path = '', $line ='')
{
parent::load($langfile, $idiom, $return, $add_suffix, $alt_path, $log_errors, $line);
}
function get_array()
{
return $this->language;
}
function line($line = '')
{
$value = ($line == '' OR ! isset($this->language[$line])) ? FALSE : $this->language[$line];
// Because killer robots like unicorns!
if ($value === FALSE)
{
log_message('debug', 'Could not find the language line "'.$line.'"');
}
return $value;
}
}
i don't know where is wrong, i already brake a little of fatal error warnings, but this one has my dry.
On the other hand, a second mistake that bounces me is this
Message: Call to undefined function config_db_item()
Filename: /home/casabla6/public_html/application/core/MY_Loader.php
and in the controller i have this.
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class MY_Loader extends CI_Loader
{
function __construct()
{
parent::__construct();
$this->_ci_view_paths += array(FCPATH.'templates/'=>TRUE);
}
public function view($view, $vars = array(), $return = FALSE)
{
$admin_template = '';
if( config_db_item ('admin_template') !== FALSE)
$admin_template = config_db_item('admin_template');
if(strpos($view, 'admin/') === 0)
$view = $admin_template.'/'.$view;
if(isset($vars['subview']))
{
if(strpos($vars['subview'], 'admin/') === 0)
$vars['subview'] = $admin_template.'/'.$vars['subview'];
}
return parent::view($view, $vars, $return);
}
function common_view($view, $vars = array(), $return = FALSE)
{
$view = 'common/'.$view;
return parent::view($view, $vars, $return);
}
}
?>
can u give me a hand please?
Your class MY_Lang extends CI_Lang, which means the method MY_Lang::line() should have a definition that matches the method CI_Lang::line().
The original method in CI_Lang:
public function line($line, $log_errors = true)
Your extension in MY_Lang:
public function line($line = '')
You'll need to change your code to take the same arguments:
public function line($line, $log_errors = true)
So, I've created a webshop system. It all worked perfectly, except for that projects can always be improved. So someone told me that 'Templating' and 'Routing' would be nice improvements. I've written classes for both of them, and they work fine! Now, I do wish to know how to combine them? How can I combine these classes so that data from the routing (to determine the content) needs to be placed inside the template. How would I do this?
Templating class:
class Template
{
private $assignedValues = array();
private $tpl;
/*
** #description Creates one single instance of itself and checks whether the template file exists.
** #param $path [string] This is the path to the template
*/
public function __construct($_path = '')
{
if(!empty($_path)){
if(file_exists($_path)){
$this->tpl = file_get_contents($_path);
}
else{
echo '<b>Template Error:</b> File Inclusion Error.';
}
}
}
/*
** #description Assign a value to a part in the template.
** #param $_searchString [string] This is the part in the template that needs to be replaced
** #param $_replaceString [string] This is the code/text that will replace the part in the template
*/
public function assign($_searchString, $_replaceString)
{
if(!empty($_searchString)){
$this->assignedValues[strtoupper($_searchString)] = $_replaceString;
}
}
/*
** #description Shows the final result of the page.
*/
public function show()
{
if(count($this->assignedValues > 0)){
foreach ($this->assignedValues as $key => $value) {
$this->tpl = str_replace('{'.$key.'}', $value, $this->tpl);
}
}
echo $this->tpl;
}
/*
** #description Quickly load a part of the page
** #param $quickLoad [string] This is the name of the file that will be loaded and assigned
** #param $_searchString [string] This is the part in the template that needs to be replaced
*/
public function quickLoad($_searchString, $part)
{
if(file_exists(INCLUDES.DS.$part.'.php')){
$this->assign($_searchString,include(INCLUDES.DS.$part.'.php'));
}
else{
return "That file does not exist!";
}
}
}
And the routing class:
class Route
{
protected $controller = 'App';
protected $method = 'Call';
protected $params = [];
/*
** #description Loads the classes and methods which are referred to.
*/
public function __construct()
{
$url = $this->parseUrl();
if($this->checkUrl())
{
unset($url[0]);
if(isset($url[1]))
{
if (file_exists('core/classes/' . $url[1] . '.class.php'))
{
$this->controller = $url[1];
unset($url[1]);
}
}
require_once('core/classes/' . $this->controller . '.class.php');
$this->controller = new $this->controller;
if (isset($url[2]))
{
if (method_exists($this->controller, $url[2]))
{
$this->method = $url[2];
unset($url[2]);
}
}
$this->params = $url ? array_values($url) : [];
$this->arrayUrl($this->params);
call_user_func_array([$this->controller, $this->method], $this->params);
}
}
/*
** #description Check whether the URL part contains a string
*/
public function checkUrl($index = '0',$value = 'Route'){
$url = $this->parseUrl();
if($url[$index] == $value){
return true;
}
return false;
}
/*
** #description Splits the url into pieces.
*/
protected function parseUrl()
{
if(isset($_GET['url']))
{
return $url = explode('/', filter_var(rtrim(urldecode($_GET['url']), '/'), FILTER_SANITIZE_URL));
}
}
/*
** #description Sets arrays in routes.
*/
protected function arrayUrl($params = array())
{
foreach($params as $index => $param)
{
if (preg_match('/>/',$param))
{
$newParam = explode('>', $param);
unset($this->params[$index]);
$this->params['fields'][$newParam[0]] = $newParam[1];
}
else{
unset($this->params[$index]);
$this->params[] = $param;
}
}
print_r($this->params);
}
}
I can access my routes by URL's like this:
http://localhost:8080/Webshop/Route/User/logout
With: Class & Method.
This is a simple example that I already use, because no data needs to be showed using this method. It only logs out the user that is logged in. After that, you get redirected to the home page. But how could I use routing for other pages? For example, update some user data without having to create an update file?
EDIT:
This is an example of a page I use now (index.php):
<?php
/*
** #description Includes config.php once so that we can use classes, defines etcetera.
*/
require_once('core/preferences/config.php');
/*
** #description Instanciate new route object.
*/
$route = new Route();
/*
** #description Check if a route isset. When not, continue, else: run route
*/
if(!$route->checkUrl())
{
/*
** #description Instanciate new template object.
*/
$template = new Template(TEMPLATES_PATH .'/camerashop24.tpl.html');
/*
** #description Assign values.
*/
$template->assign('title', 'Home');
$template->assign('root', '');
$template->quickLoad('loginout', 'loginout');
$template->quickLoad('title_block', 'title_block');
$template->quickLoad('cart','cart');
$template->quickLoad('menu', 'menu');
$template->assign('page', 'Home');
$breadcrumbs = new Breadcrumbs($_SERVER["REQUEST_URI"],'');
$template->assign('breadcrumbs', $breadcrumbs->data());
$content = "";
foreach(explode(",",Config::get('settings/front_page_cat')) as $item) {
$content .= "<div id='blue-box' class='blue-box'><h2 style='color: white;'>" . strtoupper($item) . "</h2></div>";
$category = new Category();
$category->find($item);
if($category->exists($item)){
foreach (explode(",",$category->data()->products) as $item) {
$product = new Product($item);
$product->find($item);
$content .= '' . $product->showProduct($product->data()->type,$product->data()->name,$product->data()->price) . '';
}
}
}
$template->assign('text', $content);
$template->quickLoad('footer','footer');
/*
** #description Showing content.
*/
$template->show();
}
But, what I want as an answer, how can I show the data from the routing (select users profile for example) in this template without having to create a page for it like this one.
I think you're gonna have to modify your Routing class. Ideally you want the routing class to give YOU the ability to tell it what controller the route should use. Th controller you select would act as the middle man for processing. Each controller's method would represent a route, something like this:
/**
* URL = www.test.com/index.php/home
* URL = www.test.com/index.php/about
*
* ROUTE Class:
* - should define routes. Example:
*
* $route["/home"] = MainController/home
* $route["/about"] = MainController/about
* $route["/*"] = MainController/*
*
*/
class MainController
{
public function home()
{
$template = new Template(TEMPLATES_PATH . 'home.tpl.html');
$template->show();
}
public function about()
{
$template = new Template(TEMPLATES_PATH . 'about.tpl.html');
$template->show();
}
}
I am makeing a small framework for scheduled job that are run by a nodejs external process. I would like to use an auto loader but for some reason data is not getting to it. I am also using namespaces. Here is what my folder structure looks like:
Library
|_ Core
|_ JobConfig.php
|_ Utilities
|_ Loader.php
|_ Scheduled
|_ SomeJob.php
|_ Config.php
My Config.php just has some definitions and an instance of my Loader.php.
Loader.php looks like:
public function __constructor()
{
spl_autoload_register(array($this, 'coreLoader'));
spl_autoload_register(array($this, 'utilitiesLoader'));
}
private function coreLoader($class)
{
echo $class;
return true;
}
private function utilitiesLoader($lass)
{
echo $class;
return true;
}
So for my SomeJob.php I am including Config.php and then try to instantiate JobConfig.php when it fails. My namespaces look like Library\Core\JobConfig and so on. I am not sure if this is the best approach without the ability to bootsrapt things. But I am not seeing the echo's from the loader before it fails.
Edit:
I tried the sugestion by #Layne but did not work. I am still getting a class not found and seems like the class in not getting in the spl stack. Here is a link to the code
If you actually use namespaces in the same way you're using your directory structure, this should be rather easy.
<?php
namespace Library {
spl_autoload_register('\Library\Autoloader::default_autoloader');
class Autoloader {
public static function default_autoloader($class) {
$class = ltrim($class, '\\');
$file = __DIR__ . '/../';
if ($lastNsPos = strrpos($class, '\\')) {
$namespace = substr($class, 0, $lastNsPos);
$class = substr($class, $lastNsPos + 1);
$file .= str_replace('\\', '/', $namespace) . '/';
}
$file .= $class . '.php';
include $file;
}
}
}
Put that into your Library directory and require it on a higher level. Hopefully I didn't mess that one up, didn't test it.
EDIT: Fixed path.
use this class to index of your project, just replace WP_CONTENT_DIR with another directory level to scan php files, this class automatically include files:
<?php
class autoloader
{
public static function instance()
{
static $instance = false;
if( $instance === false )
{
// Late static binding
$instance = new static();
}
return $instance;
}
/**
* #param $dir_level directory level is for file searching
* #param $php_files_json_name name of the file who all the PHP files will be stored inside it
*/
private function export_php_files($dir_level, $php_files_json_name)
{
$filePaths = array(mktime());
/**Get all files and directories using iterator.*/
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir_level));
foreach ($iterator as $path) {
if (is_string(strval($path)) and pathinfo($path, PATHINFO_EXTENSION) == 'php') {
$filePaths[] = strval($path);
}
}
/**Encode and save php files dir in a local json file */
$fileOpen = fopen($dir_level . DIRECTORY_SEPARATOR . $php_files_json_name, 'w');
fwrite($fileOpen, json_encode($filePaths));
fclose($fileOpen);
}
/**
* #param $php_files_json_address json file contains all the PHP files inside it
* #param $class_file_name name of the class that was taken from #spl_autoload_register_register plus .php extension
* #return bool Succeeding end of work
*/
private function include_matching_files($php_files_json_address, $class_file_name)
{
static $files;
$inc_is_done = false;
if ($files == null) {
$files = json_decode(file_get_contents($php_files_json_address), false);
}
/**Include matching files here.*/
foreach ($files as $path) {
if (stripos($path, $class_file_name) !== false) {
require_once $path;
$inc_is_done = true;
}
}
return $inc_is_done;
}
/**
* #param $dir_level directory level is for file searching
* #param $class_name name of the class that was taken from #spl_autoload_register
* #param bool $try_for_new_files Try again to include new files, that this feature is #true in development mode
* it will renew including file each time after every 30 seconds #see $refresh_time.
* #return bool Succeeding end of work
*/
public function request_system_files($dir_level, $class_name, $try_for_new_files = false)
{
$php_files_json = 'phpfiles.json';
$php_files_json_address = $dir_level . DIRECTORY_SEPARATOR . $php_files_json;
$class_file_name = $class_name . '.php';
$files_refresh_time = 30;
/**Include required php files.*/
if (is_file($php_files_json_address)) {
$last_update = json_decode(file_get_contents($php_files_json_address), false)[0];
if ((mktime() - intval($last_update)) < $files_refresh_time || !$try_for_new_files) {
return $this->include_matching_files($php_files_json_address, $class_file_name);
}
}
$this->export_php_files($dir_level, $php_files_json);
return $this->include_matching_files($php_files_json_address, $class_file_name);
}
/**
* Make constructor private, so nobody can call "new Class".
*/
private function __construct()
{
}
/**
* Make clone magic method private, so nobody can clone instance.
*/
private function __clone()
{
}
/**
* Make sleep magic method private, so nobody can serialize instance.
*/
private function __sleep()
{
}
/**
* Make wakeup magic method private, so nobody can unserialize instance.
*/
private function __wakeup()
{
}
}
/**
* Register autoloader.
*/
try {
spl_autoload_register(function ($className) {
$autoloader = autoloader::instance();
return $autoloader->request_system_files(WP_CONTENT_DIR, $className, true);
});
} catch (Exception $e) {
var_dump($e);
die;
}
I'm developing a WebApp which (as usual) must support customer specific functionalities.
To achieve it I plan to set the customer name in the local app configuration (config/autoload/local.php )
configuration file so that I can use it to call the specialized code later on.
The module folder structure is this:
/module/Application
/module/Application/config
/module/Application/src
/module/Application/src/Application
/module/Application/src/Application/Controller
/module/Application/src/Application/Controller/[customer_instance_name]
/module/Application/src/Application/Model
/module/Application/src/Application/Model/[customer_instance_name]
/module/Application/view
/module/Application/view/Application
/module/Application/view/Application/[action]
/module/Application/view/Application/[action]/[customer_instance_name]
Using a custom ViewModel I inject the specific customer name to the template path:
namespace Application\Model;
use Zend\View\Model\ViewModel;
use Zend\View\Resolver\TemplatePathStack;
use Zend\Mvc\Service\ViewTemplatePathStackFactory;
class MyViewModel extends ViewModel
{
private $customInstanceName;
private $pathStack;
/**
* Constructor
*
* #param null|array|Traversable $variables
* #param array|Traversable $options
*/
public function __construct($variables = null, $options = null)
{
parent::__construct ( $variables, $options );
$serviceLocator = MySingleton::instance()->serviceLocator;
$factory = new ViewTemplatePathStackFactory();
$this->pathStack = $factory->createService($serviceLocator);
$config = $serviceLocator->get('config');
if (isset($config['custom_instance_name']) AND ($config['custom_instance_name']!='')) {
$this->customInstanceName = $config['custom_instance_name'];
} else {
$this->customInstanceName = false;
}
}
/**
* Set the template to be used by this model
*
* #param string $template
* #return ViewModel
*/
public function setTemplate($template)
{
$this->template = (string) $template;
if ( $this->customInstanceName === false) {
return $this;
}
$pathComponents = explode('/', (string) $template);
$last = array_pop($pathComponents);
array_push($pathComponents, $this->customInstanceName);
array_push($pathComponents, $last);
$customTemplate = implode('/', $pathComponents);
if ($this->pathStack->resolve($customTemplate) !== false) {
$this->template = $customTemplate;
}
return $this;
}
}
Using the "Decorator Pattern" I can achieve the same customization level on my Models.
I'm having problem to handle specific behavior. In this case I plan to create custom Controllers extending
my base controller class, but I'unable to call those controllers since the routing is defined on the module
config (and I was unable to change it in runtime).
My questions are:
1) Is this approach correct, or there is a better way to do it?
2) If the approach is correct, how can I define a custom router to be used when the ServiceManager reads my routing config?
Just found a solution. Will register it here hoping someone will benefit from it.
All I had to do was to create a specific router class with a match method which returns the correct routing target for each customer controller, and add it to my module.config.php as the type for each action.
namespace TARGETNAMESPACE;
use Traversable;
use Zend\Mvc\Router\Exception;
use Zend\Mvc\Router\Http\RouteInterface;
use Zend\Mvc\Router\Http\RouteMatch;
use Zend\Mvc\Router\Http\Literal;
use Zend\Stdlib\ArrayUtils;
use Zend\Stdlib\RequestInterface as Request;
class MyRouterLiteral extends Literal {
public function match(Request $request, $pathOffset = null) {
if (! method_exists($request, 'getUri')) {
return null;
}
$uri = $request->getUri();
$path = $uri->getPath();
if ($pathOffset !== null) {
if ($pathOffset >= 0 && strlen($path) >= $pathOffset && ! empty($this->route)) {
if (strpos($path, $this->route, $pathOffset) === $pathOffset) {
return new RouteMatch($this->getDefaults(), strlen($this->route));
}
}
return null;
}
if ($path === $this->route) {
return new RouteMatch($this->getDefaults(), strlen($this->route));
}
return null;
}
private function getDefaults() {
$aux = explode('\\', $this->defaults['controller']);
$last = array_pop($aux);
array_push($aux, '[CUSTOM_INSTANCE_NAME]');
array_push($aux, '[CUSTOM_INSTANCE_NAME]'.$last);
$result = $this->defaults;
$result['controller'] = implode('\\', $aux);
return $result;
}
}
To address all cases I had to create a second custom router (for segment routes) which follows the same rules and can be easily derived from the above code.