-
-
Notifications
You must be signed in to change notification settings - Fork 453
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
[2.0] Serializer interface #454
Conversation
nokitakaze
commented
Apr 30, 2017
9f837f6
to
d56a1b6
Compare
tests/ClientTest.php
Outdated
@@ -210,6 +207,24 @@ public function join($timeout = null) | |||
} | |||
} | |||
|
|||
class Dummy_Serializer implements \Raven\SerializerInterface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems bad because there is logic code in a test that doesn't reflect what the real implementation of the serializer does. You should use a mocked object instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should use a mocked object instead
I don't know how to create method with mock object library. That's why I never used it in my tests
doesn't reflect
Why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ClientTest::testOurSerializer
method is actually testing the value returned from the serializer instance. I think that this is not appropriate, as the test class about the serializer should do these types of assertions. Instead, here we should check if the serializer is correctly set using the options/setters and if it's called. This is what mocks are for, they verify that something happens. PHPUnit doc is great and has a lot of examples on how to do it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should check if the serializer is correctly set
Yes. This is exactly what I'm testing. Look at this https://github.com/getsentry/sentry-php/pull/454/files#diff-668268a1040801a44451100bf9062b17R2510
I set some serializer and then I check if it has been set
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know the scope of that test is to check that the serializer is correctly set, but there is no need to check the value returned after serializing a message to do it. You should create a mock, pass it in place of the current Dummy_Serializer
object and assert that its serialize
method is called when capturing an exception/message. Nothing more, nothing else. No checks on how the sent message is serialized itself
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and assert that its serialize method is called
Okay. I will change it tomorrow
lib/Raven/SerializerInterface.php
Outdated
* @param int $_depth | ||
* @return string|bool|double|int|null|object|array | ||
*/ | ||
public function serialize($value, $max_depth = 3, $_depth = 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about changing the signature of this method to serialize($value, array $context = [])?
This way, it's more standardized according to other serializer libraries and mostly important it allows us to pass additional options in the future without breaking compatibility by adding a new method to the interface
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How should I change signature of existing method without breaking back compatibility? @dcramer will grumble
And before I break BC I want to know the maintainer's opinion
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fortunately, as we're working on 2.x
branch we can break compatibility with older versions, so there shouldn't be any problem in doing it. Plus, I think it was a really big mistake to have all properties/methods public in the 1.x
version. We should expose only things that actually serves a purpose and I don't think that a public serialize
method is so useful since the scope of this lbrary is not to be a serializer. So, here what I would do: make the Serializer::serializeObject
protected (there is no reason to let it public), change the signature of the Serializer::serialize
method according to the previously suggested definition (yes, breaking compatibility) and rework the code calling it to use the $context
argument to pass the old arguments like $max_depth
as key/values of the array. Plus, the $depth
argument is something related to internal behaviour and should not be exposed anyway in a public method. One final though: should we provide a basic implementation of a serializer or should we just delegate it to a 3rd party library? I mean, there are a lot of cases to handle (circular references, how to cast objects/closures/different types) and there are already existing projects that do it well. Why should we reinvent the wheel?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we provide a basic implementation of a serializer
We did it already. \Raven\Serializer is it. Anyone can extend it
make the Serializer::serializeObject protected
change the signature of
I will do it tomorrow.
it was a really big mistake to have all properties/methods public
Okay. Which of us will make a pull request that will make all methods protected?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay. Which of us will make a pull request that will make all methods protected?
This is off-topic, but I don't think there is a need now to open a PR to change all methods from public to protected because we will probably do it with the future PRs regarding other changes. Mine was just a statement,related to public methods that were strictly connected with the serializer (Client::serialize
for example): only those should be changed in this PR
@@ -1602,7 +1620,11 @@ public function close_curl_resource() | |||
|
|||
public function setAllObjectSerialize($value) | |||
{ | |||
$this->serializer->setAllObjectSerialize($value); | |||
$this->reprSerializer->setAllObjectSerialize($value); | |||
if (method_exists($this->serializer, 'setAllObjectSerialize')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Accordind to the comment left below about the serialize
function definition, we could use the $context
parameter to pass additional options to the serializer (in this case about serializing all objects) so that we don't need to rely on some methods not even defined in the interface
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not even defined in the interface
if (method_exists...
tests/ClientTest.php
Outdated
$events = $client->getSentEvents(); | ||
$event = array_pop($events); | ||
$this->assertEquals(Dummy_Serializer::$default_value, $event['extra']['foo']); | ||
$serializer->__phpunit_verify(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is wrong. There is no need to call an internal PHPUnit function to assert that a given method has been called. You should mock the methods and then just call the code as you would in the real use-case and PHPUnit will do the rest for you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know how to check asserting after each call. Assert must be done in every iteration of foreach block
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As we're not testing anymore the return value of the serializer, the whole foreach
block isn't needed. Just capture a single message and check that the serialize
method of the mocked object is called
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We must be sure serialize is invoked on every accessible type. I create foreach block for this and only for this purpose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No we don't. we just have to test that the Serializer::serialize
method is called when appropriate, e.g. when sending a message to the Sentry server. There is no reason to test that (this is just an example) a boolean
is passed to the method
lib/Raven/SerializerInterface.php
Outdated
@@ -10,9 +10,8 @@ | |||
* sanitization and encoding. | |||
* | |||
* @param mixed $value | |||
* @param int $max_depth | |||
* @param int $_depth | |||
* @param array $context |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing doc for the argument. Plus, I would just put mixed
as return value instead of a long list of scalar types
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which argument I missed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$value
and $mixed
don't have a description
lib/Raven/Serializer.php
Outdated
* @param int $max_depth | ||
* @param int $_depth | ||
* @return string|bool|double|int|null|object|array | ||
*/ | ||
public function serialize($value, $max_depth = 3, $_depth = 0) | ||
protected function serialize_inner($value, $max_depth, $_depth) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment applies to any code: properties, variables and methods should all use camel case, so for example $max_depth
should be renamed to $maxDepth
@@ -67,17 +67,44 @@ public function __construct($mb_detect_order = null) | |||
* sanitization and encoding. | |||
* | |||
* @param mixed $value | |||
* @param array $context | |||
* @return string|bool|double|int|null|object|array |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing documentation, and return value should be mixed
(it's easier to read than having a so long list of scalar types)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got this doc from this file itself. Do we want to change all serializing method to "return mixed" INCLUDING SerializerInterface::serialize
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would, because it's prettier imho
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Pretiest" ways are always wrong in our art. This is the programming not a drawing Mona Lisa
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mixed
is used in places where a value can be of different types and it's appropriate here instead of a infinite list of possible return types (what if in the future we decide that we return class instances, etc?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Accept — of course. But we are talking about param and return types, aren't we? Of course we should change param types to "mixed". I will do it. But what about return types?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Return types should be only string
, int
, float
, bool
, array
and null
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait a minute
return value should be mixed
There is no other doc items which contain some long string with types. Only mixed
value and array
context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was wrong in saying that the return value of the function should be that because the return types can only be scalars. I would use mixed
anyway, because I find it prettier and easier to read, but feel free to do whathever you want on this. Doc is still missing for the $value
and $context
arguments and that is my main concern
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doc is still missing
I don't know what should I write. And I am afraid that my line should be corrected because english is not my native language. I know it isn't yours either.
*/ | ||
protected function get_full_context(array $context) | ||
{ | ||
if (!array_key_exists('max_depth', $context)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need to always set this option? If he wants to serialize all objects he can't this way, but should be able to do it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean "max_depth"? I saved it for back compatibility
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The max_depth
option (correct me if I'm wrong) seems to be always set from the lines of code that call the serialize function, so backwards compatibility seems to be guaranteed. With these lines of code, the user won't be able to serialize everything regardless of depth because a max depth of 3 is always forced to be set
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that call the serialize function
You are wrong. Serialize function could be called beyond \Raven\Client. So we must make sure $context['max_depth']
is set
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Serialize function could be called beyond \Raven\Client
Is this referring to the fact that Client::serialize
is public? Imho, it shouldn't because we're not a library to serialize things, so we don't provide a serializer that can be used for something else than serializing values being sent to a Sentry server. There is no need for this method to be public
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You forgot that Sentry is a library for many real users
Trust me when I say that I always think about it, and rest assured that any decision I take is to give a clear public API to the end-users, and to me exposing something used internally is a bad practice. As this discussion is going too much further in time: @dcramer can you please say what's your opinion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just checked. There is no \Raven\Client::serialize. Only "sanitize".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, I meant to say that Client::getSerializer
and Client::getReprSerializer
are public when they shouldn't, but it was done because at that time I had no other choice. The plan is to remove them anyway. By the way, Client::sanitize
should too not be public. Having all these methods private/protected means that there would be no way to access the serializer outside of the client (without inheriting the class, at least) and this means that there is no need the check the max_depth
option because no BC would exists (as we control where the serializer is used). Does this makes sense?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tomorrow (2017/05/09) I will do pull request which will make all methods and properties protected and will break all compatibility we have at this moment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make all methods and properties protected
Okay, It's already done: #457
e736e40
to
d053f83
Compare
I'm going to close this as it's really old and it has been superseded by multiple PRs attempting to solve the same issue, last of all #701 |