Getting CSS files based on production or development
One quick one.  

if(!defined("ENVIRONMENT") or ENVIRONMENT == 'development')


If ENVIRONMENT has only two defined values e.g. 'development' and 'production' then use:

if(ENVIRONMENT != 'production')




For the Group loop:

foreach(array_keys($groups) as $css_group_file)


array_keys is not needed:

 foreach($groups as $css_group_file =&gt; $val)


As far as optimization of two loops to one, it does not matter as these two loops would execute in less than a millisecond, I would guess about about 400 microseconds.



I would rather see ENVIRONMENT equal to 0 instead of 'development'  
and 1 instead of 'production'

This is so if it is undefined it will equate to zero with intval().

if (intval(ENVIRONMENT)){}




The following is rather unconventional but it is simple and fast.

Run this only when CSS files needs editing:

$css_files = array(
    array('path' =&gt;'css/jquery.gritter.css', 'media' =&gt; 'all', 'group' =&gt; '1.css'),
    array('path' =&gt;'css/jquery-ui.css', 'media' =&gt; 'all', 'group' =&gt; '1.css'),
    array('path' =&gt;'css/bootstrap-select.css', 'media' =&gt; 'all', 'group' =&gt; '1.css'),
    array('path' =&gt;'css/select2.css', 'media' =&gt; 'all', 'group' =&gt; '1.css'),
    array('path' =&gt;'css/font-awesome.min.css', 'media' =&gt; 'all', 'group' =&gt; '1.css'),
    array('path' =&gt;'css/jquery.loadmask.css', 'media' =&gt; 'all', 'group' =&gt; '1.css'),
    array('path' =&gt;'css/token-input-facebook.css', 'media' =&gt; 'all', 'group' =&gt; '1.css'),
    array('path' =&gt;'css/KeyTips.min.css', 'media' =&gt; 'all', 'group' =&gt; '1.css'),
    array('path' =&gt;'css/bootstrap-colorpicker.min.css', 'media' =&gt; 'all', 'group' =&gt; '2.css'),
    array('path' =&gt;'css/jquery.ml-keyboard.css', 'media' =&gt; 'all', 'group' =&gt; '2.css')
);
if(!defined("ENVIRONMENT") or ENVIRONMENT == 'development'){
   $fp = '/home/user/css/p0.ser','w');   
   fwrite($fp,serialize($css_files));
   fclose($fp);
   $css_files[] = array('path' =&gt;'css/rtl.css', 'media' =&gt; 'all');
   $fp = '/home/user/css/p1.ser','w');   
   fwrite($fp,serialize($css_files));
   fclose($fp);
   $groups = array();
   foreach($css_files as $css){
     $groups[$css['group']] = TRUE;
   }
   foreach(array_keys($groups) as $css_group_file){
        $return[] = array('path' =&gt;"css/$css_group_file", 'media' =&gt; 'all');
   }
   $fp = '/home/user/css/d0.ser','w');   
   fwrite($fp,serialize($return));
   fclose($fp);
   $return[] = array('path' =&gt;'css/rtl.css', 'media' =&gt; 'all');
   $fp = '/home/user/css/d1.ser','w'); 
   fwrite($fp,serialize($return));
   fclose($fp);


New Production Function:

This is based on is_rtl_lang() returning a value of zero or one and

$CI =&amp; get_instance();
$css_file = array(); 
$css_file ['production'][0]) = '/home/user/css/p0.ser';
$css_file ['production'][1]) = '/home/user/css/p1.ser';
$css_file ['development'][0]) = '/home/user/css/d0.ser';
$css_file ['development'][1]) ='/home/user/css/d1.ser' ;
return unserialize(file_get_contents($css_files[ENVIRONMENT][is_rtl_lang()]);   


If these more efficient changes were made:

From: ENVIRONMENT = 'production'; 
To:   ENVIRONMENT = 1 ;

From: ENVIRONMENT = 'development'; 
To:   ENVIRONMENT = 0 ;


Then:

$css_file [1][0]) = '/home/user/css/p0.ser';  // production without RTL
$css_file [1][1]) = '/home/user/css/p1.ser';  // production with RTL
$css_file [0][0]) = '/home/user/css/d0.ser';  // development without RTL
$css_file [0][1]) ='/home/user/css/d1.ser' ;  // development with RTL

unserialize(file_get_contents($css_files[intval(ENVIRONMENT)][is_rtl_lang()]);   


NOTE:  intval() was added to $css_files[ENVIRONMENT]   to become: $css_files[intval(ENVIRONMENT)] 

Where intval(ENVIRONMENT) takes care of the undefined condition.



Better Yet:

Seeing you are using PHP to populate the CSS links, there is an even better method.

Because each CSS file requires a round trip HTTP Request/Response, why not reduce it to one.
This is especially beneficial on a mobile phone Browser.  To conserve battery a smartphone will drop the radio signal between requests.  It can take seconds for the phone to reestablish the radio connection.
This is why it is important that each file loaded by the Browser contain Connection: keep-alive in the Request Header.

Furthermore because the Browser will not begin rendering until all the CSS is loaded, it is advantageous to bring it in with a single HTTP Request.

$css = file_get_contents('/home/user/public_html/css/css/jquery.gritter.css') . "\n";
$css .= file_get_contents('/home/user/public_html/css/css/jquery-ui.css') . "\n";
$css .= file_get_contents('/home/user/public_html/css/css/bootstrap-select.css') . "\n";
$css .= file_get_contents('/home/user/public_html/css/css/select2.css') . "\n";
$css .= file_get_contents('/home/user/public_html/css/css/font-awesome.min.css') . "\n";
$css .= file_get_contents('/home/user/public_html/css/css/jquery.loadmask.css') . "\n";
$css .= file_get_contents('/home/user/public_html/css/css/token-input-facebook.css') . "\n";
$css .= file_get_contents('/home/user/public_html/css/css/KeyTips.min.css') . "\n";
$css .= file_get_contents('/home/user/public_html/css/css/bootstrap-colorpicker.min.css') . "\n";
$css .= file_get_contents('/home/user/public_html/css/css/jquery.ml-keyboard.css') . "\n";
$fp = '/home/user/public_html/css/styles.css';
fwrite($fp,$css);
fclose($fp);