Overriding classes with class_alias - php

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!

Related

PHPUnit signals a warning when mocking a namespace-ed class

My composer project consists of the src and tests folders.
The code inside src is auto-loaded using composers psr4 autoloader like so
"autoload": {
"psr-4": {
"christmas\\":"src/"
}
}
The class to be tested looks like so
namespace christmas;
class Hello
{ //constructor & dependency injection included in real class
public function sayHello()
{
return "HELLO";
}
}
And Finally my test class looks like so
<?php
use PHPUnit\Framework\TestCase;
require_once('../vendor/autoload.php');
use christmas\Hello;
class TestHello extends TestCase
{
public function testSayHelloMethod()
{
$hello = $this->getMockBuilder('Hello')
->getMock();
$hello->expects($this->once())
->method('sayHello')
->will($this->returnValue('HELLO'));
$this->assertEquals(
"HELLO",
$hello->sayHello()
);
}
}
And here is how my i run phpunit
phpunit tests/TestHello
phpunit echoes back the following output
Time: 45 ms, Memory: 4.00 MB
There was 1 warning:
1) tests\Hello::testSayHelloMethod()
Trying to configure method "sayHello" which cannot be configured because it does not exist, has not been specified, is final, or is static
/usr/share/php/PHPUnit/TextUI/TestRunner.php:641
/usr/share/php/PHPUnit/TextUI/Command.php:206
/usr/share/php/PHPUnit/TextUI/Command.php:162
WARNINGS!
Tests: 1, Assertions: 0, Warnings: 1.
Below is a view of how my code is organized,
├── composer.json
├── src
│   └── Hello.php
├── tests
│   └── TestHello.php
└── vendor
What am i missing ? I need to have a passing test with no single warning.
You need to pass the full, namespaced name of the class to the mock builder, i.e. 'christmas\Hello'. Since you need to pass it as a string, just using "Hello" isn't enough to properly identify the class.
Since you're already have a use for the class, you can use ::class to get the full name. For example:
$hello = $this->getMockBuilder(Hello::class)
->getMock();

How to make sure the correct class file is loaded when calling / extending class

I did brows through that question page but the answers given, I felt, didn't sufficiently answer the question I was struggling with.
I have a little php project. In this project I define two classes both in their own files. The second class extends the first class. In the app.php file I instantiate the second class and call a method.
├── Models/
│ ├── Class1.php
│ └── Class2.php
└── app.php
<?php
// Class1.php
namespace Models\Class1;
/**
* Class1 does things
*/
class Class1 {
public function someMethod() {
// code
}
}
<?php
// Class2.php
namespace Models\Class2;
use \Models\Class1\Class1;
include('./Class1.php');
/**
* Class2 does other things
*/
class Class2 extends Class1 {
public function someMethod() {
// code
}
}
<?php
// app.php
use Models\Class1\Class1;
use Models\Class2\Class2;
require('Models/Class1.php');
require('Models/Class2.php');
$c1 = new Class1();
$c2 = new Class2();
Now I'm not a really experienced php programmer but I thought I had a solid grasp of this class/namespace business and including/requiring files, but apparently I don't.
If I copy the methods I need from Class1 to Class2 and not extend/include/use Class2 everything works fine, but want to extend Class1, so I don't have to repeat myself! Damn it!
This is what I get when running the app.php file.
Warning: include(../Models/Class1.php): failed to open stream: No such file or directory in C:\wamp64\www\project\Models\Class2.php on line 8
Warning: include(): Failed opening '../Models/Class1.php' for inclusion (include_path='.;C:\php\pear') in C:\wamp64\www\project\Models\Class2.php on line 7
I've been reading all I can find online about this stuff, have written and rewritten my code in a dozen different ways but have yet to find a solution. Some help would be greatly appreciated!
Quick Answer
Unless you've specified the full path to the include file, PHP will always try to resolve files according to the location of the entry script. In your case, your entry script seems to be app.php at the root of the application.
So in your Class2.php, instead of include('./Class1.php');, you should write include('Models/Class1.php'); and things should work. But while it fix a short term problem, your code would be really non-portable. Besides, including the same file twice would result in another error (e.g. re-declaring class).
Slightly Smarter Approach
A slightly smarter would be to do this in Class2.php instead.
<?php
// Class2.php
namespace Models\Class2;
use \Models\Class1\Class1;
include_once __DIR__ . '/Class2.php';
/**
* Class2 does other things
*/
class Class2 extends Class1 {
public function someMethod() {
// code
}
}
The variable __DIR__ will always be resolved to the directory of the script file, not the entry script.
But again, it is clumsy and error prone to do file includes by hand.
An Even Smarter Approach: Autoload
PHP supports file autoloading when a class is declare. That is to have a piece of code to do the file include when a class is called. If you want to have fun, you're welcome to write your own autoloader function and register to spl_autoload_register. With a combination of the __DIR__ technique we talked about, you can easily resolve the autoloading path from namespace.
A quick and ugly autoloading app.php would probably look like this:
<?php
// app.php
use Models\Class1\Class1;
use Models\Class2\Class2;
spl_autoload_register(function ($class_name) {
$realClassName = basename(str_replace('\\', DIRECTORY_SEPARATOR, $class_name));
include_once __DIR__ . DIRECTORY_SEPARATOR . 'Models' . DIRECTORY_SEPARATOR . $realClassName . '.php';
});
$c1 = new Class1();
$c2 = new Class2();
The Smart Approach: Composer Autoload
If you're learning OOP PHP today, I'd highly recommend you to learn Composer and PSR-4. PSR-4 defines how you should structure a PHP application for class autoloading. Composer implements a PSR-4 autoloader by default.
First you should comply with the namespace standard. The least change to do is to loose the extra "Class1" and "Class2" in your namespace:
<?php
// Class1.php
namespace Models;
/**
* Class1 does things
*/
class Class1 {
public function someMethod() {
// code
}
}
<?php
// Class2.php
namespace Models;
use \Models\Class1;
/**
* Class2 does other things
*/
class Class2 extends Class1 {
public function someMethod() {
// code
}
}
With a nicely structured application folder, namespace structure and a correctly written autoload.psr-4 section in composer.json, composer will help you to generate a class autoloader. Your composer.json would probably look like this:
{
"autoload": {
"psr-4": {
"Model\\": "Model/"
}
}
}
You may now run composer dump-autoload to create the autoloader. Then you can simply add this to your entry script app.php. When things are ready, you may simply add use and new statement anywhere in the application.:
<?php
// app.php
use Models\Class1;
use Models\Class2;
require_once './vendor/autoload.php';
$c1 = new Class1();
$c2 = new Class2();
All includes are handled by the autoloader. Hands free.
Remove the line
include('./Class1.php');

