March 2022: wsurvey.utilsP.php

wsurvey.utilsP.php provides a set of useful php functions.

Example usage:
      require_once('wsurvey.utilsP.php');
      use wSurvey\utilsP as utils ;
      ...
      $cnumber=utils\custom_number_format(151253);


Alphabetical list of functions:

 array_msort($array, $cols,$sortType)                    :: sort an associative array on a column
 custom_number_format($n, $precision,$ismult,$pre,$post) ::  convert a number to xxK  or xxM, or xxG
 extractCols($vv,$vars)                                  ::  extract columns from an array-of-arrays
 extract1Cols($vv,$vname,$adefault)                      ::  extract 1 column from an array of arrays
 extractRequestVar($varname,$default,$errorMess)         ::  extract request variable
 extractRequestVars($searchfor)                          ::  extract request variables
 extToMimetype($aext)                                    :: match extension to mime type
 formatMilliseconds($milliseconds,$nodec)                ::  convert milliseconds to hh:mm:ss.xxx
 formatScientific($someFloat)                            :: convert to scientific notation
 get_dir_size($directory,$doSubs=1)                      :: size of a directory
 get_subdir_list($directory,$fullpath=1)                 :: array of all subdirectories under $directory
 getBytes($val)                                          :: convert xxK, xxM, xxG, xxP to integer.
 getWords($astring)                                      ::  retrieve words in a string
 getwordsN( $str, $wordCount = 100000 )
 html_element_disabler($text,$aclass,$atitle,$badtags)    :: remove "active (hence possibly dangerous) elements, and disable links
 mb_truncString($avar,$mxlen,$adef)                       :: truncate a string to be maxlen characters. Multibyte safe (won't truncate middle of a multbye character
 nthValue($aval,$useword)                                 :: convert number to 'nth'
 NumHash::encrypt($danum,$keyString);                     :: encypt and decrypt integer values
 NumHash::decrypt($encryptedNumber,$keyString)
 obsfucate($astring)                                      :: simple string obsfucator
 secondsToDays($seconds,$useDec,$acc)                     :: format seconds to a dd hh:mm or dd.ee format
 unobsfucate($obsString)                                  :: simple string unobsfucator


Descriptions

 ::::::::: array_msort  ::       sort an associative array on a column

 array_msort($array, $cols,$sortType)

     $newArray=array_msort($array,$colSortInstructions,$sortType)

   where
      $array: the associative array to sort
      $colSortInstructions: an associative array congaining instructions on how to sort $array -- which columns to sort on first, in what order
                              Each element in this array is  'colName'=>sortOrder
                              sortOrder is either SORT_ASC or SORT_DESC (php constants, so do NOT preced with a $)
      $sortType: optional. The type of sort (string, numeric, etc)
           This can be one of: SORT_REGULAR, SORT_NUMERIC, SORT_STRING, SORT_LOCALE_STRING, SORT_NATURAL, SORT_FLAG_CASE
           SORT_FLAG_CASE can be OR with SORT_STRING or SORT_NATURAL (using |)
      If not specified, SORT_REGULAR is used

  Returns sorted array. Thus, $array is NOT changed.

  Example (assumes $arr1 had an 'id1' and 'id2' colum, perhaps other columns also
       $newarrray=array_msort($arr1, array('id1'=>SORT_DESC, 'id2'=>SORT_ASC'],SORT_STRING);

  Sorted in order of indices. So first sort on "id1", and then within blocks of data with the same value of 'id1', sort by 'id2'

  See php array_multisort for description of the sortOrder and sortType options


  ::::::::: custom_number_format   ::  convert a nunber to k,m

       $nString= custom_number_format($n, $precision,$ismult,$pre',$post)

         where

     $n : number to convert
     $precision: decimal precision, default is 1. (i.e.; if $n=1265.21, $precision=1 yields `1,265.2`)
                 Or, a 4 element array of precision values (see below)
     $ismult: set the switch points. Default is 1.
                  I.e.; if ismult=10, then switch at >10k, >10m, and >10G (so 1312 becomes 1,312 while 13124 becomes 13.1K
     $pre: add after number ,before K,M,G
     $post :add after number, and after K,M,G

    Add commas to numeric part. Default 1 decimal points of accuracy; switch to K,M,B at >1000, >1million, >1 billion

   Note that if specified $pre and $post  are added even for small numbers
      ie: pre=' * ', post=' bytes', then custom_number_format(123456,1,1,' * ',' bytes') yields "123.4 * k bytes"
      
   Specifying precision based on size of value

     Instead of a scalar, $precision can be a 4 element array.
     Each element is used to specify the precision based on the size of the value
        [0] : precision for values < 1,000 * ismult
        [1] : precision for values < 1,000,000*ismult
        [2] : precision for values < 100,000,000*ismult
        [3] : precision for values >= 100,000,000*ismult

        Examples:
           custom_number_format(6311,0,10 )  --> 6,311
           custom_number_format(15811,0,10 )  --> 16k
           custom_number_format(15811,1,10 )  --> 15.8k
           custom_number_format(6311,[0,1,1,],10 )  --> 6,311
           custom_number_format(15811,[0,1,1,2],10 )  --> 15.8k
           
        Note that if there are < 4 elements in $precision, or an element is non-numeric, a value of 0 is used (for the missing or bad elements)

 ::::::::::  extractCols   ::     extract columns from an array-of-arrays

     $newvv=extractCols($vv,$vars)

           where

     $vv : array or arrays
     $vars : case sensitive, space delimited list of columns to extract

    Return each column as a row
    If only one column requested ($vars contains just one word), return as a simple array
    If more than one, return as an associative array, with each row  (indexed with the column name)
    containing contents of the column (as an indexed array)

    Example:
    If $vv is an array of arrays with the following values:
      row  NAME   AGE  WEIGHT
      1   Joe      51   156
      2   Susan    61  122
      3   Bill      33  184
      4   Bob      12   93

  $foo=extractCols($vv,'AGE');
     would return a 4 element array: $foo[0]=51  $foo[1]=61   $foo[2]=33   $foo[3]=12

  foo=extractCols($vv,'AGE NAME');
    would return a 2 element associative array, each element being an array:
       $foo['AGE'][0]=51  $foo['AGE'][1]=61   $foo['AGE'][2]=33   $foo['AGE'][3]=12
       $foo['NAME'][0]='Joe'  $foo['NAME'][1]='Susan'   $foo['NAME'][2]='Bill'  $foo['NAME'][3]='Bob'

 Notes:
   * If a variable does NOT exist in a row (say, the associative arrays don't always contain the same set of indices),
     that value is a null.
     Thus, asking for a non-existent variable yields an array of nulls
   * If no variables specified, return empty array.
   * If $vv is empty, return empty array


 ::::::::: extract1Col    ::   extract 1 column from an array of arrays

      $vcol=extract1Cols($vv,$vname,$adefault)

         where

      $vv : array of arrays
      $vname: column to extract
      $adefault : value to use if a rows does not have a $vname element. Derault is null.

   An indexed array, containing the values of $vname for each row of $vcol, is returned.


 :::::::::   extractRequestVar  ::    extract request variable

      $avalue=extractRequestVar($varname,$default,$errorMess)

          where

      $varname : name of request field to get. Can be a space delimited list of names (first one found is returned)

      $default : value to return if no match to $varname

      $errorMess : optional. If not '',  return an error message if $varname not found.
                 Special value: if '1', checks for  overly large file upload

 :::::::::   extractRequestVars  ::    extract request variables whose case-insensitve name starts with a prefix string

    $res=extractRequestVars($searchfor)

       where

     $searchfor:    the "prefix" to look for -- all request variables with that prefix will be read.

  Returns a N row array, each row has  NAME, MATCH, and VALUE
     name: full name of request var
     match: part after the searchFor
     value : value of request var

  Example:
     If the request string:  v_11=101&v_13=1671&foo=help
     and: $vv=extractRequestVars('V_');
     Then $vv will have 2 rows (N=2) with values:
        [0][NAME]='V_11', [0][MATCH]='11', [0]['VALUE']=101
        [1][NAME]='V_13', [1][MATCH]='13'  [1]['VALUE']=167

  If no match , an empty array is returned


   :::::::::   extToMimetype  ::    match extension to mimetype.

    $amimeType=extToMimeType($aext);
    
       where
       
     $aext = a file extension.  Leading '.' are stripped.

    This is not always dependable, but most of the time is accurate.
    
    Example:
      $useFile="myPicture.jpg";
      $ext=pathinfo($useFile,PATHINFO_EXTENSION)   ;
      $useMime=extToMimetype($ext);

      will return: 'image/jpeg'

   This is based on https://gist.github.com/raphael-riel/1253986

 :::::::::  formatMilliseconds  ::  convert milliseconds to hh:mm:ss.xxx

    $timestr=formatMilliseconds($milliseconds,$nodec)

         where

     $milliseconds : number of milliseconds
     $ndec :  controls display of fractional seconds.
           0 : do not display .xxx
           1 : display .xxx
           2 : srop the ':ss.xxx' (so only display minutes)

     By default: convert milliseconds to hh:mm:ss.xxx. $ndec controls how to display the ss.xxx compoment.

 :::::::::  formatScientific  ::  convert real number to scientific notation

    $sciNumber= formatScientific($someFloat)

    Example:
       $sciNumber= formatScientific(34217728.12) yields '  '13.4217728e7'
 
 :::::::::   get_dir_size                       :: size of a directory

    $dirSize=get_dir_size($directory,$doSubs=1)

    where
      $directory: string -- directory to examing
      $doSubs : Optional (default =1). Include subdirectories. If 0, only measure size of files in directory (ignore subdirectdories)

:::::::::::  getCurrentUrl        :: return the url of the current script.
     
     $aurl=getCurrentUrl(noQueryString)
     
     where:
        noQueryString: 1 or 0 (1 is the default). If 1, do NOT include the current query string (the ?xxx)
     
     This will appropriately deal with http: or https:, and will include the port number if necessary.
     
     Example:
       If the current script is run via url of :  http://myServer.org/funStuff/oldJokes.php?num=51
     Then
       $aurl=getCurrentUrl();
     will return
        http://myServer.org/funStuff/oldJokes.php
     and
       $aurl=getCurrentUrl();
     will return
        http://myServer.org/funStuff/oldJokes.php?num=51

    This is based on: https://stackoverflow.com/questions/6768793/get-the-full-url-in-php


:::::::::::  get_subdir_list

   $subdirList=get_subdir_list($directory,$fullpath=1)

   where

     $directory: fully qualified path
        If not fully qualified (does NOT begin with x:/ or with /), $directory is intereprted as 
        relative to the current directory (getCwd())

    fullpath:0, 1, 2 (default is 1)
       0  = return relative path (relative to directory).
       1 = fullpath (the default)
       2 =  both in a 2 element array {fullpath,relpath]


  Return array with all subdirectories under $directory.
  Note that in all cases, \ are converted to /.
  And subdirectories never start or end with a /

  The 0th element is self.
     [original path, fully qualified original path]
   where original path is the $directory  specified (which might be a relative path)

   If there is NO such original path, a 1 element [0] array is returned.
      If fullpath=0 : [0]=original path (as specified)
      If fullpath=1 : [0]=false
      If fullpath=2  [0]=[original path,false]

 :::::::::   getBytes  ::    convert xxK, xxM, xxG, xxP to integer

    $anInt=getBytes($xxM);

    $xxM is a string of the form: 'nnX' where nn is a number, and X is k,m,g,or t.
    
    This is useful for converting values returned by ini_get() into actual numbers


 :::::::::  getWords and getWordsN ::   retrieve words in a string

      $res=getWords($astring,$docomma=0)

    where:

      $astring = string containing spaced (or other "preg \s" characters, such as CR and LF and TAB) delimited words
      $docomma1 : if 1, treat comma,tabe,s and newlines as word delimiters

    Returns array of the words in $astring. All "spaces" are stripped out

    Note:
       getwordsN($str,$wordCount) 
    will only get up to $wordCount words in the string.


::::::::: html_element_disabler    ::    remove elements, disable links

    $res=html_element_disabler($text,$aclass,$atitle,$badTags0,$whatDo)

        where:

    $text : string containing html.
    $aclass: a string containing the name of a class to all links (that are being  disabled).
             If not specified, or '0', : "htmlDisablerC" is used. Thus: if not specified a ' class="htmlDisablerC" ' is added to all <a> links.
             If '1',  style="border-bottom:blue; color:blue" is used instead of a class
   $aTitle: a title added to disabled links. If not specified, or '0', a "This link has been disabled" is used (pre-pended to any exsiting title)
   $badTags : list of tags to remove. Default is many "possibly active/dangerous" tags. If specified, use a CSV (no spaces). Default, or "1", to use default list
   $whatDo  : what do do: 'both','link','elem' :  'both' (the default)-- do both. 'link' : just disable links.  'elem': just remove elements. Case insenstive, only first char is relevant!

  Remove active tags, and disable  links, in $text (a string containing HTML)

 Returns:   [newText,# links disabled,# elems removed,elements removed by type  as csv]
    The elems removed has syntax:  tag n,tag n,...
    i.e.;  'head 1, object 1, script 3' (in this case # elems removed will be 5)


  Notes:
    * this can can work with  html fragments
    * This uses php's DOMDocument library (it does NOT use regex)
    * removal of <head> can be buggy. If there is any content within <head> .. </head>, but not in some other tag, it may not be removed.
    * For all <a> links: the "href="xxx" is removed (that's why they are disabled).
      But a 'disabled_href' attibute is added, containing the value of href.
      disabled_href can be used by click handlers, to "go get the link anyways" ... where the click handler is assigned to the class
      For example:  $('.htmlDisablerC').on('click',myfunc)
    * Do not use < > and other special html characters in the $atitle (they will be converted to html entities, such as '&lt;'
    * The default list of "bad tags" is:  'script','embed','object','applet','head','style','noframes','frame','noscript'
      This is case insensitive (everything is converted to lower case)
    * Where an element is removed, a comment is inserted that looks like  <!-- style  element  #1 removed (6299) -->
      Where the (nnnn) is the size (in bytes) of the element removed.


 :::::::::  mb_truncString  :: multi-byte safe string truncations

   $avarNew=mb_truncString($avar,$mxlen,$adef)
       where:

      $avar : string to be truncated. Can include multi-byte charactes
      $mxlen : length of string. If longer, will try to truncate to this length
      $adef :  default (if truncation is impossible)

  Truncate string to at most mxlen bytes.
  If this is a standard string, same as substr($avar,0,$mxlen)

  If a multibyte string, will return a string no more than mxlen  -- and this string will NOT break a multibyte string.
  So the actual byte length could be less

  If no such string possible (say, mxlen=2 and the string starts with a 4 byte character, returns adef (default of '');

   The implemention is kind of clunky, so it isn't very fast

 :::::::::  nthValue    ::     convert number to 'nth'

     $anth=nthValue($aval,$useword)

          where

     $aval : numeric value to convert
     $useword: if 1, and aval < 13, return word. Otherwise, return number with suffix.

     Examples:
        nthValue(5,1)   -- returns 'five'
        nthValue(5)     -- returns '5th'

        nthValue(22)    -- returns 22nd (useword is ignored
        nthValue(-1,1)  -- returns '-first'

:::::::::  NumHash::encrypt and NumHash::decrypt   ::  encrypt and decrypt integer values.


      $encryptedNumber=NumHash::encrypt($danum,$keyString);
      $decrtypedNumber=NumHash::decroypt($encryptedNumber,$keyString);
   where :
      $danum is an integer value. If a floating point is used, it will be truncated to an integer.
      $keyString : a string used to encrypt (and decrypt) the string. Should contain some numbers. 9 character max

   This is a mildly secure encryption. It is unlikely to be secure against any kind of sophisticated attack, but it is not easily broken.

   Examples:
     NumHash::encrypt(0,'abcd1234')  ==>   61626364313233342
     NumHash::encrypt(10,'abcd1234')  ==>  58811614546126782
     NumHash::encrypt(-12,'abcd1234')  ==>   -59093089522837439
     NumHash::encrypt(512512,'abcd1234')  ==>   -3253022961431451719

     NumHash::decrypt(61626364313233342 ,'abcd1234')  ==>   0
     NumHash::decrypt(58811614546126782 ,'abcd1234')  ==>  10
     NumHash::decrypt(-59093089522837439,'abcd1234')  ==>   -12
     NumHash::decrypt( -3253022961431451719,'abcd1234')  ==>   512512

     keyString notes:
       * all "z" are converted to #
       * only the first 9 characters are used
       * should contain two or more numeric digits

  Adapted from https://stackoverflow.com/questions/24350891/how-to-encrypt-decrypt-an-integer-in-php/24350928


 :::::::::  obsfucate   ::    simple string obsfucator

     $obsString= obsfucate($astring)

         where

     $astring : string to "obsfucate"

    Simple string obsfucator and unobsfuctor. Works with similar named functions in wsurveyUtils1.js

    This can be used to obsfucate strings sent to javascript (and back again).

    It really is NOT an encryption, but might stop idle snoops (i.e.; those looking at source code of html documents)


 ::::::::: secondsToDays   ::     format seconds to a dd hh:mm or dd.ee format

     $dayString=secondsToDays($seconds,$useDec,$acc)

         where

    $seconds : number of seconds (unix epoch)
    $useDec : Optional: if 1, display time as decimal (fraction of a day)
    $acc : Optional: how to display time (if $useDec not 1)
         1 : use hh
         2  : hh:mm
         otherwise : hh:mm:ss

    Returns string containing # of days and time in day


 :::::::::  unobsfucate  ::  simple string unobsfucator

     $cleanString= nobsfucate($obsString)

         where

     $obsString : string to "unobsfucate" -- say, created using ws_obsfucate (either php or javascript versions)

    Note:  js version
    function ws_obsfucate(astring) {
        var a1=$.base64('encode',astring);
        var a1a=a1.replace(/=/g,'$');
        var a2=rot13(a1a);
        return a2;
    }
    function ws_unobsfucate(astring) {
         var a1=rot13(astring);
         var a1a=a1.replace(/\$/g,'=');
         var a1=$.base64('decode',a1a);
        return a1 ;
    }

 -------

Contact and disclaimer:

  Contact: Daniel Hellerstein. danielh@crosslink.net.
       http://www.wsurvey.org/distrib, or  https://github.com/dHellerstein 

  Disclaimer:

    wsurvey.utilsP is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    wsurvey.utilsP is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    If you did not receive a copy of the GNU General Public License,
    see <http://www.gnu.org/licenses/>.

  Note: wsurvey.utilsP2.php contains other useful functions (as of March 2022, they are in beta).