sonicsleuth / mvc-framework-php

A Model-View-Controller framework written in PHP. Easy Install. Auto/Pattern-Routing, Schema Mapping Models using PDO.
MIT License
3 stars 1 forks source link

Bug regarding : Undefined offset: 0 fix... #12

Closed jackttcoms closed 6 years ago

jackttcoms commented 6 years ago

Ok so I saw that you fixed the bug but unfortunately when I go to use it with the $data array to hold more data all those awful messages come back!

My View Code:

<!doctype html>
<html>
<head>
    <!-- Head -->
    <?php extend_view(['common/head'], $data) ?>
    <!-- CSS -->
    <?php load_style(['bootstrap.min','main','custom']) ?>
</head>
<body class="d-flex flex-column">
    <!-- Global Message -->
    <?php echo flash('message'); ?>
    <!-- Header -->
    <?php extend_view(['common/header'], $data) ?>

    <!-- Page Content -->
    <section class="container-fluid flex-grow">

    </section>
    <!-- End Page Content -->

    <!-- Footer -->
    <?php extend_view(['common/footer'], $data) ?>
    <!-- JS -->
    <?php load_script(['jquery-3.2.1.min','bootstrap.bundle.min','main']); ?>
</body>
</html>

My Controller Code:

<?php if (!defined('BASE_PATH')) exit('No direct script access allowed');

class Home extends Controller
{
    public function __construct()
    {
        $this->load_helper(['view','session']);
    }

    public function index($param1 = '', $param2 = '')
    {
        $data = [
        'title' => 'This is a meta title'
        ];
        $this->view('home', $data);
    }

    public function phpinfo()
    {
        echo phpinfo();
    }

}

This is what triggers it:

    public function index($param1 = '', $param2 = '')
    {
        $data = [
        'title' => 'This is a meta title',
                'description' => ''
        ];
        $this->view('home', $data);
    }

To not trigger it:

    public function index($param1 = '', $param2 = '')
    {
        $this->view('home');
    }

Thanks

image

sonicsleuth commented 6 years ago

I need to clarify the documentation regarding passing values into a View using $data, here's why:

When setting your own values you are using the $data Keys directly, but when assigning records from a database the $data Keys are numerical indexes [0],[1].

The proper way to assign a db record to avoid a use case conflict would be

$data = [ "my_records" => $db_results];
echo my_records['some-key'];

As for your BUG, change your bug from $data = ... to $data[0] = ... and that should fix it.

PLEASE NOTE: Once I update the documenation I may need to alter the View Helper and it may break legacy code if you install an update.

jackttcoms commented 6 years ago

Ok so the code below removes the undefined offset error but then everything inside the array fails to be recognised, i'm clearly doing something silly wrong! Thanks for your help in advance!

    public function index()
    {
        $getSettings = $this->setting->getAll();
        **$data[0]** = [
            'getSettings' => $getSettings,
            'title' => LANG['homepage'],
            'description' => 'this is a description'
        ];
        $this->view('home', $data);
    }

and in view i have tried: $data['title'] || $data[0]['title']; || $data[1]['title']... but none work


Can I also ask you a question regarding a counter as well? Currently i'm trying to simply count all the rows in a table that meet a certain criteria which all works great if there is 1 row, 2 rows and so on but when there are 0 rows it returns 1. I'm pretty sure this is simply do with the arrays but I just can't get it to show 0. I have tried an if statement where if false it sets it to 0 but that fails to work so i'm lost but i really don't want to go back to my old ways where i counted everything via SQL. 👎

Here is my model code:

    public function getTotalUsers()
    {
        $results = $this->db->select('msi_users','status = 1');
        $totalRows = count($results);
        return $totalRows;
    }

and in controller I simply do this...

$getTotalUsers = $this->setting->getTotalUsers();
        $data[0] = [
            'getTotalUsers' => $getTotalUsers,
        ];

Thanks and sorry if i'm asking the easiest question ever!


Oh and regarding your new session model does that start the session itself or do i still have to initiate it? In the sense that will it interfere with flash alerts? (flash alerts are instead of the classic success/error messages but then if you were to click the refresh button it would ask you to resubmit the form etc. now it simply redirects you and the flash message appears once and then disappears!)

/*
 * @param $name
 * @param $message
 * @param $class
 * @return message
 *
 * Loads Alert Message.
 */
