php教程 php手册 ThinkPHP框架安全实现分析

ThinkPHP框架安全实现分析

Jun 07, 2016 am 11:35 AM

ThinkPHP框架安全实现分析
ThinkPHP框架是国内比较流行的PHP框架之一,虽然跟国外的那些个框架没法比,但优点在于,恩,中文手册很全面。最近研究SQL注入,之前用TP框架的时候因为底层提供了安全功能,在开发过程中没怎么考虑安全问题。

一、不得不说的I函数

TP系统提供了I函数用于输入变量的过滤。整个函数主体的意义就是获取各种格式的数据,比如I('get.')、I('post.id'),然后用htmlspecialchars函数(默认情况下)进行处理。

如果需要采用其他的方法进行安全过滤,可以从/ThinkPHP/Conf/convention.php中设置:'DEFAULT_FILTER'    => 'strip_tags',<br> //也可以设置多种过滤方法<br> 'DEFAULT_FILTER'    => 'strip_tags,stripslashes',从/ThinkPHP/Common/functions.php中可以找到I函数,源码如下:/**<br>  * 获取输入参数 支持过滤和默认值<br>  * 使用方法:<br>  * <code><br>  * I('id',0); 获取id参数 自动判断get或者post<br>  * I('post.name','','htmlspecialchars'); 获取$_POST['name']<br>  * I('get.'); 获取$_GET<br>  * 
 * @param string $name 变量的名称 支持指定类型
 * @param mixed $default 不存在的时候默认值
 * @param mixed $filter 参数过滤方法
 * @param mixed $datas 要获取的额外数据源
 * @return mixed
 */
function I($name,$default='',$filter=null,$datas=null) {
  static $_PUT  =  null;
  if(strpos($name,'/')){ // 指定修饰符
    list($name,$type)   =  explode('/',$name,2);
  }elseif(C('VAR_AUTO_STRING')){ // 默认强制转换为字符串
    $type  =  's';
  }
  /*根据$name的格式获取数据:先判断参数的来源,然后再根据各种格式获取数据*/
  if(strpos($name,'.')) {list($method,$name) =  explode('.',$name,2);} // 指定参数来源
  else{$method =  'param';}//设定为自动获取
  switch(strtolower($method)) {
    case 'get'   :  $input =& $_GET;break;
    case 'post'  :  $input =& $_POST;break;
    case 'put'   :  /*此处省略*/
    case 'param'  :  /*此处省略*/
    case 'path'  :  /*此处省略*/
  }
  /*对获取的数据进行过滤*/
  if('' // 获取全部变量
    $data    =  $input;
    $filters  =  isset($filter)?$filter:C('DEFAULT_FILTER');
    if($filters) {
      if(is_string($filters)){$filters  =  explode(',',$filters);} //为多种过滤方法提供支持
      foreach($filters as $filter){
        $data  =  array_map_recursive($filter,$data); //循环过滤
      }
    }
  }elseif(isset($input[$name])) { // 取值操作
    $data    =  $input[$name];
    $filters  =  isset($filter)?$filter:C('DEFAULT_FILTER');
    if($filters) {   /*对参数进行过滤,支持正则表达式验证*/
      /*此处省略*/
    }
    if(!empty($type)){ //如果设定了强制转换类型
      switch(strtolower($type)){
        case 'a': $data = (array)$data;break;  // 数组 
        case 'd': $data = (int)$data;break;  // 数字 
        case 'f': $data = (float)$data;break;  // 浮点  
        case 'b': $data = (boolean)$data;break;  // 布尔
        case 's':  // 字符串
        default:$data  =  (string)$data;
      }
    }
  }else{ // 变量默认值
    $data    =  isset($default)?$default:null;
  }
  is_array($data) && array_walk_recursive($data,'think_filter'); //如果$data是数组,那么用think_filter对数组过滤
  return $data;
}恩,函数基本分成三块:

第一块,获取各种格式的数据。

第二块,对获取的数据进行循环编码,不管是二维数组还是三维数组。

