首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从文件中验证电话号码的PHP程序

从文件中验证电话号码的PHP程序
EN

Code Review用户
提问于 2019-05-29 15:00:01
回答 1查看 402关注 0票数 5

作为工作面试的一部分,我被要求编写一个cli脚本,它从文件中获取要验证的电话号码列表,并作为命令行中的一个选项。然后,它将生成一个CSV文件,其中包含三列“电话号码”、“载体”和“状态”。

“状态”列应指示电话号码是否有效。如果电话号码有效,则载波字段应包含载波名称。

我使用libphonenumber for-php和https://github.com/splitbrain/php-cli

他们要求我展示使用最新技术和开源技术的PHP编程知识。有人能看到明显的改善,而我没有这样做吗?

到目前为止,我提出了以下几点:

代码语言:javascript
复制
require 'vendor/autoload.php';

use splitbrain\phpcli\CLI;
use splitbrain\phpcli\Options;
use libphonenumber\PhoneNumberUtil;
use libphonenumber\PhoneNumberToCarrierMapper;
use libphonenumber\PhoneNumberType;


class mobileValidator extends CLI
{
// override the default log level
protected $logdefault = 'info';

// register options and arguments
protected function setup(Options $options)
{
    $options->setHelp('This script takes a list of phone numbers from a file (or as an option) and validates them as UK mobile numbers.');
    $options->registerOption('file', 'The file containing a list of phone numbers to validate', 'f', 'filename');
    $options->registerOption('numbers', 'List of numbers passed in as an option (numbers should separated by a comma).', 'n', 'numbers');
}


/**
 * The main Program
 * Arguments and options have been parsed when this is run
 * @param Options $options
 * @return void
 */

protected function main(Options $options)
{
    if (!$options->getOpt('file') && !$options->getOpt('numbers')) {
        $this->error('No files or numbers have been supplied - Cannot Continue.');
        exit();
    }

    $this->notice('Validation Process Started');

    //Get options
    $opNum = $options->getOpt('numbers');
    $file = $options->getOpt('file');

    $numbersFromOp = array();
    $numbersFromFile = array();

    //Set country code uk
    $countryCode = 44;

    //Set regions codes to Uk, Guernsey, Jersey and Isle of Man
    $regionCodes = array('GB', 'GG', 'JE', 'IM');

    //get validator dependencies
    $phoneUtil = PhoneNumberUtil::getInstance();
    $carrierMapper = PhoneNumberToCarrierMapper::getInstance();
    $phoneNumberType = PhoneNumberType::MOBILE;

    //Get numbers from passed in option
    if ($opNum) {
        $numbersFromOp = explode(',', $opNum);
    }

    //Check if files is set and actually exists
    if ($file) {
        if(file_exists($file)) {
            $numbersFromFile = file($file);
        }
        else {
            $this->warning("File not found ".$file);
            if(!$opNum){
                $this->critical('File not found and no numbers have been supplied - Cannot Continue.');
                exit();
            }
        }
    }

    //marge all number arrays to be validated
    $phoneNumbers = array_merge($numbersFromFile, $numbersFromOp);

    //Validate the numbers
    $validator = new validator($phoneNumbers, $phoneUtil, $carrierMapper, $phoneNumberType, $countryCode, $regionCodes);
    $results = $validator->validateNumbers();

    //write numbers to the csv file
    $this->generateCSV($results);

    $this->success('Validation process completed successfully');
}

/**
 * generateCSV
 * Simple function to write a csv file
 * @param array $results
 * @return void
 */

protected function generateCSV(array $results){
    // Open a file to write to
    $fp = fopen('output/phone_numbers_'. date('d-M-Y-H-i').'.csv' , 'wb');

    $headerRow = array ('Phone Number', 'Carrier', 'Status');

    $i = 0;
    foreach( $results as $fields ){
        if( $i === 0 ){
            fputcsv($fp, $headerRow ); // First write the headers
        }
        fputcsv($fp, $fields); // Then write the fields
        $i++;
    }
    fclose($fp);
}


/**
 * Override log function
 * Added error_log
 * @param string $level
 * @param string $message
 * @param array $context
 */
public function log($level, $message, array $context = array())
{
    // is this log level wanted?
    if (!isset($this->loglevel[$level])) return;

    /** @var string $prefix */
    /** @var string $color */
    /** @var resource $channel */
    list($prefix, $color, $channel) = $this->loglevel[$level];
    if (!$this->colors->isEnabled()) $prefix = '';

    $message = $this->interpolate($message, $context);
    error_log($message);
    $this->colors->ptln($prefix . $message, $color, $channel);
}

}
// execute it
$cli = new mobileValidator();
$cli->run();

验证器类:

代码语言:javascript
复制
use libphonenumber\PhoneNumberUtil;
use libphonenumber\PhoneNumberToCarrierMapper;
use libphonenumber\NumberParseException;