function flash($name, $message = '', $class = '')
{
  if (isset($_SESSION[$name]) && empty($message)) {
      $value = $_SESSION[$name];
      unset($_SESSION[$name]);
      return $value;
  } elseif(!empty($message)) {
      $message = $class ? "<div data-auto-dismiss=\"10000\" class='fade show f-14 rounded-0 " . $class . "'><div class=\"container\"><i class=\"fas fa-info-circle mr-2\"></i> " . $message . "</div></div>" : "<div><div class=\"container\"><i class=\"fas fa-info-circle\"></i>&nbsp; " . $message . "</div></div>";
      return $_SESSION[$name] = $message;
  }
}

Thanks

sonicsleuth commented 6 years ago

About $data in VIEWS:

  1. Try a print_r($data) on the View page itself to see what the structure of the $data array looks like.
  2. In the View you should be able to access a value as $data[0]['title'], unsure why not.
  3. There is also an alternative way to access values from $data. The View Helper auto-magically creates new PHP variables from each Key of $data and assigns its value. So you could also use $title, $description, etc in the View.

About Counting Rows: If you are getting "1" when counting an empty array you can try checking it there is a value in the first index of the array.

About SESSIONS Read the last section "Sessions" of the README for this repository for details but basically you need to set up the sessions database table then call the sessions model as your FIRST action inside a Controller, like so:

// Add the following line to enable database sessions.
$session = $this->model('Session');

Technically, you could just call the Session model $this->model('Session'); without assignment to the $session variable. The reason for the assignment is if you want to access a debug method of the object $session->getSessionData(); which is also detailed in the README.

jackttcoms commented 6 years ago

Yes, i forgot about print_r but yes that shows all the data fine and it has started working now! I just had to clear the cache! (Sorry bout that) Array ( [0] => Array ( [getSettings] => Array ( [0] => Array ( [id] => 1 [activate_registration] => 1 [global_announcement] => [global_announcement_type] => primary [global_company] => Brand [global_email] => email@email.com [global_favicon] => favicon.jpg [global_logo] => logo.jpg [global_ogimg] => og-img.jpg ) ) [title] => Homepage [description] => ) )

Regarding Counting Rows: If i do the print_r and var_dump these are my responses var_dump($data['getTotalUsers']); print_r($data['getTotalUsers']); Response: int(2) 2 (not even array, simply returning total counted rows just not the right amount when there are 0); Which is why its confusing me! Very odd! I'm simply doing a select query, counting the results returned and then returning the amount counted.

I am currently calling the session model but unfortunately it does not let the flash messages don't activate :( - They are very handy, maybe implement it as an helper for everyone but i made it appear when i added the old piece of code: session_start(); but then you get this:

Notice
: A session had already been started - ignoring session_start() in
C:\xampp\htdocs\mvcnew\app\models\Sessions.php
on line
35

so how would you recommend i approach showing flash messages without messing up your session system but theoretically it should work just fine as you have this piece of code in your session model:

            // This sets a persistent cookie that lasts a day.
            session_start([
                'cookie_lifetime' => 86400,
            ]);

Yes I have set up the sessions database and with this does it basically add it to the db automatically so i could do my create session method for all the user data and then it stores that in the db until 24 hours but then what if the user updates their profile, do i just reassign the session value again? Or is that not what this is for? How would you recommend i make use of this if i don't have the right gist!

Thanks again as always!

sonicsleuth commented 6 years ago

About the Sessions Model It replaces the normal functionality of PHP Sessions whereby PHP typically stores session data in a physical file on the server. These days dealing with virtual servers and containers and that is not good because the server might scale down. Once you invoke the Session model you use the PHP session features like normal - PHP Sessions understands it will now store/get/clear the session records from the Db as if it were still using physical files. Lastly, you DO NOT have to run session_start() as the Model does this itself.

As for Flash Messages, which rely on calling session_start() itself, you might have to modify that code so there is not a collision between the Session Model and it. Basically, they are fighting to be first to create a session. ie: Typical problem when using packages vs custom coding - alignment of needs.

As for the purpose of the Session Model - if you need "persistent session data" for "congruency" then you should store it in a DB.

About Counting Rows You may need some conditional code in your Model that checks if the results

sonicsleuth commented 6 years ago

...continued...

