Algorithm for USSD ( State machine ) Application - php

I have created a USSD application in PHP, Since USSD requests are unique ( new request for each selection ) I need a way to track them , Track in the sence i need a way to store the flow of the application, In my USSD app there are Static menus as well as Dynamic menus ( Retrieved from the Database ) , My problem is , I have created the app with lots of if, else and switch statements, And It's really difficult to update the menu items. So can anyone suggest me a good algorithm to create this USSD app? I mean a efficient way to tackle the problem, I need a way where I could be able to add and remove menus according the clients request, When I do that , my code shouldn't break. Can anyone help me on this?
-Thanks & Regards
PS: I have read this and this and it doesn't actually answer my question.

The best method working with USSD is making each command a separate class. This way you can work , debug and maintain a large USSD application
Example
$ussd = new USSDMessage(); // USSD Class
$main = new Command("menu"); // Starting Command
// Register multiple command
$main->register(new Command("help"));
$main->register(new Command("account"));
$main->register(new Command("transfer"));
$main->register( new Recharge("reacharge"));
$main->run($ussd); // Run Request
Basic Class Structure
// Class To receve message from USSD
class USSDMessage {
function next();
function saveSession();
}
// Basic Command Class
class Command {
private $name;
private $cmd = array();
function __construct($name) {
$this->name = $name;
}
function register(Command $menu) {
$this->cmd[$menu->getName] = $menu;
}
function run(USSDMessage $ussd) {
$key = $ussd->next(); // get next inpur from ussd
if (isset($this->cmd[$key])){
$ussd->run($ussd);
}
}
}
// Recharge Command Example
class Recharge extends Command {
function run(USSDMessage $ussd) {
$key = $ussd->next();
switch ($key){
case "1":
// Proccess card recharge
// Do Somthing
$ussd->saveSession(); // End Session
break;
case "2":
// Process direct deposit
// Do Somthing
$ussd->saveSession(); // End Session
break;
default:
// Format Not supported
// Do Somthing
$ussd->saveSession(); // End Session
break;
}
}
}

Based on what I got from OP's question, here is some hint:
This problem is related to tree traversal. Each state of the menu will be store at one node of the tree.
If you don't have any knowledge about Suffix tree/trie, read here. The idea is simple, as there is a limited option for user to input: 0,1,2,3,4,5,6...9,#,*, we can represent all the states in one tree.
Each node of the tree will be something like:
Node{
Node [] next = new Node[12];//All options from 0 to *
Menu menu;
}
So instead of if and switch, we can easily locate and store all states by using and traversing through the tree from root to leaf. And to refer to all the states, we only need to store one variable: Node root
For example, if we have a series of action #111#. (Assume that # is at index 10 and * at index 11) We will always begin at root -> go to Node at index 10-> go to Node at index 1 ... and finally, return the menu.
Pseudo code:
Menu traverse(String input){
Node cur = root;
for(int index in input){
cur = cur.next[index];
}
return cur.menu;
}
For deleting a state/branch, just traverse from root to that leaf, and replace that leaf node in the parent next array with a null.
Note: You can manage list of menu even more easily if you store all menus in an array Menu[]menu, and for each node, instead of return a Menu, you will return an integer indicates the index of desired menu in the array menu.