第三块,也就是倒数第二行,调用了think_filter对数据进行了最后一步的神秘处理。

让我们先来追踪一下think_filter函数://1536行 版本3.2.3最新添加<br> function think_filter(&$value){// 过滤查询特殊字符  <br>   if(preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)$/i',$value)){    <br>     $value .= ' ';  <br>   }<br> }这个函数很简单,一眼就可以看出来,在一些特定的关键字后面加个空格。

但是这个叫think_filter的函数,仅仅加了一个空格,到底起到了什么过滤的作用?

我们都知道重要的逻辑验证,如验证是否已登录,用户是否能购买某商品等,必须从服务器端验证,如果从前端验证的话,就很容易被绕过。同一个道理,在程序中,in/exp一类的逻辑结构,最好也是由服务器端来控制。

当从传递到服务器端的数据是这样:id[0]=in&id[1]=1,2,3,如果没有think_filter函数的话,会被解析成下表中的1,也就会被当成服务器端逻辑解析。但如果变成如下表2的样子,因为多了一个空格,无法被匹配解析,也就避免了漏洞。$data['id']=array('in'=>'1,2,3') <br> //经过think_filter过滤之后,会变成介个样子:<br> $data['id']=array('in '=>'1,2,3')二、SQL注入

相关的文件为:/ThinkPHP/Library/Think/Db.class.php(在3.2.3中改为了/ThinkPHP/Library/Think/Db/Driver.class.php) 以及 /ThinkPHP/Library/Think/Model.class.php。其中Model.class.php文件提供的是curd直接调用的函数,直接对外提供接口,Driver.class.php中的函数被curd操作间接调用。//此次主要分析如下语句:<br> M('user')->where($map)->find();  //在user表根据$map的条件检索出一条数据大概说一下TP的处理思路:

首先将Model类实例化为一个user对象,然后调用user对象中的where函数处理$map,也就是将$map进行一些格式化处理之后赋值给user对象的成员变量$options(如果有其他的连贯操作,也是先赋值给user对象的对应成员变量,而不是直接拼接SQL语句,所以在写连贯操作的时候,无需像拼接SQL语句一样考虑关键字的顺序),接下来调用find函数。

find函数会调用底层的,也就是driver类中的函数――select来获取数据。到了select函数,又是另一个故事了。

select除了要处理curd操作,还要处理pdo绑定,我们这里只关心curd操作,所以在select中调用了buildSelectSql,处理分页信息,并且调用parseSQL按照既定的顺序把SQL语句组装进去。

虽然拼接SQL语句所需要的参数已经全部放在成员变量里了,但是格式不统一,有可能是字符串格式的,有可能是数组格式的,还有可能是TP提供的特殊查询格式,比如:$data['id']=array('gt','100');,所以在拼接之前,还要调用各自的处理函数,进行统一的格式化处理。我选取了parseWhere这个复杂的典型来分析。

关于安全方面的,如果用I函数来获取数据,那么会默认进行htmlspecialchars处理,能有效抵御xss攻击,但是对SQL注入没有多大影响。

在过滤有关SQL注入有关的符号的时候,TP的做法很机智:先是按正常逻辑处理用户的输入,然后在最接近最终的SQL语句的parseWhere、parseHaving等函数中进行安全处理。这样的顺序避免了在处理的过程中出现注入。

当然处理的方法是最普通的addslashes,根据死在沙滩上的前浪们说,推荐使用mysql_real_escape_string来进行过滤,但是这个函数只能在已经连接了数据库的前提下使用。

感觉TP在这个地方可以做一下优化,毕竟走到这一步的都是连接了数据库的。

恩,接下来,分析开始:

