diff --git a/CHANGELOG b/CHANGELOG
index 9151439..50a3edc 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,9 @@
+3.10.0 (unreleased):
+ * Feature: Allow complex types to be handled by symfony/serializer if available
+ * Feature: Leverage dependency injection for Serializers
+ * Fix: Profiler tab was broken in some case
+ * Enhancement: Better looking profiling tab
+
3.9.0 (2025-01-17):
* Feature: (SF6.2+) Supports automatic value resolving in controllers (with or without MapEntity), see [https://github.com/symfony/symfony/blob/7.2/src/Symfony/Bridge/Doctrine/ArgumentResolver/EntityValueResolver.php](Doctrine Bridge)
* Feature: supports serializer options in attributes
diff --git a/composer.json b/composer.json
index 4012097..bd97aec 100644
--- a/composer.json
+++ b/composer.json
@@ -5,7 +5,7 @@
"license": "Apache-2.0",
"require": {
"php": ">=8.1",
- "ccmbenchmark/ting": "^3.10",
+ "ccmbenchmark/ting": "^3.11",
"doctrine/cache": "^1.10",
"symfony/validator": "^4.4 || ^5.0 || ^6.0 || ^7.0",
"symfony/http-kernel": "^4.4 || ^5.0 || ^6.0 || ^7.0",
@@ -18,7 +18,9 @@
"atoum/stubs": "^2.2",
"brick/geo": ">=0.5 <=1.0",
"symfony/expression-language": "^6.3 || ^7.2",
+ "symfony/serializer": "^6.0 || ^7.0",
"symfony/security-bundle": "^6.0 || ^7.0",
+ "symfony/security-core": "^6.0 || ^7.0",
"symfony/uid": "^6.0 || ^7.0"
},
"autoload": {
diff --git a/src/TingBundle/DataCollector/TingCacheDataCollector.php b/src/TingBundle/DataCollector/TingCacheDataCollector.php
index c78f2f5..b81d030 100644
--- a/src/TingBundle/DataCollector/TingCacheDataCollector.php
+++ b/src/TingBundle/DataCollector/TingCacheDataCollector.php
@@ -38,8 +38,6 @@ class TingCacheDataCollector extends DataCollector implements LateDataCollectorI
*/
protected $cacheLogger = null;
- protected array|Data $data = [];
-
public function __construct()
{
$this->init();
@@ -57,7 +55,7 @@ public function __construct()
public function collect(Request $request, Response $response, ?\Throwable $exception = null): void
{
if ($this->cacheLogger !== null) {
- $this->data['cache']['operations'] = $this->cacheLogger->getOperations();
+ $this->data['cache']['operations'] = $this->cloneVar($this->cacheLogger->getOperations());
$this->data['cache']['operationsCount'] = count($this->data['cache']['operations']);
$this->data['cache']['time'] = $this->cacheLogger->getTotalTime();
$this->data['cache']['hits'] = $this->cacheLogger->getHits();
@@ -68,7 +66,7 @@ public function collect(Request $request, Response $response, ?\Throwable $excep
public function lateCollect(): void
{
if ($this->cacheLogger !== null) {
- $this->data['cache']['operations'] = $this->cacheLogger->getOperations();
+ $this->data['cache']['operations'] = $this->cloneVar($this->cacheLogger->getOperations());
$this->data['cache']['operationsCount'] = count($this->data['cache']['operations']);
$this->data['cache']['time'] = $this->cacheLogger->getTotalTime();
$this->data['cache']['hits'] = $this->cacheLogger->getHits();
diff --git a/src/TingBundle/DataCollector/TingDriverDataCollector.php b/src/TingBundle/DataCollector/TingDriverDataCollector.php
index ae4f8f2..4bc978d 100644
--- a/src/TingBundle/DataCollector/TingDriverDataCollector.php
+++ b/src/TingBundle/DataCollector/TingDriverDataCollector.php
@@ -38,8 +38,6 @@ class TingDriverDataCollector extends DataCollector implements LateDataCollector
*/
protected $driverLogger = null;
- protected array|Data $data = [];
-
public function __construct()
{
$this->init();
@@ -57,44 +55,24 @@ public function __construct()
public function collect(Request $request, Response $response, ?\Throwable $exception = null): void
{
if ($this->driverLogger !== null) {
- $this->data['driver']['queries'] = $this->driverLogger->getQueries();
- $this->data['driver']['execs'] = $this->driverLogger->getExecs();
+ $this->data['driver']['queries'] = $this->cloneVar($this->driverLogger->getQueries());
+ $this->data['driver']['execs'] = $this->cloneVar($this->driverLogger->getExecs());
$this->data['driver']['queryCount'] = count($this->data['driver']['queries']);
$this->data['driver']['time'] = $this->driverLogger->getTotalTime();
- $this->data['driver']['connections'] = $this->driverLogger->getConnections();
- $this->data['driver']['connectionsHashToName'] = $this->driverLogger->getConnectionsHashToName();
-
- // HttpKernel < 3.2 compatibility layer
- // For >= 3.2 cloneVar is always present and MUST be used.
- if (method_exists($this, 'cloneVar')) {
- foreach ($this->data['driver']['queries'] as &$query) {
- if (isset($query['params']) === true) {
- $query['params'] = $this->cloneVar($query['params']);
- }
- }
- }
+ $this->data['driver']['connections'] = $this->cloneVar($this->driverLogger->getConnections());
+ $this->data['driver']['connectionsHashToName'] = $this->cloneVar($this->driverLogger->getConnectionsHashToName());
}
}
public function lateCollect(): void
{
if ($this->driverLogger !== null) {
- $this->data['driver']['queries'] = $this->driverLogger->getQueries();
- $this->data['driver']['execs'] = $this->driverLogger->getExecs();
+ $this->data['driver']['queries'] = $this->cloneVar($this->driverLogger->getQueries());
+ $this->data['driver']['execs'] = $this->cloneVar($this->driverLogger->getExecs());
$this->data['driver']['queryCount'] = count($this->data['driver']['queries']);
$this->data['driver']['time'] = $this->driverLogger->getTotalTime();
- $this->data['driver']['connections'] = $this->driverLogger->getConnections();
- $this->data['driver']['connectionsHashToName'] = $this->driverLogger->getConnectionsHashToName();
-
- // HttpKernel < 3.2 compatibility layer
- // For >= 3.2 cloneVar is always present and MUST be used.
- if (method_exists($this, 'cloneVar')) {
- foreach ($this->data['driver']['queries'] as &$query) {
- if (isset($query['params']) === true) {
- $query['params'] = $this->cloneVar($query['params']);
- }
- }
- }
+ $this->data['driver']['connections'] = $this->cloneVar($this->driverLogger->getConnections());
+ $this->data['driver']['connectionsHashToName'] = $this->cloneVar($this->driverLogger->getConnectionsHashToName());
}
}
diff --git a/src/TingBundle/DependencyInjection/TingExtension.php b/src/TingBundle/DependencyInjection/TingExtension.php
index bb72c56..bcce466 100644
--- a/src/TingBundle/DependencyInjection/TingExtension.php
+++ b/src/TingBundle/DependencyInjection/TingExtension.php
@@ -28,6 +28,7 @@
use CCMBenchmark\TingBundle\ArgumentResolver\EntityValueResolver;
use CCMBenchmark\TingBundle\Schema\Column;
use CCMBenchmark\TingBundle\Schema\Table;
+use CCMBenchmark\TingBundle\Serializer\SymfonySerializer;
use Doctrine\Common\Cache\VoidCache;
use Symfony\Component\DependencyInjection\ChildDefinition;
use CCMBenchmark\TingBundle\TingBundle;
@@ -43,6 +44,7 @@
use Symfony\Component\HttpKernel\Controller\ValueResolverInterface;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\Config\FileLocator;
+use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Uid\Uuid;
use Symfony\Component\PropertyAccess\PropertyAccessor;
@@ -152,6 +154,11 @@ public function load(array $configs, ContainerBuilder $container): void
$container->setDefinition(EntityValueResolver::class, $definition);
}
+
+ $serializerFactoryDefinition = $container->getDefinition('ting.serializerfactory');
+ foreach ($container->findTaggedServiceIds('ting.serializer') as $id => $tags) {
+ $serializerFactoryDefinition->addMethodCall('add', [new Reference($id)]);
+ }
}
/**
@@ -205,15 +212,24 @@ function getMetadata(\ReflectionClass $reflector, Table $attribute): Definition
\DateTime::class => 'datetime',
\DateTimeZone::class => 'datetimezone',
Uuid::class => 'uuid',
- default => 'string'
+ default => (interface_exists(SerializerInterface::class) ? 'symfony_serializer' : 'string')
};
}
+ $options = $mappingAttribute->getArguments()['serializerOptions'] ?? [];
+ if ($newField['type'] === 'symfony_serializer') {
+ $defaultOptions = [
+ 'serialize' => ['context' => ['groups' => ['*']]],
+ 'unserialize' => ['context' => ['groups' => ['*']], 'type' => $property->getType()->getName()]
+ ];
+ $options = array_merge_recursive($defaultOptions, $options);
+ $newField['serializer'] = SymfonySerializer::class;
+ }
if ($mappingAttribute->getArguments()['serializer'] ?? false) {
$newField['serializer'] = $mappingAttribute->getArguments()['serializer'];
}
- if ($mappingAttribute->getArguments()['serializerOptions'] ?? false) {
- $newField['serializer_options'] = $mappingAttribute->getArguments()['serializerOptions'];
+ if ($options !== []) {
+ $newField['serializer_options'] = $options;
}
$newMetadata->addMethodCall('addField', [$newField]);
diff --git a/src/TingBundle/Resources/config/services.xml b/src/TingBundle/Resources/config/services.xml
index e4335e1..a0ebcf6 100644
--- a/src/TingBundle/Resources/config/services.xml
+++ b/src/TingBundle/Resources/config/services.xml
@@ -5,6 +5,11 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
+
+
+
+
+
@@ -28,7 +33,7 @@
-
+
@@ -118,5 +123,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/TingBundle/Resources/views/Collector/driverCollector.html.twig b/src/TingBundle/Resources/views/Collector/driverCollector.html.twig
index 0311091..4ad659d 100644
--- a/src/TingBundle/Resources/views/Collector/driverCollector.html.twig
+++ b/src/TingBundle/Resources/views/Collector/driverCollector.html.twig
@@ -2,132 +2,321 @@
{% block toolbar %}
{% set icon %}
-
- {{ collector.querycount }}
- {% if collector.querycount > 0 %}
- in {{ '%0.2f'|format(collector.time) }} ms
- {% endif %}
+
+
+ {{ collector.querycount }}
+
+ {% if collector.querycount > 0 %}
+
+
+ {{ '%0.2f'|format(collector.time) }} ms
+
+
+ {% endif %}
{% endset %}
+
{% set text %}
-
- DB Queries
- {{ collector.querycount }}
-
-
- Query time
- {{ '%0.2f'|format(collector.time) }} ms
-
+
+ Queries
+
+ {{ collector.querycount }}
+
+
+
+ Execution time
+
+ {{ '%0.2f'|format(collector.time) }} ms
+
+
{% endset %}
{% include '@WebProfiler/Profiler/toolbar_item.html.twig' with { 'link': profiler_url } %}
{% endblock %}
{% block menu %}
-
-
- Ting Queries
-
- {{ collector.querycount }}
- {{ '%0.0f'|format(collector.time) }} ms
+
+
+
+
+ Ting Queries
+
+ {{ collector.querycount }}
+ {{ '%0.0f'|format(collector.time) }} ms
+
-
{% endblock %}
{% block panel %}
- {#{% if 'explain' == page %}
- {{ render(controller('TingBundle:Profiler:explain', {
- 'token': token,
- 'panel': 'db',
- 'connectionName': app.request.query.get('connection'),
- 'query': app.request.query.get('query')
- })) }}
- {% else %}
- {{ block('queries') }}
- {% endif %}#}
{{ block('queries') }}
{% endblock %}
{% block queries %}
- Queries
-
- {% if collector.queries is empty %}
-
- No queries.
-
- {% else %}
-
- {% for i, query in collector.queries %}
- -
-
- Connection : {{ collector.connectionsHashToName[query.connection] }} |
- Database : {{ query['database'] }} | {{ query['time'] }} ms{% if query['type'] == 'statement' %}(prepare only){% endif %}
-
-
- {{ query['sql']|trim }}
- {% if query['params'] is defined and query['params'] is not empty %}
- {{ profiler_dump(query['params']) }}
- {% endif %}
- {% if query['type'] == 'statement' %}
- {% if collector.execs[query['name']] is not defined %}
- This prepared query has not been executed
- {% else %}
-
- {% for j, exec in collector.execs[query['name']] %}
- -
-
{{ exec['time'] }} ms
- {{ profiler_dump(exec['params']) }}
+
+
+
+
Queries
+
+ {{ collector.querycount }}
+
+
+
+
+
+ {% if collector.queries is empty %}
+
+ {% else %}
+
+ {% for i, query in collector.queries %}
+ -
+
+
+
+
{{ query['sql']|trim }}
+
+ {% if query['params'] is defined and query['params'] is not empty %}
+
+
Parameters
+ {{ profiler_dump(query['params']) }}
+
+ {% endif %}
+
+ {% if query['type'] == 'statement' %}
+ {% if collector.execs[query['name']] is not defined %}
+
This prepared query has not been executed.
+ {% else %}
+
+
Executions
+
+ {% for j, exec in collector.execs[query['name']] %}
+ -
+
+ {{ profiler_dump(exec['params']) }}
+
+ {% endfor %}
+
+
+ {% endif %}
+ {% endif %}
+
+
+ {% endfor %}
+
+ {% endif %}
+
+
+
Connections
+ {% if collector.connections is empty %}
+
No connection.
+ {% else %}
+
+ {% for name, connections in collector.connections %}
+ -
+
{{ name }}
+
+ {% for connectionHash, connection in connections %}
+ -
+ {{ connection['config']['user'] }}@{{ connection['config']['host'] }}:{{ connection['config']['port'] }}
{% endfor %}
- {% endif %}
- {% endif %}
-
- {% endfor %}
-
- {% endif %}
-
-
Database Connections
-
- {% if collector.connections is empty %}
-
No connection.
- {% else %}
-
- {% for name, connections in collector.connections %}
- -
-
{{ name }}
-
- {% for connectionHash, connection in connections %}
- - {{ connection['config']['user'] }}@{{ connection['config']['host'] }}:{{ connection['config']['port'] }}
- {% endfor %}
-
-
- {% endfor %}
-
- {% endif %}
+
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
-{% endblock %}
+{% endblock %}
\ No newline at end of file
diff --git a/src/TingBundle/Resources/views/Collector/prism.css.twig b/src/TingBundle/Resources/views/Collector/prism.css.twig
index 725f45d..7821739 100644
--- a/src/TingBundle/Resources/views/Collector/prism.css.twig
+++ b/src/TingBundle/Resources/views/Collector/prism.css.twig
@@ -1,132 +1,3 @@
-/* http://prismjs.com/download.html?themes=prism&languages=sql */
-/**
-* prism.js default theme for JavaScript, CSS and HTML
-* Based on dabblet (http://dabblet.com)
-* @author Lea Verou
-*/
-
-code[class*="language-"],
-pre[class*="language-"] {
-color: black;
-text-shadow: 0 1px white;
-font-family: Consolas, Monaco, 'Andale Mono', monospace;
-direction: ltr;
-text-align: left;
-white-space: pre;
-word-spacing: normal;
-word-break: normal;
-line-height: 1.5;
-
--moz-tab-size: 4;
--o-tab-size: 4;
-tab-size: 4;
-
--webkit-hyphens: none;
--moz-hyphens: none;
--ms-hyphens: none;
-hyphens: none;
-}
-
-pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
-code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
-text-shadow: none;
-background: #b3d4fc;
-}
-
-pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
-code[class*="language-"]::selection, code[class*="language-"] ::selection {
-text-shadow: none;
-background: #b3d4fc;
-}
-
-@media print {
-code[class*="language-"],
-pre[class*="language-"] {
-text-shadow: none;
-}
-}
-
-/* Code blocks */
-pre[class*="language-"] {
-padding: 1em;
-margin: .5em 0;
-overflow: auto;
-}
-
-:not(pre) > code[class*="language-"],
-pre[class*="language-"] {
-background: #f5f2f0;
-}
-
-/* Inline code */
-:not(pre) > code[class*="language-"] {
-padding: .1em;
-border-radius: .3em;
-}
-
-.token.comment,
-.token.prolog,
-.token.doctype,
-.token.cdata {
-color: slategray;
-}
-
-.token.punctuation {
-color: #999;
-}
-
-.namespace {
-opacity: .7;
-}
-
-.token.property,
-.token.tag,
-.token.boolean,
-.token.number,
-.token.constant,
-.token.symbol,
-.token.deleted {
-color: #905;
-}
-
-.token.selector,
-.token.attr-name,
-.token.string,
-.token.char,
-.token.builtin,
-.token.inserted {
-color: #690;
-}
-
-.token.operator,
-.token.entity,
-.token.url,
-.language-css .token.string,
-.style .token.string {
-color: #a67f59;
-background: hsla(0, 0%, 100%, .5);
-}
-
-.token.atrule,
-.token.attr-value,
-.token.keyword {
-color: #07a;
-}
-
-.token.function {
-color: #DD4A68;
-}
-
-.token.regex,
-.token.important,
-.token.variable {
-color: #e90;
-}
-
-.token.important {
-font-weight: bold;
-}
-
-.token.entity {
-cursor: help;
-}
+/* PrismJS 1.29.0
+https://prismjs.com/download.html?#themes=prism&languages=sql */
+code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}
diff --git a/src/TingBundle/Resources/views/Collector/prism.js.twig b/src/TingBundle/Resources/views/Collector/prism.js.twig
index 7da55be..a81432a 100644
--- a/src/TingBundle/Resources/views/Collector/prism.js.twig
+++ b/src/TingBundle/Resources/views/Collector/prism.js.twig
@@ -1,3 +1,4 @@
-/* http://prismjs.com/download.html?themes=prism&languages=sql */
-self=typeof window!="undefined"?window:typeof WorkerGlobalScope!="undefined"&&self instanceof WorkerGlobalScope?self:{};var Prism=function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={util:{encode:function(e){return e instanceof n?new n(e.type,t.util.encode(e.content),e.alias):t.util.type(e)==="Array"?e.map(t.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(m instanceof i)continue;l.lastIndex=0;var g=l.exec(m);if(g){h&&(p=g[1].length);var y=g.index-1+p,g=g[0].slice(p),b=g.length,w=y+b,E=m.slice(0,y+1),S=m.slice(w+1),x=[v,1];E&&x.push(E);var T=new i(u,c?t.tokenize(g,c):g,d);x.push(T);S&&x.push(S);Array.prototype.splice.apply(s,x)}}}}return s},hooks:{all:{},add:function(e,n){var r=t.hooks.all;r[e]=r[e]||[];r[e].push(n)},run:function(e,n){var r=t.hooks.all[e];if(!r||!r.length)return;for(var i=0,s;s=r[i++];)s(n)}}},n=t.Token=function(e,t,n){this.type=e;this.content=t;this.alias=n};n.stringify=function(e,r,i){if(typeof e=="string")return e;if(Object.prototype.toString.call(e)=="[object Array]")return e.map(function(t){return n.stringify(t,r,e)}).join("");var s={type:e.type,content:n.stringify(e.content,r,i),tag:"span",classes:["token",e.type],attributes:{},language:r,parent:i};s.type=="comment"&&(s.attributes.spellcheck="true");if(e.alias){var o=t.util.type(e.alias)==="Array"?e.alias:[e.alias];Array.prototype.push.apply(s.classes,o)}t.hooks.run("wrap",s);var u="";for(var a in s.attributes)u+=a+'="'+(s.attributes[a]||"")+'"';return"<"+s.tag+' class="'+s.classes.join(" ")+'" '+u+">"+s.content+""+s.tag+">"};if(!self.document){if(!self.addEventListener)return self.Prism;self.addEventListener("message",function(e){var n=JSON.parse(e.data),r=n.language,i=n.code;self.postMessage(JSON.stringify(t.util.encode(t.tokenize(i,t.languages[r]))));self.close()},!1);return self.Prism}var r=document.getElementsByTagName("script");r=r[r.length-1];if(r){t.filename=r.src;document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)}return self.Prism}();typeof module!="undefined"&&module.exports&&(module.exports=Prism);;
-Prism.languages.sql={comment:{pattern:/(^|[^\\])(\/\*[\w\W]*?\*\/|((--)|(\/\/)|#).*?(\r?\n|$))/g,lookbehind:!0},string:{pattern:/(^|[^@])("|')(\\?[\s\S])*?\2/g,lookbehind:!0},variable:/@[\w.$]+|@("|'|`)(\\?[\s\S])+?\1/g,"function":/\b(?:COUNT|SUM|AVG|MIN|MAX|FIRST|LAST|UCASE|LCASE|MID|LEN|ROUND|NOW|FORMAT)(?=\s*\()/ig,keyword:/\b(?:ACTION|ADD|AFTER|ALGORITHM|ALTER|ANALYZE|APPLY|AS|ASC|AUTHORIZATION|BACKUP|BDB|BEGIN|BERKELEYDB|BIGINT|BINARY|BIT|BLOB|BOOL|BOOLEAN|BREAK|BROWSE|BTREE|BULK|BY|CALL|CASCADE|CASCADED|CASE|CHAIN|CHAR VARYING|CHARACTER VARYING|CHECK|CHECKPOINT|CLOSE|CLUSTERED|COALESCE|COLUMN|COLUMNS|COMMENT|COMMIT|COMMITTED|COMPUTE|CONNECT|CONSISTENT|CONSTRAINT|CONTAINS|CONTAINSTABLE|CONTINUE|CONVERT|CREATE|CROSS|CURRENT|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATA|DATABASE|DATABASES|DATETIME|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFAULT|DEFINER|DELAYED|DELETE|DENY|DESC|DESCRIBE|DETERMINISTIC|DISABLE|DISCARD|DISK|DISTINCT|DISTINCTROW|DISTRIBUTED|DO|DOUBLE|DOUBLE PRECISION|DROP|DUMMY|DUMP|DUMPFILE|DUPLICATE KEY|ELSE|ENABLE|ENCLOSED BY|END|ENGINE|ENUM|ERRLVL|ERRORS|ESCAPE|ESCAPED BY|EXCEPT|EXEC|EXECUTE|EXIT|EXPLAIN|EXTENDED|FETCH|FIELDS|FILE|FILLFACTOR|FIRST|FIXED|FLOAT|FOLLOWING|FOR|FOR EACH ROW|FORCE|FOREIGN|FREETEXT|FREETEXTTABLE|FROM|FULL|FUNCTION|GEOMETRY|GEOMETRYCOLLECTION|GLOBAL|GOTO|GRANT|GROUP|HANDLER|HASH|HAVING|HOLDLOCK|IDENTITY|IDENTITY_INSERT|IDENTITYCOL|IF|IGNORE|IMPORT|INDEX|INFILE|INNER|INNODB|INOUT|INSERT|INT|INTEGER|INTERSECT|INTO|INVOKER|ISOLATION LEVEL|JOIN|KEY|KEYS|KILL|LANGUAGE SQL|LAST|LEFT|LIMIT|LINENO|LINES|LINESTRING|LOAD|LOCAL|LOCK|LONGBLOB|LONGTEXT|MATCH|MATCHED|MEDIUMBLOB|MEDIUMINT|MEDIUMTEXT|MERGE|MIDDLEINT|MODIFIES SQL DATA|MODIFY|MULTILINESTRING|MULTIPOINT|MULTIPOLYGON|NATIONAL|NATIONAL CHAR VARYING|NATIONAL CHARACTER|NATIONAL CHARACTER VARYING|NATIONAL VARCHAR|NATURAL|NCHAR|NCHAR VARCHAR|NEXT|NO|NO SQL|NOCHECK|NOCYCLE|NONCLUSTERED|NULLIF|NUMERIC|OF|OFF|OFFSETS|ON|OPEN|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPTIMIZE|OPTION|OPTIONALLY|ORDER|OUT|OUTER|OUTFILE|OVER|PARTIAL|PARTITION|PERCENT|PIVOT|PLAN|POINT|POLYGON|PRECEDING|PRECISION|PREV|PRIMARY|PRINT|PRIVILEGES|PROC|PROCEDURE|PUBLIC|PURGE|QUICK|RAISERROR|READ|READS SQL DATA|READTEXT|REAL|RECONFIGURE|REFERENCES|RELEASE|RENAME|REPEATABLE|REPLICATION|REQUIRE|RESTORE|RESTRICT|RETURN|RETURNS|REVOKE|RIGHT|ROLLBACK|ROUTINE|ROWCOUNT|ROWGUIDCOL|ROWS?|RTREE|RULE|SAVE|SAVEPOINT|SCHEMA|SELECT|SERIAL|SERIALIZABLE|SESSION|SESSION_USER|SET|SETUSER|SHARE MODE|SHOW|SHUTDOWN|SIMPLE|SMALLINT|SNAPSHOT|SOME|SONAME|START|STARTING BY|STATISTICS|STATUS|STRIPED|SYSTEM_USER|TABLE|TABLES|TABLESPACE|TEMP(?:ORARY)?|TEMPTABLE|TERMINATED BY|TEXT|TEXTSIZE|THEN|TIMESTAMP|TINYBLOB|TINYINT|TINYTEXT|TO|TOP|TRAN|TRANSACTION|TRANSACTIONS|TRIGGER|TRUNCATE|TSEQUAL|TYPE|TYPES|UNBOUNDED|UNCOMMITTED|UNDEFINED|UNION|UNPIVOT|UPDATE|UPDATETEXT|USAGE|USE|USER|USING|VALUE|VALUES|VARBINARY|VARCHAR|VARCHARACTER|VARYING|VIEW|WAITFOR|WARNINGS|WHEN|WHERE|WHILE|WITH|WITH ROLLUP|WITHIN|WORK|WRITE|WRITETEXT)\b/gi,"boolean":/\b(?:TRUE|FALSE|NULL)\b/gi,number:/\b-?(0x)?\d*\.?[\da-f]+\b/g,operator:/\b(?:ALL|AND|ANY|BETWEEN|EXISTS|IN|LIKE|NOT|OR|IS|UNIQUE|CHARACTER SET|COLLATE|DIV|OFFSET|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b|[-+]{1}|!|[=<>]{1,2}|(&){1,2}|\|?\||\?|\*|\//gi,punctuation:/[;[\]()`,.]/g};;
\ No newline at end of file
+/* PrismJS 1.29.0
+https://prismjs.com/download.html?#themes=prism&languages=sql */
+var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(e){var n=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,r={},a={manual:e.Prism&&e.Prism.manual,disableWorkerMessageHandler:e.Prism&&e.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof i?new i(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/=g.reach);A+=w.value.length,w=w.next){var E=w.value;if(n.length>e.length)return;if(!(E instanceof i)){var P,L=1;if(y){if(!(P=l(b,A,e,m))||P.index>=e.length)break;var S=P.index,O=P.index+P[0].length,j=A;for(j+=w.value.length;S>=j;)j+=(w=w.next).value.length;if(A=j-=w.value.length,w.value instanceof i)continue;for(var C=w;C!==n.tail&&(jg.reach&&(g.reach=W);var z=w.prev;if(_&&(z=u(n,z,_),A+=_.length),c(n,z,L),w=u(n,z,new i(f,p?a.tokenize(N,p):N,k,N)),M&&u(n,w,M),L>1){var I={cause:f+","+d,reach:W};o(e,n,t,w.prev,A,I),g&&I.reach>g.reach&&(g.reach=I.reach)}}}}}}function s(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function u(e,n,t){var r=n.next,a={value:t,prev:n,next:r};return n.next=a,r.prev=a,e.length++,a}function c(e,n,t){for(var r=n.next,a=0;a"+i.content+""+i.tag+">"},!e.document)return e.addEventListener?(a.disableWorkerMessageHandler||e.addEventListener("message",(function(n){var t=JSON.parse(n.data),r=t.language,i=t.code,l=t.immediateClose;e.postMessage(a.highlight(i,a.languages[r],r)),l&&e.close()}),!1),a):a;var g=a.util.currentScript();function f(){a.manual||a.highlightAll()}if(g&&(a.filename=g.src,g.hasAttribute("data-manual")&&(a.manual=!0)),!a.manual){var h=document.readyState;"loading"===h||"interactive"===h&&g&&g.defer?document.addEventListener("DOMContentLoaded",f):window.requestAnimationFrame?window.requestAnimationFrame(f):window.setTimeout(f,16)}return a}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism);
+Prism.languages.sql={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|(?:--|\/\/|#).*)/,lookbehind:!0},variable:[{pattern:/@(["'`])(?:\\[\s\S]|(?!\1)[^\\])+\1/,greedy:!0},/@[\w.$]+/],string:{pattern:/(^|[^@\\])("|')(?:\\[\s\S]|(?!\2)[^\\]|\2\2)*\2/,greedy:!0,lookbehind:!0},identifier:{pattern:/(^|[^@\\])`(?:\\[\s\S]|[^`\\]|``)*`/,greedy:!0,lookbehind:!0,inside:{punctuation:/^`|`$/}},function:/\b(?:AVG|COUNT|FIRST|FORMAT|LAST|LCASE|LEN|MAX|MID|MIN|MOD|NOW|ROUND|SUM|UCASE)(?=\s*\()/i,keyword:/\b(?:ACTION|ADD|AFTER|ALGORITHM|ALL|ALTER|ANALYZE|ANY|APPLY|AS|ASC|AUTHORIZATION|AUTO_INCREMENT|BACKUP|BDB|BEGIN|BERKELEYDB|BIGINT|BINARY|BIT|BLOB|BOOL|BOOLEAN|BREAK|BROWSE|BTREE|BULK|BY|CALL|CASCADED?|CASE|CHAIN|CHAR(?:ACTER|SET)?|CHECK(?:POINT)?|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMNS?|COMMENT|COMMIT(?:TED)?|COMPUTE|CONNECT|CONSISTENT|CONSTRAINT|CONTAINS(?:TABLE)?|CONTINUE|CONVERT|CREATE|CROSS|CURRENT(?:_DATE|_TIME|_TIMESTAMP|_USER)?|CURSOR|CYCLE|DATA(?:BASES?)?|DATE(?:TIME)?|DAY|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFAULT|DEFINER|DELAYED|DELETE|DELIMITERS?|DENY|DESC|DESCRIBE|DETERMINISTIC|DISABLE|DISCARD|DISK|DISTINCT|DISTINCTROW|DISTRIBUTED|DO|DOUBLE|DROP|DUMMY|DUMP(?:FILE)?|DUPLICATE|ELSE(?:IF)?|ENABLE|ENCLOSED|END|ENGINE|ENUM|ERRLVL|ERRORS|ESCAPED?|EXCEPT|EXEC(?:UTE)?|EXISTS|EXIT|EXPLAIN|EXTENDED|FETCH|FIELDS|FILE|FILLFACTOR|FIRST|FIXED|FLOAT|FOLLOWING|FOR(?: EACH ROW)?|FORCE|FOREIGN|FREETEXT(?:TABLE)?|FROM|FULL|FUNCTION|GEOMETRY(?:COLLECTION)?|GLOBAL|GOTO|GRANT|GROUP|HANDLER|HASH|HAVING|HOLDLOCK|HOUR|IDENTITY(?:COL|_INSERT)?|IF|IGNORE|IMPORT|INDEX|INFILE|INNER|INNODB|INOUT|INSERT|INT|INTEGER|INTERSECT|INTERVAL|INTO|INVOKER|ISOLATION|ITERATE|JOIN|KEYS?|KILL|LANGUAGE|LAST|LEAVE|LEFT|LEVEL|LIMIT|LINENO|LINES|LINESTRING|LOAD|LOCAL|LOCK|LONG(?:BLOB|TEXT)|LOOP|MATCH(?:ED)?|MEDIUM(?:BLOB|INT|TEXT)|MERGE|MIDDLEINT|MINUTE|MODE|MODIFIES|MODIFY|MONTH|MULTI(?:LINESTRING|POINT|POLYGON)|NATIONAL|NATURAL|NCHAR|NEXT|NO|NONCLUSTERED|NULLIF|NUMERIC|OFF?|OFFSETS?|ON|OPEN(?:DATASOURCE|QUERY|ROWSET)?|OPTIMIZE|OPTION(?:ALLY)?|ORDER|OUT(?:ER|FILE)?|OVER|PARTIAL|PARTITION|PERCENT|PIVOT|PLAN|POINT|POLYGON|PRECEDING|PRECISION|PREPARE|PREV|PRIMARY|PRINT|PRIVILEGES|PROC(?:EDURE)?|PUBLIC|PURGE|QUICK|RAISERROR|READS?|REAL|RECONFIGURE|REFERENCES|RELEASE|RENAME|REPEAT(?:ABLE)?|REPLACE|REPLICATION|REQUIRE|RESIGNAL|RESTORE|RESTRICT|RETURN(?:ING|S)?|REVOKE|RIGHT|ROLLBACK|ROUTINE|ROW(?:COUNT|GUIDCOL|S)?|RTREE|RULE|SAVE(?:POINT)?|SCHEMA|SECOND|SELECT|SERIAL(?:IZABLE)?|SESSION(?:_USER)?|SET(?:USER)?|SHARE|SHOW|SHUTDOWN|SIMPLE|SMALLINT|SNAPSHOT|SOME|SONAME|SQL|START(?:ING)?|STATISTICS|STATUS|STRIPED|SYSTEM_USER|TABLES?|TABLESPACE|TEMP(?:ORARY|TABLE)?|TERMINATED|TEXT(?:SIZE)?|THEN|TIME(?:STAMP)?|TINY(?:BLOB|INT|TEXT)|TOP?|TRAN(?:SACTIONS?)?|TRIGGER|TRUNCATE|TSEQUAL|TYPES?|UNBOUNDED|UNCOMMITTED|UNDEFINED|UNION|UNIQUE|UNLOCK|UNPIVOT|UNSIGNED|UPDATE(?:TEXT)?|USAGE|USE|USER|USING|VALUES?|VAR(?:BINARY|CHAR|CHARACTER|YING)|VIEW|WAITFOR|WARNINGS|WHEN|WHERE|WHILE|WITH(?: ROLLUP|IN)?|WORK|WRITE(?:TEXT)?|YEAR)\b/i,boolean:/\b(?:FALSE|NULL|TRUE)\b/i,number:/\b0x[\da-f]+\b|\b\d+(?:\.\d*)?|\B\.\d+\b/i,operator:/[-+*\/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|DIV|ILIKE|IN|IS|LIKE|NOT|OR|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i,punctuation:/[;[\]()`,.]/};
diff --git a/src/TingBundle/Serializer/SerializerFactory.php b/src/TingBundle/Serializer/SerializerFactory.php
new file mode 100644
index 0000000..7abd134
--- /dev/null
+++ b/src/TingBundle/Serializer/SerializerFactory.php
@@ -0,0 +1,28 @@
+serializers[get_class($serializer)] = $serializer;
+ }
+
+ public function get($serializerName)
+ {
+ if (!isset($this->serializers[$serializerName])) {
+ // Support old definition, try to magically instanciate.
+ if (class_exists($serializerName) && $this->serializers[$serializerName] = new $serializerName()) {
+ return $this->serializers[$serializerName];
+ }
+ throw new \Exception("Serializer $serializerName not found");
+ }
+ return $this->serializers[$serializerName];
+ }
+}
\ No newline at end of file
diff --git a/src/TingBundle/Serializer/SymfonySerializer.php b/src/TingBundle/Serializer/SymfonySerializer.php
new file mode 100644
index 0000000..226d9cb
--- /dev/null
+++ b/src/TingBundle/Serializer/SymfonySerializer.php
@@ -0,0 +1,32 @@
+throwOnNullSerializer();
+ return $this->serializer->serialize($toSerialize, 'json', $options['context'] ?? []);
+ }
+
+ public function unserialize($serialized, array $options = [])
+ {
+ $this->throwOnNullSerializer();
+ if (!isset($options['type'])) {
+ throw new \RuntimeException('SymfonySerializer requires type option to be set');
+ }
+ return $this->serializer->deserialize($serialized, $options['type'], 'json', $options['context'] ?? []);
+ }
+
+ private function throwOnNullSerializer()
+ {
+ if ($this->serializer === null) {
+ throw new \RuntimeException('SymfonySerializer requires symfony/serializer to be installed. Use composer require symfony/serializer to add it.');
+ }
+ }
+}
\ No newline at end of file