About Counting Rows What is being returned into $results when it is Zero vs +1 rows in your Model? Identify the difference in what count() is counting and wrap it logically.

public function getTotalUsers()
    {
        $results = $this->db->select('msi_users','status = 1');
        $totalRows = count($results);
        return $totalRows;
    }
jackttcoms commented 6 years ago

regarding the About Counting Rows...

I'm simply selecting all the rows where status = 1 which now i think about it is probably not a very good way to go about it! Count is simply counting how many results are returned so maybe if $results == FALSE make $totalrows 0

Indeed yes, always a problem when using a third-party product but i do love a challenge until after i have spent 4 days on it :( then i give up and become impatient (terrible trait to have)

UPDATE: I GOT IT WORKING (flash messages) :)

Thanks again as always!

Also you said this earlier in the thread, what are you planning etc? Thanks:

PLEASE NOTE: Once I update the documenation I may need to alter the View Helper and it may break legacy code if you install an update.

sonicsleuth commented 6 years ago

Good to read you got it working. Those "hard cases" are the ones we learn the most from because they help refine our thinking process. For example, spending days on an issue means you did not yet have a previous experience solving this type of problem... and now you do.

About View Helper update - I am planning on requiring any database results be assigned to a key on the $data variable. This is how it was originally intended so you can pass records and other data, such as row counts, etc. I realized in the docs that it is unclear and has created this cat-n-mouse issue. For example:

// This is problematic b/c zero, one, more records have different structures.
$data = $this->db->select('users');

// This is better b/c you can also append more information to the view.
$users = $this->db->select('users');
$is_admin = true;
$data = ['users' => $users, 'is_admin' => $isadmin]

// which will make using $data in a view more clearly stated 
// and cleaner when using the magically created PHP variables from the Keys of $data.
foreach($user as user) ...
if($is_admin === true) ...
jackttcoms commented 6 years ago

Ok thanks, i'll look forward to it and will that remove the irritating $data[0] kind of bits where I have to keep adding [0] in certain places? From your example that is what it looks like in the sense that the first part of the array gets assigned a variable so i would do $title instead of $data[0]['title'] and it would echo $title but the current method will still work? Have i got the jist?

$data = [
    'users' => $users,
    'title' => 'test'
]

And do you have an estimated release date?

Thanks

sonicsleuth commented 6 years ago

I just pushed up the updated MVC Framework which includes a more consistent way of passing $data from a Controller into a View then using those values.

Have a look at the Controller section of the GitHub README.

The framework itself also contains an Example of pulling data from a DB via a Model, preparing it in a Controller, and passing it properly to a View. Using this update may require a little code refactoring in your project - basically, fixing any improper uses of $data preparation.

-- Enjoy!

jackttcoms commented 6 years ago

Thankyou, its all working wonderfully! My favourite update to date but can i make one recommendation to add into the documentation.

Basically its that if you want to get just the title for example you only need $title but if you want to get a database value, you go $getSettings[0]['company_name']; (The [0] is what I would forget if it was not mentioned and maybe others could to so just add the [0] in there somewhere unless you already have and i have missed it but thanks again.

<?php if (!defined('BASE_PATH')) exit('No direct script access allowed');

class Home extends Controller
{
    public function __construct()
    {
        $this->load_helper(['view','url','date','email','custom']);
        $this->session = $this->model('Sessions');
        $this->setting = $this->model('Settings');
    }

    public function index()
    {
        $getSettings = $this->setting->getAll();
        $data = [
            'getSettings' => $getSettings, // echo $getSettings[0]['database_value'] or $data['getSettings'][0]['database_value']
            'title' => LANG['homepage'], // echo $title or $data['title']
            'description' => ''
        ];
        $this->view('home', $data);
    }
}

Also regarding sessions and your session class how do we call/fetch the session data. Do we do it like we normally do or is there another way to get it from the db? Thanks

Thanks again very much!

sonicsleuth commented 6 years ago

Sessions - Use the standard PHP Session functionality. When you include the Session Model it provides PHP Session functions with everything needed to handle the DB. It works "invisibly" in the background.

Guidance on [0] - Good idea for when a single record is returned.

jackttcoms commented 6 years ago

By the way thanks for your help regarding this, you helped me figure it out :) - the sigh of relief :)

    public function getTotalUsers()
    {
        $results = $this->db->select('chewi_users','status = 1');
        $totalRows = count($results);
        if($results === FALSE){ $totalRows = '0'; }
        return $totalRows;
    }

