/**
  * @param CheckAction $action
  */
 public function perform(CheckAction $action)
 {
     $files = $this->getFiles($action->getBasePath());
     $parser = new Parser();
     foreach ($files as $file) {
         if (in_array($file->getFileName(), $action->getSkip('file'))) {
             continue;
         }
         $data = $parser->parse(file_get_contents($file->getRealPath()));
         $context = ['file_name' => $file->getFileName(), 'file_path' => $file->getPathName(), 'discriminator' => 'category', 'training' => $this->getCurrentTraining($file)];
         if (!is_array($data)) {
             $action->addError('structure', 'Output must be type of array, given ' . gettype($data), $context);
             $action->addSkip('category', $file->getFileName());
             continue;
         }
         if (empty($data)) {
             $action->addError('structure', 'Resource file cannot be empty', $context);
             continue;
         }
         //Structure without data can pass
         if (1 === count($data) && isset($data['category'])) {
             $action->addSkip('category', $data['category']);
             continue;
         }
         if (empty($data['category'])) {
             $action->addError('structure', 'Cannot have empty label', $context);
         }
         if (!isset($data['questions']) || empty($data['questions'])) {
             $action->addError('structure', 'Missing questions', $context);
             $action->addSkip('category', $data['category']);
             continue;
         }
         foreach ($data['questions'] as $questionNode) {
             $context['discriminator'] = 'question';
             if (!isset($questionNode['question'])) {
                 $action->addError('structure', 'Required a question', $context);
                 continue 2;
             }
             if (!is_string($questionNode['question'])) {
                 $action->addError('structure', 'Should be a string', $context);
                 continue 2;
             }
             if (empty($questionNode['question'])) {
                 $action->addError('structure', 'Cannot have empty label', $context);
             }
             if (!isset($questionNode['answers']) || count($questionNode['answers']) <= 1) {
                 $action->addError('structure', 'Must contains at least 2 answers to be valid', $context);
                 continue 2;
             }
             $good = 0;
             foreach ($questionNode['answers'] as $answerNode) {
                 $context['discriminator'] = 'answer';
                 if (!isset($answerNode['value']) || !is_string($answerNode['value'])) {
                     $action->addError('structure', 'Missing or invalid [value] field', $context);
                     continue 3;
                 }
                 if (empty($answerNode['value'])) {
                     $action->addError('structure', sprintf('Cannot have empty label, related question : "%s"', $questionNode['question']), $context);
                 }
                 if (!isset($answerNode['correct']) || !is_bool($answerNode['correct'])) {
                     $action->addError('structure', 'Missing or invalid [correct] field', $context);
                     continue 3;
                 }
                 if (true === $answerNode['correct']) {
                     $good++;
                 }
             }
             if (0 === $good) {
                 $action->addError('structure', 'Must have at least one valid answer', $context);
                 continue 2;
             }
         }
     }
 }
 /**
  * @param CheckAction $action
  */
 public function perform(CheckAction $action)
 {
     $files = $this->getFiles($action->getBasePath());
     $parser = new Parser();
     $collector = ['category' => [], 'question' => [], 'answer' => []];
     foreach ($files as $file) {
         if (in_array($file->getFileName(), $action->getSkip('file'))) {
             continue 1;
         }
         $data = $parser->parse(file_get_contents($file->getRealPath()));
         if (in_array($data['category'], $action->getSkip('category'))) {
             continue 1;
         }
         $trainingName = $this->getCurrentTraining($file);
         $identifier = implode('=', [$trainingName, $file->getFileName()]);
         $collector['category'][$identifier] = $data['category'];
         foreach ($data['questions'] as $questionNode) {
             if (in_array($questionNode['question'], $action->getSkip('question'))) {
                 continue 2;
             }
             $collector['question'][$identifier][] = $questionNode['question'];
             foreach ($questionNode['answers'] as $answerNode) {
                 if (in_array($answerNode['value'], $action->getSkip('answer'))) {
                     continue 3;
                 }
                 $collector['answer'][$identifier][$questionNode['question']][] = $answerNode['value'];
             }
         }
     }
     foreach ($collector as $type => $node) {
         if ($type === 'category') {
             //flatten
             foreach (array_count_values($collector[$type]) as $category => $count) {
                 if ($count > 1) {
                     $affected = array_keys($collector[$type], $category);
                     $errorFiles = [];
                     foreach ($affected as $value) {
                         list($training, $file) = explode('=', $value);
                         $errorFiles[] = $file;
                     }
                     $action->addError('integrity', sprintf('Category "%s" was find several times', $category), ['file_name' => $errorFiles, 'discriminator' => 'category', 'training' => $training]);
                 }
             }
         }
         if ($type === 'question') {
             //nested
             foreach ($node as $context => $questionsNodes) {
                 list($training, $fileName) = explode('=', $context);
                 foreach (array_count_values($questionsNodes) as $question => $count) {
                     if ($count > 1) {
                         $action->addError('integrity', sprintf('Question "%s" was find several times', $question), ['file_name' => $fileName, 'discriminator' => 'question', 'training' => $training]);
                     }
                 }
             }
         }
         if ($type === 'answer') {
             //nested
             foreach ($node as $context => $answerNode) {
                 list($training, $fileName) = explode('=', $context);
                 foreach ($answerNode as $question => $answers) {
                     foreach (array_count_values($answers) as $answer => $count) {
                         if ($count > 1) {
                             $action->addError('integrity', sprintf('Answer "%s" was find several times for question "%s"', $answer, $question), ['file_name' => $fileName, 'discriminator' => 'answer', 'training' => $training]);
                         }
                     }
                 }
             }
         }
     }
 }