I'm building a fork of OpenCart that involves some very heavy rewriting of the core functionality to including dedicated themes that allow for overriding the core, similar to the Wordpress theme system.
If a given controller, model or language file exists in the theme, use that, if not, use the core file.
I'm currently autoloading all the system files which works great. But I'm wondering if it would be better to also autoload all my controller, model, and language files as well?
I've noticed that (even in OpenCart) the same file gets searched via the file system, and loaded multiple times. (models and languages in particular)
Would it not be more efficient to autoload all these files then simply instantiate the exiting class, over searching for the files each time then using an include?
Let me give an example ...
My homepage contains the following modules:
Slideshow,
Latest Products,
Featured Products,
Carousel
This along with the header and footer controllers ends up calling:
$this->load->model('catalog/product');
23 times.
That's 23 times that the same exact file is being searched for using the file system. Plus of course all the other models being called.
So I guess the question is really more about overhead ...
Is it more expensive to autoload (and cache) all files regardless of whether they're being called, or search the file system and do includes for each file as it's needed?
I'm sure this is an age old question but I've yet to see a definitive answer.
And BTW either answer is fine, I'd just like to be as efficient as possible in my code.
Well I get Your point but to be honest You should be concerned about this only in the case You can prove that the response times are somehow slower because of this reason. After having such proof (which is relative to the server's hardware, connection speed, whether it is sunny or stormy weather or day or night) You can issue a bug and a patch-fix using the github repository.
Now the model loading performs file_exists check, then include_once and afterwards it instantiates a new model object while setting (or replacing) it as some key in the registry. Current code looks like this (system/engine/loader.php):
public function model($model) {
$file = DIR_APPLICATION . 'model/' . $model . '.php';
$class = 'Model' . preg_replace('/[^a-zA-Z0-9]/', '', $model);
if (file_exists($file)) {
include_once($file);
$this->registry->set('model_' . str_replace('/', '_', $model), new $class($this->registry));
} else {
trigger_error('Error: Could not load model ' . $model . '!');
exit();
}
}
By just slight modification we can improve this code to perform only one file_exists check, only one include_once and having only one model's object:
public function model($model) {
$key = 'model_' . str_replace('/', '_', $model);
if (!$this->registry->has($key)) {
$file = DIR_APPLICATION . 'model/' . $model . '.php';
$class = 'Model' . preg_replace('/[^a-zA-Z0-9]/', '', $model);
if (file_exists($file)) {
include_once($file);
$this->registry->set($key, new $class($this->registry));
} else {
trigger_error('Error: Could not load model ' . $model . '!');
exit();
}
}
}
You can now put some time debugging into the original code and to the fixed one and find out whether this brings so great improvement :-)
Edit: Either way I would check how is this handled and done in OpenCart 2.0 (in it's latest status since it is not release-ready so far) and file an issue only if this is done the same way. Otherwise just fix your own installation(s)...
Related
I've taken over an old CakePHP 2.X website and have no prior experience with CakePHP so please forgive me if this is a stupid question.
I was looking at making some changes to some vendor files and noticed that we appear to have multiple copies of various files (which are, for the most part, identical) in 2 different places:
app/webroot/api/vendor/API/lib/
vendors/API/lib/
Additionally I noticed there are several other vendor directories in various other places.
I am using App::import('Vendor', 'example', array('file' => 'API/lib/example.php')); to load the scripts in question.
Could someone please explain to me what the best practices are regarding file structure relating to vendor files? Additionally, am I safe to delete the duplicate copy of all the files? How does CakePHP know which copy to load?
Edit:
I have come to the conclusion that the files are being loaded from vendors/API/lib/ rather than app/webroot/api/vendor/API/lib/, is it possible that the files in the latter location are redundant? I cannot seem to find any references to them.
Well as Sudhir has commented you, there is a folder in your app project which is called Vendor. I would recommend you to put it there.
app > Vendor
For example, I have created a folder called Csv for generating my own csv files through a Shell which is launching them. It is located inside app > Vendor > Csv
For importing this to my projects, I did the next for being able to use it:
<?php
include('GenericShell.php');
require_once(ROOT . DS . 'app' . DS . 'Vendor' . DS . 'Csv' . DS .
'CsvGenerator.php');
class CsvPatientsShell extends GenericShell {
That's one only example with PHP.
One other one would be, if in this case you have a Component which is called component.php and you want to import it to a Controller which you use frequently inside your project :
Component would be located into
Controller > Component > Namecomponent.php
The next thing you would have to do would be to do the import likewise inside your controller:
Let's say your controller's name is NameController.php and is located inside the Controller folder.
Controller > NameController.php
public function main_function() {
App::import('Component', 'Namecomponent');
$NameComponent = new NameComponent();
$this->layout = null;
$this->autoLayout = false;
die();
}
That would be a more correct way to do it with CakePhp but both mentioned are legit I'd say.
I hope that will help you somehow.
I wrote custom classes and want to use them in pimcore application.
I took them to /website/lib/Custom directory on server. Afterwards, I wrote recursive script includer for each Class located in the directory and included that script in /index.php file.
It is absolutely not pimcore standard but it works.
In pimcore/config/startup.php exists snippet:
$autoloaderClassMapFiles = [
PIMCORE_CONFIGURATION_DIRECTORY . "/autoload-classmap.php",
PIMCORE_CUSTOM_CONFIGURATION_DIRECTORY . "/autoload-classmap.php",
PIMCORE_PATH . "/config/autoload-classmap.php",
];
$test = PIMCORE_ASSET_DIRECTORY;
foreach ($autoloaderClassMapFiles as $autoloaderClassMapFile) {
if (file_exists($autoloaderClassMapFile)) {
$classMapAutoLoader = new \Pimcore\Loader\ClassMapAutoloader([$autoloaderClassMapFile]);
$classMapAutoLoader->register();
break;
}
}
I guess that this provides inclusion of all those classes put into returning array from autoload-classmap.php.
Having in mind that /pimcore/config/autoload-classmap.php exists, the mentioned loop would break at first iteration so classes that I would put into custom autoload-classmap are not going to be included in project.
My question is can I change files from /pimcore directory and expect that everything would be fine after system update?
No, you should not overwrite anything in the pimcore directory, since the files in there get overwritten by the update mechanism.
You can do what you want by using the /website/config/startup.php which will not get overwritten:
https://www.pimcore.org/wiki/display/PIMCORE4/Hook+into+the+startup-process
But instead of loading all your classes as you did, take advantage of the autoloader by adding this to the /website/config/startup.php:
// The first line is not absolutely necessary, since the $autoloader variable already gets
// set in the /pimcore/config/startup.php, but it is a more future-proof option
$autoloader = \Zend_Loader_Autoloader::getInstance();
$autoloader->registerNamespace('Custom');
If you are properly using namespaces and naming your files correctly that's all you need to do.
I'm creating a system that would work on call same function names from dynamic created php files.
The file structure is as bellow :
/root/caller.php //{uses pthreads & process multi simultaniously}
/root/rules/mode1.php
/root/rules/mode2.php
/root/rules/mode{n}.php // goes till n , so unlimited
Caller.php reads data from db and calls mode{n} accordingly. Sometimes there are chances to call multiple of rules at a point of time.
Sample of caller.php {written on OOPS}
foreach ($result as $row){
$mode = $rom->modeNum;
include 'rules/mode' . $mode . '.php';
call_fuction_inside_mode();
}
Sample of mode{n}.php {procedural programming}
function call_fuction_inside_mode(){
//..Custom function depending upon mode{n}
}
This won't function properly, because caller.php would include same function name again and again.
Please note :
I do not want to play with rename function with pecl, as all includes would be required
I have tried calling mode{n}.php by introducing another file "proxy.php"
And proxy.php works in below pattern
caller.php -> curl 127.0.0.1/proxy.php?mode={n} -> calls as defined
But this is not efficient way to do this when dealing with >500 requests per sec on micro server. I noticed many requests are killed and there's problem with order number. My target is to achieve this with least memory usage and 100% utilization of provided cpu resources.
I'm confused and so curious to find the solution I should be dealing here.
create an interface called Mode in Mode.php:
interface Mode{
public static function AcivateMode();
}
and include Mode.php into your code, then change the code in your mode{n}.php to encapsulate your function into classes that implements the interface Mode like so:
class Mode1 implements Mode {
public static function AcivateMode() {
//..Custom function depending upon mode{n}
}
}
then modify your foreach to activate the modes like:
foreach ($result as $row){
$mode = $rom->modeNum;
include 'rules/mode' . $mode . '.php';
call_user_func("Mode$mode::AcivateMode");
}
OR
you can use the namespaces approach by adding the namespace to each of your mode{n}.php files like so:
mode1.php
namespace Mode1;
function call_fuction_inside_mode(){....}
and calling the function using:
call_user_func("Mode$mode\call_fuction_inside_mode");
that worked for me, hope it's gonna be helpful :)
Regards,
So I am using Less PHP to compile .less files down to .css and I decided to try out a small class I wrote where I take all the .less files in a directory and compile them to .css and move them to their own css directory.
So to test this I took all of twitter bootstrap less and placed it into a directory and then ran my class I wrote, see below, and found less php to be exploding: the current error is:
Error occurred:exception 'Exception' with message 'variable #alert-padding is undefined: failed at 'padding: #alert-padding;'
The class I wrote is just a simple wrapper:
class AisisLess_Less_Less{
protected $_less = null;
protected $_file = null;
public function __construct(){
$this->_file = new AisisCore_FileHandling_File();
$this->_less = new lessc();
$this->init();
}
public function init(){}
public function compile_directory($pattern, $compile_to_dir){
$less_files = $this->_file->get_directory_of_files($pattern);
$this->_compile($less_files, $compile_to_dir);
}
protected function _compile($array_of_files, $compile_directory){
foreach($array_of_files as $file){
$file_without_path = substr( $file, strrpos( $file, '/' )+1 );
try{
$css_file = preg_replace('"\.less"', '.css', $file_without_path);
$this->_less->checkedCompile($file, $compile_directory . $css_file);
}catch(exception $e){
throw new AisisLess_Exceptions_LessException('Error occurred:' . $e);
}
}
}
}
The concept is you use as such:
$less = new AisisLess_Less_Less();
$less->compile_directory(path/to/less/*.less, path/to/compiled/out/put);
My code is just a simple wrapper. The actual error is being thrown by the less library on some variable thing which is apart of the bootstrap less files.
So is the library acting up or is bootstrap failing at coding or have I screwed something up?
And if so how do I fix this?
What I suspect (no proof). If I understand you correctly, you just have the directory of bootstrap files and your code runs through them sequentially compiling them to css. That means the first file it is trying to compile is alerts.less which, low and behold, the very first variable reference in that file is (as of this writing) on line 10:
padding: #alert-padding;
This matches your error.
The issue is most likely that the bootstrap files are not designed to just be blindly compiled, because in most cases each is its own little piece, but requires other key pieces to compile (like variables.less and mixins.less). This is why, generally speaking, you only compile the bootstrap.less file which then will #import all the necessary files.
Basically, if I understand correctly what you have designed and how it works, you picked a bad set of files to run tests on because they are not designed to work that way. Better to just create a few small less files that are independent of each other for testing. However, what this has revealed is that your plan has a potential flaw, in that it will only function on standalone files, so you cannot blindly run any .less file through it, or you will get such compile errors because of broken dependencies.
So my project uses an MVC framework and I have a page with an Ajax script I run to get content from the server. When the PHP script is called in the Ajax script, I want to access the classes already in my library for use in the PHP script. To do this, I use what I call an ajaxBootstrap to call the appropriate function that then instantiates the objects needed for that specific Ajax script.
To load those classes from my library I have an autoload function in my ajaxBootstrap so I don't need to use a bunch of require and include statements. My problem is those files aren't being loaded due to a path issue with the autoload function. When I use a require statement with the same path, the classes load with no problems, its only when I try to load them using the autoload function that I get an 500 internal server error.
Here is my ajaxBootstrap file:
// This file routes Ajax requests made in JS files and instantiates a specific object to carry out the actions needed for that particular Ajax operation
// Autoload any classes that are required
function autoLoad($classToLoad)
{
if(file_exists('../library/' . $classToLoad . 'class.php')) // File in the library folder
{
require('../library/' . $classToLoad . '.class.php');
}
else if(file_exists('../../app/models/' . $classToLoad . 'class.php')) // File in the models folder
{
require('../../app/models/' . $classToLoad . '.class.php');
}
}
spl_autoload_register('autoLoad');
// Determine which function to call based on the url that's listed in the Ajax request
switch($_GET['action'])
{
case 'pageOne':
pageOne();
break;
case 'pageTwo':
pageTwo();
break;
}
function pageOne()
{
$test = new Test();
$test->funcThatReturnStuff();
}
function pageTwo()
{
$test2 = new Test2();
$test2->funcThatReturnStuff();
}
Like I mentioned eariler, if I use a require statement such as:
require('../library/Test.class.php');
$test = new Test();
$test->funcThatReturnStuff();
The class loads and works just fine. But using the same path in the autoloader function throws an error. The really odd thing is if I put an else if statement in the autoloader that loads a class from the folder where my ajaxBootstrap is it also works fine too...
I know I could just use the require statements and be done with the problem but I want to be able to scale the project and not need to use loads of require statements in the future. BTW, I use '../' to get from where my ajaxBootstrap file is to my other folders.
Also, to add to my previous post, I've tried replacing the ../ with absolute paths using define('ROOT', dirname(__FILE__) . '/') and also define('ROOT', $_SERVER['DOCUMENT_ROOT'] . '/path/to/folder/') neither of which worked and still gave me the internal server error in Firebug. In addition, I haven't received any errors in my error log either.
Nevermind... Even after staring at my code for the past few hours I somehow missed the missing period in two of my file paths. I hate coding sometimes... Thank you to anyone who took time to read this.