And ok thanks very much just because i'm planning on implementing permissions using a sitepoint tutorial https://www.sitepoint.com/role-based-access-control-in-php/ and just checking if i should do anything fancy with the sessions etc.

Thanks again very much!

sonicsleuth commented 6 years ago

Just an update for you. I added a new method to the core Model for selecting a single record. The selectOne() method removes the $data[0] issue. It works the same as select() without the nesting array. Below are details from the updated docs I just pushed up.

Selecting Records: There are two methods available for selecting records.

selectOne() - Use this method to return a single record. For example:

// Use selectOne() to return a single record.
$user = $user->selectOne('users','id = 100');
$print_r($user);
// OUTPUT:
array('name' => 'Bob', 'id' => '12')

// Display values from this single record.
echo "Welcome " . $user['name']; 
// OUTPUT
Welcome Bob

select() - Use this method to return multiple records. For example:

// Use select() to return multiple records.
$users = $user->select('users','dept = 12');
$print_r($users);
// OUTPUT:
array(
    [0] => array('name' => 'Bob', 'dept' => '12'),
    [1] => array('name' => 'Mary', 'dept' => '12'),
    [2] => array('name' => 'Sue', 'dept' => '12'),
)

// Loop thru a set of records.
foreach($users as $user) {
    echo "Employee: " . $user['name'] . "\r\n";
}
// OUTPUT
Employee: Bob
Employee: Mary
Employee: Sue
jackttcoms commented 6 years ago

Thanks very much! I really appreciate this!

I’ve started moving all my projects over to your MVC now and have made a nice starter MVC template that has user management, custom pages, and all the helpers etc to quickly build a new project from 😁! Only thing I gotta do left is permissions!

Can I ask you a quick general question! I need to always get the settings table 24/7 which is not the best performance wise but I need to be able to easily site settings etc. But I also need to have an updated user balance all the time so is it ok if I just call it always from the database or is there a way I can u ur database sessions to my advantage where it’s stored as a session but then updates whenever it changes? Please let me know if the current way I’m doing it is ok or if the seasons methods would be better but somehow with the same functionality of auto-update?

Thanks again as always!

sonicsleuth commented 6 years ago

Of course, you always want to minimize how often you access a database simply because of the latency and eventual scaling up. This can be minimized thru well-optimized SQL queries and tables scheme, such as indexing columns and the like. A lot can go into a super fast DB design, however, MySQL should meet your needs well over a million users for now.

As for using the Session Modle, there is no advantage there as it is accessing a database table for read/writes. This is more for "persistence" over losing session data if the server goes offline.

I would just read up on good SQL techniques. Try reading "High Performance MySQL" by Orilliey.

jackttcoms commented 6 years ago

Hello, regarding helpers and email smtp I’m currently doing the authentication through gmail as a plain text as in $this = “password”; etc. but I got a security alert on on the account that is used for emails so I was wondering if it is secure or not what I’m doing by doing it as plain text? (If what I have said makes any sense)

Thanks

Here is what I mean in helper:

<?php

use Snipworks\SMTP\Email;
require_once "../vendor/mail/src/email.php";

/*
 * @param $subject
 * @param $email
 * @param $body
 * @return status
 *
 * Sends SMTP Email.
 */
function sendEmail($subject, $email, $body)
{
    $mail = new Email('smtp.gmail.com', 587);
    $mail->setProtocol(Email::TLS);
    $mail->setLogin('email@gmail.com', 'password');
    $mail->addTo($email);
    $mail->setFrom('no-reply@domain.com', 'Brand');
    $mail->setSubject($subject);
    $mail->setMessage($body, true);
    $mail->send();
    return true;    
}
sonicsleuth commented 6 years ago

I cannot say specifically but is your site using HTTPS as Gmail port 587 is SSL secure.

Read about "enabling less secure apps"... Have a look here: https://productforums.google.com/forum/#!topic/gmail/8tUdn4zjxuc and here: https://www.wpsitecare.com/gmail-smtp-settings/

jackttcoms commented 6 years ago

