i saw a lot of ways that you can use to call a class inside another one in PHP and i want your opinion about the shortest version of calling a class.
lets say we have a class name view,
and another class name controller
class View
{
private $data = array();
private $render = FALSE;
public function __construct($template , $datas = null)
{
try {
$file = strtolower($template) . '.php';
if (file_exists($file)) {
if($datas > 0) {
foreach($datas as $data) {
array_push($this->data, $data);
}
}
$this->render = $file;
} else {
die('Template ' . $template . ' not found!');
}
}
catch (customException $e) {
echo $e->errorMessage();
}
}
public function __destruct()
{
extract($this->data);
include($this->render);
}
}
and
require_once "system/autoload.php";
class Controller {
function index() {
$view = new View('something');
}
i know that i can use
$view = new View('something');
or use OOP and extent and call a function from view inside controller like
$this->viewFunction();
but is there any way that i can call view class inside controller like this
View('something)
i want to make it shortest version possible
if it is not possible or i have to make change inside compiler well just give me the shortest version
thank you all
You can surely do this in PHP. Have a look at magic methods, especially __invoke()
class View
{
public function __invoke(string $template)
{
return $template;
}
}
You can simply invoke it by doing
$view = new View();
$view('my template');
Related
I have this file root/core/Router.php
<?php
namespace Core;
class Router {
protected $url;
protected $controller;
private function parseURL() {
// threat the $this->url; for example ["r", "product"]
}
private function request() {
$this->controller = Controller::get($this->url[1]);
}
public function __construct() {
$this->parseURL();
$this->request();
}
}
?>
then file root/core/Controller.php
<?php
namespace Core;
class Controller {
public static function model($name, $params = []) {
$model = "\\Model\\$name";
return new $model($params);
}
public static function view($name, $params = []) {
require_once APP_DIR . "view/" . $name . ".php";
}
public static function get($name, $params = []) {
require_once APP_DIR . "controller/" . $name . ".php";
$name = "\\Controller\\$name";
return new $name($params);
}
}
?>
then root/controler/Product.php
<?php
namespace Controller;
use Core\Controller;
use Model\Product;
class Product {
public function get() {
$ret['state'] = 510;
$productModel = new Product;
$products = $productModel->getAll();
if(isset($products)) {
$ret['products'] = $products;
$ret['state'] = 200;
}
return $ret;
}
}
?>
then file root/model/Product.php
<?php
namespace Model;
class Product {
public function add($values) {
return Database::insert("product", $values);
}
}
?>
and root/core/Model.php
<?php
namespace Core;
class Model {
protected $table = null;
public function getAll() {
// some code to collect data
}
}
?>
What i want to achive is that every Controller in root/controller/*.php able to load any Model in root/model/*.php but class inside root/model/*.php must able to access (inheritance/extends) the Model class inside root/core/Model.php i firstly asked on chatGPT for some AI Generated answer, that the reason why i get this far.
Then i get this error, when the AI keep giving the same answer.
Fatal error: Cannot declare class Controller\Product because the name is already in use in C:\xampp\htdocs\app\shop\controller\Product.php on line 6
I actually realize that the simple way probably with naming the class so ther no conflict between it but i became aware how to properly using the namespace if its such features in php. Those files loaded without any autoloader, so i just require_once each file in root/init.php file.
I read few documentations but hard to implement in multiple files and directorys.
I Apreciate any feedback, thanks
I am trying to load (include file) the GetRiskSummaryCommandHandler.php (GetRiskSummary\CommandHandler) at runtime dynamically, while resolving Route api/risks to HandleCommand (of class CommandHandler) method.
How can I do this? If not can I modify any taken approach including modifying autoloader?
My api class snippet looks like this:
API.php
<?php
abstract class API
{
public function processRequest()
{
$id1 = $this->requestObj->id1;
//$id2 = $this->requestObj->id2;
$endpoint1 = $this->requestObj->endpoint1;
$endpoint2 = $this->requestObj->endpoint2;
$isDestination = in_array($id1, ['first', 'prev', 'next', 'last']);
$numSetEndpoints = (int)isset($endpoint1) + (int)isset($endpoint2);
switch($numSetEndpoints)
{
case 0:
if ($isDestination)
return json_decode($this->_response("No Endpoint: ", $endpoint1));
return json_decode($this->_response("ProjectAIM API"));
case 1:
$className = $endpoint1.'Controller';
break;
case 2:
$className = $endpoint2.'Controller';
break;
}
$class = "GetRiskSummaryCommandHandler";
$method = "HandleCommand";
if (class_exists($class))
{
if (method_exists($class, $method))
{
$response = (new $class($this->requestObj))->{$method}($this->requestObj);
if ($response['Succeeded'] == false)
{
return $response['Result'];
}
else if ($response['Succeeded'] == true)
{
header("Content-Type: application/json");
return $this->_response($response);
}
else if ($response['Result'])
{
header("Content-Type: text/html");
return $this->_response($response);
}
}
}
}
**Command Handler Snippet, uses a Route Attiribute
GetRiskSummaryCommandHandler.php
<?php
namespace GetRiskSummary;
use Infrastructure\CommandHandler;
class GetRiskSummaryCommandHandler extends CommandHandler
{
#[Route("/api/risks", methods: ["GET"])]
public function HandleCommand()
{
$qb = $this->getEntityManager()
->createQueryBuilder();
$qb->select('*')
->from('Risks', 'Risks')
->orderBy('RiskID', 'DESC');
$query = $qb->getQuery();
return $query->getResult();
}
}
Autoloader.php
<?php
namespace Autoloader;
class Autoloader
{
private static function rglob($pattern, $flags = 0) {
$files = glob($pattern, $flags);
foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir) {
$files = array_merge($files, self::rglob($dir.'/'.basename($pattern), $flags));
}
return $files;
}
public static function ClassLoader($path)
{
$pathParts = explode("\\", $path);
$path = $pathParts[array_key_last($pathParts)];
$matches = self::rglob("*/$path*");
foreach ($matches as $name)
{
$filePath = realpath($name);
if (file_exists($filePath))
include $filePath;
}
}
}
spl_autoload_register("AutoLoader\AutoLoader::ClassLoader");
I think the biggest missing feature of PHP Attributes would be that as far as I'm aware PHP Attributes are static and aren't evaluated at runtime. They are just glorified comments.
If it was true, we could do some truly amazing things like non-intrusively attaching pre/post-processing functions, just like what you wrote.
They work just like in symfony or doctrine. You can't use variables in them and you must write your own code to search your php class tree for attributes in the code and use php reflection to obtain the information and run the attributes.
To be honest it's pretty disappointing, although maybe it's just the first step that will be fixed in later versions of PHP.
But for now, I think what you're asking for is impossible. Because they are not running dynamically like you appear to be asking.
I have a class that makes an economic calendar out of a json string. The only problem is that I don't know if I should use file_get_contents()(to get the data from an api) inside my class __constructor() or I should just pass the json string to the __constructor from my try{...}catch{...} block?
Which practice is better and why?
Here is my class(EconomicCalendar.php) so far:
class EconomicCalendar{
private $_data,
$_calendar = [];
public function __construct($url){
$this->_data = json_decode(file_get_contents($url));
}
private function make_economic_calendar(){
foreach($this->_data->events as $e){
$arr[$e->date][] = [
'title' => $e->title,
'date' => $e->date
];
}
if(is_array($arr) && count($arr) >= 1){
return (object)$arr;
} else{
throw new Exception('EC was not created');
}
}
public function get_calendar(){
$this->_calendar = $this->make_economic_calendar();
return $this->_calendar;
}
}
Here is the code(ec.php) that outputs the calendar:
spl_autoload_register(function($class){
require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . $class . '.php';
});
try {
$c = new EconomicCalendar('https://api.example.com/ec?token={MY_TOKEN}');
$economic_calendar = $c->get_e_list();
} catch (Exception $e) {
exit($e->getMessage());
}
Thank you!
Almost always is better to make IO operation as late (or as little) as possible. So I recommend you to use "named constructor" if you want initialize with data
class EconomicCalendar {
...
public function __construct($data){
$this->_data = $data;
}
...
public static function fromUrl($url){
return new self(json_decode(file_get_contents($url)));
}
}
And usage:
$instance = EconomicCalendar::fromUrl('https://api.example.com/ec?token={MY_TOKEN}');
Moving IO and decoding to dedicated function is closer to single responsibility principle (IO at static, logic at class instance).
Well, is there something like before() method in kostache module? For example, if I have a couple of PHP lines inside of the view file, I'd like to execute them separately inside of the view class, without echoing anything in the template itself. How can I handle that?
You can put this type of code in the constructor of your View class. When the view is instantiated, the code will run.
Here is a (slightly modified) example from a working application. This example illustrates a ViewModel that lets you change which mustache file is being used as the site's main layout. In the constructor, it chooses a default layout, which you can override if needed.
Controller:
class Controller_Pages extends Controller
{
public function action_show()
{
$current_page = Model_Page::factory($this->request->param('name'));
if ($current_page == NULL) {
throw new HTTP_Exception_404('Page not found: :page',
array(':page' => $this->request->param('name')));
}
$view = new View_Page;
$view->page_content = $current_page->Content;
$view->title = $current_page->Title;
if (isset($current_page->Layout) && $current_page->Layout !== 'default') {
$view->setLayout($current_page->Layout);
}
$this->response->body($view->render());
}
}
ViewModel:
class View_Page
{
public $title;
public $page_content;
public static $default_layout = 'mytemplate';
private $_layout;
public function __construct()
{
$this->_layout = self::$default_layout;
}
public function setLayout($layout)
{
$this->_layout = $layout;
}
public function render($template = null)
{
if ($this->_layout != null)
{
$renderer = Kostache_Layout::factory($this->_layout);
$this->template_init();
}
else
{
$renderer = Kostache::factory();
}
return $renderer->render($this, $template);
}
}
I'm building a little MVC system (learning) and I have some problems with showing variables in my view files.
This is from my View class:
private $vars = array();
public function __set($key, $value)
{
$this->vars[$key] = $value;
}
public function __get($key)
{
return $this->vars[$key];
}
public function __toString()
{
return $this->vars[$key];
}
public function show($file)
{
global $router;
$folder = strtolower($router->current_controller);
$path = VIEWPATH.$folder.'/'.$file.'.phtml';
if ( ! file_exists($path))
{
die("Template: $file, not found");
}
include ($path);
}
And here is from my controller:
$test = new View();
$test->name = 'karl';
$test->show('name_view');
And the view file (name_view)
echo $name // doesn't work
echo $this->name // Works
What am I doing wrong? Perhaps I haft to make something global?
THX / Tobias
EDIT: I just extracted the vars array in the view class right before I include the view file and then it worked.. Thank you for all help.
There is no $key in __toString()!
Also __toString() doesn't accept any parameters!
Test it with this:
public function __toString()
{
return json_encode($this->vars);
}
After your edit I realized that your problem is not on the __toString() method (you can just delete it since you're not using it). Doing echo $this->name is the correct way to show variables from inside your view in your case, however if you want to just do echo $name may I suggest a different approach?
function View($view)
{
if (is_file($view) === true)
{
$arguments = array_slice(func_get_args(), 1);
foreach ($arguments as $argument)
{
if (is_array($argument) === true)
{
extract($argument, EXTR_OVERWRITE);
}
}
require($view);
}
}
Use the View function like this:
$data = array
(
'name' => 'karl',
);
View('/path/to/your/name_view.phtml', $data);
Now it should work just by doing echo $name;, you can adapt it to your View class if you want to. If that doesn't work, try changing the name_view view extension to .php.