class validator
{
private $phoneNumbers;
private $phoneUtil;
private $carrierMapper;
private $phoneNumberType;
private $countryCode;
private $regionCodes;
private const VALID = 'Valid';
private const INVALID = 'Invalid';

function __construct( array $phoneNumbers, PhoneNumberUtil $phoneUtil, PhoneNumberToCarrierMapper $carrierMapper, $phoneNumberType, $countryCode, $regionCodes)
{
    $this->phoneNumbers = $phoneNumbers;
    $this->phoneUtil = $phoneUtil;
    $this->carrierMapper = $carrierMapper;
    $this->phoneNumberType = $phoneNumberType;
    $this->countryCode = $countryCode;
    $this->regionCodes = $regionCodes;
}

/**
 * Returns validated results
 * Loops through the phone numbers and validates as uk mobile numbers (including the channel islands).
 * Results returned as an array( number, carrier, valid)
 * @return array $results
 */

public function validateNumbers(){
    $results = array();
    //loop through supplied phone numbers
    foreach ($this->phoneNumbers as $phoneNumber) {
        $number = trim($phoneNumber);
        try {
            $phoneNumberObject = $this->phoneUtil->parse($number, 'GB');
        }
        catch (NumberParseException $e) {
            $results[] = array($number,'', self::INVALID);
            error_log($e);
            continue;
        }
        //get country code and region code
        $countryCode = $phoneNumberObject->getCountryCode();
        $regionCode = $this->phoneUtil->getRegionCodeForNumber($phoneNumberObject);
        $valid = false;

        //Number is considered valid if the country code matches supplied country code
        // and the region code matches one of the supplied region codes

        if($countryCode === $this->countryCode && in_array($regionCode, $this->regionCodes)){
            $valid = $this->phoneUtil->isValidNumber($phoneNumberObject);
        }

        $type = $this->phoneUtil->getNumberType($phoneNumberObject);
        $carrier = '';
        $validMobile = self::INVALID;
        //if the number is valid and the type is mobile attempt to get the carrier
        if ($valid && $type === $this->phoneNumberType) {
            $carrier = $this->carrierMapper->getNameForNumber($phoneNumberObject, 'en');
            if ($carrier === '') {
                $carrier = 'unknown';
            }
            $validMobile = self::VALID;
        }
        //add to the results array
        $results[] = array($number,$carrier, $validMobile);
    }

    return $results;
}
}
EN

回答 1

Code Review用户

回答已采纳

发布于 2019-05-30 12:27:17

我看过了你的代码,但老实说,我看不出有什么值得回顾的。信不信由你,这其实是件好事。我认为。

基本上是在两个类中使用两个库。但是,您并没有对构建自己的代码给予足够的关注。

例如,您可以在validator类中花费更多的精力,将其划分为验证电话号码的各个方面的方法。现在,它都在一个大的validateNumbers()方法中。它可以工作,但是作为一个validator,它远不如它可能的有用。我几乎开始想,为什么这是一门课的第一?mobileValidator的情况也是如此,几乎所有东西都在main()方法中受到限制。

方法应该执行 In任务 single类的上下文。

这意味着它应该专注于这一单一任务的细节。在validator类中,validateNumbers()处理电话号码的所有方面。如果你再把它分开一点,你可能会得到这样的方法:

代码语言:javascript
复制
__construct($phoneUtil, $carrierMapper, $phoneNumberType)
restrictCountryAndRegionCodes($countryCode, $regionCodes)
validateNumbers($phoneNumbers)
validateNumber($phoneNo)
cleanNumber($phoneNo)
isMobileNumber($phoneNo)
getCarrier($phoneNo)

当然,这些只是例子。注意我是如何将电话号码与类构造函数完全分离开的。这样你就可以给它一次,或者很多次,数次。

重点是:您的validator类是专门为一个作业而专门为该作业创建的。它不能做任何其他事情。它很难被修改、扩展或重用。作为一个类,它是不灵活的。通过将代码分解成其功能部分,它变得更加灵活,更易于阅读、重构、调试和测试。

这基本上是固体原理应用于方法的单一责任原则。虽然这些方志相当抽象,但如果一致应用,它们确实有实际的好处。

如果我要编写您的代码,我将首先考虑它处理的主要实体:文件和电话号码。

这几乎自动地向我暗示应该有一个名为“PhoneNumber”的类。然后这个类获得各种方法,比如getNumber()setNumber(),当然,它可以有一个名为isValidMobileNumber()的方法。它只处理一个电话号码。

我能看到的下一个类是带有电话号码的文件。它的类名可以是:DataFile,它将处理该文件的详细信息。

最后一个类是CSV文件。

我的扩展CLI类使用这三个类,如下所示:

代码语言:javascript
复制
class PhoneNoValidatorCLI extends CLI
{
    private $phoneData = [];

    private function __construct()
    {
        $this->phoneNo = new PhoneNumber(.....);
        $this->addPhoneData("Phone Number", "Carrier", "Status"); // header
        parent::__construct();
    }

    public function addPhoneData($number, $carrier, $status)
    {
        $this->phoneData[] = [$number, $carrier, $status];
    }

    public function writeData2CSV($filename)
    {
        $csv = new CSV($filename);
        $csv->writeData($this->phoneData);
    }

    protected function main(Options $options)
    {
        $file = new DataFile(.....);
        while ($number = $file->nextLine()) {
            $this->phoneNo->setNumber($number);
            $this->addPhoneData($this->phoneNo->getNumber(),
                                $this->phoneNo->getCarrier(),
                                $this->phoneNo->getStatus());
        }
        $file->close();
        $this->writeData2CSV("phone_numbers_" . date('d-M-Y-H-i'));
    }
}

这只是一个非常粗糙的例子,缺乏很多细节,但我希望你明白这个想法。在OOP中,您首先尝试查找正在处理的逻辑对象,然后围绕它们编写代码。您有两个库,并围绕这些库编写代码,而不考虑自己任务中可能的对象。

最后一件事。扩展CLI类。扩展您不拥有的类被认为是“不良实践”。你的延期可能会让他们分不开课。然而,我也看到库本身告诉您这样使用它。所以忘了这个吧。

票数 4
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/221270

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档