Ok thanks very much and yes its all HTTPS and using the TLS 587 port! Maybe its just a coincidence as I don't see how it would be possible to view the $mail->setLogin data as its in the PHP tags and impossible is that right?

Thanks again

sonicsleuth commented 6 years ago

Try dumping the object like print_r($mail) or see if there is a "getter" like echo $mail->getLogin

jackttcoms commented 6 years ago

Yes by doing print_r($mail) it does reveal the password! Could that be manipulated by someone? I always assumed that php hard coded cannot be manipulated or seen as its server-side not client-side?

https://github.com/daveismyname/pagination Also in better news I have found a great pagination class that works really well with your MVC in my opinion.

Model

    public function getAllPages($limit)
    {
        $results = $this->db->select('custom_pages','status = 1 '.$limit);
        return $results;
    }

Controller:

    public function index()
    {
        $this->page = $this->model('Pages');

        $getSettings = $this->setting->getAll();
        $getNavPages = $this->setting->getNavPages();
        //$getPages = $this->page->getAllPages();

        $pages = new Paginator('1','p');
        $pages->set_total( $this->page->countTotalPages() );
        $records = $this->page->getAllPages( $pages->get_limit() );
        $pageLinks = $pages->page_links();

        $data = [
            'getSettings' => $getSettings,
            'getNavPages' => $getNavPages,
            //'getPages' => $getPages,
            'title' => LANG['homepage'],
            'description' => '',
            'test' => $pages->get_limit(),
            'records' => $records,
            'pageLinks' => $pageLinks
        ];

        $this->view('home', $data);
    }

Thanks

jackttcoms commented 6 years ago

Can I also ask you a question about this: https://www.sitepoint.com/role-based-access-control-in-php/

Basically there are two classes, they both need the db so i'm gonna put them both in the models folder but as your models extend Model how would I go about doing this?

"(PrivilegedUser.php) that will extend your existing user class"

Would this even work integrating it into your framework? Otherwise I do have my own version which I feel will work better with your MVC but sitepoint uses less tables etc so i thought i would try this first!

Thanks

jackttcoms commented 6 years ago

Here is my theoretical RBAC system for your MVC:

CREATE TABLE IF NOT EXISTS `permission` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `title` varchar(255) NOT NULL,
  `index` int(11) NOT NULL,
  `group_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE IF NOT EXISTS `permission_group` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(50) NOT NULL,
  `index` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE IF NOT EXISTS `role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
CREATE TABLE IF NOT EXISTS `role_permission` (
  `role_id` int(11) NOT NULL,
  `permission_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `user_role` (
  `user_id` int(11) NOT NULL,
  `role_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Then I simply have this piece of code for controller and frontend:

        if(!$this->user->hasPermission('editArticle'))
        {
            redirect('error');
            return true;
        }

And in the model you would have something like this:

    public function hasPermission($permission)
    {
        foreach($this->_roles as $role)
        {
            if($role->hasPermission($permission))
                return true;
        }
        return false;
    }

    private function _initRoles()
    {
        $this->_roles = array();

        $sql = "SELECT r.id, r.title
            FROM user_role as ur
            RIGHT JOIN role as r ON ur.role_id = r.id
            WHERE ur.user_id = :user OR r.id= :defaultRole OR r.id = :guestRole";
        $this->setSql($sql);
        $bind = [':id' => $user_id];
        $sql = 'SELECT r.id, r.title FROM user_role AS ur RIGHT JOIN  role AS r ON ur.role_id = r.id WHERE ur.user_id = :user';
        $results = $this->db->selectExtended($sql, $bind);
        return $results;

        $roles = $this->getAll(array(
            ':user' => $this->_id,
            ':defaultRole' => DEFAULT_ROLE,
        ));

        foreach($roles as $userRole)
        {
            $role = new Role;
            $role->setId($userRole['id']);
            $this->_roles[$userRole['title']] = $role;
        }
    }

etc. which i think would kinda work but what do you think! Should I do my way (all theoretical) or how best would you start when building an RBAC system in your opinion?

Thanks as always!

sonicsleuth commented 6 years ago

About Security and exposing PHP values - Generally speaking, it should be safe to hardcode private values into the PHP server side code. That said, there is the possibility that an error can be tossed to the web page that would expose private values. This is common when MySQL fails which can expose your DB user/pass which is an issue if perhaps Google then indexes the output.

About the Roles Permission - Having the Sitepoint article is a good template for building out your own Models. I would take the time to understand the article points well, then "refactor" that code to work in the MVC as a Model.

jackttcoms commented 6 years ago

Ok thanks as always! Can I also ask you for your opinion on an experiment.

I'm planning on adding a feature that can be turned on/off where you prevent multiple logins to the same account but what technology method do you suggest I use? At first I thought of doing a simple boolean saying logged in/not etc. but then quite alot of people never logout so i'm currently thinking sessions and making it so that when you login, it creates a session token and stored it in a user token table and then if you are logged in elsewhere it will compare tokens and if they don't match it logs you out but I feel that there are a few flaws with this with one being if someone knew your password it would be a tit for tat where he/she logs in then current he/she gets kicked off then kicked off one logs in and kicks off bad person etc and its a cycle until one gives up so do you know a better approach? Thanks very much

sonicsleuth commented 6 years ago

About handling multiple logins It sounds like you want a secure authentication scheme with intellegent pattern monitoring.

Your token apprach is a good start. I would add an email notification to the account holder asking them to confirm they either A) want to allow access from this location/device.. OR B) which to deny access from that location/device effectively logging off that location. I would capture any identifiable data during each session, IP, Browser, User-Id, Time, Location (if able). You can make a "smart" AI script to watch for actions outside the norm, like: Allow login from A/B/C device. Deny logins that do not originate neat the same location (ie: NY to CA in under 15 minutes?). Archive these logs for insurance, claims, study/improvements, compliance.

