PhpStorm + Symfony
SymfonyCon 2016 - Daniel Espendiller - @BigHaehnchen
Symfony Plugin
PHP Annotations
Shopware Plugin
Drupal Symfony Bridge
Laravel Plugin
OXID Plugin
PHP Toolbox
Plugins
Daniel Espendiller
E-Mail: [email protected]
Twitter: @BigHaehnchen
GitHub: //github.com/Haehnchen
Roadmap
● Problems / Statistics / Infrastructure– Key Facets, Statistics, Travis, JetBrains API
● Features– Symfony Installer, Service and Container, Form,
Events, Doctrine, Code Generator, Twig Doc, Twig Namespaces, Translations, Profiler, Remote Container, PHP-Toolbox
First commits(2013/04)
Key Facets (since 2013/04)
● 7 Plugins● ~2,5 Mio Downloads● ~1.000 Issues● ~50.000 LOC (Java)● Time Investment: ~4h weekly● Single person project● MIT License● Framework like, feel free to copy
https://github.com/Haehnchen/idea-*
Symfony Plugin Update
Symfony Plugin / Contribution
Testing / Travis
public class XmlDicCompletionContributorTest extends SymfonyLightCodeInsightFixtureTestCase { public void setUp() throws Exception { super.setUp(); myFixture.copyFileToProject("appDevDebugProjectContainer.xml");
myFixture.configureByText("classes.php", "<?php\n" + "namespace Foo\\Name;\n" + "class FooClass {" + " public function foo() " + "}" ); }
public void testServiceCompletion() { assertCompletionContains("service.xml", "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<container>\n" + " <services>\n" + " <argument type=\"service\" id=\"<caret>\"/>\n" + " </services>\n" + "</container>" , "data_collector.router" ); }}
Testing / Travissry Java Code
JetBrains APIPsiViewer Yaml File
“There are to be some PSI structure changes; I see that your plugin use YAML extensively so something will definitely break. To be honest, I wanted to do some changes which may even break compilation, but I can leave some legacy code. However, one will need to fix the usage of PSI so I don't think it's a big problem.”
JetBrains API
“There is a patch to master which compiles but leads to about 60 tests failing. However, you can look through the changes to have an idea of what's happening.”
JetBrains API
JetBrains APIYaml: [@service, "@service2", [""], ['']]
JetBrains API / 2016.3
PhpStorm 2016.3 EAP 163.5644 / Initial PHPSpec support
Features
Symfony InstallerService and Container
FormEvents
DoctrineCode Generator
Twig DocTwig Namespaces
TranslationsProfiler
Remote ContainerPHP-Toolbox
Symfony InstallerFile → New Project → Symfony
Symfony InstallerFile → New Project → Symfony
Container
<?php
namespace espend\Container\ServiceBundle\Classes;
use Symfony\Component\Routing\RouterInterface;
class Bar extends \Twig_Extension{ public function __construct(RouterInterface $router) { } public function getName() { }}
Class Template
ContainerService Generator
ContainerService Generator
ContainerJavascript: Service Id Strategy
ContainerAutocompletion
ContainerArgument Detection
FormOption Completion and Navigation
FormOption Completion and Navigation
Form
public function buildForm(FormBuilderInterface $builder, array $options){ $builder->add('test', 'birthday');
$builder->add('test', 'file'); $builder->add('test', 'file');}
public function buildForm(FormBuilderInterface $builder, array $options){ $builder->add('test', BirthdayType::class);
$builder->add('test', FileType::class);
$builder->add('test', FileType::class);}
Symfony 3.x Migration
FormSymfony 3.x Migration: Generator / Inspection
Events
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
public function onKernelController(FilterControllerEvent $event){}
Method Generation
Events
final class KernelEvents{ /** * @Event("Symfony\Component\HttpKernel\Event\FilterControllerEvent") */ const CONTROLLER = 'kernel.controller';}
* * This event allows you to change the controller that will handle the * request. The event listener method receives a * Symfony\Component\HttpKernel\Event\FilterControllerEvent instance. * * @var string * * @api */const CONTROLLER = 'kernel.controller';
Symfony Event Definition
Doctrinenamespace Bar;
class FooBar{ private $id;
private $createdAt;
private $isActive;}
namespace Bar;
use Doctrine\ORM\Mapping as ORM;
class FooBar{ /** * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") * @ORM\Column(type="integer") */private $id;
/** * @ORM\Column(type="datetime") */ private $createdAt;
/** * @ORM\Column(type="boolean") */ private $isActive;}
Annotation Generator
DoctrineMetadata - XML
DoctrineMetadata - Annotation
GeneratorFile - Template
Generator Method - Route
class DefaultController extends Controller{ public function fooAction() { }}
GeneratorEvent Method
class YamlClass{ public function onFoobar(GetResponseForExceptionEvent $event) { }}
Twig Doc
{# @var cars \espend\[...]\Entity\Car #}
{# @var cars \espend\[...]\Entity\Car[] #}
{# @controller AcmeDemoBundle:Demo:hello #}
{# @see test.html #}
Twig Doc (var)
Twig Doc (var)
Twig Doc (controller)
public function indexAction(){ $this->render('index.html.twig'); $this->renderView('index.html.twig'); $this->renderResponse('index.html.twig');}
Twig Doc (controller)
public function indexAction(){ return ['foo_request' => new Request()]; return $this->render(null, ['foo_request_render' => new Request()]);}
Twig Doc (see)
{# @see foo/foo.html.twig #}{# @see @WebProfiler/Profiler/table.html.twig #}
{# @see ../root.html.twig #}{# @see ../root.json #}
{# @see espendRouterRoutesBundle:Default:index #}{# @see web_profiler.controller.profiler:searchBarAction #}
{# @see espend\Router\RoutesBundle\Controller\DefaultController #}{# @see [...]\DefaultController::indexAction #}
Twig Templates
Filesystem
Classes
Routes
Twig NamespaceFile → Settings → … → Symfony → Twig / Templates
Twig NamespaceFile → Settings → … → Symfony → Twig / Templates
# ide-twig.json{ "namespaces": [ { "namespace": "foo", // @foo/foobar.html.twig "path": "res" }, { "path": "res" // foobar.html.twig }, { "path": "res", // FooBundle::foobar.html.twig "type": "Bundle", "namespace": "FooBundle" } ]}
TranslationsExtraction
TranslationsExtraction
TranslationsXLIFF Support
ProfilerLanguage & Frameworks → Symfony → Profiler
Remote ContainerappDevDebugProjectContainer.xml
Tools → Deplyoment → Configuration
Remote ContainerappDevDebugProjectContainer.xml
PHP-Toolboxhttps://github.com/Haehnchen/idea-php-toolbox
PHP-Toolbox// .ide-toolbox.metadata.json{ "registrar":[ {"language":"php", "provider":"MY_FOO", "signature": ["Foo:bar"]}, {"language":"twig", "provider":"MY_FOO", "signatures":[{"function": "foo"}]} ], "providers": [ { "name": "MY_FOO", "items":[ { "lookup_string": "POST", "icon": "com.jetbrains.php.PhpIcons.PROFILER_SNAPSHOT_FILE_TYPE" }, { "lookup_string": "GET" }, { "lookup_string": "DELETE", "type_text": "Sweet Stuff" } ] } ]}
PHP-Toolbox
// .ide-toolbox.metadata.json{ "registrar":[ { "provider":"FOOBAR", "language":"php", "signatures":[{"class": "Foo", "method": "bar", "type": "type"}] } ], "providers": [ { "name": "FOOBAR", "items":[ { "lookup_string": "foobar", "type": "DateTime" } ] } ]}
PHP-Toolbox
[GET] /[GET] /projects[GET] /projects/{project}[GET] /projects/{project}/clear[POST] /projects/{project}/{provider}[GET] /projects/{project}/json-debug
Languages & Frameworks -> PHP Toolbox
curl \-X POST http://127.0.0.1:48734/projects/idea-espend/php-toolbox-json \-d @test.json
{ "success": true, "content": "items added"}
Questions
Feedback
...