Member Avatar for iamthwee

Hi I am using the latest version of codeigniter but I want to be able to allow the user to easily type in the database, username and password like in the install of wordpress.

What is the best way to do this, do I write directly over the config file? I searched online but the installer on github appears to be for versions below the latest version.

Any tips would be great.

I suggest to save the old config files and then write a new one. Another way of doing this is to write a new XML file for all of the user's settings and parse it as the config file. Similar to the one found in Symfony2's yaml file.

Or the simplest and yet pretty effective is to use the php function called parse_ini_file() which can parse configuration file. Just make sure that the ini files are not accessible by public.

Member Avatar for iamthwee

Thanks I'm going to explore that option tomorrow. I think overwriting the database config file is the best option and using dbforge to create the databases and tables.

yes, that is definitely an excellent option. I wrote installer in the past, similar to the wordpress installer. I will look for the script tomorrow.

Member Avatar for iamthwee

Just created my own installer:

database.php

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
        /*
        | -------------------------------------------------------------------
        | DATABASE CONNECTIVITY SETTINGS
        | -------------------------------------------------------------------
        | This file will contain the settings needed to access your database.
        |
        | For complete instructions please consult the 'Database Connection'
        | page of the User Guide.
        |
        | -------------------------------------------------------------------
        | EXPLANATION OF VARIABLES
        | -------------------------------------------------------------------
        |
        |   ['hostname'] The hostname of your database server.
        |   ['username'] The username used to connect to the database
        |   ['password'] The password used to connect to the database
        |   ['database'] The name of the database you want to connect to
        |   ['dbdriver'] The database type. ie: mysql.  Currently supported:
                         mysql, mysqli, postgre, odbc, mssql, sqlite, oci8
        |   ['dbprefix'] You can add an optional prefix, which will be added
        |                to the table name when using the  Active Record class
        |   ['pconnect'] TRUE/FALSE - Whether to use a persistent connection
        |   ['db_debug'] TRUE/FALSE - Whether database errors should be displayed.
        |   ['cache_on'] TRUE/FALSE - Enables/disables query caching
        |   ['cachedir'] The path to the folder where cache files should be stored
        |   ['char_set'] The character set used in communicating with the database
        |   ['dbcollat'] The character collation used in communicating with the database
        |                NOTE: For MySQL and MySQLi databases, this setting is only used
        |                as a backup if your server is running PHP < 5.2.3 or MySQL < 5.0.7
        |                (and in table creation queries made with DB Forge).
        |                There is an incompatibility in PHP with mysql_real_escape_string() which
        |                can make your site vulnerable to SQL injection if you are using a
        |                multi-byte character set and are running versions lower than these.
        |                Sites using Latin-1 or UTF-8 database character set and collation are unaffected.
        |   ['swap_pre'] A default table prefix that should be swapped with the dbprefix
        |   ['autoinit'] Whether or not to automatically initialize the database.
        |   ['stricton'] TRUE/FALSE - forces 'Strict Mode' connections
        |                           - good for ensuring strict SQL while developing
        |
        | The $active_group variable lets you choose which connection group to
        | make active.  By default there is only one group (the 'default' group).
        |
        | The $active_record variables lets you determine whether or not to load
        | the active record class
        */

        $active_group = 'default';
        $active_record = TRUE;

        $db['default']['hostname'] = '';
        $db['default']['username'] = '';
        $db['default']['password'] = '';
        $db['default']['database'] = '';
        $db['default']['dbdriver'] = 'mysql';
        $db['default']['dbprefix'] = '';
        $db['default']['pconnect'] = TRUE;
        $db['default']['db_debug'] = TRUE;
        $db['default']['cache_on'] = FALSE;
        $db['default']['cachedir'] = '';
        $db['default']['char_set'] = 'utf8';
        $db['default']['dbcollat'] = 'utf8_general_ci';
        $db['default']['swap_pre'] = '';
        $db['default']['autoinit'] = TRUE;
        $db['default']['stricton'] = FALSE;


        /* End of file database.php */
        /* Location: ./application/config/database.php */

controller

