Is it possible to make PHPStorm include necessary files automatically? - php

I have a project with lot of files. Normally each file contains one class definition. Currently when I need an instance of object I use loader, which includes necessary file and instantiates it. Such approach though doesn't allow IntelliSense to work properly. And I prefer more readable new MyObject() than $loader->load("MyObject").
I use PHPStorm IDE. Is it possible to configure it to add necessary require_once("some_file.php") when I use appropriate class type?

Solution 1:
Switch to using the autoload feature of PHP (5+) and then use new MyObject():
http://www.php.net/manual/en/function.spl-autoload-register.php
Solution 2:
Use a live template defined like this:
/** #var $CLASSNAME$ $VARNAME$ **/
$VARNAME$ = $loader->load("$CLASSNAME$");
You can then choose the class name and the vriable name each time you use the livetemplate.

The feature to add use statements automatically will be available in PhpStorm 5.0.

If it's a specific project you could always create a template wich includes all you're crap! Ten when you do file-new it will give you a file based on that template.

Related

Is it possible to load docstrings for a class from a different file in PHP (PhpStorm/IntelliJ)?

In my Laravel application I created a script that exports the attributes of my model classes to TypeScript interfaces. Thanks to those interfaces, my IDE knows which attributes the TypeScript objects should have.
Now, I'd like to go a step further and have my IDE also know about the attributes for each PHP class. For this, I'd like to create PHP Docstring definitions in separate files and link them to the actual class file. The linking would be okay to set manually.
My directory structure would look like this:
/classes/MyModel.php
/definitions/MyModel.php
The definition file for the class should only contain the Docstrings for the class:
// file: /definitions/MyModel.php
/**
* #var string $myStringAttribute
*/
Now, I'd like to include this generated file somehow in the class MyModel, so any IDE can know which attributes the Model can access from the DB. I already tried including the definitions file with include __DIR__ . '/../definitions/MyClass.php' but this did not work.
Outputting the Docstring definitions in the actual class file is not really an option for me. With separate files, I would just need to add a single line of code to the classes to link the definitions.
Is there a way of doing this that could work? Or maybe there are plugins for my IDE (PhpStorm/IntelliJ) which could do this (official Laravel Plugin did not do this)?
This is a quality-of-life question. I know I could write the docstrings for each class myself, but I prefer to have them generated automatically to be on par with the actual model definition in the database without manual work.

Overriding 2 functions in vendor package class

I'm currently trying to retrieve the SMTP Queue-ID when using the Laravel (5.6) Mail class.
I have copied the file vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/AbstractSmtpTransport.php to /app/OverriddenAbstractSmtpTransport.php and made an alias in config/app.php, made my changes:
1:
on line#395 I added return in front of the line, so we obtain the output
2:
line#492 replaced with $message->queue_ids[] = $this->streamMessage($message);
So I can access queue_ids from the message property in the Illuminate\Mail\Events\MessageSent-event
Now this works, but I don't think it's a very safe approach to modifying the vendor class, as it might cause a breaking change when running security updates.
Is there a simpler/better/safer solution to this ?
Copying the whole class is risky - if any updates are done to the vendor class in a newer version, they'll never make it into your copy. A safer way is to extend the original class and overwrite those 2 functions. There is still a risk of some changes being done to those functions in vendor class, but it's much lower now. Another option would be to extend the original class and add new methods - they will have access to all public and protected properties/methods of the original class and that could be enough to get you what you need.
Whatever version you choose, you'll need to later register the new class as a new driver/transport for Swift. Check the following snippet for an example: https://gist.github.com/maxwellimpact/46ded5c553f68946d13d

What is the best practice for using two namespaces in a single file for backwards compatibility?

