Troubles with GO! Aspect-Oriented Framework - php

If someone work with GO! framework, can you help me.
I install framework on php 5.3.13. Demo example is working.
But my own example doesn't work. Aspect(method beforeMethodExecution) is not perfomed.
Here is my code.
Main file:
//1 Include kernel and all classes
if (file_exists(__DIR__ .'/../../vendor/autoload.php')) {
$loader = include __DIR__ .'/../../vendor/autoload.php';
}
// 2 Make own ascpect kernel
use Go\Core\AspectKernel;
use Go\Core\AspectContainer;
class Kernel extends AspectKernel{
/**
* Configure an AspectContainer with advisors, aspects and pointcuts
*
* #param AspectContainer $container
*
* #return void
*/
public function configureAop(AspectContainer $container)
{
}
}
//3 Initiate aspect kernel
$Kernel = Kernel::getInstance();
$Kernel->init();
//4 Include aspect
include(__DIR__.'/aspectclass/AspectClass.php');
$aspect = new DebugAspect();
//5 register aspect
$Kernel->getContainer()->registerAspect($aspect);
//6 Include test class
include(__DIR__.'/class/class1.php');
//7 Execute test class
$Class = new General('test');
$Class->publicHello();
File with test class:
class General{
protected $message = '';
public function __construct($message)
{
$this->message = $message;
}
public function publicHello()
{
echo 'Hello, you have a public message: ', $this->message, "<br>", PHP_EOL;
}
}
File with aspect:
use Go\Aop\Aspect;
use Go\Aop\Intercept\FieldAccess;
use Go\Aop\Intercept\FunctionInvocation;
use Go\Aop\Intercept\MethodInvocation;
use Go\Lang\Annotation\After;
use Go\Lang\Annotation\Before;
use Go\Lang\Annotation\Around;
use Go\Lang\Annotation\Pointcut;
use Go\Lang\Annotation\DeclareParents;
use Go\Lang\Annotation\DeclareError;
class DebugAspect implements Aspect{
/**
* Method that should be called before real method
*
* #param MethodInvocation $invocation Invocation
* #Before("execution(General->*(*))")
*
*/
public function beforeMethodExecution(MethodInvocation $invocation)
{
$obj = $invocation->getThis();
echo 'Calling Before Interceptor for method: ',
is_object($obj) ? get_class($obj) : $obj,
$invocation->getMethod()->isStatic() ? '::' : '->',
$invocation->getMethod()->getName(),
'()',
' with arguments: ',
json_encode($invocation->getArguments()),
PHP_EOL;
}
}

As you know, go-aop isn't a PHP extension, so it couldn't transform classes that were loaded directly via require or include. Internally it tries to overwrite the source code on-the-fly, but it should receive a control (via integration with composer or custom autoloader class).
So, you have an error here:
//6 Include test class
include(__DIR__.'/class/class1.php');
You explicitly load this class into memory and there is no way to transform it from userland. To pass a control to the framework, you should make this explicitly. Look at the line AopComposerLoader.php#L99 to have an idea how it works. Here we include a source file via the stream source filter that pass control to the framework and it can transform the class to weave an aspects.
To fix your example just change an include to the following:
include (FilterInjectorTransformer::rewrite(__DIR__.'/class/class1.php'));

Related

amphp auto-load class not working as expected