public function installer()
    {

        $hostname = $this->input->post('hostname');
        $username = $this->input->post('username');
        $password = $this->input->post('password');
        $database = $this->input->post('database');




        $data = "<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
        /*
        | -------------------------------------------------------------------
        | DATABASE CONNECTIVITY SETTINGS
        | -------------------------------------------------------------------
        | This file will contain the settings needed to access your database.
        |
        | For complete instructions please consult the 'Database Connection'
        | page of the User Guide.
        |
        | -------------------------------------------------------------------
        | EXPLANATION OF VARIABLES
        | -------------------------------------------------------------------
        |
        |   ['hostname'] The hostname of your database server.
        |   ['username'] The username used to connect to the database
        |   ['password'] The password used to connect to the database
        |   ['database'] The name of the database you want to connect to
        |   ['dbdriver'] The database type. ie: mysql.  Currently supported:
                         mysql, mysqli, postgre, odbc, mssql, sqlite, oci8
        |   ['dbprefix'] You can add an optional prefix, which will be added
        |                to the table name when using the  Active Record class
        |   ['pconnect'] TRUE/FALSE - Whether to use a persistent connection
        |   ['db_debug'] TRUE/FALSE - Whether database errors should be displayed.
        |   ['cache_on'] TRUE/FALSE - Enables/disables query caching
        |   ['cachedir'] The path to the folder where cache files should be stored
        |   ['char_set'] The character set used in communicating with the database
        |   ['dbcollat'] The character collation used in communicating with the database
        |                NOTE: For MySQL and MySQLi databases, this setting is only used
        |                as a backup if your server is running PHP < 5.2.3 or MySQL < 5.0.7
        |                (and in table creation queries made with DB Forge).
        |                There is an incompatibility in PHP with mysql_real_escape_string() which
        |                can make your site vulnerable to SQL injection if you are using a
        |                multi-byte character set and are running versions lower than these.
        |                Sites using Latin-1 or UTF-8 database character set and collation are unaffected.
        |   ['swap_pre'] A default table prefix that should be swapped with the dbprefix
        |   ['autoinit'] Whether or not to automatically initialize the database.
        |   ['stricton'] TRUE/FALSE - forces 'Strict Mode' connections
        |                           - good for ensuring strict SQL while developing
        |
        | The \$active_group variable lets you choose which connection group to
        | make active.  By default there is only one group (the 'default' group).
        |
        | The \$active_record variables lets you determine whether or not to load
        | the active record class
        */

        \$active_group = 'default';
        \$active_record = TRUE;

        \$db['default']['hostname'] = '$hostname';
        \$db['default']['username'] = '$username';
        \$db['default']['password'] = '$password';
        \$db['default']['database'] = '$database';
        \$db['default']['dbdriver'] = 'mysql';
        \$db['default']['dbprefix'] = '';
        \$db['default']['pconnect'] = TRUE;
        \$db['default']['db_debug'] = TRUE;
        \$db['default']['cache_on'] = FALSE;
        \$db['default']['cachedir'] = '';
        \$db['default']['char_set'] = 'utf8';
        \$db['default']['dbcollat'] = 'utf8_general_ci';
        \$db['default']['swap_pre'] = '';
        \$db['default']['autoinit'] = TRUE;
        \$db['default']['stricton'] = FALSE;


        /* End of file database.php */
        /* Location: ./application/config/database.php */";



        $con = mysqli_connect("$hostname","$username","$password","");

        // Check connection
        if (mysqli_connect_errno()) {


          $data2['errors'] = 'Database credentials are wrong dude!';

          $this->load->view('header');
          $this->load->view('body');
          $this->load->view('installer', $data2);
          $this->load->view('footer');
        }
        else
        {
            $sql="CREATE DATABASE $database";
            if (mysqli_query($con,$sql)) 
            {
              //echo "Database my_db created successfully";
            } 
            else 
            {
              //echo "Error creating database: " . mysqli_error($con);


            }

            mysqli_close($con);


            if ( ! write_file('./application/config/database.php', $data))
            {
                 echo 'Unable to write the file do you have permissions!';
            }
            else
            {
                //echo 'File written!';

                $data2['success'] = 'All good dude!';

                $this->load->view('header');
                $this->load->view('body');
                $this->load->view('installer-2',$data2);
                $this->load->view('footer');

            }
        }


    }

     /**
      *  @Description: if db file written test db connection!
      *       @Params: params
      *
      *      @returns: returns
      */
    public function create_tables()
    {

        $sql = "
      CREATE TABLE IF NOT EXISTS `test_table` (
        `id` int(11) NOT NULL AUTO_INCREMENT,
        `test` varchar(50) NOT NULL,
        PRIMARY KEY (`id`)
      ) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
      ";


      $this->db->query($sql);

    }

So the key points to make are you have to use native php to test the connection, once legit you can then write over the database config file.

Probably a good idea to set the index.php environment to 'production' to suppress errors.

Member Avatar for iamthwee

I have a another quick question for veedeo.

After the database and tables have been created what would be best practice to change the default router from the install controller to the main page controller.

I was thinking of reading the db config file, checking if there are values for the database written, then overwrite the default route config file and do a page refresh?

Member Avatar for iamthwee

Alternatively, I was just thinking of keeping the default router the same (i.e the installer router) but in the contruct function check if the database config file has a variable written in the tables, if it has do a redirect to my homepage controller, I think this might be best.

