Skip to content

[Profiler] Add conditional profiling example config #18144

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .doctor-rst.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ rules:
ensure_order_of_code_blocks_in_configuration_block: ~
extend_abstract_controller: ~
# extension_xlf_instead_of_xliff: ~
forbidden_directives:
directives:
- '.. index::'
indention: ~
lowercase_as_in_use_statements: ~
max_blank_lines:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ jobs:
key: ${{ runner.os }}-doctor-rst-${{ steps.extract_base_branch.outputs.branch }}

- name: "Run DOCtor-RST"
uses: docker://oskarstark/doctor-rst:1.41.3
uses: docker://oskarstark/doctor-rst:1.42.1
with:
args: --short --error-format=github --cache-file=/github/workspace/.cache/doctor-rst.cache

Expand Down
161 changes: 82 additions & 79 deletions _build/composer.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion components/clock.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ for different use cases:
:class:`Symfony\\Component\\Clock\\MockClock`
Commonly used in tests as a replacement for the ``NativeClock`` to be able
to freeze and change the current time using either ``sleep()`` or ``modify()``.
:class:`Symfony\\Component\\Clock\\MonotonicClock``
:class:`Symfony\\Component\\Clock\\MonotonicClock`
Relies on ``hrtime()`` and provides a high resolution, monotonic clock,
when you need a precise stopwatch.

Expand Down
5 changes: 5 additions & 0 deletions components/process.rst
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ You can configure the options passed to the ``other_options`` argument of
// this option allows a subprocess to continue running after the main script exited
$process->setOptions(['create_new_console' => true]);

.. note::

The ``create_new_console`` option is only available on Windows!


Using Features From the OS Shell
--------------------------------

Expand Down
26 changes: 26 additions & 0 deletions components/property_info.rst
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ information from annotations of properties and methods, such as ``@var``,

// Extraction.php
use Symfony\Component\PropertyInfo\Extractor\PhpStanExtractor;
use App\Domain\Foo;

$phpStanExtractor = new PhpStanExtractor();
$phpStanExtractor->getTypesFromConstructor(Foo::class, 'bar');
Expand Down Expand Up @@ -505,6 +506,31 @@ with the ``property_info`` service in the Symfony Framework::
// Type information.
$doctrineExtractor->getTypes($class, $property);

ConstructorExtractor
~~~~~~~~~~~~~~~~~~~~

The :class:`Symfony\\Component\\PropertyInfo\\Extractor\\ConstructorExtractor`
tries to extract properties information by using either the
:class:`Symfony\\Component\\PropertyInfo\\Extractor\\PhpStanExtractor` or
the :class:`Symfony\\Component\\PropertyInfo\\Extractor\\ReflectionExtractor`
on the constructor arguments::

// src/Domain/Foo.php
class Foo
{
public function __construct(
private string $bar,
) {
}
}

// Extraction.php
use App\Domain\Foo;
use Symfony\Component\PropertyInfo\Extractor\ConstructorExtractor;

$constructorExtractor = new ConstructorExtractor([new ReflectionExtractor()]);
$constructorExtractor->getTypes(Foo::class, 'bar')[0]->getBuiltinType(); // returns 'string'

.. _`components-property-information-extractors-creation`:

Creating Your Own Extractors
Expand Down
12 changes: 7 additions & 5 deletions components/psr7.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ Usage
Converting from HttpFoundation Objects to PSR-7
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The bridge provides an interface of a factory called
:class:`Symfony\\Bridge\\PsrHttpMessage\\HttpMessageFactoryInterface`
that builds objects implementing PSR-7 interfaces from HttpFoundation objects.
The bridge provides an interface of a factory called
`HttpMessageFactoryInterface`_ that builds objects implementing PSR-7
interfaces from HttpFoundation objects.

The following code snippet explains how to convert a :class:`Symfony\\Component\\HttpFoundation\\Request`
to a ``Nyholm\Psr7\ServerRequest`` class implementing the
Expand Down Expand Up @@ -66,8 +66,8 @@ Converting Objects implementing PSR-7 Interfaces to HttpFoundation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

On the other hand, the bridge provide a factory interface called
:class:`Symfony\\Bridge\\PsrHttpMessage\\HttpFoundationFactoryInterface`
that builds HttpFoundation objects from objects implementing PSR-7 interfaces.
`HttpFoundationFactoryInterface`_ that builds HttpFoundation objects from
objects implementing PSR-7 interfaces.

The next snippet explain how to convert an object implementing the
``Psr\Http\Message\ServerRequestInterface`` interface to a
Expand All @@ -93,3 +93,5 @@ to a :class:`Symfony\\Component\\HttpFoundation\\Response` instance::
.. _`PSR-7`: https://www.php-fig.org/psr/psr-7/
.. _`PSR-17`: https://www.php-fig.org/psr/psr-17/
.. _`libraries that implement psr/http-factory-implementation`: https://packagist.org/providers/psr/http-factory-implementation
.. _`HttpMessageFactoryInterface`: https://github.com/symfony/psr-http-message-bridge/blob/main/HttpMessageFactoryInterface.php
.. _`HttpFoundationFactoryInterface`: https://github.com/symfony/psr-http-message-bridge/blob/main/HttpFoundationFactoryInterface.php
9 changes: 7 additions & 2 deletions contributing/code/standards.rst
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,14 @@ Structure

* Exception and error message strings must be concatenated using :phpfunction:`sprintf`;

* Exception and error messages must not contain backticks (e.g. 'The \`foo\` option ...'),
* Exception and error messages must not contain backticks,
even when referring to a technical element (such as a method or variable name).
Double quotes must be used at all time (e.g. 'The "foo" option ...'),;
Double quotes must be used at all time:

.. code-block:: diff

- Expected `foo` option to be one of ...
+ Expected "foo" option to be one of ...

* Exception and error messages must start with a capital letter and finish with a dot ``.``;

Expand Down
44 changes: 27 additions & 17 deletions doctrine/events.rst
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,13 @@ do so, define a listener for the ``postPersist`` Doctrine event::
namespace App\EventListener;

use App\Entity\Product;
use Doctrine\Persistence\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PostPersistEventArgs;

class SearchIndexer
{
// the listener methods receive an argument which gives you access to
// both the entity object of the event and the entity manager itself
public function postPersist(LifecycleEventArgs $args): void
public function postPersist(PostPersistEventArgs $args): void
{
$entity = $args->getObject();

Expand All @@ -140,6 +140,11 @@ do so, define a listener for the ``postPersist`` Doctrine event::
}
}

.. note::

In previous Doctrine versions, instead of ``PostPersistEventArgs``, you had
to use ``LifecycleEventArgs``, which was deprecated in Doctrine ORM 2.14.

Then, add the ``#[AsDoctrineListener]`` attribute to the class to enable it as
a Doctrine listener in your application::

Expand Down Expand Up @@ -167,12 +172,12 @@ listener in the Symfony application by creating a new service for it and
namespace App\EventListener;

use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PostPersistEventArgs;

#[AsDoctrineListener('postPersist'/*, 500, 'default'*/)]
class SearchIndexer
{
public function postPersist(LifecycleEventArgs $event): void
public function postPersist(PostPersistEventArgs $event): void
{
// ...
}
Expand Down Expand Up @@ -277,13 +282,13 @@ First, define a PHP class that handles the ``postUpdate`` Doctrine event::
namespace App\EventListener;

use App\Entity\User;
use Doctrine\Persistence\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PostUpdateEventArgs;

class UserChangedNotifier
{
// the entity listener methods receive two arguments:
// the entity instance and the lifecycle event
public function postUpdate(User $user, LifecycleEventArgs $event): void
public function postUpdate(User $user, PostUpdateEventArgs $event): void
{
// ... do something to notify the changes
}
Expand Down Expand Up @@ -419,8 +424,10 @@ want to log all the database activity. To do so, define a subscriber for the

use App\Entity\Product;
use Doctrine\Bundle\DoctrineBundle\EventSubscriber\EventSubscriberInterface;
use Doctrine\ORM\Event\PostPersistEventArgs;
use Doctrine\ORM\Event\PostRemoveEventArgs;
use Doctrine\ORM\Event\PostUpdateEventArgs;
use Doctrine\ORM\Events;
use Doctrine\Persistence\Event\LifecycleEventArgs;

class DatabaseActivitySubscriber implements EventSubscriberInterface
{
Expand All @@ -436,27 +443,25 @@ want to log all the database activity. To do so, define a subscriber for the
}

// callback methods must be called exactly like the events they listen to;
// they receive an argument of type LifecycleEventArgs, which gives you access
// they receive an argument of type Post*EventArgs, which gives you access
// to both the entity object of the event and the entity manager itself
public function postPersist(LifecycleEventArgs $args): void
public function postPersist(PostPersistEventArgs $args): void
{
$this->logActivity('persist', $args);
$this->logActivity('persist', $args->getObject());
}

public function postRemove(LifecycleEventArgs $args): void
public function postRemove(PostRemoveEventArgs $args): void
{
$this->logActivity('remove', $args);
$this->logActivity('remove', $args->getObject());
}

public function postUpdate(LifecycleEventArgs $args): void
public function postUpdate(PostUpdateEventArgs $args): void
{
$this->logActivity('update', $args);
$this->logActivity('update', $args->getObject());
}

private function logActivity(string $action, LifecycleEventArgs $args): void
private function logActivity(string $action, mixed $entity): void
{
$entity = $args->getObject();

// if this subscriber only applies to certain entity types,
// add some code to check the entity type as early as possible
if (!$entity instanceof Product) {
Expand All @@ -467,6 +472,11 @@ want to log all the database activity. To do so, define a subscriber for the
}
}

.. note::

In previous Doctrine versions, instead of ``Post*EventArgs`` classes, you had
to use ``LifecycleEventArgs``, which was deprecated in Doctrine ORM 2.14.

If you're using the :ref:`default services.yaml configuration <service-container-services-load-example>`
and DoctrineBundle 2.1 (released May 25, 2020) or newer, this example will already
work! Otherwise, :ref:`create a service <service-container-creating-service>` for this
Expand Down
6 changes: 1 addition & 5 deletions form/form_collections.rst
Original file line number Diff line number Diff line change
Expand Up @@ -643,12 +643,8 @@ the relationship between the removed ``Tag`` and ``Task`` object.

class TaskController extends AbstractController
{
public function edit($id, Request $request, EntityManagerInterface $entityManager): Response
public function edit(Task $task, Request $request, EntityManagerInterface $entityManager): Response
{
if (null === $task = $entityManager->getRepository(Task::class)->find($id)) {
throw $this->createNotFoundException('No task found for id '.$id);
}

$originalTags = new ArrayCollection();

// Create an ArrayCollection of the current Tag objects in the database
Expand Down
2 changes: 1 addition & 1 deletion form/use_empty_data.rst
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ The closure must accept a ``FormInterface`` instance as the first argument::
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'empty_data' => function (FormInterface $form) {
'empty_data' => function (FormInterface $form): Blog {
return new Blog($form->get('title')->getData());
},
]);
Expand Down
68 changes: 68 additions & 0 deletions html_sanitizer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,73 @@ the HTML sanitizer: ``src``, ``href``, ``lowsrc``, ``background`` and ``ping``.
->allowRelativeMedias()
);

Max Input Length
~~~~~~~~~~~~~~~~

In order to prevent `DoS attacks`_, by default the HTML sanitizer limits the
input length to ``20000`` characters (as measured by ``strlen($input)``). All
the contents exceeding that length will be truncated. Use this option to
increase or decrease this limit:

.. configuration-block::

.. code-block:: yaml

# config/packages/html_sanitizer.yaml
framework:
html_sanitizer:
sanitizers:
app.post_sanitizer:
# ...

# inputs longer (in characters) than this value will be truncated
max_input_length: 30000 # default: 20000

.. code-block:: xml

<!-- config/packages/html_sanitizer.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:framework="http://symfony.com/schema/dic/symfony"
xsi:schemaLocation="http://symfony.com/schema/dic/services
https://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">

<framework:config>
<framework:html-sanitizer>
<framework:sanitizer name="app.post_sanitizer">
<!-- inputs longer (in characters) than this value will be truncated (default: 20000) -->
<framework:max-input-length>20000</framework:max-input-length>
</framework:sanitizer>
</framework:html-sanitizer>
</framework:config>
</container>

.. code-block:: php

// config/packages/framework.php
use Symfony\Config\FrameworkConfig;

return static function (FrameworkConfig $framework) {
$framework->htmlSanitizer()
->sanitizer('app.post_sanitizer')
// inputs longer (in characters) than this value will be truncated (default: 20000)
->withMaxInputLength(20000)
;
};

.. code-block:: php-standalone

use Symfony\Component\HtmlSanitizer\HtmlSanitizer;
use Symfony\Component\HtmlSanitizer\HtmlSanitizerConfig;

$postSanitizer = new HtmlSanitizer(
(new HtmlSanitizerConfig())
// inputs longer (in characters) than this value will be truncated (default: 20000)
->withMaxInputLength(20000)
);

Custom Attribute Sanitizers
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -1013,3 +1080,4 @@ to enable it for an HTML sanitizer:

.. _`HTML Sanitizer W3C Standard Proposal`: https://wicg.github.io/sanitizer-api/
.. _`W3C Standard Proposal`: https://wicg.github.io/sanitizer-api/
.. _`DoS attacks`: https://en.wikipedia.org/wiki/Denial-of-service_attack
2 changes: 1 addition & 1 deletion notifier.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ The notifier component supports the following channels:
.. tip::

Use :doc:`secrets </configuration/secrets>` to securely store your
API's tokens.
API tokens.

.. _notifier-sms-channel:

Expand Down
21 changes: 21 additions & 0 deletions profiler.rst
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,27 @@ you'll need to configure the data collector explicitly:
// 'priority' => 300,
]);
};

Conditional Profiling
~~~~~~~~~~~~~~~~~~~~~

Symfony profiler provides an immense amount of debug information to quickly find the cause of any problem. However, that comes at a price, because the profiler must collect all that information while serving the request. This can slowdown the application even for requests where you don't look at the profiler information (which are most of them).

You can enable the profiler conditionally. To do that, add these two new config options:

.. configuration-block::

.. code-block:: yaml

# config/packages/dev/web_profiler.yaml
framework:
profiler:
collect: false
collect_parameter: 'profile'

The collect: false option disables the profiler by default and the collect_parameter: profile option enables it for requests that include the profile query parameter.

You can freely choose the query parameter name and you can also enable the profiler by submitting a form field with that name (useful for POST requests) and even a request attribute.

.. _`Single-page applications`: https://en.wikipedia.org/wiki/Single-page_application
.. _`Blackfire`: https://blackfire.io/docs/introduction?utm_source=symfony&utm_medium=symfonycom_docs&utm_campaign=profiler
Expand Down
2 changes: 1 addition & 1 deletion security/access_denied_handler.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ unauthenticated user tries to access a protected resource::
class AuthenticationEntryPoint implements AuthenticationEntryPointInterface
{
public function __construct(
UrlGeneratorInterface $urlGenerator,
private UrlGeneratorInterface $urlGenerator,
) {
}

Expand Down
Loading