Skip to content
This repository was archived by the owner on Jul 11, 2023. It is now read-only.
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
113 changes: 51 additions & 62 deletions EventListener/ErrorLogSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,114 +3,100 @@
namespace Oh\FormErrorLogBundle\EventListener;

use Oh\FormErrorLogBundle\Logger\ErrorLogInterface;
use Symfony\Component\Form\FormEvent;
use Oh\FormErrorLogBundle\Logger\SerializeData;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\HttpFoundation\RequestStack;

class ErrorLogSubscriber implements EventSubscriberInterface
{
use SerializeData;

/**
* Whatever you want to use as the logger
* @var Oh\FormErrorLogBundle\Logger\ErrorLogInterface
* @var ErrorLogInterface
*/
private $logger;

/**
* This is to log the request variables if the form data can't be logged
* @var Symfony\Component\HttpFoundation\Request
* @var Symfony\Component\HttpFoundation\Request
*/
private $request;

/**
* @param ErrorLogInterface $logger
* @param RequestStack $request
*/
public function __construct(ErrorLogInterface $logger, RequestStack $request)
{
$this->logger = $logger;
$this->request = $request->getMasterRequest();
}

/**
* @return array
*/
public static function getSubscribedEvents()
{
return array(FormEvents::POST_SUBMIT => 'postSubmit');
return [
FormEvents::POST_SUBMIT => 'postSubmit',
];
}

/**
*
* @param \Symfony\Component\Form\FormEvent $event
* @return null
*/
public function postSubmit(FormEvent $event)
{
$form = $event->getForm();

$errors = $this->getErrorMessages($form);
if(empty($errors)) {
return null;

if (empty($errors)) {
return;
}

$formName = $form->getName();

foreach($errors as $key => $error) {
foreach ($errors as $key => $error) {
$uri = $this->request->getUri();
$this->logger->log($formName, $key, $error['messages'], $error['value'], $uri);
}

return null;
}

private function getErrorMessages(\Symfony\Component\Form\Form $form) {

$errors = array();


/**
* @param \Symfony\Component\Form\Form $form
* @return array
*/
private function getErrorMessages(\Symfony\Component\Form\Form $form)
{
$errors = [];

/* Get the errors from this FormType */
foreach ($form->getErrors() as $key => $error) {
$data = $form->getData();

/* If it's a bound object then we need to log it somehow */
if(is_object($data))
{
// JsonSerializable is for php 5.4
if(class_exists('\JsonSerializable', false) && $data instanceof \JsonSerializable) {
$data = json_encode($data);
}
// otherwise we could just see if that method exists
elseif(method_exists($data, 'jsonSerialize'))
{
$data = json_encode($data->jsonSerialize());
}
// some people create a toArray() method
elseif(method_exists($data, 'toArray') && is_array($array = $data->toArray()))
{
// JSON_PRETTY_PRINT is > PHP 5.4
if(defined('JSON_PRETTY_PRINT')) {
$data = json_encode($array, JSON_PRETTY_PRINT);
}else {
$data = json_encode($array);
}

}
// lets try to serialize
// this could be risky if the object is too large or not implemented correctly
elseif(method_exists($data, '__sleep') || $data instanceof Serializable) {
$data = @serialize($data);
}
// lets see if we can get the form data from the request
elseif($this->request->request->has($form->getName())) {
// lets log it
$data = 'POST DATA: '.json_encode($this->request->request->get($form->getName()));
}
// it looks like the object isnt loggable
else {
$data = '';
}

$serializedData = $this->serialize($data);
if (empty($serializedData)) {
$formData = $this->request->request->has($form->getName())
? $this->request->request->get($form->getName())
: null;
$serializedData = 'POST DATA: '.json_encode($formData);
}
$errors[$key] = array('messages'=>$error->getMessage(), 'value'=>$data);

$errors[$key] = [
'messages' => $error->getMessage(),
'value' => $serializedData,
];
}

if ($form->count() > 0) {
foreach ($form->all() as $child) {
if (!$child->isValid()) {
$childErrors = $this->getErrorMessages($child);
$messages = $values = array();
$messages = $values = [];
foreach($childErrors as $childError) {
$messages[] = $childError['messages'];
$values[] = $childError['value'];
Expand All @@ -120,11 +106,14 @@ private function getErrorMessages(\Symfony\Component\Form\Form $form) {
$messages = implode(' | ', $messages);
$values = implode(' | ', $values);

$errors[$child->getName()] = array('messages'=>$messages, 'value'=>$values);
$errors[$child->getName()] = [
'messages' => $messages,
'value' => $values,
];
}
}
}

return $errors;
}
}
21 changes: 19 additions & 2 deletions Logger/DatabaseLogger.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,24 @@

class DatabaseLogger implements ErrorLogInterface
{
/**
* @var EntityManagerInterface
*/
private $em;

/**
* @var string
*/
private $entityClass;

/**
* @var EventDispatcherInterface
*/
private $eventDispatcher;

/**
* @param EntityManagerInterface $em
* @param $entityClass
* @param string $entityClass
* @param EventDispatcherInterface $eventDispatcher
*/
public function __construct(EntityManagerInterface $em, $entityClass, EventDispatcherInterface $eventDispatcher)
Expand All @@ -29,6 +38,14 @@ public function __construct(EntityManagerInterface $em, $entityClass, EventDispa
$this->eventDispatcher = $eventDispatcher;
}

/**
* @param string $formName
* @param string $key
* @param string $error
* @param string $value
* @param string $uri
* @throws InvalidArgumentException
*/
public function log($formName, $key, $error, $value = '', $uri = '')
{
if ($this->entityClass === 'Oh\FormErrorLogBundle\Entity\FormErrorLogEntityInterface') {
Expand All @@ -41,7 +58,7 @@ public function log($formName, $key, $error, $value = '', $uri = '')
$entity->setFormName($formName);
$entity->setField($key);
$entity->setError($error);
$entity->setValue(serialize($value));
$entity->setValue($value);
// for BC
if (method_exists($entity, 'setUri')) {
$entity->setUri($uri);
Expand Down
23 changes: 15 additions & 8 deletions Logger/Logger.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ class Logger implements ErrorLogInterface
*/
private $logger;

/**
* @param MonologLogger $logger
*/
public function __construct(MonologLogger $logger)
{
$this->logger = $logger;
Expand All @@ -22,16 +25,20 @@ public function __construct(MonologLogger $logger)
* @param string $key
* @param string $error
* @param string $value
* @return void
* @param string $uri
*/
public function log($formName, $key, $error, $value = '', $uri = '')
{
$this->logger->notice(strtr('%0 - Error in form "%1" in position "%2": "%3" with serialized value "%4"', array(
'%0' => $uri,
'%1' => $formName,
'%2' => $key,
'%3' => $error,
'%4' => serialize($value),
)));
$logMessage = strtr(
'%0 - Error in form "%1" in position "%2": "%3" with serialized value "%4"',
[
'%0' => $uri,
'%1' => $formName,
'%2' => $key,
'%3' => $error,
'%4' => $value,
]
);
$this->logger->notice($logMessage);
}
}
94 changes: 94 additions & 0 deletions Logger/SerializeData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

namespace Oh\FormErrorLogBundle\Logger;

trait SerializeData
{
private function serialize($data)
{
if (is_object($data)) {
return $this->serializeObject($data);
} elseif (is_resource($data)) {
return $this->serializeResource($data);
} elseif (is_array($data)) {
return $this->serializeArray($data);
} else {
return $this->serializeNonObject($data);
}
}

private function serializeObject($object)
{
$data = '';

// JsonSerializable is for php 5.4
if (class_exists('\JsonSerializable', false) && $object instanceof \JsonSerializable) {
$data = json_encode($object);

// otherwise we could just see if that method exists
} elseif (method_exists($object, 'jsonSerialize')) {
$data = json_encode($object->jsonSerialize());

// some people create a toArray() method
} elseif (method_exists($object, 'toArray') && is_array($array = $object->toArray())) {
// JSON_PRETTY_PRINT is > PHP 5.4
if (defined('JSON_PRETTY_PRINT')) {
$data = json_encode($array, JSON_PRETTY_PRINT);
} else {
$data = json_encode($array);
}

// lets try to serialize
// this could be risky if the object is too large or not implemented correctly
} elseif (method_exists($object, '__sleep') || $object instanceof Serializable) {
$data = @serialize($object);
}

return $data;
}

/**
* @param resource $resource
* @return string
*/
private function serializeResource($resource)
{
// we cann't serialize PHP resources
return '';
}

/**
* @param array $array
* @return string
*/
private function serializeArray($array)
{
foreach ($array as &$value) {
if (is_object($value)) {
$value = $this->serializeObject($value);
} elseif (is_resource($value)) {
$value = $this->serializeResource($value);
}
}

return $this->serializeNonObject($array);
}

/**
* @param int|string|array|null $nonObject
* @return string
*/
private function serializeNonObject($nonObject)
{
$data = '';
try {
$data = serialize($nonObject);
} catch (\Throwable $t) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this compatible with php 5.4?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this will work in the old-fashioned way where line 85 errors and nothing is caught.
This amendment doesn't change the existing behaviour on PHP 5.x and improves only when running on 7.x

// do nothing, will catch in PHP >= 7.0
} catch (\Exception $e) {
// do nothing, will catch in PHP <= 5.6
} finally {
return $data;
}
}
}
6 changes: 5 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@
{
"name": "Ollie Harridge",
"email": "code@oll.ie"
},
{
"name": "Lendable Developers",
"email": "dev@lendable.co.uk"
}
],
"require": {
"php": ">=5.3.2",
"php": ">=5.4",
"symfony/symfony": "^2|^3"
},
"autoload": {
Expand Down