Hello everyone I'm making mvc framework and want to use Twig template system installed via commposer. I used this tutorial for
MVC here is my composer.json file
{
"require": {
"twig/twig": "~1.0"
},
"autoload":{
"files":[
"config/config.php"
]
}
}
when I add require_once ROOT.'\vendor\autoload.php' to index.php or bootstrap.php I've got an error:
File 'items.php' containing class 'ItemsController' might be missing. 2. Method 'index' is missing in 'items.php'
But when I add require_once in library/View.php like this:
class View {
public $loader;
public $twig;
public $arg;
public function __construct() {
require_once (ROOT.'/vendor/autoload.php');
$loader = new Twig_Loader_Filesystem(ROOT.'/application/views');
$this->twig = new Twig_Environment($loader);
}
}
It works well where is the problem how to make it works when I add autoload outside View class? All suggestions will be helpful, thanks.
That tutorial is using the __autoload() function - this makes it incompatible with everything that comes with it's own autoloader, including Composer.
This restriction is due to PHP being unable to allow defining a function twice - so there can be only one autoload function (which was introduced in PHP 5.0). To solve the problem of wanting to add more than one autoloader, everyone starting with PHP 5.1.2 was asked NOT to define function __autoload(), but to register a function callback using spl_autoload_register().
PHP 5.1.2 was released in January 2006!
Now think about the quality of an MVC tutorial that doesn't even get autoloading right in 2015 (it got posted in 2013, which doesn't make it better).
In fact, Composer can help you here. You can use it to autoload your own classes as well, but it is easiest if you stick to PSR-4 if you use namespaces, or PSR-0 if you don't (I suggest you do, but that Tutorial does not - another low quality, I'd say).
Related
I am writing a simple set of PHP functions, I use only pure PHP7, no framework, nothing. These functions will later on be used in a plugin in a CMS, but that is besides the point. I want to write unit tests for my functions using Codeception (to get familiar with it, I know that Codeception essentially only runs PHPUnit here), but I don't really know how to point Codeception to my code in a reasonable way.
My structure is as follows: I have path/to/functions.php which contains the functions I want to test, something along the lines of:
<?php
namespace App;
if (!defined('CONST')) {
die('What are you doing? Get out of here!');
}
function change_string($string){
return $string . '123';
}
I have used Composer to install Codeception to the root of my project and I used Codeception bootstrap to get started and then I also used Codeception to generate the unit test file, to which I added my unit test. Now most tutorials/explanations/articles on the subject just write tests and Codeception magically knowns where to find the code to test. This makes zero sense to me, and does not work in my case. So what I did is the following:
<?php
class NamesPathsTest extends \Codeception\Test\Unit
{
/**
* #var \UnitTester
*/
protected $tester;
protected function _before()
{
defined('CONST') or define('CONST', 'XXX');
require_once('path/to/functions.php');
}
protected function _after()
{
}
// tests
public function testChangeString() {
$this->assertEquals('a123',App\change_string('a'));
}
}
This works, but I think that there must be a better way to explain to Codeception where is the code to run than using the require_once('path/to/functions.php'). How to do this? What is the smart way of pointing Codeception to my code? Can it also handle defining the constant, so that I can actually test the functions?
How does your application code knows where the functions and classes are?
The magic ingredient is called autoloading.
Since you are using Composer already, the easiest way is to configure Composer to load your classes and functions.
Autoloading only works with classes, for them you can map namespace prefix to directory,
{
"autoload": {
"psr-4": {
"Monolog\\": "src/",
"Vendor\\Namespace\\": ""
}
}
}
Files containing functions must be included, but Composer helps with that too:
{
"autoload": {
"files": ["path/to/functions.php"]
}
}
Combined result:
{
"autoload": {
"files": ["path/to/functions.php"]
"psr-4": {
"Monolog\\": "src/",
"Vendor\\Namespace\\": ""
}
}
}
Since Codeception is installed using Composer, no additional work is required to get autoloading work in tests.
To benefit from autoloading in your application code, you must require 'vendor/autoload.php'; near the entry point.
Regarding your constant question, nobody uses this way to prevent direct execution of files, it is much simpler and more secure to move the code away from public directory and leave only small index.php file that can be accessed directly in public directory.
I have a php project.
This is the composer.json
{
"require": {
"bcosca/fatfree": "3.6.4",
"sineverba/domoticz-api": "^1.0",
"sineverba/supportdate":"dev-v1.0.0-alpha"
},
"repositories": [
{
"type": "vcs",
"url": "git#github.com:sineverba/supportdate.git"
}
]
}
sineverba\domotic-api is published on packagist. sineverba\supportdate no (started tonight).
In a file Script.php under app/models folder
app
| models
Script.php
I require my 2 libraries (domoticz-api and supportdate)
I have
<?php
/**
* Main script class
*
* #since 1.0.0
*/
namespace models;
use \sineverba\domoticzapi as Api;
use \sineverba\supportdate as Supportdate;
class Script() {
//some property here
public function __construct() {
$api = new Api\Client( $this->_user,$this->_password,$this->_host );
$date = new Supportdate\SupportDate();
}
}
$api object is created correctly. $date I get the error Class 'sineverba\supportdate\SupportDate' not found
I did try also:
Remove the declaration use \sineverba\supportdate as Supportdate;
Call directly $date = new \sineverba\supportdate\SupportDate()
As said, $api (object of sineverba\domoticz-api, that is published on packagist) is instantiated correctly, so autoload mode of Fat Free Framework (the framework that I use) is working correctly.
You can also see that domoticz-api and supportdate are very similar also in composer.json.
In my webserver the folders for both libraries are correctly present under /vendor/sineverba/domoticz-api and /vendor/sineverba/supportdate
I did try also creating an index.php inside my supportdate library
<?php
require("vendor/autoload");
$date = new \sineverba\supportdate\SupportDate();
And it works.
So, we can exclude a library / composer error and a F3 framework error ('cause API is called and instantiated).
Thank you for your support
Sometimes I inadvertently add unprintable characters when typing fast. The symptoms you're experiencing fit:
one, but not another similar class works, which rules out composer dump-autoload
it works when typed in another file.
Deleting the line, then retyping slowly forces your mind to dump prior assumptions and start from scratch. This can be helpful if there are issues of unprintable characters, case differences, or any other oddities that sometimes we're blind to in the midst of development.
composer.json:
{
"require": {
"smarty/smarty": "v3.1.17"
}
}
index.php:
define('SMARTY_SPL_AUTOLOAD', 1); // now smarty should use its own autoloader
require_once __DIR__ . "/vendor/autoload.php";
function my_classes_loader($class) {
$path = "{$class}.class.php";
if (file_exists($path)) {
include_once $path;
return true;
}
return false;
}
spl_autoload_register('my_classes_loader');
$smarty = new Smarty();
$smarty->setCompileDir("templates_c");
$smarty->display('main.html');
exit();
If I open it in browser I get
Fatal error: Class 'Smarty_Internal_TemplateCompilerBase' not found in
//smarty-example/vendor/smarty/smarty/distribution/libs/sysplugins/smarty_internal_smartytemplatecompiler.php
on line XX
The file is there. And it has content. And it is accessible / readable for PHP etc.
What am I doing wrong? What is missing?
It is a good idea to only have one point deciding about autoloading, and this should be Composer alone.
Try to put your own autoload function away, use a autoload declaration in your composer.json instead. Unfortunately you are not using either PSR-0 or PSR-4 naming standard, but Composer allows you to use "classmap" in this case. Consider moving all your file names to comply with PSR-0.
And the smarty autoloading should already be done by requiring it via Composer. No need to set that constant.
Last but not least I think your autoload function should not return anything. Especially it should not return false if it cannot find the file it supposes to contain the class, because depending on how the autoload functions are ordered on the stack, your function might get called first for ALL classes, including all Smarty ones. If you return false in these cases, you destroy the working autoload stack by not allowing later functions to load that class.
So all in all it is best to use Composer for all autoloading. The developers did everything to provide the best performing autoload function - your own function probably can only be as fast as theirs, but will probably be slower.
I am using _autoload in my system, and I need to import a library that has alot of classes, it uses _autoload function to auto-load its classes.
unfortunately once I call this library the code is calling my classes with a wrong path.
does anyone have any idea how can i solve this issue?
As mentioned in the comments section, you could just register your autoload function especially for your namespace. Taken from the php docs, here is one example:
<?php
namespace Foobar;
class Foo {
static public function test($name) {
print '[['. $name .']]';
}
}
spl_autoload_register(__NAMESPACE__ .'\Foo::test'); // As of PHP 5.3.0
new InexistentClass;
Read up on PHP Namespaces to fully understand how this works.
I'm trying to use PHP namespaces for the first time and can't even get a very basic example working with 2 files. Here's my directory setup:
/Framework/
/Framework/index.php
/Framework/Models/TestModel.php
And here's the code behind the two files.
index.php:
namespace Framework;
use \Framework\Models\TestModel;
$model = new TestModel();
$model->test();
TestModel.php:
namespace Framework\Models;
class TestModel
{
public function test()
{
print("test");
}
}
The error is simply that it cannot find the TestModel class:
Fatal error: Class 'Framework\Models\TestModel' not found in C:\xampp\htdocs\Framework\index.php on line 7
I'm running the PHP via a web browser at localhost/Framework/index.php. It must be something really simple I'm not seeing, can anyone point it out for me?
The Namespace on the File itself "distinguishes" from other classes and functions, however PHP/Server does not know where the physical file is simply based on a Namespace.
So including the file directly, as people has mentioned, lets PHP know exactly what you mean.
When PHP can't find the file, it will call the function spl_autoload_register() and in that method people will usually put a little function to match namespace to directory structure and then load files according.
Another option is to include Composer in your project and use the PSR-4 autoload
{
"require": {
},
"autoload": {
"psr-4": {
"App\\": "app_directoy/",
"Framework\\": "framework_directory/",
}
}
}
When including the composer autoload it will look for everything Framework/* within your framework_directory as you defined.
You should remove 'namespace Framework' and include TestModel.php instead in your index.php - Something like this:
require_once('Models/TestModel.php');
use \Framework\Models\TestModel;
$model = new TestModel();
$model->test();