public function execute() { if (!class_exists(Validator::class)) { $this->error('The JsonSchema library cannot be found, please install it through composer.', 1); } elseif (!class_exists(SpdxLicenses::class)) { $this->error('The spdx-licenses library cannot be found, please install it through composer.', 1); } $path = $this->getArg(0); $data = json_decode(file_get_contents($path)); if (!is_object($data)) { $this->error("{$path} is not a valid JSON file.", 1); } if (!isset($data->manifest_version)) { $this->output("Warning: No manifest_version set, assuming 1.\n"); // For backwards-compatability assume 1 $data->manifest_version = 1; } $version = $data->manifest_version; if ($version !== ExtensionRegistry::MANIFEST_VERSION) { $schemaPath = dirname(__DIR__) . "/docs/extension.schema.v{$version}.json"; } else { $schemaPath = dirname(__DIR__) . '/docs/extension.schema.json'; } if ($version < ExtensionRegistry::OLDEST_MANIFEST_VERSION || $version > ExtensionRegistry::MANIFEST_VERSION) { $this->error("Error: {$path} is using a non-supported schema version, it should use " . ExtensionRegistry::MANIFEST_VERSION, 1); } elseif ($version < ExtensionRegistry::MANIFEST_VERSION) { $this->output("Warning: {$path} is using a deprecated schema, and should be updated to " . ExtensionRegistry::MANIFEST_VERSION . "\n"); } $licenseError = false; // Check if it's a string, if not, schema validation will display an error if (isset($data->{'license-name'}) && is_string($data->{'license-name'})) { $licenses = new SpdxLicenses(); $valid = $licenses->validate($data->{'license-name'}); if (!$valid) { $licenseError = '[license-name] Invalid SPDX license identifier, ' . 'see <https://spdx.org/licenses/>'; } } $validator = new Validator(); $validator->check($data, (object) ['$ref' => 'file://' . $schemaPath]); if ($validator->isValid() && !$licenseError) { $this->output("{$path} validates against the version {$version} schema!\n"); } else { foreach ($validator->getErrors() as $error) { $this->output("[{$error['property']}] {$error['message']}\n"); } if ($licenseError) { $this->output("{$licenseError}\n"); } $this->error("{$path} does not validate.", 1); } }
/** * @dataProvider providePassesValidation * @param string $path Path to thing's json file */ public function testPassesValidation($path) { $data = json_decode(file_get_contents($path)); $this->assertInstanceOf('stdClass', $data, "{$path} is not valid JSON"); $this->assertObjectHasAttribute('manifest_version', $data, "{$path} does not have manifest_version set."); $version = $data->manifest_version; if ($version !== ExtensionRegistry::MANIFEST_VERSION) { $schemaPath = __DIR__ . "/../../../docs/extension.schema.v{$version}.json"; } else { $schemaPath = __DIR__ . '/../../../docs/extension.schema.json'; } // Not too old $this->assertTrue($version >= ExtensionRegistry::OLDEST_MANIFEST_VERSION, "{$path} is using a non-supported schema version"); // Not too new $this->assertTrue($version <= ExtensionRegistry::MANIFEST_VERSION, "{$path} is using a non-supported schema version"); $licenseError = false; if (class_exists(SpdxLicenses::class) && isset($data->{'license-name'}) && is_string($data->{'license-name'})) { $licenses = new SpdxLicenses(); $valid = $licenses->validate($data->{'license-name'}); if (!$valid) { $licenseError = '[license-name] Invalid SPDX license identifier, ' . 'see <https://spdx.org/licenses/>'; } } $validator = new Validator(); $validator->check($data, (object) ['$ref' => 'file://' . $schemaPath]); if ($validator->isValid() && !$licenseError) { // All good. $this->assertTrue(true); } else { $out = "{$path} did pass validation.\n"; foreach ($validator->getErrors() as $error) { $out .= "[{$error['property']}] {$error['message']}\n"; } if ($licenseError) { $out .= "{$licenseError}\n"; } $this->assertTrue(false, $out); } }
/** * Validates the config, and returns the result. * * @param string $file The path to the file * @param int $arrayLoaderValidationFlags Flags for ArrayLoader validation * * @return array a triple containing the errors, publishable errors, and warnings */ public function validate($file, $arrayLoaderValidationFlags = ValidatingArrayLoader::CHECK_ALL) { $errors = array(); $publishErrors = array(); $warnings = array(); // validate json schema $laxValid = false; try { $json = new JsonFile($file, null, $this->io); $manifest = $json->read(); $json->validateSchema(JsonFile::LAX_SCHEMA); $laxValid = true; $json->validateSchema(); } catch (JsonValidationException $e) { foreach ($e->getErrors() as $message) { if ($laxValid) { $publishErrors[] = $message; } else { $errors[] = $message; } } } catch (\Exception $e) { $errors[] = $e->getMessage(); return array($errors, $publishErrors, $warnings); } // validate actual data if (!empty($manifest['license'])) { // strip proprietary since it's not a valid SPDX identifier, but is accepted by composer if (is_array($manifest['license'])) { foreach ($manifest['license'] as $key => $license) { if ('proprietary' === $license) { unset($manifest['license'][$key]); } } } $licenseValidator = new SpdxLicenses(); if ('proprietary' !== $manifest['license'] && array() !== $manifest['license'] && !$licenseValidator->validate($manifest['license'])) { $warnings[] = sprintf('License %s is not a valid SPDX license identifier, see https://spdx.org/licenses/ if you use an open license.' . "\nIf the software is closed-source, you may use \"proprietary\" as license.", json_encode($manifest['license'])); } } else { $warnings[] = 'No license specified, it is recommended to do so. For closed-source software you may use "proprietary" as license.'; } if (isset($manifest['version'])) { $warnings[] = 'The version field is present, it is recommended to leave it out if the package is published on Packagist.'; } if (!empty($manifest['name']) && preg_match('{[A-Z]}', $manifest['name'])) { $suggestName = preg_replace('{(?:([a-z])([A-Z])|([A-Z])([A-Z][a-z]))}', '\\1\\3-\\2\\4', $manifest['name']); $suggestName = strtolower($suggestName); $publishErrors[] = sprintf('Name "%s" does not match the best practice (e.g. lower-cased/with-dashes). We suggest using "%s" instead. As such you will not be able to submit it to Packagist.', $manifest['name'], $suggestName); } if (!empty($manifest['type']) && $manifest['type'] == 'composer-installer') { $warnings[] = "The package type 'composer-installer' is deprecated. Please distribute your custom installers as plugins from now on. See https://getcomposer.org/doc/articles/plugins.md for plugin documentation."; } // check for require-dev overrides if (isset($manifest['require']) && isset($manifest['require-dev'])) { $requireOverrides = array_intersect_key($manifest['require'], $manifest['require-dev']); if (!empty($requireOverrides)) { $plural = count($requireOverrides) > 1 ? 'are' : 'is'; $warnings[] = implode(', ', array_keys($requireOverrides)) . " {$plural} required both in require and require-dev, this can lead to unexpected behavior"; } } // check for commit references $require = isset($manifest['require']) ? $manifest['require'] : array(); $requireDev = isset($manifest['require-dev']) ? $manifest['require-dev'] : array(); $packages = array_merge($require, $requireDev); foreach ($packages as $package => $version) { if (preg_match('/#/', $version) === 1) { $warnings[] = sprintf('The package "%s" is pointing to a commit-ref, this is bad practice and can cause unforeseen issues.', $package); } } // check for empty psr-0/psr-4 namespace prefixes if (isset($manifest['autoload']['psr-0'][''])) { $warnings[] = "Defining autoload.psr-0 with an empty namespace prefix is a bad idea for performance"; } if (isset($manifest['autoload']['psr-4'][''])) { $warnings[] = "Defining autoload.psr-4 with an empty namespace prefix is a bad idea for performance"; } try { $loader = new ValidatingArrayLoader(new ArrayLoader(), true, null, $arrayLoaderValidationFlags); if (!isset($manifest['version'])) { $manifest['version'] = '1.0.0'; } if (!isset($manifest['name'])) { $manifest['name'] = 'dummy/dummy'; } $loader->load($manifest); } catch (InvalidPackageException $e) { $errors = array_merge($errors, $e->getErrors()); } $warnings = array_merge($warnings, $loader->getWarnings()); return array($errors, $publishErrors, $warnings); }