jackttcoms commented 6 years ago

Wow! Thanks. That sounds like an Apple kind of login! Super Secure!

Also can I ask a few other questions relating to login security!

$session_id_to_destroy = 'nill2if998vhplq9f3pj08vjb1';
// 1. commit session if it's started.
if (session_id()) {
    session_commit();
}

// 2. store current session id
session_start();
$current_session_id = session_id();
session_commit();

// 3. hijack then destroy session specified.
session_id($session_id_to_destroy);
session_start();
session_destroy();
session_commit();

// 4. restore current session id. If don't restore it, your current session will refer to the session you just destroyed!
session_id($current_session_id);
session_start();
session_commit();

Finally, with my registration you create an account and important account data goes into user table and then i have another table with user profile data such as description/about me and social media etc but regarding the bit where you create the account, insert row into main table (works fine) but then inserting the profile row with the id that was just generated (user_id) is where I have frozen! Basically my question is how do i access the $this->db->insert_id(); in your MVC to get the lastinsertid? (Unless you feel this is flawed to get the user id?)

Thanks again as always!

jackttcoms commented 6 years ago

Hi Sonic, don't worry I solved all of the above! Sometimes I ask for advice too early :)

Also is it possible for me to become a contributor and add a simple helper that I think would benefit everyone that uses your MVC and is great for all start projects.

Name: app/helpers/start.php Features: URL Redirection, Flash Messages, Random Custom Length String, Random Cryptographically Secure String Code:

<?php
/*
 * @param $page
 * URL Redirect.
 */
function redirect($page) {
    header('location: ' . FULL_ROOT . '/' . $page);
}

/*
 * @param $name
 * @param $message
 * @param $class
 * @return message
 *
 * Loads Alert Message.
 */
function flash($name, $message = '', $class = '')
{
  if (isset($_SESSION[$name]) && empty($message)) {
      $value = $_SESSION[$name];
      unset($_SESSION[$name]);
      return $value;
  } elseif(!empty($message)) {
      $message = $class ? "<div class='" . $class . "'>" . $message . "</div>" : "<div>" . $message . "</div>";
      return $_SESSION[$name] = $message;
  }
}

/*
 * @param $length (default = 15)
 * @return randomString
 *
 * Generate Random String.
 */
function generateRandomString($length = 15)
{
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charactersLength = strlen($characters);
    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString;
}

/*
 * @param $length
 * @param $keyspace
 * @return string
 *
 * Generate Cryptographically Secure Random String.
 */
function generateCryptoRandomString($length, $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
{
    $pieces = [];
    $max = mb_strlen($keyspace, '8bit') - 1;
    for ($i = 0; $i < $length; ++$i) {
        $pieces []= $keyspace[random_int(0, $max)];
    }
    return implode('', $pieces);
}

If you think it would be helpful?