I know this is a really bad idea, but I ran out of ideas, so I'd like to hear some opinions about it.
I'm working on a PHP project which allows the development of addons so that other developers can add their own functions, not so different from thousands of other open-source projects:
root
└── addons
└── addon-a
└── addon-b
└── addon-c
I have to implement some changes to an already made addon (let's say Addon A) without changing the original files or using Composer PSR-4. Unfortunately, the original author did not follow good practices that would help to accomplish this task. Here's how the Addon A is:
Structure
addon-a
└── main.php
└── classes
└── Foo.php
└── Bar.php
addon-a/main.php
include_once __DIR__ . '/classes/Foo.php';
function run() {
echo AddonA\Classes\Foo::returnTwo();
}
addon-a/classes/Foo.php
namespace AddonA\Classes
use AddonA\OtherFolder\FooBar
abstract class Foo {
public static function returnTwo() {
return 2;
}
public static function returnFooBar() {
return new FooBar();
}
}
I need to override the returnTwo function and return another value. Without changing other files, the only way I was able to do it was through the class_alias method, copying the whole Foo class:
First, I created another addon and made it ran before the Addon A "run" function:
Structure
new-addon-a
└── main.php
└── classes
└── Foo.php
new-addon-a/main.php
function init() {
include_once __DIR__ . '/classes/Foo.php';
class_alias( 'NewAddonA\Classes\Foo', 'AddonA\Classes\Foo', true );
}
init();
new-addon-a/classes/Foo.php
namespace NewAddonA\Classes
use AddonA\OtherFolder\FooBar // No changes to the previous "uses."
abstract class Foo {
public static function returnTwo() {
return 3;
}
public static function returnBar() {
return new AddonA\Classes\Bar(); // Here I had to change because the namespace has changed
}
}
Now, when Addon A executes the function run, the AddonA\Classes\Foo::returnTwo will be my own class.
Well, as I said before, this is pretty ugly, and I would lose any update to the Foo code since I'm overriding the entire class. However, this was the only way I thought. Do you think it would be possible to use another method?
Thanks!
I have two controller file homecontroller and backendcontroller. What is the best way to create global function and access it from both files?
I found here Arian Acosta's answer helpful but I wonder if there is an easiest way. I would appreciate any suggestions.
Solution
One way to do this is to create a class and use its instance, this way you can not only access the object of the class within a controller, blade, or any other class as well.
AppHelper file
In you app folder create a folder named Helpers and within it create a file name AppHelper or any of your choice
<?php
namespace App\Helpers;
class AppHelper
{
public function bladeHelper($someValue)
{
return "increment $someValue";
}
public function startQueryLog()
{
\DB::enableQueryLog();
}
public function showQueries()
{
dd(\DB::getQueryLog());
}
public static function instance()
{
return new AppHelper();
}
}
Usage
In a controller
When in a controller you can call the various functions
public function index()
{
//some code
//need to debug query
\App\Helpers\AppHelper::instance()->startQueryLog();
//some code that executes queries
\App\Helpers\AppHelper::instance()->showQueries();
}
In a blade file
Say you were in a blade file, here is how you can call the app blade helper function
some html code
{{ \App\Helpers\AppHelper::instance()->bladeHelper($value) }}
and then some html code
Reduce the overhead of namespace (Optional)
You can also reduce the overhead of call the complete function namespace \App\Helpers by creating alias for the AppHelper class in config\app.php
'aliases' => [
....
'AppHelper' => App\Helpers\AppHelper::class
]
and in your controller or your blade file, you can directly call
\AppHelper::instance()->functioName();
Easy Solution:
Create a new Helpers folder in your app directory.
Create a php file named your_helper_function.php in that Helpers directory.
Add your function(s) inside your_helper_function.php
function your_function($parameters){
//function logic
}
function your_another_function($parameters){
//function logic
}
Add this file to the Files key of your composer.json like
"autoload": {
...
"files": [
"app/Helpers/your_helper_function.php"
]
...
}
Finally, regenerate composer autoload files. (Run this in your project directory)
composer dump-autoload
That's it! and now you can access your_function() or your_another_function() in any part of your Laravel project.
If you still have any confusion, check my blog post on how to do this:
How to Add a Global Function in Laravel Using Composer?
Updated:
Step 1
Add folder inside app folder
app->Helper
Step 2
add php Class inside Helper folder
Eg. Helper.php
Add namespace and class to the Helper.php
namespace App\Helper;
class Helper
{
}
Register this Helper.php into config/app.php file
'aliases' => [
....
'Helper' => App\Helper\Helper::class
]
Now, write all the functions inside Helper.php and it will be accessible everywhere.
How to access from Controller?
Step 1 - Add a namespace at top of the controller.
use App\Helper\Helper;
Step 2 - Call function - Assume there a getInformation() inside the Helper Class.
$information = Helper::getInformation()
In your Controller.php which extends BaseController, you can create a function like;
public function data($arr = false)
{
$data['foo'] = 'bar';
return array_merge($data,$arr);
}
And from any controller when you send a data to a view;
public function example()
{
$data['smthg'] = 'smthgelse';
return view('myView',$this->data($data));
}
The data in the the main controller can be accessed from all controllers and blades.
The Laravel Service Provider way
I've been using global function within Laravel for a while and I want to share how I do it. It's kind of a mix between 2 answers in this post : https://stackoverflow.com/a/44021966/5543999 and https://stackoverflow.com/a/44024328/5543999
This way will load a file within a ServiceProvider and register it within your Laravel app.
Where is the difference, the scope, it's always about the scope.
Composer //Autload whitin composer.json method
|
|--->Laravel App //My method
|
|--->Controller //Trait method
|--->Blade //Trait method
|--->Listener //Trait method
|--->...
This is a really simplist way to explain my point, all three methods will achieve the purpose of the "Global function". The Traits method will need you to declare use App\Helpers\Trait; or App\Helpers\Trait::function().
The composer and service provider are almost about the same. For me, they answer better to the question of what is a global function, because they don't require to declare them on each place you want to use them. You just use them function(). The main difference is how you prefer things.
How to
Create the functions file : App\Functions\GlobalFunctions.php
//App\Functions\GlobalFunctions.php
<?php
function first_function()
{
//function logic
}
function second_function()
{
//function logic
}
Create a ServiceProvider:
//Into the console
php artisan make:provider GlobalFunctionsServiceProvider
Open the new file App\Providers\GlobalFunctionsServiceProvider.php and edit the register method
//App\Providers\GlobalFunctionsServiceProvider.php
public function register()
{
require_once base_path().'/app/Functions/GlobalFunctions.php';
}
Register your provider into App\Config\App.php wihtin the providers
//App\Config\App.php
'providers' => [
/*
* Laravel Framework Service Providers...
*/
Illuminate\Auth\AuthServiceProvider::class,
...
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
App\Providers\GlobalFunctionsServiceProvider::class, //Add your service provider
Run some artisan's commands
//Into the console
php artisan clear-compiled
php artisan config:cache
Use your new global functions
//Use your function anywhere within your Laravel app
first_function();
second_function();
Laravel uses namespaces by default. So you need to follow the method described in that answer to setup a helper file.
Though in your case you want to access a method in different controllers. For this there's a simpler way. Add a method to you base controller app/Http/Controllers/Controller.php and you can access them in every other controller since they extend it.
// in app/Http/Controllers/Controller.php
protected function dummy()
{
return 'dummy';
}
// in homecontroller
$this->dummy();
There are a few ways, depending on the exact functionality you're trying to add.
1) Create a function inside Controller.php, and make all other controller extend that controller. You could somewhat compair this to the master.blade.php
2) Create a trait, a trait can do a lot for you, and keeping ur controllers clean. I personally love to use traits as it will look clean, keep my Controller.php from being a mess with tons of different lines of code.
Creating a global function
create a Helpers.php file under a folder, let's name it 'core'.
core
|
-- Helpers.php
namespace Helpers; // define Helper scope
if(!function_exists('html')) {
function html($string) {
// run some code
return $str;
}
}
In your composer.json
"autoload": {
"psr-4": {
},
"files": [
"core/Helpers.php"
]
}
in the file that you want to use it
// the " use " statement is not needed, core/Helpers is loaded on every page
if(condition_is_true) {
echo Helpers\html($string);die();
}
Remove the namespace in Helpers.php if you want to call your function without the need to prefix namespace. However I advise to leave it there.
Credit: https://dev.to/kingsconsult/how-to-create-laravel-8-helpers-function-global-function-d8n
By using composer.json and put the function containing file(globalhelper.php) to the autoload > files section, then run
composer dump-autoload
You can access the function inside the file(globalhelper.php) without having to calling the class name, just like using default php function.
I've developed My Own MVC Framework using php.
I call view files in controller like:
include('../view/home.php');
but I want to use it like:
$this->view('home');
How can I define common function for that where I can just pass view name i.e home only and it will do include view file without passing the full file path?
No one could answer you without seeing your codes really. But this should be my approach.
You should have a class that all your controllers extend. Lets say that you have class Controllers and all your controllers extend it.
Then you may have a method inside the class named view($view_name).
public function view($view_name){
include $some_path . '/' . $view_name . '.php';
}
then whenever you call view by $this->view it will include the view if it exists.
This is not the best approach and I did not test the code. I just wanted to show you the path
I don't know Your MVC file/directory/namespace and etc structure.
But for beginner who tries to learn MVC and Frameworks by "reinventing wheel" (: I can give such example:
1) Create common abstract controller class in app/controllers folder:
namespace App\Controllers;
abstract class Controller {
public function view($name) {
include(__DIR__.'/../views/'.$name.'.php';
}
}
2) Create Your own controller i.e. PagesController and use it:
namespace App\Controllers;
class PagesController extends Controller {
public function home() {
$this->view('home');
}
}
p.s. You may omit namespace-ing, abstract word depending on autoloader logic
My question is: If we create any object of one class, that class file should be included in our file .In zend frame work. How is this possible without including that file?
example:
If I have indexcontroller in controller folder and Article model with name Article.php in model folder then how is articale.php available in index controller?
class IndexController extends Zend_Controller_Action {
private $_model;
public function indexAction() {
$model=new Model_Article();/*how it is possible where and when this file include in index controller */
}
}
?>
sorry for my poor English
assuming you are using zf1 version not zf2 , you can do it with,
$model = new Application_Model_Article();
where article.php is under model folder.
you can call this in any of the controller and any of the action.
then you can use for example $model->fetchAll(); to Invoke any function of your model..
Zend Autoloader is responsible for automatic class loading. It uses __autoload. You can read more about it here and here.
I've the next structure
-application
--controllers
---Sub
----DemoController.php
---IndexController.php
-models
-views
-Boostrap.php
Well, in my Sub/DemoController.php I've the next:
<?php
class Sub_DemoController extends Zend_Controller_Action
{
public function indexAction()
{
echo 'hello demo';
}
}
And my IndexController.php like this:
<?php
class IndexController extends Sub_DemoController
{
}
When I run my web throw the next error: Class 'Sub_DemoController' not found ...
I try with initialize class with Application_Sub_DemoController, but return the same result.
I don't want use modules, I know how uses modules in Zend Framewoork but I don't looking for it.
Any ideas?
You need to add resourceType to your resource autoloader add this to you bootstrap file:
$this->getResourceLoader()
->addResourceType('sub', 'controllers/Sub', 'Sub');
More: http://framework.zend.com/manual/1.12/en/zend.loader.autoloader-resource.html
Please have a look at Zend framework (1.7.5): how to change controller directory
I think you didn't set the controller directory properly or your autoloader does not work.