PHP namespacing and Autoloading extended classes

I'm trying to create a system that would autoload classes when they're needed.
This is my folder structure
| index.php
| app
| - app.php
| - core
| -- core.php
| -- helpers
| --- htmlhelper.php
where index.php is the bootstrap file, core.php is the core file I'd like to extend and all the classes in the helpers folder would contain classes I'd like to autoload.
Since autoloading on my own is a fairly new thing for me I'm pretty confused on how to do it. I can autoload the core file, but I cannot seem to figure out how to autoload the helper classes.
This is the code I have so far:
index.php
require_once('app/app.php');
$app = new App();
$app->display();
app.php
use App\Core\Core as Core;
// autoload
spl_autoload_extensions(".php");
spl_autoload_register();
class App{
function __construct(){
//Core::addStyle('/file/css/foo1.css');
Core::foo();
}
public function display(){
Core::getStyles();
}
}
core.php
namespace App\Core;
// dependancies
use App\Core\Helpers\HtmlHelper as Helper;
// autoload
spl_autoload_extensions(".php");
spl_autoload_register();
class Core
{
function foo(){
var_dump('bar');
}
}
htmlhelper.php
namespace App\Core\Helpers;
class HtmlHelper extends Core{
protected $styles;
protected $scripts;
public static function addStyle($data){
if(is_array($data)){
$this->styles = array_merge($this->styles, $data);
}else{
$this->styles[] = $data;
}
}
public static function getStyles(){
var_dump($this->styles);
}
}
With the code I have now the core class gets autoloaded fine, and I can call the foo() method, but the htmlhelper class won't get loaded. I know this because PHP throws an error that getStyles() is undefined.
I'm doing this to achieve one exit point for the core functions of the application, but separate the code in different files.
Ideally I'd like to load all classes in the helper folder automatically and not have to put a block of use .. definitions, if that's doable.
I think that the lack of knowledge about php namespacing is my biggest flaw at this point.
I can't seem to find any good sources that would fully explain how would I do these things in a simple language for dummies.

Phalcon - Calling function from other controller

How to call function from another controller in Phalcon PHP framework. Here is example for CakePHP http://sherwinrobles.blogspot.com/2013/02/cakephp-calling-function-from-other.html
Based on the link you provided, to my knowledge there is no direct way to call a function in another controller using the request object. However instantiating the controller and calling the function will work just it does in CakePHP
$newController = new \MyNS\Controllers\NewController();
$newController->myFunc();
If you want you can use a static function inside the controller and call it
\MyNS\Controllers\NewController::myFunc();
This is already Tested
For the people that aren't using CakePHP
another way to do this would be to do a helper Folder and write actions, in this case methods.
public/index.php
Add the path helper
$loader->registerDirs(
[
APP_PATH . '/helper/'
]
);
Add a helper Order in apps
└── apps
├── controllers
└─ exampleController.php
├── models
└── helpers
└─ myHelper.php
...
In myHelper.php
<?php
use Phalcon\Di\Injectable;
class myHelper extends Injectable
{
function myNameFunction() {
// here you should write your function
}
}
In exampleController where you want to call the other action, in this case function
<?php
use Phalcon\Mvc\Controller;
class exampleController extends Controller {
public function yourAction() {
//your code
//there are 2 ways of calling the function. either use this one
$myHelper = new myHelper();
$myHelper->myNameFunction();
//or this one
(new UnmatchHelper())->myNameFunction();
}
}

