作为工作面试的一部分,我被要求编写一个cli脚本,它从文件中获取要验证的电话号码列表,并作为命令行中的一个选项。然后,它将生成一个CSV文件,其中包含三列“电话号码”、“载体”和“状态”。
“状态”列应指示电话号码是否有效。如果电话号码有效,则载波字段应包含载波名称。
我使用libphonenumber for-php和https://github.com/splitbrain/php-cli。
他们要求我展示使用最新技术和开源技术的PHP编程知识。有人能看到明显的改善,而我没有这样做吗?
到目前为止,我提出了以下几点:
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();验证器类:
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;
}
}发布于 2019-05-30 12:27:17
我看过了你的代码,但老实说,我看不出有什么值得回顾的。信不信由你,这其实是件好事。我认为。
基本上是在两个类中使用两个库。但是,您并没有对构建自己的代码给予足够的关注。
例如,您可以在validator类中花费更多的精力,将其划分为验证电话号码的各个方面的方法。现在,它都在一个大的validateNumbers()方法中。它可以工作,但是作为一个validator,它远不如它可能的有用。我几乎开始想,为什么这是一门课的第一?mobileValidator的情况也是如此,几乎所有东西都在main()方法中受到限制。
方法应该执行 In任务 single类的上下文。
这意味着它应该专注于这一单一任务的细节。在validator类中,validateNumbers()处理电话号码的所有方面。如果你再把它分开一点,你可能会得到这样的方法:
__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类使用这三个类,如下所示:
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类。扩展您不拥有的类被认为是“不良实践”。你的延期可能会让他们分不开课。然而,我也看到库本身告诉您这样使用它。所以忘了这个吧。
https://codereview.stackexchange.com/questions/221270
复制相似问题