引言:经常在一些社区上看到莫名的一句话
PHP是世界上最好的语言
,在语言的争论上,确实是大有华山论剑
的门派之争。本文将通过编程语言的范式
角度,来了解整个编程语言的发展史,同时更清晰的认知编程范式
,以达到知己知彼,百战不殆
编程语言发展到今天,出现了好多不同的代码编写方式,但不同的方式解决的都是同一个问题,那就是如何写出更为通用、更具可重用性的代码或模块。
一、从C语言谈起
C语言的历史悠久,自其问世以来,其影响了太多太多的编程语言,到现在还一直被广泛使用,不得不佩服它的生命力。但是,我们也要清楚的知道,大多数的使用C语言来做内核的编程语言其实都是在改善C语言带来的问题+时代发展带来的变化。
下面来简单回顾下C语言的特性:
- C 语言是一个静态弱类型语言,在使用变量时需要声明变量类型,但是类型间可以有隐式转换;
- 不同的变量类型可以用结构体(struct)组合在一起,以此来声明新的数据类型;
- C 语言可以用 typedef 关键字来定义类型的别名,以此来达到变量类型的抽象;
- C 语言是一个有结构化程序设计、具有变量作用域以及递归功能的过程式语言;
- 通过指针,C 语言可以容易地对内存进行低级控制,然而这加大了编程复杂度;
- 编译预处理让 C 语言的编译更具有弹性,比如跨平台。
然而,在代码组织和功能编程上,C语言的上述,却不那么美妙了,eq:
//一个简单的交换两个变量的函数
void changeVar(int* x, int* y)
{
int tmp = *x;
*x = *y;
*y = tmp;
}
可以想一想,这里为什么要用指针呢?因为如果不用指针的话,只是传进来的行参,即函数的形参是调用实参的一个拷贝,函数闭包里对形参的修改无法影响实参的结果。
然而,这个函数最大的问题是int tmp = *x
决定了这个函数只能给int值使用,但是还有好多类型也等着能被调用呢,eq:double、float、string等,这就是个静态语言最糟糕的问题。
当然,这个时候大家都会想到类型转换,然而对于C语言的类型转换,是会出现很多问题的。
- 比如:一个 double a[10] 的数组,a[2] 意味着 a + sizeof(double) * 2。如果你把 a 强转成 int,那么 a[2] 就意味着 a + sizeof(int) * 2。我们知道 sizeof(double) 是 8,而 sizeof(int) 是 4。于是访问到了不同的地址和内存空间,这就导致程序出现严重的问题。下面这种使用临时交换数据的buffer拷贝方案可以去掉类型转换时导致的地址变换:
//加入泛型变量的交换两个变量的函数
void changeVar(void* x, void* y, size_t size)
{
char tmp[size]; //交换数据时需要用的 buffer
memcpy(tmp, y, size);
memcpy(y, x, size);
memcpy(x, tmp, size);
/**
* 1.函数接口中增加了一个size参数,用了 void* 后,类型被“抽象”掉了
,编译器不能通过类型得到类型的长度了,所以,需要我们手动地加上一个类型长度的标识。
2.函数的实现中使用了memcpy()函数,因为类型被“抽象”掉了,所以不能用赋值表达式了,很有可能传进来的参数类型还是一个结构体,因此,为了要交换这些复杂类型的值,我们只能使用内存复制的方法了。
*/
}
除了上面使用void* 来做泛型,在C语言中,还可以用宏定义来做泛型,不过却会带来宏的字符串替换,导致代码膨胀,导致编译出的执行文件相对较大,此处感兴趣的可以去深入学习一下。
二、泛型编程
-
泛型
:即是指具有在多种数据类型上皆可操作的含义,与模板有些相似。
1.举例:- C语言版本
long sum(int *a, size_t size) {
long result = 0; for(int i=0; i
- C++版本
template
T sum(Iter pStart, Iter pEnd) {
T result = 0;
for(Iter p=pStart; p!=pEnd; p++) {
result += *p;
}
return result;
}
// 那个0假设了类型是int;
// 那个T假设了 Iter 中出来的类型是T。如果类型不一样,那么就会导致转类型的问题。
- php版本
public function sum($a, $b) {return intval($a)+intval($b);}
- js版本
function sum($x, $y) {return parseInt($x)+parseInt($y);}
-
泛型编程的重要设计模式-迭代器
:
- 首先,一个迭代器需要和一个容器在一起,因为里面是对这个容器的具体的代码实现。
- 它需要重载一些操作符,比如:取值操作*、成员操作->、比较操作==和!=,还有遍历操作++,等等。
3.然后,还要typedef一些类型,比如value_type,告诉我们容器内的数据的实际类型是什么样子。 - 还有一些,如begin()和end()的基本操作。日志的记录等。
三、类型系统和泛型的本质
0. 函数式编程
对于函数式编程来说,它只关心定义输入数据和输出数据相关的关系,数学表达式里面其实是在做一种映射(mapping),输入的数据和输出的数据关系是什么样的,是用函数来定义的。
特征:
- stateless:函数不维护任何状态。函数式编程的核心精神是 stateless,简而言之就是它不能存在状态,打个比方,你给我数据我处理完扔出来。里面的数据是不变的。
- immutable:输入数据是不能动的,动了输入数据就有危险,所以要返回新的数据集。
优势: - 没有状态就没有伤害。
- 并行执行无伤害。
- Copy-Paste 重构代码无伤害。
- 函数的执行没有顺序上的问题。
- 确定性。所谓确定性,就是像在数学中那样,f(x) = y 这个函数无论在什么场景下,都会得到同样的结果,而不是像程序中的很多函数那样。同一个参数,在不同的场景下会计算出不同的结果,这个我们称之为函数的确定性。所谓不同的场景,就是我们的函数会根据运行中的状态信息的不同而发生变化。
1.基于原型的编程范式
讲到原型,相信大家的第一映像一定是JavaScript吧,来个小问题,大家有谁知道js的国际全称叫什么吗?
- 每个对象都有一个 proto 的属性
- 来看下原型链:
var a = {
x: 10,
calculate: function (z) {
return this.x + this.y + z;
}
};
var b = {
y: 20,
__proto__: a
};
var c = {
y: 30,
__proto__: a
};
// call the inherited method
b.calculate(30); // 60
c.calculate(40); // 80
- 再来看看
// 一种构造函数写法
function Foo(y) {
this.y = y;
}
// 修改 Foo 的 prototype,加入一个成员变量 x
Foo.prototype.x = 10;
// 修改 Foo 的 prototype,加入一个成员函数 calculate
Foo.prototype.calculate = function (z) {
return this.x + this.y + z;
};
// 现在,我们用 Foo 这个原型来创建 b 和 c
var b = new Foo(20);
var c = new Foo(30);
// 调用原型中的方法,可以得到正确的值
b.calculate(30); // 60
c.calculate(40); // 80
再来重温一下上面的原型讲解实例
function Person(){}
var p = new Person();
Person.prototype.name = "Hao Chen";
Person.prototype.sayHello = function(){
console.log("Hi, I am " + this.name);
}
console.log(p.name); // "Hao Chen"
p.sayHello(); // "Hi, I am Hao Chen"
在上面这个例子中:
- 我们先生成了一个空的函数对象 Person();
- 然后将这个空的函数对象 new 出另一个对象,存在 p 中;
- 这时再改变 Person.prototype,让其有一个 name 的属性和一个 sayHello() 的方法;
- 我们发现,另外那个 p 的对象也跟着一起改变了。
注意一下:
- 当创建 function Person(){} 时,Person.proto 指向 Function.prototype;
- 当创建 var p = new Person() 时,p.proto 指向 Person.prototype;
- 当修改了 Person.prototype 的内容后,p.proto 的内容也就被改变了。
小结:
通过上述可以看到,这种玩法就是一种委托的方式。在使用委托的基于原型的语言中,运行时语言可以“仅仅通过序列的指针找到匹配”这样的方式来定位属性或者寻找正确的数据。所有这些创建行为、共享的行为需要的是委托指针。
不像是基于类的面向对象语言中类和接口的关系,原型和它的分支之间的关系并不要求子对象有相似的内存结构,因为如此,子对象可以继续修改而无需像基于类的系统那样整理结构。还有一个要提到的地方是,不仅仅是数据,方法也能被修改。因为这个原因,大多数基于原型的语言把数据和方法提作“slots”。
这种在对象里面直接修改的玩法,虽然这个特性可以带来运行时的灵活性,我们可以在运行时修改一个 prototype,给它增加甚至删除属性和方法。但是其带来了执行的不确定性,也有安全性的问题,而代码还变得不可预测,这有点黑科技的味道了。因为这些不像静态类型系统,没有一个不可变的契约对代码的确定性有保证,所以,需要使用者来自己保证。
2. 面向对象与go语言的委托模式
OOP三大特性:封装、继承和多态。
OOD原则/SOLID原则:
- OCP-Open/close principle:开/闭原则;Open for extension;Closed for modification;
比如加上一些策略模式、适配器模式、观察者模式等
- DIP-Dependency Inversion Principle:依赖倒置原则
高层模块不能依赖低层模块,而是大家都依赖于抽象;
抽象不能依赖实现,而是实现依赖抽象。
DIP 倒置了:模块或包的依赖关系
开发顺序和职责
- SRP-Single Responsibility Principle:单一职责原则,又被称为“内聚性原则(Cohesion)”,意为:
一个模块的组成元素之间的功能相关性。
一个类,只能有一个引起它的变化的原因。
一个职责是一个变化的原因。
违反的后果:
- 脆弱性:switch case/if else 非常脆弱;对系统的改动会导致系统中和改动的地方无关的许多地方出现问题。出现新问题的地方与改动的地方没有概念上的关联。要修正这些问题又会引出更多的问题,从而使开发团队就像一只不停追逐自己尾巴的狗一样。
- 不可移植性
设计臭味:糟糕的代码有哪些特点?
- 僵硬-不易改变
僵化性(rigidity):很难对系统改动,因为每个改动都会迫使许多对系统其他部分的改动。如果单一的改动会导致依赖关系的模块中的连锁改动,那么设计就是僵化的,必须要改动的
模块越多,设计就越僵化。
- 脆弱-只想改变A,结果B被意外破坏。
牢固性(Immobility): 很难解开系统的纠结,使之成为一些可在其他系统中重用的组件。设计中包含了对其他系统有用的部分,而把这些部分从系统中分离出来所需的努力和风险是巨大的。
- 导致误用的陷阱-做错误的事比做正确的事更容易,引诱程序员破坏原有的设计。
粘滞性(Viscosity): 做正确的事情比做错误的事情要困难。
- 面临一个改动的时候,开发人员常常会发现会有多种改动的方法。有的方法会保持系统原来的设计,而另外一些则会破坏设计,当那些可以保持系统设计的方法比那些破坏设计的方法跟难应用是,就表明设计具有高的粘滞性,作错误的事情就很容易。
- 晦涩-代码难以理解,定义的变量名和注释都不能够见名知意。
晦涩性(Opacity):很难阅读、理解。没有很好的表现出意图。
- 代码可以用清晰、富有表现力的方式编写,也可以用晦涩、费解的方式编写。一般说来,随着时间的推移,代码会变得越来越晦涩。
不必要的复杂性(Needless Complexity):设计中包含有不具任何直接好处的基础结构。
如果设计中包含有当前没有用的组成部分,他就含有不必要的复杂性。当开发人员预测需求的变化,并在软件中放置了处理那些潜在变化的代码时,常常会出现这种情况。
- 过度设计、copy-paste代码
不必要的重复(Needless Repetition):设计中包含有重复的结构,而该重复的结构本可以使用单一的抽象进行统一。
- 当 copy,cut,paste 编程的时候,这种情况就会发生。
面向对象优点:
- 能和真实的世界交相辉映,符合人的直觉。
- 面向对象和数据库模型设计类型,更多地关注对象间的模型设计。
- 强调于“名词”而不是“动词”,更多地关注对象和对象间的接口。
- 根据业务的特征形成一个个高内聚的对象,有效地分离了抽象和具体实现,增强了可重用性和可扩展性。
- 拥有大量非常优秀的设计原则和设计模式。
面向对象缺点:
- 代码都需要附着在一个类上,从一侧面上说,其鼓励了类型。
- 代码需要通过对象来达到抽象的效果,导致了相当厚重的“代码粘合层”。
- 因为太多的封装以及对状态的鼓励,导致了大量不透明并在并发下出现很多问题。
go语言的委托模式:
type Widget struct {
X, Y int
}
type Label struct {
Widget // Embedding (delegation)
Text string // Aggregation
X int // Override
}
func (label Label) Paint() {
// [0xc4200141e0] - Label.Paint("State")
fmt.Printf("[%p] - Label.Paint(%q)\n",
&label, label.Text)
}
由上面可知:
- 我们声明了一个 Widget,其有 X和Y;
- 然后用它来声明一个 Label,直接把 Widget 委托进去;
- 然后再给 Label 声明并实现了一个 Paint() 方法。
label := Label{Widget{10, 10}, "State", 100}
// X=100, Y=10, Text=State, Widget.X=10
fmt.Printf("X=%d, Y=%d, Text=%s Widget.X=%d\n",
label.X, label.Y, label.Text,
label.Widget.X)
fmt.Println()
// {Widget:{X:10 Y:10} Text:State X:100}
// {{10 10} State 100}
fmt.Printf("%+v\n%v\n", label, label)
label.Paint()
我们可以看到,如果有成员变量重名,则需要手动地解决冲突。
3. 来看看我们日常一段程序代码的抽象解构(见draw图),logic + control
分离业务逻辑+控制逻辑,业务逻辑又可分为:校验业务类型,数据处理业务类型;数据处理业务类型又可分为:格式化组装数据处理类型、struct结构体枚举预处理CURD
-
用过的经验,以编程方式类型区分:
- 面向对象:委托、策略、桥接、修饰、IoC/DIP、MVC
- 函数式编程:修饰、管道、拼装
Logic 部分才是真正有意义的(What)
Control 部分只是影响 Logic 部分的效率(How)
一个是在解决数据和算法,一个是在解决逻辑和控制。
4. 逻辑编程范式
四、泛型思想日常编码中的引用
self::INIT,
'消息出列' => self::HANDLING,
'处理失败' => self::FAIL,
'处理成功' => self::SUCCESS,
'忽略处理' => self::IGNORE
];
}
/**
* 所有值
*
* @return array
*/
public static function all()
{
return array_values(static::comment());
}
/**
* list
*
* @return mixed
*/
public static function allObj()
{
if (static::$logObj == null) {
static::$logObj = [
static::INIT => static::create(static::INIT, '消息入列', ''),
static::HANDLING => static::create(static::HANDLING, '消息出列', ''),
static::FAIL => static::create(static::FAIL, '处理失败', ''),
static::SUCCESS => static::create(static::SUCCESS, '处理成功', ''),
static::IGNORE => static::create(static::IGNORE, '忽略处理', ''),
static::UNUSUAL => static::create(static::UNUSUAL, '传入的队列状态异常', '传入的队列状态异常')
];
}
return static::$logObj;
}
/**
* 查询
*
* @param $key
* @return false|static
*/
public static function find($key)
{
return isset(static::all()[$key]) ? static::all()[$key] : false;
}
/**
* 创建struct
*
* @param $key
* @param $name
* @param $remark
* @return $this
*/
public function create($key, $name, $remark)
{
$obj = new static();
$obj->key = $key;
$obj->name = $name;
$obj->remark = $remark;
return $obj;
}
}
// 调用代码-原始
public function updateLogStatusById($tenantCode, $logId, $status, $message = '')
{
if (!is_scalar($message)) {
$message = json_encode($message, JSON_UNESCAPED_UNICODE);
}
switch ($status) {
case '消息出列':
$this->_yfQueueLogService->updateLogStatusById($tenantCode, $logId, ['status' => '消息出列', 'message' => 'yf_queue_plus_iLogId:' . $this->getILogId().'message:' . $message]);
break;
case '忽略处理':
$this->_yfQueueLogService->updateLogStatusById($tenantCode, $logId, ['status' => '忽略处理', 'message' => $message]);
break;
case '处理失败':
$this->_yfQueueLogService->updateLogStatusById($tenantCode, $logId, ['status' => '处理失败', 'message' => $message]);
break;
case '处理成功':
$this->_yfQueueLogService->updateLogStatusById($tenantCode, $logId, ['status' => '处理成功', 'message' => $message]);
break;
}
}
// 调用代码-改造后
public function updateQueueStatue($tenantCode, $logId, $status, $message = '')
{
if (!is_scalar($message)) {
$message = json_encode($message, JSON_UNESCAPED_UNICODE);
}
if (!in_array($status, QueueResultLogStatus::comment())) {
$statusName = QueueResultLogStatus::find($status)->name;
} else {
$statusName = QueueResultLogStatus::find(QueueResultLogStatus::UNUSUAL)->name;
}
if ($status == QueueResultLogStatus::HANDLING) {
$message = 'yf_queue_plus_iLogId:' . $this->getILogId().'message:' . $message;
}
$updateRow = ['status' => $statusName, 'message' => $message];
$this->_yfQueueLogService->updateLogStatusById($tenantCode, $logId, $updateRow);
}
写在最后,留个小作业,下面这段代码请提出你的优化思路:
private function checkClassProblemSheet($sheet, $highestColumn, $highestRow)
{
try {
// 不合法的行
$failRow = [];
//返回合法数据
$legalImportRows = [];
// 检查项为空的行
$nullClassRow = [];
// 检查项名称包含特殊字符的行
$illegalCharacterRow = [];
// 检查项不存在的行
$notExistClassRow = [];
// 问题描述为空的行
$nullClassProblemRow = [];
// 问题描述超过特定字符
$exceedMaxLengthClassProblemRow = [];
// 问题描述重复的行
$classProblemRepeatRow = [];
// 非末级检查项的行
$notEndClassRow = [];
// 装修标准为空的行
$nullFitmentStandardRow = [];
// 装修标准不合法的行
$illegalFitmentStandardRow = [];
// 装修标准超过检查项的装修标准
$exceedClassFitmentStandardRow = [];
// 内验期整改时限为空的行
$nullInternalRepairRow = [];
// 内验期整改时限不合法的行
$illegalInternalRepairRow = [];
// 交付期整改时限为空的行
$nullDeliveryRepairRow = [];
// 交付期整改时限不合法的行
$illegalDeliveryRepairRow = [];
// 补充说明是否必填为空的行
$nullIsRemarkRequiredRow = [];
// 补充说明是否必填不合法的行
$illegalIsRemarkRequiredRow = [];
// 字段到Excel列的转换
$fieldToColumnMapping = $this->getImportFieldMapping($sheet, $highestColumn, $this->_importClassProblemFields);
// 提示不存在的列
$this->checkNotExistColumn($fieldToColumnMapping, $sheet->getTitle());
for ($row = 3; $row <= $highestRow; $row++) {
$checkName1 = (string) $sheet->getCellByColumnAndRow($fieldToColumnMapping['一级检查项名称'], $row)->getValue();
$checkName1 = !empty($checkName1) ? trim($checkName1) : '';
$checkName2 = (string) $sheet->getCellByColumnAndRow($fieldToColumnMapping['二级检查项名称'], $row)->getValue();
$checkName2 = !empty($checkName2) ? trim($checkName2) : '';
$checkName3 = (string) $sheet->getCellByColumnAndRow($fieldToColumnMapping['三级检查项名称'], $row)->getValue();
$checkName3 = !empty($checkName3) ? trim($checkName3) : '';
$checkName4 = (string) $sheet->getCellByColumnAndRow($fieldToColumnMapping['四级检查项名称'], $row)->getValue();
$checkName4 = !empty($checkName4) ? trim($checkName4) : '';
$checkName5 = (string) $sheet->getCellByColumnAndRow($fieldToColumnMapping['五级检查项名称'], $row)->getValue();
$checkName5 = !empty($checkName5) ? trim($checkName5) : '';
$classProblem = (string) $sheet->getCellByColumnAndRow($fieldToColumnMapping['问题描述'], $row)->getValue();
$classProblem = !empty($classProblem) ? trim($classProblem) : '';
$fitRange = (string) $sheet->getCellByColumnAndRow($fieldToColumnMapping['适用装修标准'], $row)->getValue();
$internalRepairDeadline = (string) $sheet->getCellByColumnAndRow($fieldToColumnMapping['内验期整改时限'], $row)->getValue();
$deliveryRepairDeadline = (string) $sheet->getCellByColumnAndRow($fieldToColumnMapping['交付期整改时限'], $row)->getValue();
$isRemarkRequired = (string) $sheet->getCellByColumnAndRow($fieldToColumnMapping['补充说明是否必填'], $row)->getValue();
if (empty($checkName1) && empty($checkName2) && empty($checkName3) && empty($checkName4) && empty($checkName5) && empty($classProblem) && empty($fitRange) && empty($internalRepairDeadline) && empty($deliveryRepairDeadline) && empty($isRemarkRequired)) {
// 忽略空行
continue;
}
if ((empty($checkName1) && empty($checkName2) && empty($checkName3) && empty($checkName4) && empty($checkName5))
|| ((!empty($checkName2) && empty($checkName1)) || (!empty($checkName3) && empty($checkName2)) || (!empty($checkName4) && empty($checkName3)) || (!empty($checkName5) && empty($checkName4)))) {
// 检查项为空(包含该级检查项不为空但是上级检查项为空的情况)
$nullClassRow[] = $row;
}
if (strpos($checkName1, '-') !== false || strpos($checkName2, '-') !== false || strpos($checkName3, '-') !== false || strpos($checkName4, '-') !== false || strpos($checkName5, '-') !== false) {
// 检查项名称有特殊字符
$illegalCharacterRow[] = $row;
}
$strCheckName = $checkName1;
if (!empty($checkName2)) {
$strCheckName .= '-' . $checkName2;
}
if (!empty($checkName3)) {
$strCheckName .= '-' . $checkName3;
}
if (!empty($checkName4)) {
$strCheckName .= '-' . $checkName4;
}
if (!empty($checkName5)) {
$strCheckName .= '-' . $checkName5;
}
if (!empty($strCheckName) && !in_array($strCheckName, $this->_checkName) && !in_array($row, $illegalCharacterRow)) {
// 问题描述中的检查项不存在
$notExistClassRow[] = $row;
}
if (!empty($strCheckName) && $this->isNotEndClass($strCheckName)) {
// 问题描述对应的检查项非末级检查项
$notEndClassRow[] = $row;
}
if (empty($classProblem)) {
// 问题描述为空
$nullClassProblemRow[] = $row;
}
if (!empty($classProblem) && mb_strlen($classProblem, 'utf-8') > 30) {
// 问题描述长度超过特定字符
$exceedMaxLengthClassProblemRow[] = $row;
}
$importData = [
'check_name1' => $checkName1,
'check_name2' => $checkName2,
'check_name3' => $checkName3,
'check_name4' => $checkName4,
'check_name5' => $checkName5,
'class_problem' => $classProblem
];
$repeatRow = $this->getRepeatClassProblemRow($legalImportRows, $importData);
if ($repeatRow) {
if (!in_array($repeatRow, $classProblemRepeatRow)) {
// 记录重复的行数
$classProblemRepeatRow[] = $repeatRow;
}
// 问题描述重复
$classProblemRepeatRow[] = $row;
}
if (empty($fitRange)) {
// 适用装修标准为空
$nullFitmentStandardRow[] = $row;
}
if (!empty($fitRange) && !in_array($fitRange, ['毛坯', '精装', '毛坯&精装'])) {
// 装修标准不合法
$illegalFitmentStandardRow[] = $row;
}
if (!empty($fitRange) && !empty($this->_classRange[$strCheckName])) {
// 问题描述的适用装修标准不超过检查项的适用装修标准
$checkItemFitRange = $this->_classRange[$strCheckName];
if (($checkItemFitRange == '精装' && strpos($fitRange, '毛坯') !== false) || ($checkItemFitRange == '毛坯' && strpos($fitRange, '精装') !== false)) {
$exceedClassFitmentStandardRow[] = $row;
}
}
if ($internalRepairDeadline == '') {
// 内验整改期限为空
$nullInternalRepairRow[] = $row;
} elseif (!preg_match('/^[1-9][0-9]*$/', $internalRepairDeadline)) {
// 内验整改期限不合法
$illegalInternalRepairRow[] = $row;
}
if ($deliveryRepairDeadline == '') {
// 交付整改期限为空
$nullDeliveryRepairRow[] = $row;
} elseif (!preg_match('/^[1-9][0-9]*$/', $deliveryRepairDeadline)) {
// 交付整改期限不合法
$illegalDeliveryRepairRow[] = $row;
}
if (empty($isRemarkRequired)) {
// 补充说明是否必填为空
$nullIsRemarkRequiredRow[] = $row;
}
if (!empty($isRemarkRequired) && !in_array($isRemarkRequired, ['是', '否'])) {
// 补充说明是否必填不合法
$illegalIsRemarkRequiredRow[] = $row;
}
$legalImportRows[$row] = [
'check_name1' => $checkName1,
'check_name2' => $checkName2,
'check_name3' => $checkName3,
'check_name4' => $checkName4,
'check_name5' => $checkName5,
'full_name' => $strCheckName,
'class_problem' => $classProblem,
'is_for_rough' => strpos($fitRange, '毛坯') !== false ? 1 : 0,
'is_for_decorated' => strpos($fitRange, '精装') !== false ? 1 : 0,
'internal_repair_deadline' => $internalRepairDeadline,
'delivery_repair_deadline' => $deliveryRepairDeadline,
'is_remark_required' => $isRemarkRequired == '是' ? 1 : 0
];
}
if (!empty($nullClassRow)) {
$failRow['检查项为空'] = $nullClassRow;
}
if (!empty($illegalCharacterRow)) {
$failRow['检查项名称包含非法字符'] = $illegalCharacterRow;
}
if (!empty($notExistClassRow)) {
$failRow['检查项不存在'] = $notExistClassRow;
}
if (!empty($notEndClassRow)) {
$failRow['非末级检查项不能添加问题描述'] = $notEndClassRow;
}
if (!empty($nullClassProblemRow)) {
$failRow['问题描述为空'] = $nullClassProblemRow;
}
if (!empty($exceedMaxLengthClassProblemRow)) {
$failRow['问题描述超过30个字'] = $exceedMaxLengthClassProblemRow;
}
if (!empty($classProblemRepeatRow)) {
$failRow['检查项+问题描述重名'] = $classProblemRepeatRow;
}
if (!empty($nullFitmentStandardRow)) {
$failRow['适用装修标准为空'] = $nullFitmentStandardRow;
}
if (!empty($illegalFitmentStandardRow)) {
$failRow['适用装修标准不合法'] = $illegalFitmentStandardRow;
}
if (!empty($exceedClassFitmentStandardRow)) {
$failRow['问题描述的适用装修标准与检查项的不匹配'] = $exceedClassFitmentStandardRow;
}
if (!empty($nullInternalRepairRow)) {
$failRow['内验期整改时限为空'] = $nullInternalRepairRow;
}
if (!empty($illegalInternalRepairRow)) {
$failRow['内验期整改时限不合法'] = $illegalInternalRepairRow;
}
if (!empty($nullDeliveryRepairRow)) {
$failRow['交付期整改时限为空'] = $nullDeliveryRepairRow;
}
if (!empty($illegalDeliveryRepairRow)) {
$failRow['交付期整改时限不合法'] = $illegalDeliveryRepairRow;
}
if (!empty($nullIsRemarkRequiredRow)) {
$failRow['补充说明是否必填为空'] = $nullIsRemarkRequiredRow;
}
if (!empty($illegalIsRemarkRequiredRow)) {
$failRow['补充说明是否必填不合法'] = $illegalIsRemarkRequiredRow;
}
return ['failRow' => $failRow, 'legalImportRows' => $legalImportRows];
} catch (InfoException $iex) {
throw $iex;
}
}