laravel models, class names, table names and directory structure best practices

I was wondering what would be the best practice to create the following tables and relations in eloquent / laravel 4.1 models.
table name magazines with fields id, title
table name magazine_issues with fields id, title, magazine_id
table name magazine_issue_articles with fields id, title, body, magazine_issue_id
I need the file/dir structure and the naming of all the Class and files, because I am really confused, for example should I make a directory named magazines and place the Issue.php file there and like so for the articles? And what should be the name of the class itself?
Thank you in advance.
EDIT
I need to have them in subfolders, since that would be much more portable. The question is...
will a class with the name magazineIssue (with table name magazine_issues) will be autoloaded and registered from a file called Issue.php in a directory named Magazine/?
I hope you are not as confused as I am :(
If you want to create thinks like Taylor Otwell and 'the core' is trying to teach people do things in Laravel, this is a good start:
Your files could be organized as
├── app
│   ├── Publishing
│   │   ├── Models
│   │   │   ├── Magazine
│   │   │   │   ├── Magazine.php
│   │   │   │   ├── Issue.php
│   │   │   │   ├── Article.php
│   │   ├── BaseModel.php
│   │   ├── Repositories
│   │   │   ├── MagazineRepository.php
Configure a PSR-0 or PSR-4 (better) to autoload your classes:
"psr-0": {
"Publishing": "app/"
},
Create your namespaced BaseModel:
<?php namespace Publishing\Models
use Eloquent;
class BaseModel extends Eloquent {
}
Create namespaced models, according to your source tree:
<?php namespace Publishing\Models\Magazine
use Publishing\Models\BaseModel;
class Magazine extends BaseModel {
protected $table = 'magazines';
}
<?php namespace Publishing\Models\Magazine
use Publishing\Models\BaseModel;
class Issue extends BaseModel {
protected $table = 'magazines_issues';
}
<?php namespace Publishing\Models\Magazine
use Publishing\Models\BaseModel;
class Article extends BaseModel {
protected $table = 'magazines_issues_articles';
}
You also may want to create a MagazineRepository class, to ease the access to your Magazine Domain:
<?php namespace Publishing\Repositories;
use Publishing\Models\Magazine\Magazine;
use Publishing\Models\Magazine\Issue;
use Publishing\Models\Magazine\Article;
class MagazineRepository implements DataRepositoryInterface {
private $state;
public function __construct(
Magazine $magazine,
Issue $issue,
Article $article
)
{
$this->magazine = $magazine;
$this->issue = $issue;
$this->article = $article;
}
public function getAll()
{
return $this->magazine->all();
}
public function findArticle($articleId)
{
return $this->article->find($articleId);
}
public function getByArticle($articleId)
{
return $this->findArticle($articleId)->magazine;
}
}
First off, remember that your tables should all be plural - magazine_issues not magazine_issue, magazine_issue_articles not magazine_issue_article.
As for directory structure, with Laravel it's kinda a do-what-you-want. A lot of people try to use namespaces and put models usually in a Models sub-namespace.
That said, your models should* match your tables names but converting the plural snake_case to singular StudlyCaps. So you should end up with three models:
class Magazine extends Eloquent
{
public function issues()
{
return $this->hasMany('MagazineIssue');
}
}
class MagazineIssue extends Eloquent
{
public function magazine()
{
return $this->belongsTo('Magazine');
}
public function articles()
{
return $this->hasMany('MagazineIssueArticle');
}
}
class MagazineIssueArticle extends Eloquent
{
public function issue()
{
return $this->belongsTo('MagazineIssue');
}
}
However, where you put them is still up to you. Just ensure that you use fully-qualified class names in your relationships if you mess round with namespaces. I'd suggest they all go in the same directory and namespace, though.
* I say you should do all this, but you are free to do what you want. The reason for the 'should' is that if you follow these conventions, Laravel will make your life a little easier.
Before going deeper into DB and table, you should have you domain layer organized around Laravel model. In your case, since you are working with issues, the name of your model should be Issue. So create new file under models directory, and place the following line inside:
class Issue extends Eloquent{};
Now you have starting point for your domain logic. Inside Issue model you can create relations and all related rules (fillable,guarded...).
That is about model, I dont know have migrate anything to DB.

Categories