I need to refactor a PHP project where the vendor has undergone a re-brand. The project currently uses the namespace OldCompany, and I need to change this to NewCompany, however I've realized I need to keep the old namespace for backwards compatibility, in the cases where existing users are using try {} catch (/OldCompany/Exception $e) {}... If I simply change the namespace to NewCompany, I will break their integration if they upgrade SDK versions straight up. After reading the PHP Namespace docs, I tried the method outlined in Example #3, and modified all of my files like this:
<?
namespace NewCompany{ /* no namespace-specific code needed */ };
namespace OldCompany{ /* no namespace-specific code needed */ };
namespace {
/* global namespace code. code that applies to both namespaces? */
require_once('file1.php');
require_once('file2.php');
/* classes and functions within the global namespace */
}
The above throws a PHP Fatal Exception and can't find the NewCompany namespace.
I definitely do not want to duplicate code as per Example #2 of the docs, since there isn't namespace-specific code.
What is the best way to preserve the existing namespace of OldCompany for existing users while refactoring a re-brand to `NewCompany' for new users? Should I be looking for a different solution to this problem?
Thanks in advance :)
namespace NewCompany{ /* no namespace-specific code needed */ };
namespace OldCompany{ /* no namespace-specific code needed */ };
This is setting namespaces. But surely your issue is old vendor namespace has changed to a new one? This means you need to import (use) the new namespace instead of the old one?
Maybe I've misunderstood you, but are you confused about the difference between setting and importing namespaces? If the vendor has changed to a new namespace then you need to import the new one, rather than the old one. But this has nothing to do with setting namespaces.
I definitely do not want to duplicate code as per Example #2 of the
docs, since there isn't namespace-specific code.
If there's no namespace specific code then what problem are you trying to resolve?
I need to keep the old namespace for backwards
compatibility, in the cases where existing users are using
try {} catch (/OldCompany/Exception $e) {}.
Surely whatever namespace they have in their end won't affect your side of things? So you could update all your code's namespaces and not worry about what they use? They just call your endpoint or whatever as normal?
Perhaps be more specific about that if it's a real issue somehow.
It sounds to me like you just need to update your import statements for the vendor's new namespace.
Something else to consider is refactor how you manage vendors.
I presume you are not using a pre-made framework, such as Symfony (they have predetermined ways of managing vendors and things).
The fact you are considering refactoring throughout your code rather than a single config file (or whatever) makes me think your code has a design flaw. As it seems you are changing code (namespace) within your class files based on a 3rd party company (vendor) changing their name. And where possible your code should be entirely abstract from such changes to this degree.
I suggest considering abstracting things out into centralised places whenever it makes sense. This allows the one centralised thing to be altered and changes just automatically ripple down to all your code without any need for a huge refactor.
You could make your own generic names for your vendors so whatever they call themselves doesn't matter in your code.
E.g. vendor FunkyJoesEmailer in your app will just be Emailer. Then whichever emailer library you decide to use now and in the future will be in the same Emailer DIR and namespaces won't change, always be Whatever\Emailer.
Then in some file high up the load chain you'd have some wrapper class (or service or container like thing) which would load FunkyJoesEmailer in whatever namespace that is in via your generic name, such as $this->Emailer. So in your code you'd call on $this->Emailer which would return an instance of whatever emailer (vendor) you are using.
If you ever needed to do a namespace change or even entirely swap out the Emailer vendor you use, the change is in one place and would ripple down in your code because it's still $this->Emailer.
While this approach doesn't resolve your having to change everything now, it does mean you only ever have to change it this once. Then in the future can just replace vendors or let their renaming happen within their code and your path (namespace) to it remains the same.

Behat - programatic test step definition (ArrayLoader)

I am successfully using Behat 3.0 with the tests defined in Feature files, using Gherkin language. However, in some cases, it would be useful to define the steps programatically - Gherkin is readable, but difficult to define multiple variants.
Is there a way to programatically define the test steps (in PHP classes), so these can be picked up by Behat? I have found ArrayLoader class, which seems to be able to do that. However, I wasn't able to make it working with Behat. It seems Behat is using Gherkin FileLoader by default and I haven't found a way to rewrite this behavior (or rather extend) in the config file.
How can I combine test input from Gherkin files with custom definitions specified in PHP files?
Is there a way to programatically define the test steps (in PHP
classes),...
If I don't misunderstand what you want, you can do like this:
use Behat\Behat\Definition\Call\Then;
use Behat\Behat\Definition\Call\When;
use Behat\MinkExtension\Context\MinkContext;
class FeatureContext extends MinkContext
{
public function iWaitSeconds($second)
{
new Then(.....);
new When(.....);
new Given(.....);
}
}
You need to do a bit of research for more examples.
e.g.: new When("The content is:", new PyStringNode($string));

Load a class with a different name than the one passed to the autoloader as argument

basically, I have the following problem: I want to make use of PHP's new namespace features. Unfortunately, I'm running a PHP version (5.3.2) in which namespace-autoload-support for linux still seems buggy and does not work (PHP should be able to load the class file automatically by its namespace without having to use a custom autoloader, but that doesn't work).
What I want to achieve is to write an autoloader that I can simply remove as soon as the php's namespace features work correctly (there seems to be a speed advantage when not using a custom autoloader) with having to change as less code as possible afterwards.
So I have a call like this:
$filereader = new system\libraries\file\XML();
which gets passed correctly as the string "system\libraries\file\XML" to my autoload-function. I can load the corresponding file "system/libraries/file/XML.class.php". However, the class in there will be named
class XML { ... }
(or something different than "system\libraries\file\XML") and so have a different name than the one by which PHP will try to load it. So is there an easy way to load that class ("XML") which has a different name than the name which I pass to the autoloader function? Can I perhaps do something in the autoloader to achieve that behaviour? (I'm using spl_autoload_register).
I know that even if it worked out I would still not be able to use all features of namespacing, since a (simple) autoloader would not respect the "use namespace" directive and I would still have to use rather long names for loading a class. However, if I understood PHP's namespace-features correctly, I could leave the code as it is when I later switch to using native namespace support instead of my autoloader.
If what I try to do does not make sense at all in your opinion or if I misunderstood namespaces, please tell me (- I have not used PHP's namespace features yet).
I would load the file (which creates the XML class) and then alias the XML class to the properly namespaced system\libraries\file\XML class:
class_alias('XML', 'system\libraries\file\XML');
More generally:
class_alias(basename($class), $class));
Though I'm not quite sure whether class_alias can alias to namespaced classes...

Categories