You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel中ORCID校验(ISO 7064 11,2)的PHP实现及正则验证正确性咨询

ORCID Validation in Laravel: Checking Your Implementation

Let's walk through your questions one by one to get your ORCID validation working correctly.

1. Is the Java-to-PHP Checksum Conversion Correct?

The core calculation logic you translated from Java to PHP is mathematically correct—the steps for ISO 7064 11,2 checksum match perfectly. However, your current PHP code has a critical logical flaw that will break the validation:

The Problem

Your Java generateCheckDigit function expects the first 15 digits of an ORCID (without hyphens) as input. But your PHP code uses the full ORCID string (including - characters) as $baseDigits. This means:

  • You're looping through 19 characters (not 15), including hyphens. IntlChar::getNumericValue('-') returns -1, which completely corrupts the checksum calculation.
  • If the input ORCID includes an X (the valid check digit), your code will try to process it as part of the base digits, which is invalid (base digits should only be 0-9).

Fixed PHP Checksum Logic

Here's the corrected approach, including proper ORCID preprocessing:

use IntlChar;

// Step 1: Clean the input ORCID (remove hyphens)
$rawOrcid = str_replace('-', '', $request->orcid);

// Step 2: Validate basic length and format first
if (strlen($rawOrcid) !== 16 || !preg_match('/^[0-9]{15}[0-9X]$/i', $rawOrcid)) {
    abort(400, 'ORCID must follow the format xxxx-xxxx-xxxx-xxxx');
}

// Step 3: Extract the first 15 digits for checksum calculation
$baseDigits = substr($rawOrcid, 0, 15);

// Step 4: Calculate checksum (your core logic is correct here)
$total = 0;
for ($i = 0; $i < strlen($baseDigits); $i++) {
    $digit = IntlChar::getNumericValue($baseDigits[$i]);
    // Ensure we're only working with valid digits
    if (!is_int($digit) || $digit < 0 || $digit > 9) {
        abort(400, 'Invalid ORCID base digits');
    }
    $total = ($total + $digit) * 2;
}
$remainder = $total % 11;
$result = (12 - $remainder) % 11;
$calculatedCheckDigit = $result === 10 ? 'X' : (string)$result;

// Step 5: Compare calculated check digit with input's check digit
$inputCheckDigit = strtoupper(substr($rawOrcid, 15, 1));
if ($inputCheckDigit !== $calculatedCheckDigit) {
    abort(400, 'Invalid ORCID check digit');
}

2. Is the Regex Validation Rule Correct?

Your regex rules are not ideal—here's why and how to fix them:

The Issues

  • regex:/[0-9X]/u: This matches any string that contains at least one digit or X (e.g., "123X" or "abcX123" would pass), which isn't what you want.
  • regex:/[0-9]{1}|X/u: While this targets single characters, it's redundant and lacks start/end anchors (so it would still match a string like "Xabc" because it contains an X).

Correct Regex Options

  • For validating a single check digit: Use regex:/^[0-9X]$/u—the ^ and $ anchors ensure the entire string is exactly one character (either a digit 0-9 or X).
  • For validating a full ORCID format: Use regex:/^[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9X]{4}$/u—this enforces the standard xxxx-xxxx-xxxx-xxxx structure, with the last segment allowing an X as the final character.

Better: Use a Custom Laravel Validation Rule

Instead of splitting validation into regex and manual checksum logic, create a reusable custom rule for ORCID:

// In App\Providers\AppServiceProvider.php, add this to the boot() method
use Illuminate\Support\Facades\Validator;
use IntlChar;

Validator::extend('orcid', function ($attribute, $value, $parameters, $validator) {
    $rawOrcid = str_replace('-', '', $value);
    
    // Check basic length and format
    if (strlen($rawOrcid) !== 16 || !preg_match('/^[0-9]{15}[0-9X]$/i', $rawOrcid)) {
        return false;
    }
    
    // Calculate checksum
    $baseDigits = substr($rawOrcid, 0, 15);
    $total = 0;
    foreach (str_split($baseDigits) as $char) {
        $digit = IntlChar::getNumericValue($char);
        $total = ($total + $digit) * 2;
    }
    $remainder = $total % 11;
    $result = (12 - $remainder) % 11;
    $calculatedCheckDigit = $result === 10 ? 'X' : (string)$result;
    
    // Compare with input's check digit
    $inputCheckDigit = strtoupper(substr($rawOrcid, 15, 1));
    return $inputCheckDigit === $calculatedCheckDigit;
});

Then use it in your validation like this:

$this->validate($request, [
    'orcid' => 'nullable|orcid',
]);

内容的提问来源于stack exchange,提问作者Cr1xus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:17:20