I'm trying to use a custom class in a worker using amphp but it doesn't seem to be working. The below class is already auto-loaded using composer. Please help me out with this issue. My code is below:
Class (this implements Task as mentioned on their docs):
<?php
namespace Jobs;
require '/home/xxx/vendor/autoload.php';
use Amp\Parallel\Worker\Environment;
use Amp\Parallel\Worker\Task;
class GetMarketJob implements Task {
/**
* #var callable
*/
private $function;
/**
* #var mixed[]
*/
private $args;
public function __construct($function, ...$args) {
$this->function = $function;
$this->args = $args;
}
/**
* {#inheritdoc}
*/
public function run(Environment $environment)
{
if ($this->function instanceof \__PHP_Incomplete_Class) {
throw new \Error('When using a class instance as a callable, the class must be autoloadable');
}
if (\is_array($this->callable) && ($this->callable[0] ?? null) instanceof \__PHP_Incomplete_Class) {
throw new \Error('When using a class instance method as a callable, the class must be autoloadable');
}
if (!\is_callable($this->function)) {
$message = 'User-defined functions must be autoloadable (that is, defined in a file autoloaded by composer)';
if (\is_string($this->function)) {
$message .= \sprintf("; unable to load function '%s'", $this->function);
}
throw new \Error($message);
}
return ($this->function)(...$this->args);
}
public function testMe($url = NULL) {
$test = file_get_contents($url);
return $test;
}
}
File using amphp to assign worker using above class:
<?php
require '/home/xxxx/vendor/autoload.php';
use Jobs\GetMarketJob;
// Example async producer using promisor
use Amp\Parallel\Worker;
use Amp\Promise;
use Amp\Loop;
use Amp\Parallel\Worker\DefaultPool;
use Amp\Parallel\Worker\Task;
use Amp\Parallel\Worker\Environment;
use Amp\Parallel\Worker\TaskFailureError;
use Amp\Parallel\Worker\DefaultWorkerFactory;
Amp\Loop::run(function () {
$factory = new DefaultWorkerFactory();
$worker = $factory->create();
$result = yield $worker->enqueue(new GetMarketJob('testMe', ['https://www.syhtek.com']));
print($result);
$code = yield $worker->shutdown();
\printf("Code: %d\n", $code);
});
running this script gives me the below output:
[26-May-2021 01:23:11 UTC] PHP Fatal error: Uncaught
Amp\Parallel\Worker\TaskFailureError: Uncaught Error in worker with
message "User-defined functions must be autoloadable (that is, defined
in a file autoloaded by composer); unable to load function 'testMe'"
and code "0"; use
Amp\Parallel\Worker\TaskFailureError::getOriginalTrace() for the stack
trace in the worker in
/home/xxxx/vendor/amphp/parallel/lib/Worker/Internal/TaskFailure.php:60
Thank you so much for reading!
The issue here is that you're passing 'testMe' and then check if (!\is_callable($this->function)) {, while it should be if (!\method_exists($this, $this->function)) {.
And return ($this->function)(...$this->args); should be return ($this->{$this->function})(...$this->args); if you're trying to call that method. You might also call that method directly instead of giving it to the constructor.
If everything you do in the worker is an HTTP request, you should look into amphp/http-client instead of amphp/parallel, as non-blocking I/O is much more efficient than several chlid processes with blocking I/O.

Php use a class in the constructor of another class and require it with namespace

