I'm creating an API wrapper at the moment, and I decided to create an index.php which loads the Client, just for testing/debugging purposes.
Here's my index.php
require_once("src/API/Client.php");
$client = new \API\Client();
Here's my API\Client
namespace API;
class Client
{
public function __construct()
{
$this->_requester = new Client\Request();
return $this;
}
}
The error I'm getting is that API\Client\Request is not found in the Client.php
It does exist, and can be viewed below:
namespace API\Client
class Request
{
protected $_requestUrl;
public function __construct()
{
return $this;
}
}
This is my first foray into making an application that has fully namespaced classes, so I'd appreciate your help in getting this working.
You're missing the require_once statement to include the script that contains the Request class definition.
require_once("src/API/Client.php");
require_once("src/API/Client/Request.php"); // <-- or whatever the filename is
I recommend using an autoloader which means you don't need any include statements. For example this PSR-0 autoloader.
Also, your use of the return statement in the constructors serves no purpose. Constructors can't return values.
Related
I thought this would be easy. For troubleshooting, I wish to call my _logger() function from anywhere in my application.
function _logger(?string $msg=null, int $offset=0, bool $time=false):void
{
$d1 = debug_backtrace()[$offset+1];
$d2 = debug_backtrace()[$offset+2];
syslog(LOG_INFO, sprintf('_logger%s: %s::%s(%s) called by %s::%s on line %s%s', $time?'('.date("Y-m-d H:i:s").')':'', $d1['class'], $d1['function'], json_encode($d1['args']), $d2['class'], $d2['function'], $d2['line'], $msg?' | '.$msg:''));
}
First, I tried adding it to index.php. When added directly in the main body, I got a server error and either with error reporting, couldn't get any info about it (just curious, but anyone know why?). I then placed it into the closure function and it works for HTTP requests only.
<?php
use App\Kernel;
// Added my logger() function here resulted in unexplained server error.
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
return function (array $context) {
// Added my logger() function here works
return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
};
Then I tried adding it to to Kernal.php, and it seems to work, but must be called as \App\_logger().
<?php
declare(strict_types=1);
namespace App;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
class Kernel extends BaseKernel
{
use MicroKernelTrait;
}
if(!function_exists('_logger')) {
// Added my logger() function here works but must be called as \App\_logger()
}
So, then I created a new file called _logger.php which I located in src, and it works sometimes but other times the get the below error and need to clear cache to get it working again.
<?php
declare(strict_types=1);
if(!function_exists('_logger')) {
// Added my logger() function sometimes works but other times doesn't
}
Expected to find class "App_logger" in file
"/var/www/src/_logger.php" while importing services from resource
"../src/", but it was not found! Check the namespace prefix used with
the resource in /var/www/config/services.yaml (which is being
imported from "/var/www/src/Kernel.php"). (500 Internal Server Error)
So, I went back to locating it in Kernel.php, and all is kind of good, but would really know the correct way to add a global function and what are the implications of Symphony's caching when I tried my various approaches.
Design decisions aside, you can achieve this by overriding the namespace in the Kernal.php file:
<?php
declare(strict_types=1);
namespace App {
// Must use bracket notation in order to use global namespace in the same file
// See https://www.php.net/manual/en/language.namespaces.definitionmultiple.php#example-295
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
class Kernel extends BaseKernel
{
use MicroKernelTrait;
}
}
namespace {
// Begin the global namespace
if(!function_exists('_logger')) {
// Added my logger() function here works but must be called as
\App\_logger()
}
}
Note: this code is untested.
Sources:
Define global function from within PHP namespace
https://www.php.net/manual/en/language.namespaces.definitionmultiple.php#example-295
I have a class that contains methods used globally, and am using them by extending the class:
App.php
final class App extends Core {
// The app class handles routing and basically runs the show
}
Core.php
abstract class Core {
public function __construct() { // Here we bring in other classes we use throughout the app
$this->Db = new Db($this);
$this->Mail = new Mail($this);
}
// Then we define multiple methods used throughout the app
public function settings($type) {
// You see this used by the model below
}
}
index.php
$App = new App(); // This fires up the app and allows us to use everything in Core.php
Up until now, this is all great, because everything is handled throughout the site from within $App. However, within my MVC structure, the models need to pull data from the database, as well as retrieve other settings all contained in Core. We do not need the entire $App class to be used by the models, but we need Core to be.
MyModel.php
class MyModel extends Core {
public function welcome() {
return 'Welcome to '.$this->settings('site_name');
}
}
Once MyModel.php comes into play, the Core constructor is run a second time. How do I keep the Core constructor from being run twice?
you can use a static instance in the core class and reuse it.
abstract class Core {
public static $instance; //create a static instance
public function __construct() { // Here we bring in other classes we use throughout the app
$this->Db = new Db($this);
$this->Mail = new Mail($this);
self::$instance = $this; // initialise the instance on load
}
// Then we define multiple methods used throughout the app
public function settings($type) {
// You see this used by the model below
}
}
in the model class, use it like this
class MyModel extends Core {
public function welcome() {
$_core = Core::instance; // get the present working instance
return 'Welcome to '.$_core->settings('site_name');
}
}
you can take a look at this singleton reference
additionally you can check this answer explain-ci-get-instance
I basically have the following directory structure
MiniCrawler
Scripts/
htmlCrawler.php
index.php
This is the index.php
use Scripts\htmlCrawler;
class Main
{
public function init()
{
$htmlCrawler = new htmlCrawler();
$htmlCrawler->sayHello();
}
}
$main = new Main();
$main->init();
And this is the /Scripts/htmlCrawler.php
namespace Scripts;
class htmlCrawler
{
public function sayHello()
{
return 'sfs';
}
}
The code throws the following error
Fatal error: Class 'Scripts\htmlCrawler' not found in
/mnt/htdocs/Spielwiese/MiniCrawler/index.php on line 9
You forgot to include the file /Scripts/htmlCrawler.php in your index.php file.
require_once "Scripts/htmlCrawler.php";
use Scripts\htmlCrawler;
class Main
{
public function init()
{
$htmlCrawler = new htmlCrawler();
$htmlCrawler->sayHello();
}
}
$main = new Main();
$main->init();
Your index file cannot find the definition of the htmlCrawler file if you never provide the file defining this class, and the use of namespaces doesn't automatically include the required classes.
The reason why frameworks don't require you to include manually the file and you can simply add the use statement is because they're handling the inclusion of required classes for the developer. Most of the frameworks are using composer to handle the automatic inclusion of the files.
You can obtain a somewhat similar functionality using autoloading.
Currently I am trying to understand how OOP applies to PHP and I am having trouble with calling my class when I am getting into inheritance.
I am using the following PHP code:
require_once("init.php");
$table = new Table();
$table->draw();
$customer1 = new Customer();
Since the table class is just a plain class (not inherited) it will load correctly.
The init.php has the following PHP code:
function __autoload($class_name) {
require_once('classes/'.$class_name . '.class.php');
}
Because the Customer class is inhereting the User class, the code for the Customer class is inside the User class, however the __autoload function is trying to call for the customer.class.php now.
My Customer class would look something like this:
class User
{
private $_username;
public function __construct($name)
{
$this->_username = $name;
}
public function getUsername()
{
return $this->_username;
}
}
class Customer extends User
{
public function __construct()
{
//some code here
}
}
Cany anyone explain me please how I should call an inherited class with PHP?
You should only have one class per file. Your autoload will handle everything for you as long as every class is in its own file, named the same way...
Put all your Customer class code inside Customer.class.php
Each class should be written in its own file, exactly because of this autoload. There's no reason why class Customer must be within the same file as class User, just put it in its own file and your autoloader will handle it correctly.
Otherwise, you'll have to adopt some other naming scheme and write a more intelligent autoloader which can find classes in files with different names. (Don't do that, not really.)
What I want:
If session is not set, I want to redirect to zf2.localhost/authUser/index
When I'm using toRoute in CreatorController everything works.
However, I want to use this method in several files. When I attempt to call toRoute in my SessionServiceController, I get the following error.
Error:
Url plugin requires that controller event compose a router; none found
Could you write me what I'm doing wrong?
Code:
CreatorController (zf2.localhost/creator/index)
<?php
namespace Creator\Controller;
use Zend\View\Model\ViewModel;
use Zend\Mvc\Controller\AbstractActionController;
use Creator\Controller\SessionServiceController;
class CreatorController extends AbstractActionController{
public function __construct(){
$this -> sessionService = new SessionServiceController();
}
public function indexAction(){
if($this-> sessionService ->checkSession()){
echo 'Witam';
}
}
}
SessionServiceController:
<?php
namespace Creator\controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Session\Container;
use Creator\Controller\SessionServiceController;
class SessionServiceController extends AbstractActionController {
const CONTAINER_SESSION_ID = 'usr_id';
public function __construct(){
$this -> session_id = new Container( self::CONTAINER_SESSION_ID);
}
public function checkSession(){
if (empty($this ->session_id->usr_id)){
//$this->redirect()->toUrl("zf2.localhost/authUser/index");
return $this->redirect()->toRoute('creator/default',
array('controller'=>'authUser', 'action'=>'index'));
}
if (!empty($this ->session_id->usr_id)){
return true;
}
}
}
You don't want to directly instantiate your controllers, use the ServiceManager. For toRoute to work, the Url helper needs a instance of the router. This is usually handled by the ControllerPluginManager when a controller is called from the ControllerManager.
But, beyond that, this logic is probably in the wrong place. If you're checking for authenticated users, you probably want to load and utilize zend's authentication service in an event before the controller is even loaded. I would guess the best place is somewhere in your ON_ROUTE event loop, but I can't speak to your specific application.
You will know if a user is logged in or not very early in the request chain, no reason to boot a controller. Take a peek at ZfcUser (https://github.com/ZF-Commons/ZfcUser), it already has versions of auth redirect listeners you can use as inspiration for your own (or you might find you just want to use that project for what you're doing)