You should try to use your DB queries to maintain the state of the application, using the DB queries will help you track the users' progress and also graduate them to the upper levels and demote them to lower levels, that way you can make your life easier in maintaining your code. Take a look at a code snippet below to help you understand session based USSD progress using AfricasTalking platform.
if($userResponse==""){
// this is when you receive nothing from the user. serve them the registration menu
switch ($level) {
case 0:
//. Graduate the user to the next level, so you dont serve them the same menu
$sql1 = "INSERT INTO `some_seesion_name`(`session_id`, `yourIdentifier`,`level`) VALUES('".$sessionId."','".$yourIdentifier."', 1)";
$db->query($sql1);
// Insert the Identifier, since it comes with the first POST
$sql1a = "INSERT INTO YourDB(`yourIdentifier`) VALUES ('".$yourIdentifier."')";
$db->query($sql1a);
// Serve the menu request for name
$response = "CON Please enter your name";
// Print the response onto the page so that our gateway can read it
header('Content-type: text/plain');
echo $response;
break;
// continue the flow of the app ......

Related

How to set & lock SiteTree in Silverstripe 4?

We're working on a project where Silverstripe acts as headless CMS with a single API data point.
The content is all from non-SiteTree dataobjects & the API data point is the only SiteTree record we need.
What is the best way of making sure the API data point exists & it is the only SiteTree record on each /dev/build. We could then disable PagesAdmin in LeftAndMain so users couldn't edit/ break it.
I've found code here which would look a bit like this for our case:
$api = new API/AccessPoint();
$api->Title = 'API';
$api->URLSegment = 'home';
$api->ShowInMenus = '1';
$api->Version = '1';
$api->ParentID = '0';
$api->write();
$api->doRestoreToStage();
But I'm not sure of the correct approach for making sure that a dev\build removes all the other pages & creates 1 record of this page.
Any help would be appreciated.
You can use requireDefaultRecords() in your DataObject to create records on a dev/build. You'll need to do some kind of check before creating a new one though, e.g.
public function requireDefaultRecords()
{
parent::requireDefaultRecords();
if (AccessPoint::get()->filter('URLSegment', 'home')->exists()) {
return;
}
// create your new object here
}

PHP Handling complex requests through single entry point app

I have been developing a Sports Management website with user, roster, and schedule management using PHP and JS. This app has about 15 database tables to work with altogether. Recently I learned I was doing something majorly wrong: I was using individual PHP scripts as their own "apps".
Example: Individual scripts app
JS
var params = {
action : 'addGame',
date : 'someday',
hometeam : 1,
awayteam : 2
}
$.post('/php/scheduler.php', params);
PHP
class Scheduler extends TableManager{
public function addGame($date, $hometeam, $awayteam){
// do sql stuff
}
public function doAction($request){
switch($request){
case "addGame":
return $this->addGame($_REQUEST['date'], $_REQUEST['hometeam'], $_REQUEST['awayteam'];
break;
}
}
}
if(isset($_REQUEST['action'])){
$scheduler = new Scheduler();
$scheduler->doAction($_REQUEST['action']);
}
I learned that writing a proper PHP application means there should be a single "app" where all requests are routed. This is fine and I've done this - but now I face unknown territory. If I have 15 tables, and they all have a few very specific functions (they all extend TableManager to provide basic table functions, but some are obviously more specific) how can I write a request processor in my main App that handles requests for every table, without getting insanely complex?
Example: Single entry point app
JS
var params = {
action : 'addGame',
table : 'scheduler',
date : 'someday',
hometeam : 1,
awayteam : 2
}
$.post('/php/app.php', params);
PHP
class App {
private $scheduleTable;
public function createTable($name){
switch($name){
case "scheduler":
$this->scheduleTable = new Scheduler();
break;
}
}
public doAction($request){
switch($request){
case "addGame":
$this->createTable('scheduler');
return $this->scheduleTable->addGame($_REQUEST['date'], $_REQUEST['hometeam'], $_REQUEST['awayteam'];
break;
}
}
}
if(isset($_REQUEST['action'])){
$app = new App();
$app ->doAction($_REQUEST['action']);
}
Now I need to start using complex and bloated switch statements, as my tables all inherit TableManager and thus do many of the same actions, but also have unique functions. I'll need to switch on the incoming action, and then switch on the table. Not to mention all of the other features that this App will have (user system, for one). Is the only answer to write a massive switch statement?
From what I understand of your current predicament, yes. You could always use a PHP framework (Google them, there are many) but if you want to use your existing structure then of course you need a switch statement or a way to load the correct models for the page.
Note that you could always use something like this:
<?php
// I assume $request is sanitized.
// Check to see if the class file exists.
if( file_exists( 'class-directory/' . $request . '.inc.php' ) ) {
$class_name = ucfirst( $request );
$this->scheduleTable = new $class_name;
} else {
// Show error page, the request is not valid.
}
?>
Important note: for more security you should use $_POST, $_GET and $_COOKIE instead of $_REQUEST.

Maintain item maintenance for a items in a class created by developer using subclass per item

If anyone has an idea, I couldn't think of a better way to phase the question.
I'll try to not make this to complicated an explination.
I'm writing a "quotes" class that is the main class. This class has "overall" functions that preform calculations based on "items" stored in its array. Suffice it to say, the end-developer will call it as $q = new apiQuote/quote().
Then, before it's of any use, the first item must be added and it's properties set so it can do it's "own" calculations. Something like $q->createItem(). Once the item is created with this call, an "item" of the class "item" is added to an array in "quotes" named "items".
The currently editable item, $q->item is always the last one added to the array via the createItem method. Which looks like:
public function createNewItem() {
$this->items[] = new item();
$this->item = $this->items[count($this->items)-1];
}
I added setItem method, whereby the parameter would be an integer representing item index in the array and would set $q->item to the item index chosen. This works, but still seems "not as productive" as I'd like".
What I'm curious about, is if anyone has any suggestions on a better way to go about this. I tried looking for a "cards/deck" php example, but all I could find was array shuffles, which is kinda useless here. I know how to do such associations in .NET and thought this would be just as easy, but I don't have the same property abilities in PHP that I have in a .NET language, thus negating what I'm used to in created this kind of "class/subclass[items]" type structure.
Really I would just like to know if anyone has done anything similar and if I'm doing things to the "best of ability" or if there might be a better way, via PHP, to allow an "end-developer" to call on one main class and create a list "items" based on a subclass that can later be used for methods of the main class?
I really hope this sums it all up well and I havn't gone outside the guidelines of "question asking" here, but I can't think of a better place, other than maybe Code Review to pose such a question and get great developer feed back. If y'all feel I need move it to Code Review, let me know. My main reason for choosing this over CR is this site tends to get faster responses.
Perhaps a view of what I have and what I "might" like to see:
Way it works now
$q = new apiQuote\quote(TRUE);
$q->createNewItem();
$q->item->totalHeight = 100;
$q->item->totalWidth = 250;
...
$q->createNewItem();
$q->item->totalHeight = 300;
$q->item->set_coverage('25%');
...
$q->setItem(1);
$q->item->totalHeight = 250;
...
$q->getTotalsCurv(); // an array to create a graph curve of totals from each item
What I "think" I might like:
$q = new apiQuote\quote(TRUE);
$q->items[] = new apiQuote\item();
$q->items[0]->totalHeight = 100;
$q->items[0]->totalWidth = 250;
...
$q->items[] = new apiQuote\item();
$q->items[1]->totalHeight = 300;
$q->items[1]->set_coverage('25%');
...
$q->items[0]->totalHeight = 250;
...
$q->getTotalsCurv();
However, something like the second idea mean leaving the "items" array public, which could lead to a vast amount of "other" problems as I'm trying to set this whole thing up to be "near dummy proof". Thus the usage of protected variables with specific get/set methods as if they where C# properties.
I think your problem is how to identify an "item" outside of the quote instance. And by using its array index you feel you are going to run into the problems. And you will, when you will try to delete an item. It would successfully invalidate any index already known/stored outside. The simplest patch to it is to give every item a unique ID and store them in the map instead of storing it as a vector.
Also, in your solution item by itself cannot provide you with any helpful information how to access this item in your collection of items (a quote).
public function createNewItem() {
static $counter;
$id = $counter++;
return $this->item = $this->items[$id] = new item($id);
}
public function editItem($id) {
return $this->item = $this->items[$id];
}
public function removeItem($id) {
$this->item = null;
unset($this->item[$id]);
}
Alternatively I recommend you not to reinvent the wheel and take a look here:
http://php.net/manual/en/spl.datastructures.php
and here in specific
http://www.php.net/manual/en/class.splobjectstorage.php
Anything that implements Iterator interface can be iterated with foreach
http://www.php.net/manual/en/class.iterator.php
Ah well you could return the new item from the createItem function.
An equivalent of your .NET example would be to simply return the new item
public function createNewItem() {
$this->items[] = new item();
$this->item = $this->items[count($this->items)-1];
return $this->items[count($this->items)-1];
}
then you could do
$item = $q->createNewItem();
$item->totalHeight = 100;
...
And as you are already adding the new item to the array from within createNewItem so no need for something like $q->add($item);
And to get at any other item you could do a
function getItem($index){
return $this->items[count($this->items)-1];
}
$otheritem = $q->getItem(3);
$otheritem->totalHeight = 100;

Site Design: How to award users tasks with achievements?

So I'm wanting to setup an achievements system on my site. People perform tasks and upload this information which is then stored in a database (think 'time', 'date', 'task', etc.). What would be the best method of checking their information and awarding achievements? Would I just have like an achievement.php that once information is uploaded it would trigger this document to run through all the checks to determine if the user needs to be awarded an achievement? Or is there something server side I should set up to award the user?
Thanks for any help or suggestions, comments, etc. :D
EDIT: I currently have the achievements listed in the database, (id, name, class)
Tasks are stored as ('date_time','time','device','user_id[fk]')
EDIT 2: Also many achievements will be calculated based on not only the tasks the user is currently submitting but takes into account previous tasks in addition to the newly added task. EX: If the user has completed 3 tasks within 3 consecutive days, then they will be awarded for it
Your best bet is probably to create a table of point values for the tasks, and then create a stored procedure that can fetch the appropriate counts from the appropriate tables and multiply them by the point values. That's what I've done in the past - it allows you modify point values on the fly from the DB as well.
it really depends on where your preference for business logic placement lies, and how real time you want acheivements to be. if you're looking to offload a bunch of business logic on you sql server, put it in a stored procedure, otherwise, class out the calculations into a class in php, and use that class to determine what new achievements have been.
i would definitely suggest doing the processing outside of the normal page response. perhaps kick off a server-side call to the php cli, or set up a cron job to run all individuals through a check for achievements at a certain interval.
edit:
as for the actual methods of awarding achievements, i would think you're most flexible and simple implementation (you will find more simple/less flexible and more flexible/less simple options i'm sure) would be to create an AwardRunner class, an IAward interface and a bunch of individual implementations of IAward for each award you have. the basic idea would be something like:
<?php
class AwardRunner {
var $UserId = 0;
function AwardRunner($userId) {
$this->UserId = $userId;
$dir = "/path/to/your/folder/full/of/IAwards/";
$includes = read_dir($dir);
//include all files that exist
foreach($includes as $include)
{
if (is_file($include))
{
require($include);
}
}
}
public function Run() {
$classList = get_declared_classes();
foreach($classList as $key => $className)
{
if (in_array('IAward', class_implements($className))) {
$award = $className();
$award->UserId = $this->UserId;
$award->GrantIfUserQualifies();
}
}
}
//function for reading all files in a directory.
//this is recursive, so any files in subfolders will also make it in
function read_dir($dir)
{
$array = array();
$d = dir($dir);
while (false !== ($entry = $d->read())) {
if($entry!='.' && $entry!='..') {
$entry = $dir.'/'.$entry;
if(is_dir($entry)) {
$array = array_merge($array, read_dir($entry));
} else {
$array[] = $entry;
}
}
}
$d->close();
return $array;
}
}
?>
i would think the idea of what the IAward interface would look like would be pretty clear from the usage, though you'd probably add to it the Id field from your table so it would be able to insert itself into the database, as would the way to call the AwardRunner class.
this idea should work whether you have something batching the awards process looping through all your users, or just fire it off after every task submission.
How about you create a trigger on the task submission proc (or however you insert the data when the user completes a task), that then performs the necessary actions for that user to determine if he/she is awarded an achievement, and then updates the achievements table accordingly.
Then, every-time you load up the information for the user on the front end, the data will already be in for him/her in the achievements table, and you can directly access it (which I'm sure you already do).

Reduce database calls for php web shop

I'm looking for a way to prevent repeated calls to the database if the item in question has already been loaded previously. The reason is that we have a lot of different areas that show popular items, latest releases, top rated etc. and sometimes it happens that one item appears in multiple lists on the same page.
I wonder if it's possible to save the object instance in a static array associated with the class and then check if the data is actually in there yet, but then how do I point the new instance to the existing one?
Here's a draft of my idea:
$baseball = new Item($idOfTheBaseballItem);
$baseballAgain = new Item($idOfTheBaseballItem);
class Item
{
static $arrItems = array();
function __construct($id) {
if(in_array($id, self::arrItems)){
// Point this instance to the object in self::arrItems[$id]
// But how?
}
else {
// Call the database
self::arrItems[id] = $this;
}
}
}
If you have any other ideas or you just think I'm totally nuts, let me know.
You should know that static variables only exist in the page they were created, meaning 2 users that load the same page and get served the same script still exist as 2 different memory spaces.
You should consider caching results, take a look at code igniter database caching
What you are trying to achieve is similar to a singleton factory
$baseball = getItem($idOfTheBaseballItem);
$baseballAgain =getItem($idOfTheBaseballItem);
function getItem($id){
static $items=array();
if(!isset($items[$id])$items[$id]=new Item($id);
return $items[$id];
}
class Item{
// this stays the same
}
P.S. Also take a look at memcache. A very simple way to remove database load is to create a /cache/ directory and save database results there for a few minutes or until you deem the data old (this can be done in a number of ways, but most approaches are time based)
You can't directly replace "this" in constructor. Instead, prepare a static function like "getById($id)" that returns object from list.
And as stated above: this will work only per page load.

Categories