I discovered JSON Schema today it looks to be 100% fitting my needs, but it's actually driving me nuts with one of the most basic cases. Before creating this post, I obviously did my best to follow existing documentation and looked on the internet trying to find something helping.
Stack : PHP7 - justinrainbow/json-schema 2.0.5
Here is the schema :
{
"description":"UserCreate",
"type":"object",
"properties":{
"login":{
"type":"string",
"required":true
},
"idAsc":{
"type":"string",
"required":true
}
},
"required":[
"login",
"idAsc"
]
}
As you can see I am using both required definition (boolean and array), just to be sure I am using the good one, I also tried with only array and or boolean with the same result.
Here is the data
{
"login":"email#email.com"
}
I am expecting the following code to detect the lack of the required idAsc parameter.
$validator = new JsonSchema\Validator;
$validator->check($data, $schema);
if ($validator->isValid() == false)
echo("Missing something");
else
echo("Good");
But this code keep printing "Good”… I am definitely missing something about JSON Schema.
Thank's for your help, best regards.
I finally make it work, after debugging step by step the implementation of JSON Schema I was using :
First of all, I loaded the JSON Schema myself, the example given in the Github repo look to be broken for me (https://github.com/justinrainbow/json-schema):
$data = json_decode(file_get_contents('data.json'));
// Validate
$validator = new JsonSchema\Validator;
$validator->check($data, (object)['$ref' => 'file://' . realpath('schema.json')]);
I was not able to find any call to file_get_contents or relatives inside the library during my step by step debug. For this reason I decided to load the schema myself instead of giving the path to it:
function getSchema($filePath) {
// NOT WORKING -> return json_decode(file_get_contents($filePath), true);
return json_decode(file_get_contents($filePath));
}
$data = json_decode($rest->getRequest()->getBody());
$schema = $this->getSchema(realpath('controllers/schemas/userCreate.json'));
$validator = new JsonSchema\Validator;
$check_return = $validator->check($data, $schema);
Please pay attention to the commented line in the getSchema function, if you use json_decode with true as 2nd parameter it's not going to work either, the schema has to be an object and not an array !
Now everything is working like a charm for me ;)
Best regards,
It seems to be an issue of the validator implementation you use. This validation must fail. You can try it out with other implementations.
Related
I am using a PHP 5.6.40 development environment. Many of my classes implement JsonSerializable. The jsonSerialize method of one of these classes uses json_decode, since the value of one of its data members may or may not be encoded as JSON. The problem is that after calling json_decode on a data member whose value is just a plain string, the json_encode fails.
Here is an example:
class JsonSerializableClass implements JsonSerializable
{
private $dataMember;
public function __construct($dataMember)
{
$this->dataMember = $dataMember;
}
public function jsonSerialize()
{
$decodedDataMember = json_decode($this->dataMember, 1);
if ($decodedDataMember && $decodedDataMember['jsonDataMember'])
{
return $decodedDataMember['jsonDataMember'];
}
// json_encode(''); This keeps json_encode from failing, but I'm looking for a different solution.
return $this->dataMember;
}
}
// This works
$test1 = new JsonSerializableClass(json_encode(array(
'jsonDataMember' => 'plain string'
)));
var_export(json_encode($test1));
// This fails
$test2 = new JsonSerializableClass('plain string');
var_export(json_encode($test2));
This is fixed by adding a call to json_encode in the jsonSerialize method after the failed json_decode; however, another developer may not immediately realize what that accomplishes. Of course I would comment my reasoning for doing it, but I'm looking for a more clear solution. Is there error state data that json functions rely on that is being cleared by a successful json_encode? If so, is there a way to clearly reset that error state data, like clear_json_error_state?
I'm sorry if my explanation is hard to understand; it was difficult for me to explain. Please ask me for any clarification you need.
I downgrade the version to V4.1.5, It works.
"name": "pusher/pusher-php-server", "version": "v4.1.5",
I'm using configureMonologUsing() to add in two custom loggers. Doing the standard SOLID principal, I have two providers: ConsoleLoggerProvider and MailLogProvider.
Both of these have a register similar to:
public function register()
{
app()->configureMonologUsing(function(\Monolog\Logger $monolog) {
$monolog->pushHandler(new HandlerClass());
});
}
However, I have noticed over logger will overwrite another logger... How do I stack these?
I've tried to use boot() as well, and that didn't work. I couldn't find any other way to add to the Monolog stack.
Preferable, I want to stack onto Laravel's built-in logger as well.
I (finally) found the answer my question:
Within my providers, instead of using configureMonologUsing(), I used Log::getMonolog()->pushHandler([..])
That works! All loggers, including built-in Laravel file logger, are firing. Finally!
(I've honestly been looking for days for a way to add onto the Monolog stack; I apparently wasn't searching by the right terms)
According to the Laravel documentation:
You should place a call to the configureMonologUsing method in your bootstrap/app.php file right before the $app variable is returned by the file.
In that case, thus should work for you: create two handler classes and add them to monolog this way (in your bootstrap/app.php):
$app->configureMonologUsing(function ($monolog) {
$monolog->pushHandler(new EmailLogHandler);
$monolog->pushHandler(new ConsoleLogHandler);
});
return $app;
Following Laravel 5.2 docs, in bootstrap/app.php, I added the following code right before return $app;:
$app->configureMonologUsing(function($monolog) {//IMPORTANT: I think the order of pushHandler matters, and the ones defined last here will be the first to be called, which affects anything where bubble=false
if (config('services.slack.send_errors_to_slack')) {
$bubble = false; //I think that if I set the 'bubble' argument to false and handle the most severe logging levels first (which counterintuitively means lower in this function), less severe logging levels don't bother reporting the same message.
$useShortAttachment = false;
$includeContextAndExtra = true; //This is important because otherwise 404 errors wouldn't report the URL, give how 'report' function is coded within App\Exceptions\Handler.php.
$handlerForWarningsToNotifyPhone = new \Monolog\Handler\SlackHandler(config('services.slack.token'), config('services.slack.channel_warnings'), 'Monolog', true, null, \Monolog\Logger::WARNING, $bubble, $useShortAttachment, $includeContextAndExtra);
$monolog->pushHandler($handlerForWarningsToNotifyPhone);
$handlerForErrorsToNotifyPhone = new \Monolog\Handler\SlackHandler(config('services.slack.token'), config('services.slack.channel_errors'), 'Monolog', true, null, \Monolog\Logger::ERROR, $bubble, $useShortAttachment, $includeContextAndExtra);
$monolog->pushHandler($handlerForErrorsToNotifyPhone);
}
if (config('app.send_logs_to_loggy')) {
$logglyHandler = new \Monolog\Handler\LogglyHandler(config('services.loggly.token'), config('app.send_logs_to_loggy')); //See \Monolog\Logger::INFO. Log level 200 is "info".
$logglyHandler->setTag(config('services.loggly.tag'));
$monolog->pushHandler($logglyHandler);
}
if (config('app.log_to_local_disk')) {
$localHandler = new \Monolog\Handler\StreamHandler(storage_path("/logs/laravel.log"));
$monolog->pushHandler($localHandler);
}
});
It's just an example that may help you.
Be sure to edit your config files accordingly (e.g. so that app.log_to_local_disk, services.slack.send_errors_to_slack, etc are available).
http://stackoverflow.com/a/36259944/470749 was helpful.
Here is how I able to configure on Laravel Lumen v5.4
in app.php:
$publisher = new \Gelf\Publisher(new \Gelf\Transport\HttpTransport(env('GRAYLOG_HOST'), env('GRAYLOG_PORT'), env('GRAYLOG_PATH')));
//WhatFailureGroupHandler does not break app execution
//if some exceptions happen happens while logging
$failureHandler = new \Monolog\Handler\WhatFailureGroupHandler([
new \Monolog\Handler\GelfHandler($publisher)
]);
\Log::pushHandler($failureHandler);
\Log::getMonolog() as on accepted answer threw error.
Also tried to configure using $app->configureMonologUsing() which threw A facade root has not been set. error. But at the end, I found out that was because we need to return logger:
$app->configureMonologUsing(function ($monolog) {
$publisher = new \Gelf\Publisher(new \Gelf\Transport\HttpTransport(env('GRAYLOG_HOST'), env('GRAYLOG_PORT'), env('GRAYLOG_PATH')));
$failureHandler = new \Monolog\Handler\WhatFailureGroupHandler([new \Monolog\Handler\GelfHandler($publisher)]);
$monolog->pushHandler($failureHandler);
//fixes error: A facade root has not been set
return $monolog;
});
All the examples of $app->configureMonologUsing() usage I have seen do not have a return statement, even in the other answers, which did not work for me.
I'm re-engineering an application, and I've chosen to use TDD to do it. I'm new to TDD and have yet to fall in love with the process. Currently, I've run into a problem that I'm not able to find any clear help on. I might just be overthinking, but would certainly appreciate some help understanding what I'm running into.
The project I am working on persists to MongoDB. I've been working with Mongo for a while now and like it a lot, but the PHP driver doesn't seem to fit into my limited understanding of how (and what) to test with a fake/mock.
Here is a sample class (I haven't checked to see if it will run):
class UserCollection {
protected $_mongo_client; //connection to persistence layer (MongoDB or mock)
public function __construct($mongo_client, $id = NULL) {
$this->_mongo_client = $mongo_client;
}
public function getUserInfo($mid) {
$collection = $this->_mongo_client->vertical->primaryMember;
$user = $collection->findOne(array('memberId' => intval($mid)), array('memberId'=> true, 'name'=>true,'stats' => true));
if($user['memberId']) {
$return['status'] = "Success";
$return['user'] = $user;
} else {
$return['status'] = "Failure";
$return['message'] = "User not found";
}
return $return;
}
}
As I understand it, if I am to create this writing the tests first, I need to create a fake for the DB - and I have been trying to come up with a mock to inject into the constructor. How do I create a mock that handles lines like:
$collection = $this->_mongo_client->vertical->primaryMember;
I'm hoping that I am just overthinking the issue, and there is an easy or better way to do this. At any rate I would appreciate any links, sage advice, or blunt corrections to my thinking.
Another question here addresses this topic as well.
Phactory provides direct support for mocking MongoDB. Here is a complete example from the creators of Phactory in their guide explaining how to mock MongoDB in PHP.
Edit: The above links are now dead, and Phactory appears to be no-longer maintained. There's a new project called `php-mongomock' that aims to solve this problem:
use Helmich\MongoMock\MockCollection;
$collection = new MockCollection();
$collection->createIndex(['foo' => 1]);
$documentId = $collection->insertOne(['foo' => 'bar'])->insertedId();
$collection->updateOne(['_id' => $documentId], ['$set' => ['foo' => 'baz']]);
For those who might stumble across this later, I found a couple of solutions. In the case referenced above I realized that it is more appropriate to pass MongoCollection's in as dependancies, and those are very easy to mock.
However, as I am using Mockery for my mocking library, there are some options for handling "Demeter chains", like the one I asked specifically about:
$collection = $this->_mongo_client->vertical->primaryMember;
Here is a link to the docs:
http://docs.mockery.io/en/latest/reference/demeter_chains.html
It basically states that you can theoretically mock the chain like this:
$mock = \Mockery::mock('\MongoClient');
$mock->shouldReceive('vertical->primaryMember')->andReturn($mockMongoCollection);
You can return anything that you want, and seems like a pretty workable solution.
I have been recently playing around with HHVM. Went through a lot of trouble getting it to work on my computer. I know that not all PHP functions are available. As a test, I am writing a new website using it instead of using my current code. I ran into a problem when trying to use
filter_var($var,FILTER_SANITIZE_URL);
From the error.log file, it turns out that this function is undefined. Is the filter_var function not available for use in HHVM or am I just doing something wrong here. I like to keep things DRY, this would mean I have to do a lot more validation than I expected.
filter_var is now implemented in hhvm. Open github issues if you have any problems with it.
This function appears to not have been implemented on HHVM See http://comments.gmane.org/gmane.science.linguistics.wikipedia.technical/70038
An option if you want to rely on this functionality with the hopes that it will enter the fold is to polyfill it in (partial implementation to inspire the motivated).
if (!function_exists("filter_var")){
// define the constants used by the function
define("FILTER_VALIDATE_EMAIL", "email");
function filter_var(){
$args = func_get_args();
// $args[1] is the filter type (second parameter)
switch ($args[1]){
case FILTER_VALIDATE_EMAIL:
if (preg_match("/^[_a-z0-9-]+(\.[_a-z0-9-]+)*#[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$/", $args[0])?$args[0]:false;
break;
}
}
}
I am stuck using a pre 5.2 release of PHP (v 5.1.6), and therefore don't have access to the handy functions like json_decode()
To complicate matters, I also don't have server privileges to install any extensions.
It would be really nice to simply include a class definition that i can use to create objects (or even a complex array) from a string of json data.
Does anyone know of a lightweight and reliable class definition that will work for me?
(I don't really feel like re-inventing the wheel here.)
Thanks in Advance!
You can download the pear JSON library directly and include the script in your app. Check out this link: http://pear.php.net/package/Services_JSON/download
You can use Services_JSON on PEAR, you'll use it like this:
if ( !function_exists('json_decode') ){
function json_decode($content, $assoc=false){
include_once('JSON.php');
if ( $assoc ){
$json = new Services_JSON(SERVICES_JSON_LOOSE_TYPE);
} else {
$json = new Services_JSON;
}
return $json->decode($content);
}
function json_encode($content){
include_once('JSON.php');
$json = new Services_JSON;
return $json->encode($content);
}
}
Check out Zend_Json, it seems OK, but I don't know the minimum php version required.