先说几个Model对象中的成员变量:// 主键名称<br> protected $pk   = 'id';<br> // 字段信息<br> protected $fields = array();<br> // 数据信息<br> protected $data  = array();<br> // 查询表达式参数<br> protected $options = array();<br> // 链操作方法列表<br> protected $methods = array('strict','order','alias','having','group','lock','distinct','auto','filter','validate','result','token','index','force')<br> 接下来分析where函数:<br> public function where($where,$parse=null){<br>   //如果非数组格式,即where('id=%d&name=%s',array($id,$name)),对传递到字符串中的数组调用mysql里的escapeString进行处理<br>   if(!is_null($parse) && is_string($where)) { <br>     if(!is_array($parse)){ $parse = func_get_args();array_shift($parse);}<br>     $parse = array_map(array($this->db,'escapeString'),$parse);<br>     $where = vsprintf($where,$parse); //vsprintf() 函数把格式化字符串写入变量中<br>   }elseif(is_object($where)){<br>     $where =  get_object_vars($where);<br>   }<br>   if(is_string($where) && '' != $where){<br>     $map  =  array();<br>     $map['_string']  =  $where;<br>     $where =  $map;<br>   }   <br>   //将$where赋值给$this->where<br>   if(isset($this->options['where'])){     <br>     $this->options['where'] =  array_merge($this->options['where'],$where);<br>   }else{<br>     $this->options['where'] =  $where;<br>   }<br>    <br>   return $this;<br> }where函数的逻辑很简单,如果是where('id=%d&name=%s',array($id,$name))这种格式,那就对$id,$name变量调用mysql里的escapeString进行处理。escapeString的实质是调用mysql_real_escape_string、addslashes等函数进行处理。

最后将分析之后的数组赋值到Model对象的成员函数――$where中供下一步处理。

再分析find函数://model.class.php  行721  版本3.2.3<br> public function find($options=array()) {<br>   if(is_numeric($options) || is_string($options)){ /*如果传递过来的数据是字符串,不是数组*/<br>     $where[$this->getPk()] =  $options;<br>     $options        =  array();<br>     $options['where']    =  $where; /*提取出查询条件,并赋值*/<br>   }<br>   // 根据主键查找记录<br>   $pk = $this->getPk();<br>   if (is_array($options) && (count($options) > 0) && is_array($pk)) {<br>     /*构造复合主键查询条件,此处省略*/<br>   }<br>   $options['limit']  =  1;                 // 总是查找一条记录<br>   $options      =  $this->_parseOptions($options);   // 分析表达式<br>   if(isset($options['cache'])){<br>     /*缓存查询,此处省略*/<br>   }<br>   $resultSet = $this->db->select($options);<br>   if(false === $resultSet){  return false;}<br>   if(empty($resultSet)) {  return null; }      // 查询结果为空    <br>   if(is_string($resultSet)){  return $resultSet;}  //查询结果为字符串<br>   // 读取数据后的处理,此处省略简写<br>   $this->data = $this->_read_data($resultSet[0]);<br>   return $this->data;<br> }$Pk为主键,$options为表达式参数,本函数的作用就是完善成员变量――options数组,然后调用db层的select函数查询数据,处理后返回数据。

