I'm having trouble with this very confusing error. I have a class and an extended class. If my extended file name starts with a capital B then it cannot find my database class. If I name anything else, literally anything else it works.
My database class looks like so
class Database
{
public $db;
public function __construct()
{
if (DATABASE == true)
{
$this->db = new mysqli(HOSTNAME, USERNAME, PASSWORD, DBNAME);
if ($this->db->connect_error)
{
exit('Some of the database login credentials seem to be wrong.' . '-' . $this->db->connect_error);
}
}
}
}
my extended class is like so
class BlogModel extends Database
{
public function getBlogPosts()
{
$query = array();
if ($result = $this->db->query('SELECT * FROM blog'))
{
while ($row = $result->fetch_assoc())
{
$query[] = $row;
}
$result->free();
}
return $query;
$mysqli->close();
}
}
The filename BlogModel.php causes the error.
Fatal error: Class 'Database' not found in C:\wamp\www\website\app\model\BlogModel.php on line 4
If I change it to blogModel.php it works. I don't mind just changing the filename but in the interest of understanding and learning I'd like to know why this is happening.
Edit:
This is how I include the files
define('ROOT', str_replace('\\', '/', $_SERVER['DOCUMENT_ROOT']));
define('HOST', $_SERVER['HTTP_HOST']);
define('URL', $_SERVER['REQUEST_URI']);
define('APP', ROOT . '/app/');
define('MODEL', ROOT . '/app/model/');
define('VIEW', APP . '/view/');
define('CONTROLLER', ROOT . '/app/controller/');
$models = array_diff(scandir(MODEL), array('.', '..'));
foreach ($models as $m)
{
require_once MODEL . $m;
}
To use an autoloader that is PSR compliant you would need to restructure your directory tree.
The code I use is a (not fully) PSR-4 compliant code:
<?php
#cn is class name
#fn is file name
#ns is namespace
#np is namespace pointer
class Loader{
public function load($class){
$cn = ltrim($class, '\\');
$fn = '';
$ns = '';
if($np = strrpos($cn, '\\')){
$ns = substr($cn, 0, $np);
$cn = substr($cn, $np + 1);
$fn = str_replace('\\', DIRECTORY_SEPARATOR, $ns) . DIRECTORY_SEPARATOR;
}
require ($fn .= strtolower(str_replace('_', DIRECTORY_SEPARATOR, $cn)) . '.php');
}
public function __construct(){
spl_autoload_register([$this, 'load']);
}
}
new Loader;
?>
It will load files in a lower case filename, if you do not use namespaces it will attempt to load from the document root if the object does not exist.
If you are using namespaces, aka:
$var = new \path\to\ObjeCt();
It would attempt to load object.php from webroot\path\to\
How one would use this is simple, just include this file and the next time you create an object keep the namespace valid to the path.
Related
I'm testing PSR-4 autoloader from https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader-examples.md and something is not working.
Error:
Fatal error: Uncaught Error: Class 'Foo\Bar\Qux\Quux' not found in C:\xampp\htdocs\Home\foo-bar\index.php:11 Stack trace: #0 {main} thrown in C:\xampp\htdocs\Home\foo-bar\index.php on line 11
Line:
new \Foo\Bar\Qux\Quux;
My files:
index.php
example
loader.php
src
Qux
Quux.php
Baz.php
tests
Qux
Quux.php
BazTest.php
index.php:
<?php
require_once "Example/loader.php";
$loader = new \Example\Psr4AutoloaderClass;
$loader->register();
$loader->addNamespace('Foo\Bar', '/src');
$loader->addNamespace('Foo\Bar', '/tests');
new \Foo\Bar\Qux\Quux;
loader.php:
<?php namespace Example;
class Psr4AutoloaderClass
{
protected $prefixes = array();
public function register()
{
spl_autoload_register(array($this, 'loadClass'));
}
public function addNamespace($prefix, $base_dir, $prepend = false)
{
// normalize namespace prefix
$prefix = trim($prefix, '\\') . '\\';
// normalize the base directory with a trailing separator
$base_dir = rtrim($base_dir, DIRECTORY_SEPARATOR) . '/';
// initialize the namespace prefix array
if (isset($this->prefixes[$prefix]) === false) {
$this->prefixes[$prefix] = array();
}
// retain the base directory for the namespace prefix
if ($prepend) {
array_unshift($this->prefixes[$prefix], $base_dir);
} else {
array_push($this->prefixes[$prefix], $base_dir);
}
}
public function loadClass($class)
{
// the current namespace prefix
$prefix = $class;
while (false !== $pos = strrpos($prefix, '\\')) {
// retain the trailing namespace separator in the prefix
$prefix = substr($class, 0, $pos + 1);
// the rest is the relative class name
$relative_class = substr($class, $pos + 1);
// try to load a mapped file for the prefix and relative class
$mapped_file = $this->loadMappedFile($prefix, $relative_class);
if ($mapped_file) {
return $mapped_file;
}
$prefix = rtrim($prefix, '\\');
}
return false;
}
protected function loadMappedFile($prefix, $relative_class)
{
// are there any base directories for this namespace prefix?
if (isset($this->prefixes[$prefix]) === false) {
return false;
}
foreach ($this->prefixes[$prefix] as $base_dir) {
$file = $base_dir
. str_replace('\\', '/', $relative_class)
. '.php';
// if the mapped file exists, require it
if ($this->requireFile($file)) {
// yes, we're done
return $file;
}
}
// never found it
return false;
}
protected function requireFile($file)
{
if (file_exists($file)) {
require $file;
return true;
}
return false;
}
}
Quux.php:
<?php namespace Foo\Bar\Qux;
class Quux {
public function __construct() {
echo "Hello";
}
}
Thank you for your help.
Change this in your index.php:
$loader->addNamespace('Foo\Bar\Qux', __DIR__ . '/src/Qux');
$loader->addNamespace('Foo\Bar\Qux', __DIR__ . '/tests/Qux');
There is an error in the PSR-4-autoloader-examples.md documentation.
Unable to start project in Kohana. I have cloned it from github, than set my database info in config file and get error: Cannot redeclare class.
I have 2 methods autoload functions.
public static function auto_load($class, $directory = 'classes')
{
// Transform the class name according to PSR-0
$class = ltrim($class, '\\');
$file = '';
$namespace = '';
if ($last_namespace_position = strripos($class, '\\'))
{
$namespace = substr($class, 0, $last_namespace_position);
$class = substr($class, $last_namespace_position + 1);
$file = str_replace('\\', DIRECTORY_SEPARATOR, $namespace).DIRECTORY_SEPARATOR;
}
$file .= str_replace('_', DIRECTORY_SEPARATOR, $class);
if ($path = Kohana::find_file($directory, $file))
{
// Load the class file
require_once $path;
// Class has been found
return TRUE;
}
// Class is not in the filesystem
return FALSE;
}
/**
* Provides auto-loading support of classes that follow Kohana's old class
* naming conventions.
*
* This is included for compatibility purposes with older modules.
*
* #param string $class Class name
* #param string $directory Directory to load from
* #return boolean
*/
public static function auto_load_lowercase($class, $directory = 'classes')
{
// Transform the class name into a path
$file = str_replace('_', DIRECTORY_SEPARATOR, strtolower($class));
if ($path = Kohana::find_file($directory, $file))
{
// Load the class file
require_once $path;
// Class has been found
return TRUE;
}
// Class is not in the filesystem
return FALSE;
}
I have tried to add class_exists() before require() but it doesn't work& What I should do to start a project?
Possibly required class is included in some other file already.
I don't know how exactly you check class collision problem, but you should do something like:
function include_quietly($file) {
$conflicts = false;
$txt = file_get_contents($file);
preg_match_all("#class\s+(\w+)\s*{#muis", $txt, $matches, PREG_SET_ORDER);
foreach ($matches as $m) {
if (class_exists($m[1])) {
$conflicts = true;
break;
}
}
if (!$conflicts)
include_once $file;
else
echo "include conflicts in file {$file}\n";
}
include_quietly($path);
I'm new to PHP namespace. and there is a problem when I use auto-load.
ROOT/Application/Instance.php
<?php
namespace Application;
class Instance {
public static $_database;
public function __construct() {
self::$_database = new \Application\Module\Database();
}
public static function database() {
return self::$_database;
}
public static function ID(){
return md5(uniqid(mt_rand(), TRUE) . mt_rand() . uniqid(mt_rand(), TRUE));
}
public static function autoload($_className) {
$thisClass = str_replace(__NAMESPACE__.'\\', '', __CLASS__);
$baseDir = __DIR__;
if (substr($baseDir, -strlen($thisClass)) === $thisClass) {
$baseDir = substr($baseDir, 0, -strlen($thisClass));
}
$_className = ltrim($_className, '\\');
$fileName = $baseDir;
$namespace = '';
if ($lastNsPos = strripos($_className, '\\')) {
$namespace = substr($_className, 0, $lastNsPos);
$_className = substr($_className, $lastNsPos + 1);
$fileName .= str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
}
$fileName .= str_replace('_', DIRECTORY_SEPARATOR, $_className) . '.php';
if (file_exists($fileName)) {
require $fileName;
}
}
public static function registerAutoloader() {
spl_autoload_register(__NAMESPACE__ . "\\Instance::autoload");
}
}
ROOT/Application/Module/Database.php
<?php
namespace Application\Module;
include 'FluentPDO/FluentPDO.php';
class Database extends Module {
public static $_instance;
public function __construct() {
if(self::$_instance === NULL) {
self::$_instance = new FluentPDO(new PDO("mysql:host=8273639.mysql.rds.aliyuncs.com;dbname=db", 'name', 'password'));
}
}
}
When I run this:
new \Application\Instance();
I got this error:
Fatal error: Class 'Application\Module\FluentPDO' not found in /mnt/www/airteams_com/public/Application/Module/Database.php on line 13
I'm pretty sure that 'FluentPDO/FluentPDO.php' exists. and the error shows a wrong path of the file. the right path is 'ROOT/Application/Module/FluentPDO/FluentPDO.php'
So how can i use a no namespace class in my situation? thanks.
When you are working with namespaces you must fully qualify each class unless it's a child of the current namespace.
As such the FluentPDO is probably on the root namespace which means you need to access it like such:
self::$_instance = new \FluentPDO(new \PDO("mysql:host=8273639.mysql.rds.aliyuncs.com;dbname=db", 'name', 'password'));
All,
I am reading the following article on a lightweight PHP dynamic front controller: http://www.w3style.co.uk/a-lightweight-and-flexible-front-controller-for-php-5
Here is the code:
index.php
<?php
define("PAGE_DIR", dirname(__FILE__) . "/pages");
require_once "FrontController.php";
FrontController::createInstance()->dispatch();
FrontController.php
<?php
class FrontController {
public static function createInstance() {
if (!defined("PAGE_DIR")) {
exit("Critical error: Cannot proceed without PAGE_DIR.");
}
$instance = new self();
return $instance;
}
public function dispatch() {
$page = !empty($_GET["page"]) ? $_GET["page"] : "home";
$action = !empty($_GET["action"]) ? $_GET["action"] : "index";
//e.g. HomeActions
$class = ucfirst($page) . "Actions";
//e.g. pages/home/HomeActions.php
$file = PAGE_DIR . "/" . $page . "/" . $class . ".php";
if (!is_file($file)) {
exit("Page not found");
}
require_once $file;
$actionMethod = "do" . ucfirst($action);
$controller = new $class(); // I DON'T UNDERSTAND WHAT THIS DOES...
if (!method_exists($controller, $actionMethod)) {
exit("Page not found");
}
//e.g. $controller->doIndex();
$controller->$actionMethod();
exit(0);
}
}
pages/guestbook/GuestbookActions.php
<?php
class GuestbookActions {
public function doIndex() {
echo "Index action called...";
}
public function doCreatePost() {
echo "CreatePost action called...";
}
}
In the front controller class, could someone explain to me what $controller = new $class(); does? I don't understand it. It seems to be creating a class on the fly? In the example above, $class is a string with a value like "HomeActions". So $controller would be a new instance of a class named "HomeActions", but those are not defined anywhere. I'm confused.
Many thanks,
JDelage
$controller = new $class();
That does indeed create a new object of the type contained in $class, so it is equivalent to $controller = new HomeActions() in your example. From the manual:
If a string containing the name of a class is used with new, a new instance of that class will be created
The classes are not all present initially. However, the necessary one is loaded dynamically:
$file = PAGE_DIR . "/" . $page . "/" . $class . ".php";
if (!is_file($file)) {
exit("Page not found");
}
require_once $file;
require_once loads the file which presumably contains the class definition, so you can create the object as shown above.
The example request in the article is to http://localhost/index.php?page=guestbook&action=index, so $class would be GuestbookActions, which is defined in the third code sample.
REWROTE: SOLVED
Hi there,
I currently worked on a simple application with a database, a bunch of controllers, views and a model class.
I coded the controllers and inserted the db connections directly
E.g.
Each controller method has his own PDO to connect to a specific database+table.
I refactored this because I had too many active PDOs per 1 controller, so I started to code the model class.
A short information: The model class is once accessed by a controller, when the controller is called.
Once the model object is constructed it is available through the whole controller, and you can pass custom request to it.
E.g. getUserById => Gets the User from the current controller table with the id "xy".
Now that I finally finished the model class and added my PDO class to the model:
Everytime I want to access any of my controllers, my FireFox asks me where to safe the empty "test.php" (test.php is my index file).
Restarting Apache2 / PHP / MySQL did not work, if I remove a certain part of my code, there is no error, but this part is essential. ;)
model.php
class Model extends db_pdo_adapter{
public function __construct($name)
{
$name = strtolower($name);
$this->dbh = parent::connect(Model::ATTR_HOST, Model::ATTR_USR, Model::ATTR_PASSWD, Model::ATTR_DB);
$this->name = $name.'s';
//$this->ATTR_TBL = $this->name;
}
public function __call($name,$values)
{
$string = preg_replace('/^get/','',$name);
$string = strtolower($string);
$by = preg_split('/by/',$string);
$by = strtolower($by[1]);
return $this->get($string, $by, $values); // when I remove this part no empty file is served.
}
public function get($item, $by, $conditions) // single item if is_no_array
{
if($item = preg_replace('/$s/','',$this->name))
{
$item = '*';
}
//if(count($conditions) <= 1)
//{
$query = 'SELECT ' . $item . ' FROM ' . $this->name . ' WHERE ' . $by . ' = :' . $by . '';
$pname = ':'.$by;
//}
$this->dbh->getStatement($query);
$this->dbh->bindParam($pname,$conditions[0]); // ->dbh-> also was missing
$this->dbh->exec();
return ($this->dbh->fetchAll());
}
}
Extract of test.php
header('Content-Type: text/html;');
$time_start = microtime(true);
include_once('db/model.php');
//include_once('village.php');
//include_once('player.php');
include_once('building.php');
//$village = Village::getVillage('12');
//$player = Player::getPlayer('423');
//$data = array('name' => 'peter','password' => 'nopasswd','email' => 'peter#hasnomail.org');
//$player->newPlayer($data);
//print_r($village->attr);
//print_r($player->playerObj);
//include('interface.phtml');
//var_dump($_SERVER);
//print_r($village);
//print_r($player);
echo '<br />';
var_dump(Building::getBuilding('321'));
Extract of the building.php (controller)
class Building{
private function __construct($id,$village = NULL)
{
$this->model = new Model(__CLASS__);
$model = $this->model;
$this->buildingObj = $model->getBuildingById($id);
}
public function getBuilding($id,$village = NULL)
{
return (new Building($id));
}
}
I found the problem:
I have to extend the Model class with the PDO adapter, I do not know why, but this was the problem.