Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to use a string variable as the entire condition in an if block (without using eval() )?

Tags:

php

As background, I'm writing php code that parses csv files and does something with each row of each csv file. What that "something" is depends on the values in the row. It's easy to test the values with an "if" structure, however, hardcoding the conditions is not optimal for two reasons:

  1. there are several hundred possible conditions to be tested. That's just to begin with. More conditions will be added in the future.

  2. Each csv row does not need to be tested for each condition; as soon as a condition for a row evaluates to true, no further conditions need be evaluated.

Ideally, for my situation, the "if" conditions would be stored in a postgres table, put into a string variable one by one, and then each variable would tested by a single if structure (inside a loop of some kind) until a condition evaluates to true.

Simplified example:

$arrayOne[3] = "foo";

// in practice, the value of this variable would not be hard-coded;
// it would come from a postgres table
$conditionString="\$arrayOne[3] == \"VANILLA\"";

if($conditionString) {
    // do something, then exit the loop this if statement would be
    // inside of in actual practice
}

This question was essentially asked at

PHP - if condition inside string

There were three basic answers:

  1. You could use eval(), but DON'T! It's evil! (agreed)
  2. Hard-code all the conditions (not optimal for reasons provide above and others)
  3. Store the conditions in a schema (which is what I'm doing) and then parse and convert to php code.

Solution 3 is generally what I'm looking for, but second part seems inefficient and unnecessarily complex. After all, why build up php code from numerous strings (of an unknown number, by the way, which complicates storage in postgres) when it seems so much easier to just store and then evaluate the single string you need?

Is there a way to do that?

Many thanks for the responses so far. ComFreek, thanks to you in particular for the detailed response. The solution you suggest may be just what I need, but frankly, I don't have the experience to know immediately if that's the case. I'll definitely spend time trying to understand what you're saying. Hopefully, it will solve my problem.

In case it doesn't, and in the meantime, to answer a few questions others asked:

1) the if conditions will not usually be simple. Many will contain multiple and compound AND and OR tests. A sample condition, in pseudo-code, might be: ( field2=="BUY" AND ( strpos("REINVEST DIVIDEND", field6) OR strpos("CASH MERGER, field6) ) AND field2!="TTXY" AND field3>0 ).

2) The CSV files come from numerous financial institutions. They contain much the same information, but each has unique data, and all of them have the data in different locations. What's more they express the data differently. In some, a payment is indicated by a negative number; in others, by a positive number. Some have separate fields for deposits and withdrawals; some indicate deposits and withdrawals with a code in another column. And so on. The code needs to determine the nature of the transaction (credit card purchase, check, stock buy or sell, retirement contribution, and on and on) and then, if it can, assign the correct debit/credit account numbers (from a chart of accounts) to that transaction. Altogether, there are hundreds of possible conditions, likely to become thousands. (In case anyone is wondering, the code can determine the institution a particular csv file comes from and will test the transactions in that file only against conditions relevant to that institution.)

3) The code needs to be flexible enough to easily (in other words, without having to write new code) allow for new tests to be added in the future. For me, being able to add a new condition to a postgres table (which will then be another test for the code to check) is sufficient flexibility.

To try to answer Phil's questions and comments (which I may not be understanding correctly):

1) I know what preg_match is, but haven't really explored what it can do, so it may in fact be the answer to my problem. I'll check it out.

2) currently, the code does not group transactions (which is to say a single row from a single csv file); rather, it looks at each row, determines what it is, then stores it an additional data in appropriate postgres tables, then moves to the next row. There are certain "types" of transactions (say credit card purchases) but they're never grouped for further processing.

3) Each transaction should satisfy one and only condition (though that condition may be complex).

4) With regard to matching the entire string, unless I'm missing something (very possible), it's not that simple. For example, let's say a given transaction is a stock purchase. The code can determine that by seeing that "action" field contains the word "Buy" AND the "quantity" field is greater than zero (one or the other of these conditions alone might not be enough to be certain the transaction is a stock purchase), but the "ticker" field could be any of thousands of strings that aren't known in advance--"GOOG", "MSFT", "KO" or whatever.

Again, thanks to all for the responses so far.

like image 577
user3183198 Avatar asked Jan 10 '14 20:01

user3183198


People also ask

What does eval () do in Python?

Python's eval() allows you to evaluate arbitrary Python expressions from a string-based or compiled-code-based input. This function can be handy when you're trying to dynamically evaluate Python expressions from any input that comes as a string or a compiled code object.

What happens when a condition inside of the conditional block is met?

When met (which means when the condition evaluates to true ) the code specified inside the conditional block executes. Otherwise, it is ignored and not executed.

Can we write if condition in else block?

if else statements will execute a block of code when the condition in the if statement is truthy . If the condition is falsy , then the else block will be executed. There will be times where you want to test multiple conditions and you can use an if...else if...else statement.

What happens when the condition in an IF block is true?

If the condition is true, the if block code is executed and if the condition is false, the else block code is executed.


1 Answers

Summary: build an extensible system of handlers for specific comparision types, and store related data in the database.

You need:

  • Well-known types of conditions
  • An extensible system of registerable handlers which deal with specific types of conditions (e.g. EqualityHandler, StringLengthComparisionHandler)
  • Each handler is associated with a documented object format

Advantages:

  • The system is highly extensible. If you ever need comparison type X or Y, just write a handler. This is really comparable with a plugin system of a browser or editor.

  • You don't store code in the database. Storing code for equal types of comparison is totally against the DRY principle (Don't repeat yourself).

  • Unit tests. I cannot imagine how unit tests would work when you have a database containing those codes. They would be really painful.

Disadvantages:

  • It requires you to write some code before you can actually start evaluating your data.

    However, this type of problem is virtually crying for an OOP solution! It really teaches you how OOP can be applied and used. At least in my opinion, it is fun to see how only adding one handler gives your application whole new functionalities!

Pseudo code:

class EqualityHandler implements Handler
  public function handle($handlerData, $data) {
    // checks for equality and returns true or false
    return true;
  }
}
// TODO Act like Java: EqualityHandler.class (pass type of class)
$app->registerHandler('EqualityHandler');

// loop all rows
foreach ($row as $csvFields) {

  foreach  (retrieveConditions($row) as $condition) {
    handleCondition($condition, $csvFields);
  }

}

function handleCondition($condition, $csvFields) {
  if ($app->getHandler($condition['type'])) {
    return $app->instantiateHandler($condition['type'])->handle($condition, $csvFields);
  }
  else {
    throw new HandlerNotFoundException('...');
  }
}
like image 179
ComFreek Avatar answered Oct 13 '22 10:10

ComFreek