跟进_parseOptions函数:protected function _parseOptions($options=array()) { //分析表达式<br>   if(is_array($options)){<br>     $options = array_merge($this->options,$options);<br>   }<br>   /*获取表名,此处省略*/<br>   /*添加数据表别名,此处省略*/<br>   $options['model']    =  $this->name;// 记录操作的模型名称<br>   /*对数组查询条件进行字段类型检查,如果在合理范围内,就进行过滤处理;否则抛出异常或者删除掉对应字段*/<br>   if(isset($options['where']) && is_array($options['where']) && !empty($fields) && !isset($options['join'])){<br>     foreach ($options['where'] as $key=>$val){<br>       $key = trim($key);<br>       if(in_array($key,$fields,true)){  //如果$key在数据库字段内,过滤以及强制类型转换之<br>         if(is_scalar($val)) { <br>         /*is_scalar 检测是否为标量。标量是指integer、float、string、boolean的变量,array则不是标量。*/     <br>           $this->_parseType($options['where'],$key);<br>         }<br>       }elseif(!is_numeric($key) && '_' != substr($key,0,1) && false === strpos($key,'.') && false === strpos($key,'(') && false === strpos($key,'|') && false === strpos($key,'&')){<br>         // 如果$key不是数字且第一个字符不是_,不存在.(|&等特殊字符<br>         if(!empty($this->options['strict'])){  //如果是strict模式,抛出异常<br>           E(L('_ERROR_QUERY_EXPRESS_').':['.$key.'=>'.$val.']');<br>         }  <br>         unset($options['where'][$key]); //unset掉对应的值<br>       }<br>     }<br>   } <br>   $this->options =  array();      // 查询过后清空sql表达式组装 避免影响下次查询<br>   $this->_options_filter($options);    // 表达式过滤<br>   return $options;<br> }本函数的结构大概是,先获取了表名,模型名,再对数据进行处理:如果该条数据不在数据库字段内,则做出异常处理或者删除掉该条数据。否则,进行_parseType处理。parseType此处不再跟进,功能为:数据类型检测,强制类型转换包括int,float,bool型的三种数据。

函数运行到此处,就该把处理好的数据传到db层的select函数里了。此时的查询条件$options中的int,float,bool类型的数据都已经进行了强制类型转换,where()函数中的字符串(非数组格式的查询)也进行了addslashes等处理。

继续追踪到select函数,就到了driver对象中了,还是先列举几个有用的成员变量:// 数据库表达式<br> protected $exp = array('eq'=>'=','neq'=>'','gt'=>'>','egt'=>'>=','lt'=>'''NOT LIKE','like'=>'LIKE','in'=>'IN','notin'=>'NOT IN','not in'=>'NOT IN','between'=>'BETWEEN','not between'=>'NOT BETWEEN','notbetween'=>'NOT BETWEEN');<br> // 查询表达式<br> protected $selectSql = 'SELECT%DISTINCT% %FIELD% FROM %TABLE%%FORCE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT% %UNION%%LOCK%%COMMENT%';<br> // 当前SQL指令<br> protected $queryStr  = '';<br> // 参数绑定<br> protected $bind     =  array();<br> select函数:<br> public function select($options=array()) {<br>   $this->model =  $options['model'];<br>   $this->parseBind(!empty($options['bind'])?$options['bind']:array());<br>   $sql  = $this->buildSelectSql($options);<br>   $result  = $this->query($sql,!empty($options['fetch_sql']) ? true : false);<br>   return $result;<br> }版本3.2.3经过改进之后,select精简了不少。parseBind函数是绑定参数,用于pdo查询,此处不表。

buildSelectSql()函数及其后续调用如下:public function buildSelectSql($options=array()) {<br>   if(isset($options['page'])) {<br>     /*页码计算及处理,此处省略*/<br>   }<br>   $sql =  $this->parseSql($this->selectSql,$options);<br>   return $sql;<br> }<br> /* 替换SQL语句中表达式*/<br> public function parseSql($sql,$options=array()){<br>   $sql  = str_replace(<br>     array('%TABLE%','%DISTINCT%','%FIELD%','%JOIN%','%WHERE%','%GROUP%','%HAVING%','%ORDER%','%LIMIT%','%UNION%','%LOCK%','%COMMENT%','%FORCE%'),<br>     array(<br>       $this->parseTable($options['table']),<br>       $this->parseDistinct(isset($options['distinct'])?$options['distinct']:false),<br>       $this->parseField(!empty($options['field'])?$options['field']:'*'),<br>       $this->parseJoin(!empty($options['join'])?$options['join']:''),<br>       $this->parseWhere(!empty($options['where'])?$options['where']:''),<br>       $this->parseGroup(!empty($options['group'])?$options['group']:''),<br>       $this->parseHaving(!empty($options['having'])?$options['having']:''),<br>       $this->parseOrder(!empty($options['order'])?$options['order']:''),<br>       $this->parseLimit(!empty($options['limit'])?$options['limit']:''),<br>       $this->parseUnion(!empty($options['union'])?$options['union']:''),<br>       $this->parseLock(isset($options['lock'])?$options['lock']:false),<br>       $this->parseComment(!empty($options['comment'])?$options['comment']:''),<br>       $this->parseForce(!empty($options['force'])?$options['force']:'')<br>     ),$sql);<br>   return $sql;<br> }可以看到,在parseSql中用正则表达式拼接了sql语句,但并没有直接的去处理各种插叙你的数据格式,而是在解析变量的过程中调用了多个函数,此处拿parseWhere举例子。protected function parseWhere($where) {<br>   $whereStr = '';<br>   if(is_string($where)) {   // 直接使用字符串条件<br>     $whereStr = $where;<br>   }<br>   else{            // 使用数组表达式<br>     /*设定逻辑规则,如or and xor等,默认为and,此处省略*/<br>     $operate=' AND ';<br>     /*解析特殊格式的表达式并且格式化输出*/<br>     foreach ($where as $key=>$val){<br>       if(0===strpos($key,'_')) {  // 解析特殊条件表达式<br>         $whereStr  .= $this->parseThinkWhere($key,$val);<br>       }<br>       else{            // 查询字段的安全过滤<br>         $multi = is_array($val) && isset($val['_multi']); //判断是否有复合查询<br>         $key  = trim($key);<br>         /*处理字段中包含的| &逻辑*/<br>         if(strpos($key,'|')) { // 支持 name|title|nickname 方式定义查询字段<br>           /*将|换成or,并格式化输出,此处省略*/<br>         }<br>         elseif(strpos($key,'&')){<br>           /*将&换成and,并格式化输出,此处省略*/<br>         }<br>         else{<br>           $whereStr .= $this->parseWhereItem($this->parseKey($key),$val);<br>         }<br>       }<br>       $whereStr .= $operate;<br>     }<br>     $whereStr = substr($whereStr,0,-strlen($operate));<br>   }<br>   return empty($whereStr)?'':' WHERE '.$whereStr;<br> }<br> // where子单元分析<br> protected function parseWhereItem($key,$val) {<br>   $whereStr = '';<br>   if(is_array($val)){<br>     if(is_string($val[0])){<br>       $exp  =  strtolower($val[0]);<br>       //如果是$map['id']=array('eq',100)一类的结构,那么解析成数据库可执行格式<br>       if(preg_match('/^(eq|neq|gt|egt|lt|elt)$/',$exp)){<br>         $whereStr .= $key.' '.$this->exp[$exp].' '.$this->parseValue($val[1]);<br>       }<br>       //如果是模糊查找格式<br>       elseif(preg_match('/^(notlike|like)$/',$exp)){// 模糊查找,$map['name']=array('like','thinkphp%');<br>         if(is_array($val[1])) { //解析格式如下:$map['b'] =array('notlike',array('%thinkphp%','%tp'),'AND');<br>           $likeLogic =  isset($val[2])?strtoupper($val[2]):'OR';  //如果没有设定逻辑结构,则默认为OR<br>           if(in_array($likeLogic,array('AND','OR','XOR'))){<br>             /* 根据逻辑结构,组合语句,此处省略*/<br>             $whereStr .= '('.implode(' '.$likeLogic.' ',$like).')';             <br>           }<br>         }<br>         else{<br>           $whereStr .= $key.' '.$this->exp[$exp].' '.$this->parseValue($val[1]);<br>         }<br>       }elseif('bind' == $exp ){ // 使用表达式,pdo数据绑定<br>         $whereStr .= $key.' = :'.$val[1];<br>       }elseif('exp' == $exp ){ // 使用表达式 $map['id'] = array('exp',' IN (1,3,8) ');<br>         $whereStr .= $key.' '.$val[1];<br>       }elseif(preg_match('/^(notin|not in|in)$/',$exp)){ //IN运算 $map['id'] = array('not in','1,5,8');<br>         if(isset($val[2]) && 'exp'==$val[2]){<br>           $whereStr .= $key.' '.$this->exp[$exp].' '.$val[1];<br>         }else{<br>           if(is_string($val[1])) {<br>              $val[1] = explode(',',$val[1]);<br>           }<br>           $zone   =  implode(',',$this->parseValue($val[1]));<br>           $whereStr .= $key.' '.$this->exp[$exp].' ('.$zone.')';<br>         }<br>       }elseif(preg_match('/^(notbetween|not between|between)$/',$exp)){ //BETWEEN运算<br>         $data = is_string($val[1])? explode(',',$val[1]):$val[1];<br>         $whereStr .= $key.' '.$this->exp[$exp].' '.$this->parseValue($data[0]).' AND '.$this->parseValue($data[1]);<br>       }else{ //否则抛出异常<br>         E(L('_EXPRESS_ERROR_').':'.$val[0]);<br>       }<br>     }<br>     else{  //解析如:$map['status&score&title'] =array('1',array('gt','0'),'thinkphp','_multi'=>true);<br>       $count = count($val);<br>       $rule = isset($val[$count-1]) ? (is_array($val[$count-1]) ? strtoupper($val[$count-1][0]) : strtoupper($val[$count-1]) ) : '' ; <br>       if(in_array($rule,array('AND','OR','XOR'))){<br>         $count = $count -1;<br>       }else{<br>         $rule  = 'AND';<br>       }<br>       for($i=0;$i         $data = is_array($val[$i])?$val[$i][1]:$val[$i];<br>         if('exp'==strtolower($val[$i][0])) {<br>           $whereStr .= $key.' '.$data.' '.$rule.' ';<br>         }else{<br>           $whereStr .= $this->parseWhereItem($key,$val[$i]).' '.$rule.' ';<br>         }<br>       }<br>       $whereStr = '( '.substr($whereStr,0,-4).' )';<br>     }<br>   }<br>   else {<br>     //对字符串类型字段采用模糊匹配<br>     $likeFields  =  $this->config['db_like_fields'];<br>     if($likeFields && preg_match('/^('.$likeFields.')$/i',$key)) {<br>       $whereStr .= $key.' LIKE '.$this->parseValue('%'.$val.'%');<br>     }else {<br>       $whereStr .= $key.' = '.$this->parseValue($val);<br>     }<br>   }<br>   return $whereStr;<br> }<br> protected function parseThinkWhere($key,$val) {   //解析特殊格式的条件<br>   $whereStr  = '';<br>   switch($key) {<br>     case '_string':$whereStr = $val;break;                 // 字符串模式查询条件<br>     case '_complex':$whereStr = substr($this->parseWhere($val),6);break;  // 复合查询条件<br>     case '_query':// 字符串模式查询条件<br>       /*处理逻辑结构,并且格式化输出字符串,此处省略*/<br>   }<br>   return '( '.$whereStr.' )';<br> }上面的两个函数很长,我们再精简一些来看:parseWhere首先判断查询数据是不是字符串,如果是字符串,直接返回字符串,否则,遍历查询条件的数组,挨个解析。

由于TP支持_string,_complex之类的特殊查询,调用了parseThinkWhere来处理,对于普通查询,就调用了parseWhereItem。

在各自的处理过程中,都调用了parseValue,追踪一下,其实是用了addslashes来过滤,虽然addslashes在非utf-8编码的页面中会造成宽字节注入,但是如果页面和数据库均正确编码的话,还是没什么问题的。

您可能感兴趣的文章:
ThinkPHP php 框架学习笔记
PHP隐形一句话后门,和ThinkPHP框架加密码程序(base64_decode)
ThinkPHP框架实现session跨域问题的解决方法
对于ThinkPHP框架早期版本的一个SQL注入漏洞详细分析
ThinkPHP框架任意代码执行漏洞的利用及其修复方法
ThinkPHP框架设计及扩展详解
thinkphp3.2中Lite文件替换框架入口文件或应用入口文件的方法
使用Thinkphp框架开发移动端接口
ThinkPHP开发框架函数详解:C方法

AD:真正免费,域名+虚机+企业邮箱=0元

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

인기 기사

R.E.P.O. 에너지 결정과 그들이하는 일 (노란색 크리스탈)
1 몇 달 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 최고의 그래픽 설정
1 몇 달 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 아무도들을 수없는 경우 오디오를 수정하는 방법
1 몇 달 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 채팅 명령 및 사용 방법
1 몇 달 전 By 尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

Python 프로그래밍을 위한 소개 코드 예제에 대해 알아보세요. Python 프로그래밍을 위한 소개 코드 예제에 대해 알아보세요. Jan 04, 2024 am 10:50 AM

소개 코드 예제를 통해 Python 프로그래밍에 대해 알아보세요. Python은 배우기 쉽지만 강력한 프로그래밍 언어입니다. 초보자의 경우 Python 프로그래밍의 입문 코드 예제를 이해하는 것이 매우 중요합니다. 이 문서에서는 빠르게 시작하는 데 도움이 되는 몇 가지 구체적인 코드 예제를 제공합니다. Print HelloWorldprint("HelloWorld") 이것은 Python에서 가장 간단한 코드 예제입니다. print() 함수는 지정된 내용을 출력하는 데 사용됩니다.

실제 사용되는 PHP 변수: 실제 사용 사례 10가지 실제 사용되는 PHP 변수: 실제 사용 사례 10가지 Feb 19, 2024 pm 03:00 PM

PHP 변수는 프로그램 런타임 중에 값을 저장하며 동적 및 대화형 웹 애플리케이션을 구축하는 데 중요합니다. 이 기사에서는 PHP 변수를 심층적으로 살펴보고 10가지 실제 예제를 통해 실제로 작동하는 모습을 보여줍니다. 1. 사용자 입력 저장 $username=$_POST["username"];$passWord=$_POST["password"] 이 예에서는 양식 제출에서 사용자 이름과 비밀번호를 추출하여 추가 처리를 위해 변수에 저장합니다. 2. 구성 값 $database_host="localhost";$database_username="username";$database_pa를 설정합니다.

Go 언어 프로그래밍 예제: 웹 개발의 코드 예제 Go 언어 프로그래밍 예제: 웹 개발의 코드 예제 Mar 04, 2024 pm 04:54 PM

"Go 언어 프로그래밍 예제: 웹 개발의 코드 예제" 인터넷의 급속한 발전과 함께 웹 개발은 다양한 산업에서 없어서는 안 될 부분이 되었습니다. 강력한 기능과 뛰어난 성능을 갖춘 프로그래밍 언어인 Go 언어는 웹 개발 개발자들의 선호도가 점점 높아지고 있습니다. 이 기사에서는 특정 코드 예제를 통해 웹 개발에 Go 언어를 사용하는 방법을 소개하므로 독자는 Go 언어를 더 잘 이해하고 사용하여 자신의 웹 애플리케이션을 구축할 수 있습니다. 1. 간단한 HTTP 서버 먼저 시작해 보겠습니다.

Java는 간단한 버블 정렬 코드를 구현합니다. Java는 간단한 버블 정렬 코드를 구현합니다. Jan 30, 2024 am 09:34 AM

Java 버블 정렬의 가장 간단한 코드 예제 버블 정렬은 인접한 요소의 비교와 교환을 통해 순서가 지정된 순서로 정렬되는 순서를 점진적으로 조정하는 일반적인 정렬 알고리즘입니다. 다음은 버블 정렬을 구현하는 방법을 보여주는 간단한 Java 코드 예제입니다. publicclassBubbleSort{publicstaticvoidbubbleSort(int[]arr){int

초보자부터 숙련자까지: Go 언어에서 일반적으로 사용되는 데이터 구조의 코드 구현 초보자부터 숙련자까지: Go 언어에서 일반적으로 사용되는 데이터 구조의 코드 구현 Mar 04, 2024 pm 03:09 PM

제목: 초보자에서 숙달까지: Go 언어에서 일반적으로 사용되는 데이터 구조의 코드 구현 데이터 구조는 프로그래밍에서 중요한 역할을 하며 프로그래밍의 기초입니다. Go 언어에는 일반적으로 사용되는 데이터 구조가 많이 있으며 이러한 데이터 구조의 구현을 마스터하는 것은 좋은 프로그래머가 되는 데 중요합니다. 이 기사에서는 Go 언어에서 일반적으로 사용되는 데이터 구조를 소개하고 독자가 이러한 데이터 구조에 익숙해지는 데 도움이 되는 해당 코드 예제를 제공합니다. 1. 배열(Array) 배열은 기본 자료구조로, 같은 형태의 그룹

Huawei Cloud Edge Computing 상호 연결 가이드: 인터페이스를 빠르게 구현하기 위한 Java 코드 예제 Huawei Cloud Edge Computing 상호 연결 가이드: 인터페이스를 빠르게 구현하기 위한 Java 코드 예제 Jul 05, 2023 pm 09:57 PM

Huawei 클라우드 엣지 컴퓨팅 상호 연결 가이드: 인터페이스를 신속하게 구현하기 위한 Java 코드 샘플 IoT 기술의 급속한 발전과 엣지 컴퓨팅의 부상으로 점점 더 많은 기업이 엣지 컴퓨팅 애플리케이션에 관심을 갖기 시작했습니다. Huawei Cloud는 엣지 컴퓨팅 서비스를 제공하여 기업에 매우 안정적인 컴퓨팅 리소스와 편리한 개발 환경을 제공하여 엣지 컴퓨팅 애플리케이션을 보다 쉽게 ​​구현할 수 있도록 합니다. 이 기사에서는 Java 코드를 통해 Huawei Cloud 엣지 컴퓨팅 인터페이스를 빠르게 구현하는 방법을 소개합니다. 먼저 개발 환경을 준비해야 합니다. Java Development Kit가 설치되어 있는지 확인하십시오(

PHP를 사용하여 재고 관리 시스템에서 재고 관리 기능 코드를 작성하는 방법 PHP를 사용하여 재고 관리 시스템에서 재고 관리 기능 코드를 작성하는 방법 Aug 06, 2023 pm 04:49 PM

PHP를 사용하여 재고 관리 시스템에서 재고 관리 기능 코드를 작성하는 방법 재고 관리는 많은 기업에서 없어서는 안될 부분입니다. 창고가 여러 개인 회사의 경우 재고 관리 기능이 특히 중요합니다. 재고를 적절하게 관리하고 추적함으로써 회사는 서로 다른 창고 간에 재고를 할당하고, 운영 비용을 최적화하며, 협업 효율성을 향상시킬 수 있습니다. 이 기사에서는 PHP를 사용하여 재고 창고 관리 기능을 위한 코드를 작성하는 방법을 소개하고 관련 코드 예제를 제공합니다. 1. 재고 창고 관리 기능에 대한 코드 작성을 시작하기 전에 데이터베이스를 구축하십시오.

지침 및 예: Java에서 선택 정렬 알고리즘을 구현하는 방법 알아보기 지침 및 예: Java에서 선택 정렬 알고리즘을 구현하는 방법 알아보기 Feb 18, 2024 am 10:52 AM

Java 선택 정렬 방법 코드 작성 가이드 및 예제 선택 정렬은 정렬되지 않은 요소 중에서 가장 작은(또는 가장 큰) 요소를 매번 선택하고 모든 요소가 정렬될 때까지 교환하는 간단하고 직관적인 정렬 알고리즘입니다. 이 기사에서는 선택 정렬을 위한 코드 작성 가이드를 제공하고 특정 Java 샘플 코드를 첨부합니다. 알고리즘 원리 선택 정렬의 기본 원리는 정렬할 배열을 정렬된 부분과 정렬되지 않은 부분의 두 부분으로 나누는 것입니다. 매번 정렬되지 않은 부분에서 가장 작은(또는 가장 큰) 요소가 선택되어 정렬된 부분의 끝에 배치됩니다. 위의 내용을 반복하세요.

See all articles