eftec / chaosmachineone
PHP 的混沌机器
Requires
- php: >=7.2
- ext-ctype: *
- ext-pdo: *
- composer/installers: ^2.2.0
- eftec/minilang: ^2.27
- eftec/pdoone: 3.12.2
Requires (Dev)
- phpunit/phpunit: ^9.0
README
PHP 的受控随机生成器数据。该库的目的是帮助生成和填充随机值到内存、Mysql、Sql Server 和 Oracle,同时我们可以添加一些倾向/偏差,使得这些值不是完全随机的。
目录
- PHP 的 ChaosMachineOne
- 目标是什么?
- 字段
- gen
- 范围函数(数字)
- 固定函数(数字)
- 数组与文本
- 其他特性
- 文件特性
- 数据库
- table($table, $conditions,$prefix='origin_')
- update($table,$indexcolumn,$indexvalue,$updatecolumn1,$updatevalue1...)
- insert($storeCache=false,$echoProgress=null,$continueOnError=false,$maxRetry=3)
- setInsert($continueOnError=false,$maxRetry=3)
- setArrayFromDBQuery($name,$query,$probability=[1],$queryParam=null)
- setArrayFromDBTable($name,$table,$column,$probability=[1])
- 示例
目标是什么?
有时我们希望为数据库生成受控且一致的数据。因此,这个库试图创建一个有序的混沌。
尽管有生成随机值的库(如 fzaninotto/Faker),但这个库的目标是填充表格,使得数据随机但可信/有趋势。
让我们做一个练习。我们想为一个新的系统(销售)生成随机值
如果我们生成随机值,图表会看起来像
->gen('when _index<200 then idtable.value=random(-10,10,0.2)')
为什么这么随机?因为它们是随机值(sic)。
所以,它们是对的,但它们看起来不真实,因为没有趋势或信息的自然流动,它只是静态噪音。
那么,让我们用正弦(例如,让我们说销售有一个周期)生成相同的值
->gen('when _index<200 then idtable.add=sin(0,0,10,30)')
图表有趋势,但太可预测了。所以,让我们添加所有因素。
->gen('when _index<200 then idtable.value=random(-10,10,0.2) and idtable.add=sin(0,0,10,30)')
虽然这个图表远非真实,但它不是太随机,并且有一个趋势。
字段
field($name,$type,$special='database',$initValue=0,$min=PHP_INT_MIN,$max=PHP_INT_MAX)
字段是我们的值。它们可以是数字、日期和字符串。
-
字段可以有速度和加速度。然而,如果您手动更改值,则忽略速度和加速度。
-
"field.value=X" 设置字段的值。
-
"field.speed=X" 设置字段的速度。
-
"field.accel=X" 用于设置场加速。如果手动设置速度,则忽略加速。
-
"field.stop=X" 将速度和加速设置为0,并将值设置为X。
-
速度和加速在执行时(run() 方法)进行评估。
示例
"set field.value=20" // it sets the value of the field to 20
"set field.speed=3" // it sets the speed of the field by 3. The field increases the value every cycle by 3
"set field.accel=1" // it sets the acceleration of the field by 1. The field increases the speed every cycle by 1
"set field.value=30 and field.speed=1" // the value is always 30, no matter the speed
$this->gen('when _index<40 then idtable.accel=1'); // the acceleration is 1 until index=40 $this->gen('when _index<60 then idtable.accel=-1'); // the acceleration is -1 until index=60 $this->gen('when _index<100 then idtable.accel=-1'); // the acceleration is -1 until index=100
gen
它使用Minilang语法生成一个值。
语法如下
when logic and/or logic2 and/or logic3 then setvalue1 , setvalue2 , setvalue3
minilang
保留变量
示例
myvar = 是一个变量
当始终设置 myvar.value=null
当 _index<20 设置 myvar.value=false
限制。
- 不允许使用括号(除非它定义了一个函数)。
-
-
a1=(20+30) 不允许,然而 a1=20,a1+30 是允许的
-
- 语法分为两部分,一部分是逻辑(when),另一部分是设置(then)。
- 条件按顺序评估。如果一个条件满足,则停止其他评估。
-
-
$this->gen('when a1=1 then b1=1'); $this->gen('when a1<10 then b1=10'); // if a1=1 then this expression is not evaluated.
-
逻辑
使用“与”或“或”分隔条件。
不允许在逻辑中使用多个运算符。a1=20+30 不允许。
范围函数(数字)
生成一系列值的函数
ramp($fromX, $toX, $fromY, $toY)
它生成斜坡值(线性值)
->gen('when _index<200 then idtable.value=ramp(0,100,10,1000)')
->gen('when _index<200 then idtable.value=ramp(0,100,1000,10)')
log($startX,$startY,$scale=1)
它生成对数值
->gen('when _index<200 then idtable.value=log(0,0,100)')
exp($startX,$startY,$scale=1)
它生成指数值。尺度是Y的除法
->gen('when _index<200 then idtable.value=exp(0,0,10)')
sin($startX,$startY,$speed=1,$scale=1,$angle=null)
它生成正弦值。角度
- startX 是X的位置
- startY 是Y的位置(你可以上下移动值)
- 速度是角度的速度。
- 例如 sin(0,0,1,1),每个 _index 值相当于一个度。
- 例如 sin(0,0,2,1),每个 _index 值相当于 x2 度。
- scale 是图表的Y轴尺度。
- angle,如果未设置,则使用当前索引x速度(以度为单位)计算。如果不设置,则用于确定正弦值的角
$this->gen('when _index<200 then idtable.value=sin(0,0,1,1)'); // speed (horizontal) is 1.
$this->gen('when _index<200 then idtable.value=sin(0,0,10,1)'); // speed (horizontal) is 10
atan($centerX,$startY,$speed=1,$scale=1)
它生成反正切值
$this->gen('when _index<200 then idtable.value=atan(50,0,20,10)');
parabola($centerX,$startY,$scaleA=1,$scaleB=1,$scale=1)
它生成抛物线。可以通过将scaleA设置为负值来反转抛物线
$this->gen('when _index<200 then idtable.value=parabola(50,0,1,1,1)');
$this->gen('when _index<200 then idtable.value=parabola(50,0,-1,1,1)');
$this->gen('when _index<200 then idtable.value=parabola(50,2500,-1,1,1)');
bell($centerX, $startY, $sigma=1, $scaleY=1)
它生成钟形值,sigma是钟的“宽度”。
$this->gen('when _index<=360 then idtable.value=bell(50,0,30,100)');
$this->gen('when _index<=360 then idtable.value=bell(50,0,1,100)');
固定函数(数字)
生成单个值的函数
randomprop(...$args)
它通过使用不同的比例或概率生成随机值。
randomprop(1,2,3,30,50,20)
- 有30%的概率是1
- 有50%的概率是2
- 有20%的概率是3
示例
$this->gen('when _index<200 then idtable.value=randomprop(1,2,3,30,50,20)'); // 30% chance of 1, 50% chance of 2, 20% change of 3 $this->gen('when always then idtable.value=randomprop(idtable,null,1,1)); // there is a 50% chance the value is keep and 50% chance the value is null
random($from,$to,$jump=1,...$probs)
它从$from生成一个随机值到$to。
random(1,10) // 1,2,3,4,5,6,7,8,9,10 random(1,10,2) // 1,3,5,7,9
$this->gen('when _index<200 then idtable.value=random(-10,10,0.2)');
可选地,你可以为每个段添加一个概率。
random(0,100,1,10,20,70)
- 有10%的概率随机值在0到33之间
- 有20%的概率随机值在34到66之间
- 有70%的概率随机值在67到100之间
idtable.value=random(0,200,1,80,10,10) // 值趋势保持在底部
idtable.value=random(0,200,1,10,80,10) // 值趋势保持在中心
idtable.value=random(0,200,1,10,10,80) // 值趋势保持在顶部
你还可以使用值名称的下一个名称
示例
$this->gen('when _index<200 then idtable.value=random(1,100,1,"fakebell")');
field.speed=xxxx
它设置场的速度。如果场有速度,则每次交互时值会增加。
示例
$this->gen('when date.weekday<6 set counter.speed=1'); // the speed during the working days is 1 $this->gen('when date.weekday>=6 set counter.speed=2'); // the speed during the weekends is 2 // counter=1 if counter initially is 1, and the variable date changes 1 day per iteraction the: // monday: counter 2 // thuesday: counter 3 // wednesday: counter 4 // thursday: counter 5 // friday: counter 6 // saturday: counter 8 // sunday: counter 10 // monday: counter 11
field.accel=xxxx
它设置场的加速。如果场有加速度,则每次迭代时速度会增加。
示例
$this->gen('when always set counter.acceleration=1'); // counter.value: 1 counter.velocity: 0 // counter.value: 2 counter.velocity: 1 // counter.value: 4 counter.velocity: 2 // counter.value: 7 counter.velocity: 3
field.value=xxxx
它设置场的值
示例
$this->gen('when always set var1=1 and var2="hello world" and var3=2+3');
field.getvalue
它返回场的值
示例
$this->gen('when always set var1=var2.getvalue');
field.valueabs
它将场的值转换为绝对值(总是正值)
示例
$this->gen('when always set var1=var2.getvalueabs');
field.day , field.month , field.year, field.hour, field.minute, field.weekday
它返回当前日期部分(日、月、年、小时、分钟和星期)。
它用于日期时间类型的字段。它不是用于设置值,而是仅用于获取值,无论值如何。星期:1=星期一,7=星期日
月份:1 = 一月,12=十二月
示例
$this->gen('when field.day=1 set var1="first day of the month"');
field.stop=xxxx
设置或返回字段的值,同时将速度和加速度标记为零。
field.add=xxxx
向字段添加一个值。如果字段是datetime类型,则可以添加"hour"、"minute"和"day"。
$this->gen('when always set field.add=20'); // add 20 to the field $this->gen('when always set field.add="5h"'); // adds 5 hours to the field. 5m = 5 minutes, 5d = 5 days
field.concat=xxxx
将一个值连接到字段。
this->gen('when always set field1.value="hello" and field2.value="world"'); this->gen('when always set field1.concat=field2'); // field1: "helloworld"
field.skip=xxx
跳过值到下一个值。用于日期。
field.skip='day' // it skips to the next day (it start at 00:00)
field.skip='month' // it skips to the next month (it start at the first day of the month 00:00)
field.skip='monday' // (or the name of the day), it skips to the next monday (00:00 hours)
field.skip='hour' // it skips to the next hour (minute 00)
$this->gen('when field.month=3 set field.skip="month"'); // we skip march.
数组与文本
->setArray('arrayname',[])
设置一个数组。如果数组是关联的,则值是选择概率。
$this->setArray('arrayname',['a','b','c']) // it sets an array with 3 values with the same chances.
$this->setArray('arrayname',['a'=>80,'b'=>10,'c'=>10]) // it sets an array with 3 values with the changes // of a(80%),b(10%) and c(10%)
注意:数组和变量共享相同的内存空间,因此如果我们有一个变量和数组具有相同的值,那么其中一个将被覆盖。
randomarray("arrayname",'field'=null)
返回由setArray()声明的数组内的随机行。如果数组是关联的,则根据其概率返回值。
如果数组是对象的列表,则返回字段的值。
$this->gen('when always set name.value=randomarray("arrayname")');
示例
$this->setArray('namearr',['john','bob','peter']); $this->gen('when always set name.value=randomarray("namearr")'); // example: "john"
->setFormat('formatName',[])
设置一个格式(模板)以合并不同的数组。
此函数与randomformat()同时使用。
数组标记为{{数组名称}}。如果数组未定义,则返回字段的值。
如果数组是关联的,则根据其概率返回值。
$this->setFormat('maleNameFormats',['{{namearr}} {{lastnamearr}}','Dr.{{namearr}} {{lastnamearr}}'])
$this->setFormat('maleNameFormats',['{{namearr}} {{lastnamearr}}'=>80,'Dr.{{namearr}} {{lastnamearr}}'=>20]) //probability of 80% and 20%
示例
$this->setArray('namearr',['john','bob','peter']); $this->setArray('lastnamearr',['doe','smith','johnsons']); $this->setFormat('maleNameFormats',['{{namearr}} {{lastnamearr}}','Dr.{{namearr}} {{lastnamearr}}']); // "john doe" or "dr. john doe"
randomformat($nameFormat)
使用格式和不同数组混合生成随机文本。
$this->gen('when always set fullname.value=randomformat("nameFormat")');
示例
$this->setArray('namearr',['john','bob','peter']); $this->setArray('lastnamearr',['doe','smith','johnsons']); $this->setFormat('maleNameFormats',['{{namearr}} {{lastnamearr}}','Dr.{{namearr}} {{lastnamearr}}']); // "john doe" or "dr. john doe" $this->gen('when always set fullname.value=randomformat("nameFormat")'); // fullname could be "john doe", "dr. john doe", "bob doe" or others.
randomtext($starting,$arrayName,$paragraph,$wordMinimum,$wordMaximum)
使用名为$arrayName的数组生成随机文本。文本可以以默认文本开头。
如果$paragraph不是0,则可以生成段落(换行符)
如果arrayName为空,则使用包含"lorem ipsum"单词的数组。
$this->gen('when always then text.value=randomtext("Lorem ipsum dolor","loremIpsumArray",1,4,30)')
randommask($mask,$arrayName='')
根据掩码生成文本。
- # = 一个(可选)随机数字
- 0 = 一个随机数字。
- u = 一个大写字母
- l = 一个小写字母
- v = 一个大写或小写字母。
- w = 一个随机字母(大写或小写)或数字。
- X = 一个可选的大写字母
- x = 一个可选的小写字母
- ? = 来自数组或格式的随机单词。
- \ = 转义字符,(下一个字符将返回而不进行处理)
randommask("##-00 uu ll \0 - oo (a)","lastName") // 其中lastName是一个数组
示例
$this->gen('when always then text.value=randommask("ulllll####000")');
其他特性
end
结束当前运行。当您想停止插入值时很有用。
示例
$this->gen('when counter>100 then end()');
omit
跳过当前循环。当您想跳过一些值时很有用。
示例
$this->gen('when counter>20 and counter<30 then omit()'); // values where counter are in between 20 and 30, are omited.
见跳过以省略日期
文件特性
arrayFromFolder()
读取一个文件夹并返回一个文件数组。读取不是递归的,并且可以通过扩展名进行过滤。
$filesWithoutExtension=$chaos->arrayFromFolder($localfolder,'jpg',false);
Script function destionationArray.copyfilefrom()=originArray.getvalue
从一个位置复制文件到另一个位置。
$this->gen('when always set ImageDestination.copyfilefrom=ImageSource.getvalue')
数据库
此库允许直接与数据库交互,读取和插入信息。
例如,让我们看看下面的练习
$db=new PdoOne("mysql","localhost","root","abc.123","chaosdb"); // connect to mysql $db->open(); // it opens the connection $chaos = new ChaosMachineOne(); $chaos->debugMode=true; $chaos->table('SOMETABLE', 1000) // we will work with the table SOMETABLE ->setDb($db) // we indicates to our library to use the connection to the database ->field('fixedid','int','local',5) // we created a field (local), it will not be stored in the database ->field('idcustomer', 'int','identity', 0, 0, 1000) // we created a field (database), however it is identity so it will not be stored in the database ->field('name', 'string', 'database', '', 0, 45) // this field will be stored in the database //... ->setInsert() ->run(); // finally we insert the new values (1000 values) : insert into SOMETABLE (name) values(...);
table($table, $conditions,$prefix='origin_')
设置工作表和要处理值的数量。
如果您正在使用数据库,则表用于确定值将插入的位置。
如果您没有使用数据库,则表仅作参考。
- $table = 表名
- $conditions (int) = 它可以指示要生成的行数。
- $conditions (array) = 它指示要迭代的值。
- $conditions (string) = 它指示用作值“来源”的表(或查询)。您可以使用查询的内连接,但查询必须返回唯一的列。
- $prefix (string) = 它设置一个前缀值。
$chaos->table('SOMETABLE', 1000) // insert 1000 rows into SOMETABLE $chaos->table('SOMETABLE', ['a','b','c'],'letter') // it will iterate between a,b,c, each letter could be obtained in the fields called letter $chaos->table('SOMETABLE', 'ORIGINTABLE') // insert "n" rows into SOMETABLE. "n" depends in the number of rows of ORIGINTABLE. $chaos->table('SOMETABLE', 'select * from ORIGINTABLE') // insert "n" rows into SOMETABLE. "n" depends in the number of rows of ORIGINTABLE.
查询值存储在称为:origin_列名的字段中
$chaos->table('SOMETABLE', 'select col1,col2 from ORIGINTABLE','i_'); // it will generate the fields i_col1 and i_col2 with the values of the origin table, that you can read and process.
update($table,$indexcolumn,$indexvalue,$updatecolumn1,$updatevalue1...)
更新表的简单行。您可以更新最多3个值。
示例
$this->update('table1','id',20,'col','hello world') // update table1 set col='hello world' where id=20 $this->update('table1','id',20,'col','hello','col2','world','col3','hi') // update table1 set col='hello',col2='world',col3='hi' where id=20 $this->gen('when field.value>10 then update("table1","id",field.value,"col","hello world")'); // if field>10 then update table1 set col='hello world' where id=field
insert($storeCache=false,$echoProgress=null,$continueOnError=false,$maxRetry=3)
向数据库中插入随机值。
注意:此方法已弃用。请改用setInsert()
- $storecache : 如果为true,则插入值并将其值存储到内存中。
- $echoProgress : (printf格式)如果不为空,则显示进度(echo)
- $continueOnError = 如果为真,则插入失败时继续。
- $maxRetry = 重试次数(如果插入失败)
setInsert($continueOnError=false,$maxRetry=3)
它将值插入数据库。此操作在执行run()命令时执行。
- $continueOnError = 如果为真,则插入失败时继续。
- $maxRetry = 重试次数(如果插入失败)
$this->table('SOMETABLE', 1000); $this->field('name', 'string', 'database', '', 0, 45) // this field will be stored in the database // here we assign the value to name $this->setInsert(); // insert into sometable(name) values(... ); we repeat it 1000 times. $this->run();
setArrayFromDBQuery($name,$query,$probability=[1],$queryParam=null)
它使用查询设置一个数组
- $name = 数组名称
- $query = 原始值的源查询。它必须返回单列。
- $probability = 概率数组。如果[1],则表示每个值100%,[80,20]表示数组前半部分的值为80%,后半部分为20%。
- $queryParam = (可选)查询参数。
->setArrayFromDBQuery('namemale','select first_name from sakila.actor') ->setArrayFromDBQuery('lastname','select last_name from sakila.actor where actor_id={{fixedid}}',[1])
注意:数组和变量共享相同的内存空间。
setArrayFromDBTable($name,$table,$column,$probability=[1])
它使用表设置一个数组
- $name = 数组名称
- $query = 原始值的源查询。它必须返回单列。
- $probability = 概率数组。如果[1],则表示每个值100%,[80,20]表示数组前半部分的值为80%,后半部分为20%。
- $queryParam = (可选)查询参数。
$this->setArrayFromDBTable('namemale','sakila.actor','first_name'); $this->setArrayFromDBTable('lastname','sakila.actor','first_name',[1]);
注意:数组和变量共享相同的内存空间,所以如果您使用相同的名称,则可以覆盖其中一个。
示例
连接到 Sql Server
$chaos=new ChaosMachineOne(); $chaos->setDb(new PdoOne('sqlsrv','localhost\sqlexpress','sa','password','testdb')); $chaos->getDb()->logLevel=3; // for debug purpose $chaos->getDb()->connect();
连接到 Mysql
$chaos=new ChaosMachineOne(); $chaos->setDb(new PdoOne('mysql','localhost','root','password','testdb')); $chaos->getDb()->logLevel=3; // for debug purpose $chaos->getDb()->connect();
基于表的生成代码
假设我们在数据库中有一个名为table1的表。
// $chaos is an instance of ChaosMachineOne and it must be connected to the database. echo "<pre>"; echo $chaos->generateCode('table1'); // the table to generate the code. // echo $chaos->generateCode('*'); // you also could generate code of all tables at once. echo "</pre>";
后面的代码将显示以下值
$chaos->table('table1', 1000) ->setDb($db) ->field('id', 'int','identity', 0) ->field('number', 'int','database') ->isnullable(true) ->field('text', 'string','database','',0,50) ->isnullable(true) ->gen('when always set number.value=random(1,100,1,10,10)') ->gen('when always set text.value=random(0,50)') ->setInsert(true) ->showTable(['id','number','text'],true) ->run(true);
因此,您不需要从头开始。
此代码生成器也理解外键。
插入 1000 个随机值
$chaos->table('table1', 1000) ->field('id', 'int','identity', 0) ->field('number', 'int','database') ->isnullable(true) ->field('text', 'string','database','',0,50) ->isnullable(true) ->gen('when always set number.value=random(1,100,1,10,10)') // later you can change the values. ->gen('when always set text.value=random(0,50)') ->setInsert(true) // if you don't want to insert, then you could remove this line ->showTable(['id','number','text'],true) // if you don't want to show the values, then you could remove this line. ->run(true);
注意:此代码的部分可以自动生成。
更新表的所有行
$chaos->table('table1', 'table1') // the first table is used for insert, since we are not inserting, then this value is not used. The second table is the table where the values will be read. ->field('text', 'string','database','',0,50)->isnullable(true) ->gen('when always set text.value="hello world"') ->gen('when always set update("table1","id",origin_id,"text",text.value)') // here whe update the same table. the prefix "origin_" is added automatically //->setInsert(true) we don't want to insert. ->showTable(['text'],true) ->run(true);
版本
- 1.13 2022-09-17
- 为代码添加了类型提示。
- 清理了代码。
- 更新了要求。现在,此库需要PHP 7.2及以上版本。
- 更新了库依赖。
- 1.12 2022-01-03
- 在构造函数中添加了随机种子。默认情况下,随机种子使用处理器的微秒数生成。
- 修复了一些操作的精度问题
- 随机现在允许任何值数组。
- 1.11 2021-12-01
- 添加了end()、omit()和update()方法
- 更新了sin()方法。现在可以指定当前角度。
- 修复了日期处理过程中的某些错误。
- 1.10 2021-11-22
- 一些清理。
- 1.9 2020-08-12
- 更新了数据库问题(无限循环)。
- 更新了代码。
- 更新了依赖。
- 1.8 2020-04-09
- 方法insert()已弃用。请改用setInsert()。
- 新方法setInsert()。
- 1.7 2020-04-09 更新依赖项 eftec/pdoone (1.19 => 1.28.1)
- 1.6 添加了showTable()和show()函数
- 1.5 更新MiniLang 2.12 -> 2.14
- 1.4 我们可以同时运行 ->insert(true)->show()。Insert(true)将保留值(因此我们可以显示它而不重新计算)
- 1.3 现在它可以复制文件。新方法arrayFromFolder()读取文件夹中的所有文件。
脚本方法field.copyfilefrom=origin从源(文件数组)复制。
现在,randommask (?) 通配符与数组和格式一起工作。 - 1.2 一些清理。
- 1.1 现在Minilib是一个外部库
- 1.0 第一个开源版本
许可证
双许可协议
LGPL-V3和商业许可。
版权所有:Jorge Patricio Castro Castillo (2018)