I have som legacy code that broke after an update of a dependency class of my ex colleague's Handler class of the Cosenary Instagram Class using composer.
The former reference to include the class in the handler class was this:
namespace Acme\Instagram;
class Handler
{
/* GOT SOME USEFUL VARS HERE */
const API_URL = 'https://api.instagram.com/v1';
/**
* Initialize the library
*
* #param array $settings Contains our ClientId and upload dir
* #param string $root_dir The root of our application
*/
public function __construct( $settings, $root_dir = __DIR__ )
{
include_once $root_dir . '/vendor/cosenary/instagram/instagram.class.php'; //HERE WAS THE INCLUDE BEFORE
// THROW SOME EXCEPTIONS HERE IF SETTINGS MISSING ETC...
// New instance of the instagram class
$this->_instagram = new \Instagram($this->_clientId);
}
/* HANDLE SOM FUNCTIONS HERE */
}
And if I change the include_once to:
require_once $root_dir . '/vendor/cosenary/instagram/src/Instagram.php';
Then I get:
Fatal error: Class 'Acme\Instagram\Instagram' not found in
I guess I need to pass it in as a reference in the constructor but that means I need to rewrite a lot of code and there is probably 5-10 other projects that is depending on this Hanlder class. Is there a way to use the instagram class in this other class?
Tried moving out the include_once and:
use MetzWeb\Instagram\Instagram;
But no luck, any help or pointers i greatly appreciated.
I'm not sure how is Your app's structure looks.
But try this:
namespace Acme\Instagram;
require_once $root_dir.'/vendor/autoload.php';
use MetzWeb\Instagram\Instagram AS InstagramClient;
class Handler
{
/* GOT SOME USEFUL VARS HERE */
const API_URL = 'https://api.instagram.com/v1';
/**
* Initialize the library
*
* #param array $settings Contains our ClientId and upload dir
* #param string $root_dir The root of our application
*/
public function __construct( $settings, $root_dir = __DIR__ )
{
// New instance of the instagram class
$this->_instagram = new InstagramClient($this->_clientId);
}
/* HANDLE SOM FUNCTIONS HERE */

Doctrine annotation loader fails

I'm trying to run JMSSerializer. My simple code
use JMS\Serializer\Annotation\Type;
class Comment
{
private $msg;
public function __construct($msg)
{
$this->msg = $msg;
}
}
class Person
{
/**
* #Type("array<Comment>")
*/
private $commentList;
public function addComment(Comment $comment)
{
$this->commentList[] = $comment;
}
}
$type = new Type;
$serializer = JMS\Serializer\SerializerBuilder::create()->build();
$data = new Person();
$data->addComment(new Comment('hey'));
var_dump($serializer->serialize($data, 'json'));
fails with
PHP Fatal error: Uncaught exception 'Doctrine\Common\Annotations\AnnotationException' with message '[Semantical Error] The annotation "#JMS\Serializer\Annotation\Type" in property Person::$commentList does not exist, or could not be auto-loaded.' in xxx.php:52
OK, but if I add line
$type = new Type;
to trigger autoloader manually, it works:
string(32) "{"comment_list":[{"msg":"hey"}]}"
As I see AnnotationRegistry doesn't use autoloader, it tries to use some own autoloader. It looks ugly, what do I have to do to fix it?
OK, I answer my question myself. I have to register annotations somewhere in autoloader file:
\Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespace(
'JMS\Serializer\Annotation', __DIR__.'/vendor/jms/serializer/src'
);
Other ways: http://docs.doctrine-project.org/projects/doctrine-common/en/latest/reference/annotations.html#registering-annotations
A full configuration sample for the standalone JMS serializer library could be:
<?php
namespace iMSCP\Service;
use JMS\Serializer\Serializer;
use JMS\Serializer\SerializerBuilder;
use Doctrine\Common\Annotations\AnnotationRegistry;
use iMSCP_Registry as Registry;
/**
* Class SerializerServiceFactory
* #package iMSCP\Service
*/
class SerializerServiceFactory
{
/**
* #var Serializer
*/
static $serialiszer;
public static function create()
{
if (static::$serialiszer === null) {
$config = Registry::get('config');
AnnotationRegistry::registerAutoloadNamespace(
'JMS\Serializer\Annotation', $config['CACHE_DATA_DIR'] . '/packages/vendor/jms/serializer/src'
);
static::$serialiszer = SerializerBuilder::create()
->setCacheDir(CACHE_PATH . '/serializer')
->setDebug($config['DEVMODE'])
->build();
}
return static::$serialiszer;
}
}
Here, I register the JMS\Serializer\Annotation namespace using the Annotation registry as provided by Doctrine. Once done, all is working as expected.

Custom classes in CodeIgniter

Seems like this is a very common problem for beginners with CodeIgniter, but none of the solutions I've found so far seems very relevant to my problem. Like the topic says I'm trying to include a custom class in CodeIgniter.
I'm trying to create several objects of the class below and place them in an array, thus I need the class to be available to the model.
I've tried using the load (library->load('myclass') functions within CodeIgniter which sort of works, except it tries to create an object of the class outside the model first. This is obviously a problem since the constructor expects several parameters.
The solutions I've found so far is
A simple php include which seems fine enough, but since I'm new to
CodeIgniter I want to make sure I'm sticking to it as much as
possible.
Creating a "wrapper class" as suggested here, however I'm uncertain how I would implement this.
The class I want to include,
User.php
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class User{
public $ID = 0;
public $username = 0;
public $access_lvl = 0;
public $staff_type = 0;
public $name = 0;
public function __construct($ID, $username, $access_lvl, $staff_type, $name)
{
$this->ID = $ID;
$this->username = $username;
$this->access_lvl = $access_lvl;
$this->staff_type = $staff_type;
$this->name = $name;
}
public function __toString()
{
return $this->username;
}
}
?>
Method (Model) which needs the User.php
function get_all_users()
{
$query = $this->db->get('tt_login');
$arr = array();
foreach ($query->result_array() as $row)
{
$arr[] = new User
(
$row['login_ID'],
$row['login_user'],
$row['login_super'],
$row['crew_type'],
$row['login_name']
);
}
return $arr;
}
And finally the controller,
function index()
{
$this->load->library('user');
$this->load->model('admin/usersmodel', '', true);
// Page title
$data['title'] = "Some title";
// Heading
$data['heading'] = "Some heading";
// Data (users)
$data['users'] = $this->usersmodel->get_all_users();
If you have PHP version >= 5.3 you could take use of namespaces and autoloading features.
A simple autoloader library in the library folder.
<?php
class CustomAutoloader{
public function __construct(){
spl_autoload_register(array($this, 'loader'));
}
public function loader($className){
if (substr($className, 0, 6) == 'models')
require APPPATH . str_replace('\\', DIRECTORY_SEPARATOR, $className) . '.php';
}
}
?>
The User object in the model dir. ( models/User.php )
<?php
namespace models; // set namespace
if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class User{
...
}
And instead of new User... new models\User ( ... )
function get_all_users(){
....
$arr[] = new models\User(
$row['login_ID'],
$row['login_user'],
$row['login_super'],
$row['crew_type'],
$row['login_name']
);
...
}
And in controller just make sure to call the customautoloader like this:
function index()
{
$this->load->library('customautoloader');
$this->load->model('admin/usersmodel', '', true);
// Page title
$data['title'] = "Some title";
// Heading
$data['heading'] = "Some heading";
// Data (users)
$data['users'] = $this->usersmodel->get_all_users();
CodeIgniter doesn't really support real Objects.
All the libraries, models and such, are like Singletons.
There are 2 ways to go, without changing the CodeIgniter structure.
Just include the file which contains the class, and generate it.
Use the load->library or load_class() method, and just create new objects. The downside of this, is that it will always generate 1 extra object, that you just don't need. But eventually the load methods will also include the file.
Another possibility, which will require some extra work, is to make a User_Factory library.
You can then just add the object on the bottom of the file, and create new instances of it from the factory.
I'm a big fan of the Factory pattern myself, but it's a decision you have to make yourself.
I hope this helped you, if you have any questions that are more related to the implementation, just let me/us know.
Including a class file is not a bad approach.
In our projects, we do the same, add an another layer to MVC, and thats a Service Layer which the Controllers calls and Service calls the Model. We introduced this layer to add Business Logic seperate.
So far, we have been using it, and our product has gone large too, and still we find no difficulty with the decision of including files that we had made in the past.
Codeigniter has a common function to instantiate individual classes.
It is called load_class(), found in /system/core/Common.php
The function;
/**
* Class registry
*
* This function acts as a singleton. If the requested class does not
* exist it is instantiated and set to a static variable. If it has
* previously been instantiated the variable is returned.
*
* #access public
* #param string the class name being requested
* #param string the directory where the class should be found
* #param string the class name prefix
* #return object
*/
The signature is
load_class($class, $directory = 'libraries', $prefix = 'CI_')
An example of it being used is when you call the show_404() function.
After a brief google search, I was inspired to make my own autoloader class. It's a bit of a hack, since I use custom Codeigniter library to preform auto-loading, but for me this is the best way, that I'm aware of, of loading all the classes, I require, without compromising my application architecture philosophy, to fit it into Codeigniter way of doing things. Some might argue that Codeigniter is not the right framework for me and that might be true, but I'm trying things out and playing around with various frameworks and while working on CI, I came up with this solution.
1. Auto-load new custom library by editing applicaion/config/autoload.php to include:
$autoload['libraries'] = array('my_loader');
and any other libraries you might need.
2. Then add library class My_loader. This class will be loaded on every request and when its constructor is run, it will recursively search through all sub-folders and require_once all .php files inside application/service & application/models/dto folders. Warning: folders should not have dot in the name, otherwise function will fail
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class My_loader {
protected static $_packages = array(
'service',
'models/dto'
);
/**
* Constructor loads service & dto classes
*
* #return void
*/
public function __construct($packages = array('service', 'models/dto'))
{
// files to be required
$toBeRequired = array();
// itrate through packages
foreach ($packages as $package) {
$path = realpath(APPPATH . '/' . $package . '/');
$toBeRequired = array_merge($toBeRequired, $this->findAllPhpFiles($path));
}
/**
* Require all files
*/
foreach ($toBeRequired as $class) {
require_once $class;
}
}
/**
* Find all files in the folder
*
* #param string $package
* #return string[]
*/
public function findAllPhpFiles($path)
{
$filesArray = array();
// find everithing in the folder
$all = scandir($path);
// get all the folders
$folders = array_filter($all, get_called_class() . '::_folderFilter');
// get all the files
$files = array_filter($all, get_called_class() . '::_limitationFilter');
// assemble paths to the files
foreach ($files as $file) {
$filesArray[] = $path . '/' . $file;
}
// recursively go through all the sub-folders
foreach ($folders as $folder) {
$filesArray = array_merge($filesArray, $this->findAllPhpFiles($path . '/' . $folder));
}
return $filesArray;
}
/**
* Callback function used to filter out array members containing unwanted text
*
* #param string $string
* #return boolean
*/
protected static function _folderFilter($member) {
$unwantedString = '.';
return strpos($member, $unwantedString) === false;
}
/**
* Callback function used to filter out array members not containing wanted text
*
* #param string $string
* #return boolean
*/
protected static function _limitationFilter($member) {
$wantedString = '.php';
return strpos($member, $wantedString) !== false;
}
}
After 18 hours I managed to include a library in my control without initialisation (the constructor was the problem, because of that and i could't use the standard codeiginiter $this->load->library() ).
Follow the https://stackoverflow.com/a/21858556/4701133 . Be aware for further native class initialization use $date = new \DateTime()with back-slash in front otherwise the function will generate an error !

Autoloader for functions

Last week I learned that classes can be included in your project by writing an __autoload() function. Then I learned that using an autoloader isn't only a technique but also a pattern.
Now I'm using the autoloader in my project and I've found it very very useful. I was wondering if it could be possible to do the same thing with functions. It could be very useful to forget about including the right PHP file with functions inside it.
So, is it possible to create a function autoloader?
There is no function auto-loader for functions. You have four realistic solutions:
Wrap all functions into namespacing classes (context appropriate). So let's say you have a function called string_get_letters. You could add that to a class called StringFunctions as a static function. So instead of calling string_get_letters(), you'd call StringFunctions::get_letters(). You would then __autoload those namespaced classes.
Pre-load all functions. Since you're using classes, you shouldn't have that many functions, so just pre-load them.
Load functions prior to using them. In each file, require_once the function files that are going to be used in that file.
Don't use functions in the first place. If you are developing OOP code (which it seems like you are anyway), there should be little to no need for functions at all. Everything you would need a function (or multiple) for, you could build in a OO manner and avoid the need for functions.
Personally, I'd suggest either 1, 2 or 4 depending on your exact need and the quality and size of your codebase...
If you are using Composer in your Project, you can add a files directive to the autoload section.
This will than actually generate a require_once in the autoloader, but it feels like real autoloading, because you dont have to take care of that.
Its not lazy loading though.
Example taken from Assetic:
"autoload": {
"psr-0": { "Assetic": "src/" },
"files": [ "src/functions.php" ]
}
I read something a while back about an ugly hack that caught fatal errors and tried to include and execute the missing function(s), but I definitely wouldn't go that road.
The closest thing you have is the __call() magic method, which is sort of a __autoload() for methods, not functions. It might be good enough for your needs; if you can afford to call a class and require each different function separately. Since PHP 5.3.0, you also have __callStatic().
An example using __callStatic():
class Test
{
public function __callStatic($m, $args)
{
if (function_exists($m) !== true)
{
if (is_file('./path/to/functions/' . $m . '.php') !== true)
{
return false;
}
require('./path/to/functions/' . $m . '.php');
}
return call_user_func_array($m, $args);
}
}
Test::functionToLoad(1, 2, 3);
This would call the functionToLoad() function defined in ./path/to/functions/functionToLoad.php.
Well, as usual there is a PECL extension for that:
automapPECL
(via: http://phk.tekwire.net/joomla/support/doc/automap.htm)
It's supposed to autoload functions as well as classes. Which however doesn't work with the current PHP interpreter yet.
(An alternative option btw, is generating stub functions that load and run namespaced counterparts.)
That being said. Autoloading is not universally considered a good practice. It leads to overly fractured class hierarchies and object happiness. And the real reason PHP has autoloading is because include and dependency management systems are inmature.
namespace MyNamespace;
class Fn {
private function __construct() {}
private function __wakeup() {}
private function __clone() {}
public static function __callStatic($fn, $args) {
if (!function_exists($fn)) {
$fn = "YOUR_FUNCTIONS_NAMESPACE\\$fn";
require str_replace('\\', '/', $fn) . '.php';
}
return call_user_func_array($fn, $args);
}
}
And using namespaces, we can do: Fn::myFunc() and spl_autoload_register(). I've used this code with examples at: https://goo.gl/8dMIMj
I use a Class and __invoke. The __invoke method is called when a script calls a class as a function. I often do something like this:
<?php
namespace API\Config;
class Slim {
function __invoke() {
return [
'settings' => [
'displayErrorDetails' => true,
'logger' => [
'name' => 'api',
'level' => Monolog\Logger\Logger::DEBUG,
'path' => __DIR__ . '/../../logs/api.log',
],
]
];
}
}
I can then call like a function:
$config = API\Config\Slim;
$app = Slim\App($config())
new Functions\Debug() will load functions to root namespace.
namespace Functions
{
class Debug
{
}
}
namespace
{
if (! function_exists('printr')) {
/**
*
* #param mixed $expression
*/
function printr()
{
foreach (func_get_args() as $v) {
if (is_scalar($v)) {
echo $v . "\n";
} else {
print_r($v);
}
}
exit();
}
}
}
Here is another rather complex example, based on the suggestions in this discussion.
The code can also be seen here: lib/btr.php
<?php
/**
* A class that is used to autoload library functions.
*
* If the function btr::some_function_name() is called, this class
* will convert it into a call to the function
* 'BTranslator\some_function_name()'. If such a function is not
* declared then it will try to load these files (in this order):
* - fn/some_function_name.php
* - fn/some_function.php
* - fn/some.php
* - fn/some/function_name.php
* - fn/some/function.php
* - fn/some/function/name.php
* The first file that is found will be loaded (with require_once()).
*
* For the big functions it makes more sense to declare each one of them in a
* separate file, and for the small functions it makes more sense to declare
* several of them in the same file (which is named as the common prefix of
* these files). If there is a big number of functions, it can be more
* suitable to organize them in subdirectories.
*
* See: http://stackoverflow.com/questions/4737199/autoloader-for-functions
*/
class btr {
/**
* Make it TRUE to output debug info on '/tmp/btr.log'.
*/
const DEBUG = FALSE;
/**
* The namespace of the functions.
*/
const NS = 'BTranslator';
/**
* Relative directory where the functions are located.
*/
const FN = 'fn';
private function __construct() {}
private function __wakeup() {}
private function __clone() {}
/**
* Return the full name (with namespace) of the function to be called.
*/
protected static function function_name($function) {
return self::NS . '\\' . $function;
}
/**
* Return the full path of the file to be loaded (with require_once).
*/
protected static function file($fname) {
return dirname(__FILE__) . '/' . self::FN . '/' . $fname . '.php';
}
/**
* If a function does not exist, try to load it from the proper file.
*/
public static function __callStatic($function, $args) {
$btr_function = self::function_name($function);
if (!function_exists($btr_function)) {
// Try to load the file that contains the function.
if (!self::load_search_dirs($function) or !function_exists($btr_function)) {
$dir = dirname(self::file($fname));
$dir = str_replace(DRUPAL_ROOT, '', $dir);
throw new Exception("Function $btr_function could not be found on $dir");
}
}
return call_user_func_array($btr_function, $args);
}
/**
* Try to load files from subdirectories
* (by replacing '_' with '/' in the function name).
*/
protected static function load_search_dirs($fname) {
do {
self::debug($fname);
if (file_exists(self::file($fname))) {
require_once(self::file($fname));
return TRUE;
}
if (self::load_search_files($fname)) {
return TRUE;
}
$fname1 = $fname;
$fname = preg_replace('#_#', '/', $fname, 1);
} while ($fname != $fname1);
return FALSE;
}
/**
* Try to load files from different file names
* (by removing the part after the last undescore in the functin name).
*/
protected static function load_search_files($fname) {
$fname1 = $fname;
$fname = preg_replace('/_[^_]*$/', '', $fname);
while ($fname != $fname1) {
self::debug($fname);
if (file_exists(self::file($fname))) {
require_once(self::file($fname));
return TRUE;
}
$fname1 = $fname;
$fname = preg_replace('/_[^_]*$/', '', $fname);
}
return FALSE;
}
/**
* Debug the order in which the files are tried to be loaded.
*/
public static function debug($fname) {
if (!self::DEBUG) {
return;
}
$file = self::file($fname);
$file = str_replace(DRUPAL_ROOT, '', $file);
self::log($file, 'Autoload');
}
/**
* Output the given parameter to a log file (useful for debugging).
*/
public static function log($var, $comment ='') {
$file = '/tmp/btr.log';
$content = "\n==> $comment: " . print_r($var, true);
file_put_contents($file, $content, FILE_APPEND);
}
}
While you can't autoload functions and constants, you can use something like jesseschalken/autoload-generator which will automatically detect what files contain things which can't be autoloaded and load them eagerly.
The solution I came up with. As lightweight as I could come up with.
class functions {
public static function __callstatic($function, $arguments) {
if (!function_exists($function)) {
$file = strtok($function, '_') .'.php';
include '/path/to/functions/'.$file;
}
return call_user_func_array($function, $arguments);
}
}
Use it by calling functions::foo_bar($anything).
I try to use the autoloading of classes to my advantage. So, when a class is auto-loaded, the class file is executed. Therefore, I create a class with a static method called 'boot' that does nothing. When I invoke that method, the class will be autoloaded, hence every function in that file will be defined in the global scope. What's even more interesting is that the functions will be defined in the namespace of the class, so there are no clashes.
For example:
<?PHP
namespace Functions;
// functions are defined in this file
class GlobalFunctions{
public static function boot(){};
}
function foo(){ // code... }
?>
// the client file
<?php
// VS Code automatically does this.
use Functions\GlobalFunctions;
use function Functions\foo;
// I usually put this in the bootstrap file
GlobalFunctions::boot();
// call foo() from the Functions namespace
foo();
?>
Include all functions file in one file and then include it
//File 1
db_fct.php
//File 2
util_fct.php
//In a functions.php include all other files
<?php
require_once 'db_fct.php';
require_once 'util_fct.php';
?>
Include functions.php whenever you need functions ..
try this
if ($handle = opendir('functions')) {
while (false !== ($entry = readdir($handle))) {
if (strpos($entry, '.php') !== false) {
include("functions/$entry");
}
}
closedir($handle);
}

Categories