There are many styles and ways of writing an installer and I would normally write my installer in procedural. The reason is that, at the end of the installation process, the user will be instructed to delete or rename the install directory.

Here are steps I must undertake for pretty much all the installer I wrote.

  1. The very first thing is to check if the php.ini directives required by your application has been met. For example, in the installer I wrote for Youtube clone CMS type of application, the application will need to meet the following requirements:

    max_execution_time: 3000
    max_input_time: 600
    upload_max_filesize: 200M
    post_max_size: 200M
    register_argc_argv:
    open_basedir: no value
    safe_mode: off

The above php.ini file directive screening process must be in the index.php of the install directory

application
system
install * same level as the application directory in CI

Now, the index.php in the install directory are devided in many functions that will handle all the steps of the installation along the way.

step one: php.ini directives check point, file permission check. We capture the steps through a form submission.

    if(!isset($_POST['step']){
        $step = 1; // this makes the user be defaulted at step one and force the script to check the php.ini directives and file permission requirements.

         switch ($step){
           ## we initialize at server_failed as false
           $server_failure = FALSE;
         ## check if the server's php.ini directive meets our requirements.
             case 1: //we are on initialization of the installer
          ## now, check the directives at once

      $check_max_exec = ini_get('max_execution_time') < 1000 ?"<font color=red>(we recommend 1000)</font>":"<font color=green> OK</font>";

    $check_max_input = ini_get('max_input_time') < 1000 ?"<font color=red>(we recommend 1000)</font>":"<font color=green> OK</font>";

    $check_max_upload = ini_get('upload_max_filesize')< '200M'?"<font color=red>(we recommend 200M)</font>":"<font color=green> OK</font>";

    $check_post_max = ini_get('post_max_size') < '200M'?"<font color=red>    (we recommend 200M)</font>":"<font color=green> OK</font>";
    $check_argc_argv =((bool) ini_get('register_argc_argv')?'on':'off')=='off'?"<font color=red>(we recommend on)</font>":"<font color=green> OK</font>";

    $check_open_base = ini_get('open_basedir')!=''?"<font color=red>(we recommend 'no value')</font>":"<font color=green> OK</font>";
    $check_safe_mode =((bool) ini_get('safe_mode')?'on':'off')=='on'?"<font color=red>(we recommend off)</font>":"<font color=green> OK</font>";

Done with php.ini directives check points. If one failed on check, we remind the user to fix it first before moving on by hiding the next button.

we check for the application directories and files requirements

if (!is_writable('../application/config/'))
    {
        $config_error ="<font color=red>Folder 'data' is not writable - please CHMOD to 755 or 777</font>";
        $server_failure = true;
    }
    else
    {
        $config_error ="<font color=green>Folder 'config' is writable</font>";
    }

On CHMOD check, we can make PHP change the permission to our requirements. However, it is best to make the user do it on their own. So that they know which directories are being CHMODed to 777 or 755

We check and run validations for all directories and files needed in the installation process.

Once all the php.ini and directories have passed, we can move on to database credentials collection.

This can be done in two ways.

First: full script control NOT rECOMMENDED: This is when we only ask for the root username and password and we let our installer create the database table.

Second: We tell the user to create database table VIA phpMyAdmin : Recommended. We want our installer to be transparent as much as possible.

Step Two:
we can check if the database credentials are valid by initiating a simple connection like..

$db_host = validate($_POST['host'];
$db_user = validate($_POST['user'];
$db_pass = validate($_POST['pass'];
$db_name = validate($_POST['db_name'];

$can_connect = mysqli_connect($db_host, $db_username, $db_pass);

if(!$can_connect){

            echo 'database credentials are not valid! Please check again and resubmit the form';

   }

if it passes on step two, we generate our config file.

Again, there are many ways of writing and rewriting configuration files by way of installers. My preferred choice is writing them from text stock file.

Text stock file is like a template file for the config.php written save as text file. By doing this we can save time and can make our installer a less beefy.

For example, my installer needs to write a new database.php configuration file in application/config/database/ directory. All I need to do is save the non-configured database.php as a text file in my installer directory, and then just change the following settings

$active_group = 'default';
$active_record = TRUE;

$db['default']['hostname'] = '%%$hostname%%';
$db['default']['username'] = '%%$username%%';
$db['default']['password'] = '%%$password%%';
$db['default']['database'] = '%%databasename%%';

## the rest goes here 

We use the str_replace function to replace %%$hostname%% and then eventually save the generated database.php.

We need to store these credentials in a temp file inside the installer directory. We need it for creating the application's database table.

Step Three: Importing the database tables and default values. There are two ways of achieving this.

First, write everything in xml file and let the installer parse the data.
Second, do it the safest way. use the database export from your development phpMyAdmin output. This is recommended because you will extracting the same database tables from your release candidate.

Step three: this is how I would write the database. Say our application have these tables. Example below were taken from the actual installer I wrote some 5 years ago, so the database charsets were still on Latin1.

Below this line are my answers to your questions

we have a file called application.sql

-- --------------------------------------------------------

--
-- Table structure for table `approval`
--

CREATE TABLE IF NOT EXISTS `approval` (
  `id` int(11) NOT NULL auto_increment,
  `Sitename` varchar(255) NOT NULL default '',
  `Url` varchar(255) NOT NULL default '',
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=7 ;

-- --------------------------------------------------------

--
-- Table structure for table `ban`
--

CREATE TABLE IF NOT EXISTS `ban` (
  `ip` varchar(15) NOT NULL default '',
  PRIMARY KEY  (`ip`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

-- --------------------------------------------------------

--
-- Table structure for table `bannerads`
--

CREATE TABLE IF NOT EXISTS `bannerads` (
  `id` int(11) NOT NULL auto_increment,
  `title` varchar(50) NOT NULL default '',
  `description` varchar(100) NOT NULL default '',
  `link` varchar(255) NOT NULL default '',
  `thumbnail` varchar(255) NOT NULL default '',
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=7 ;

-- --------------------------------------------------------

--
-- Table structure for table `blocked`
--

CREATE TABLE IF NOT EXISTS `blocked` (
  `blocker_id` int(11) NOT NULL default '0',
  `blockee_id` int(11) NOT NULL default '0'
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

-- --------------------------------------------------------

and this is the PHP script that will read add those tables

$entries = file("application.sql");
        foreach ($entries as $entry_num => $line)
         {
            if (substr($line, 0, 2) != '--' && $line != '') 
            {
                $temp_line .= $line;
                if (substr(trim($line), -1, 1) == ';') 
                {
                    $db->query($temp_line);
                    $temp_line = '';
                }
            }
        }

$db->query is my insert method ( just in case people are wondering, what just happened in the codes.

Lastly, we take liberty on lowering the file CHMOD permssion of the config/database.php back to 655 and others files that may have been CHMODed to 777 during the installation.

For the router, we set the default values to the application's routing requirements.

We conclude the installation by instructing the user to delete or rename the installation directory.

On the application initialization, we need to check if the installer directory still exists. If it is, we need to show an error critical message.

that's it for now. My mind is beginning to wonder all over the place again..

here is an old screenshot I found..

b037009383ff432ed784d0f7607815e5

Additional information.

Since distributions of application, requires version maintenance and upgrades. The installer should be able to check the version being installed agains the latest version available. This can be done by adding an xml parser on your installer to check what version is currently available and then compare the version being installed.

If the version being installed is less than the current version, you execute your download function and deflate the download file replacing everything in the application directory, except the installation directory. This is the reason install script must have its own directory and must not reside in the application directory.

a simple release_info.xml file

<?xml version="1.0" encoding="UTF-8"?>
<release>
<version>2.0</version>
<release_date>June 22, 2014 </release_date>
<release_note>This is version 2.0</release_note>

<release_changes> List of changes
<change> upgraded index.php</change>
<change> Added more security</change>
</release_changes>

<update_info>
<database>columns
<col>new_column</col>
<col>new_column2</col>
</database>
<files>files
<file>application/controllers/new.php</file>
</files>
</update_info>


</release>
Member Avatar for iamthwee

Thanks veedeo, I ended up going for checking if the database.php file had the line written and then redirecting.

public function check_if_written()
{
    $string = read_file('./application/config/database.php');

    //echo $string;

    $lines = explode("\n", $string);

    $is_written = false;

    foreach ($lines as $line) 
    {

        if (strpos($line,"['default']['database']") !== false) 
        {

            //echo '<pre>';
            $line2 = trim($line);

            //why 32? This is the character count
            //when there is nothing written on the
            //database name line

            if (strlen($line2) > 32 )
            {
                //database has been written
                $is_written = true;
            }
        }
    }

    return $is_written;
}



public function index()
{
    if($this->check_if_written()==true)
    {
        redirect("shortcodes/load_builder_page", "refresh");


    }
    else
    {

        $this->load->view('header');
        $this->load->view('body');
        $this->load->view('installer');
        $this->load->view('footer');
    }   
}

that's pretty cool approach iamthwee :). Your ultimate CMS is looking great indeed.

Member Avatar for iamthwee

The only issue with my install is the base url set in the config file.

As it needs to be set before hand. I think I've got an idea based on a codeigniter install I've seen on github.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.