I have a package called "SamplePkg". I have another script that uses SamplePkg, creates an object and calls a method.
package SamplePkg;
use strict;
use DBI;
use Try::Tiny;
my $dbh = DBI_>connect(..., { RaiseError => 1 });
sub new {
my $self = {};
$self->{CODE} = 0;
bless($self);
return $self;
}
sub do_something {
my $self = shift;
try {
my $query = "select myvalue from mytable";
$sth = $dbh->prepare($query);
$sth->execute();
} catch { $self->{CODE} = 100; return; }
$self->{CODE} = 50;
}
The other script
use SamplePkg;
my $object = SamplePkg->new();
$object->do_something();
print "Code is: $object->{CODE}\n";
Questions:
For some reason, the try block doesn't catch the DB error (myvalue is not a valid column name)
The "return" in the catch block does not return to the calling script
The output gives the error code as 50
try { ... } catch { ... };
is really
try(sub { ... }, catch(sub { ... }));
Returning from the sub that's called when an exception is caught returns from that sub, not from the sub in which try is located.
You could use
try {
my $query = "select myvalue from mytable";
$sth = $dbh->prepare($query);
$sth->execute();
$self->{CODE} = 50;
} catch {
$self->{CODE} = 100;
};
Or maybe you need something more like
my $success = try {
my $query = "select myvalue from mytable";
$sth = $dbh->prepare($query);
$sth->execute();
return 1;
} catch {
return 0;
};
... do stuff ...
$self->{CODE} = $succes ? 50 : 100;
Related
Say I have a tiny object that has a reference to a huge object:
package Tiny;
sub new {
my ($class, $tiny, $large) = #_;
return bless { tiny => $tiny, large => $large };
}
I'd like to create a STORABLE_freeze/STORABLE_thaw pair that lets me (recursively) clone $tiny but maintain/keep the reference to $large as-is without cloning $large too.
I tried temporarily deleting $self->{large} (see below), and putting it in a hash with a Scalar::Util::refaddr key and a weak reference to $large, serializing the rest of $self, and then putting the (weak) reference back into both the original object immediately and the cloned one in STORABLE_thaw, but it is a mess, and on every clone, the weak ref value gets deleted when it goes out of scope, but the key remains in the hash forever leaking memory and I need a global class member hash (%largeWeakRefs) to hold the temporary $large reference. Has a smell.
How it that possible to do this in a cleaner way?
Here is my solution using the hash to hold the large ref temporarily:
package Tiny;
use Scalar::Util qw(refaddr weaken);
sub new {
my ( $class, $tiny, $large ) = #_;
return bless { tiny => $tiny, large => $large }, $class;
}
# Ugly temporary storage to hold $large refs from _freeze to _thaw...
my %largeWeakRefs;
sub STORABLE_freeze {
my ( $self, $cloning ) = #_;
my $large = delete local $self->{large};
my $refaddr = refaddr $large;
$largeWeakRefs{$refaddr} = $large;
weaken $largeWeakRefs{$refaddr};
my %restOfSelf = %$self;
$self->{large} = $large;
return $refaddr, \%restOfSelf;
}
sub STORABLE_thaw {
my ($self, $cloning, $refaddr, $restOfSelf) = #_;
%$self = %$restOfSelf;
$self->{large} = $largeWeakRefs{$refaddr};
return $self;
}
(Yes I know, my example only handles cloning, not straight-up freeze and thaw)
You could add reference counts.
my %larges;
sub STORABLE_freeze {
my ( $self, $cloning ) = #_;
if ($cloning) {
my $large_key = pack('j', refaddr(self->{large}));
$larges{$large_key} //= [ $self->{large}, 0 ];
++$larges{$large_key}[1];
return ( $large_key, $self->{tiny} );
} else {
return ( "", $self->{tiny}, $self->{large} );
}
}
sub STORABLE_thaw {
my ( $self, $cloning, $serialized ) = splice(#_, 0, 3);
if ($cloning) {
my $large_key = $serialized;
$self->{ tiny } = shift;
$self->{ large } = $larges{$large_key}[0];
--$larges{$large_key}[1]
or delete($larges{$large_key});
} else {
$self->{ tiny } = shift;
$self->{ large } = shift;
}
}
Untested.
If the cloning process dies, you'll have a memory leak.
Alternatively, you could avoid the need for external resources as follows:
use Inline C => <<'__EOS__';
IV get_numeric_ref(SV *sv) {
SvGETMAGIC(sv);
if (!SvROK(sv))
croak("Argument not a reference");
sv = MUTABLE_SV(SvRV(sv));
SvREFCNT_inc(sv);
return PTR2IV(sv); /* Despite its name, can be used to convert pointer to IV */
}
SV* get_perl_ref_from_numeric_ref(IV iv) {
SV* sv = PTR2IV(iv);
return newRV_noinc(sv);
}
__EOS__
sub STORABLE_freeze {
my ( $self, $cloning ) = #_;
if ($cloning) {
return ( pack('j', get_numeric_ref($self->{large})), $self->{tiny} );
} else {
return ( "", $self->{tiny}, $self->{large} );
}
}
sub STORABLE_thaw {
my ( $self, $cloning, $serialized ) = splice(#_, 0, 3);
if ($cloning) {
$self->{ tiny } = shift;
$self->{ large } = get_perl_ref_from_numeric_ref(unpack('j', $serialized));
} else {
$self->{ tiny } = shift;
$self->{ large } = shift;
}
}
Didn't test STORABLE_freeze and STORABLE_thaw, but tested the C/XS code using the following:
use strict;
use warnings;
use feature qw( say state );
use Cpanel::JSON::XS qw( );
sub _dump {
state $encoder = Cpanel::JSON::XS->new->canonical->allow_nonref;
return $encoder->encode($_[0]);
}
{
my %h = ( a => 4, b => 5 );
say _dump(\%h); # {"a":4,"b":5}
say sprintf "0x%x", \%h; # 0x32cdbf8
say Internals::SvREFCNT(%h); # 1
my $i = get_numeric_ref(\%h);
say sprintf "0x%x", $i; # 0x32cdbf8
say Internals::SvREFCNT(%h); # 2
my $ref = get_perl_ref_from_numeric_ref($i);
say sprintf "0x%x", $ref; # 0x32cdbf8
say Internals::SvREFCNT(%h); # 2
say _dump($ref); # {"a":4,"b":5}
}
If the cloning process dies, you'll have a memory leak. I suppose it would be safe to rely on "large" not going anywhere during the cloning process, so you could remove the SvREFCNT_inc and change newRV_noinc to newRV to avoid the potential memory leak.
To avoid the possible memory leak, never store "large" in the object.
my %larges;
sub new {
my $class = shift;
my $self = bless({}, $class);
return $self->_init(#_);
}
sub _init {
my ($self, $tiny, $large) = #_;
$self->{ tiny } = $tiny;
{
my $large_key = pack('j', refaddr($self));
$self->{ large_key } = $large_key;
$larges{ $large_key } = $large;
}
return $self;
}
sub DESTROY {
my ($self) = #_;
if (defined( my $large_key = $self->{ large_key } )) {
delete( $larges{ $large_key } );
}
}
sub STORABLE_freeze {
my ( $self, $cloning ) = #_;
if ($cloning) {
return ( $self->{large_key}, $self->{tiny} );
} else {
return ( "", $self->{tiny}, $larges{ $self->{large_key} } );
}
}
sub STORABLE_thaw {
my ( $self, $cloning, $serialized ) = splice(#_, 0, 3);
if ($cloning) {
my ($tiny) = #_;
my $large_key = $serialized;
$self->_init($tiny, $larges{ $large_key });
} else {
$self->_init(#_);
}
}
Untested.
No memory leaks if the cloning process dies.
I'm writing a Perl module Result to manage HTTP errors.
I am a little lost to generate the function managing all this.
Result.pm
package Result;
use strict;
use warnings;
use Data::Dumper;
use Log::Log4perl;
sub new
{
my $class = shift;
my %params = #_;
my $self = {
code => $params{'code'},
value => $params{'value'}
};
bless $self, $class;
return $self;
}
sub generateError
{
my $self = shift;
my %params = #_;
if($self->{'code'} == 200)
{
return "Your request was well executed !";
}
elsif($self->{'code'} == 400)
{
return "There is an error in your request! Please verify it!";
}
elsif($self->{'code'} == 404)
{
return "We did not find for what you ask !"
}
elsif($self->{'code'} == 500)
{
return "Internal error of the server, please re-try later !";
}
elsif($self->{'code'} == 504)
{
return "Your request has set of time to be executed, please retry !";
}
}
1;
App.pm
sub template
{
my $self = shift;
my $query = $self->query;
my $id = $query->param('id');
my $session = $self->param('session');
my $profile = $session->param('profile');
my $Project = Project->newFromId($id);
if(!$Project or $Project eq 'NOT_FOUND')
{
return $self->redirect('?rm=notfound');
}
if(!$profile->{'uid'} or $Project->{'userId'} != $profile->{'uid'})
{
return $self->redirect('?rm=notfound');
}
my $mailContent = from_json($Project->{'mail'});
my $templateContent = formatTemplate($Project->{'template'});
my $infos = [
{
'ID' => $id,
'TEMPLATE' => $templateContent,
'MAILSUBJECT' => $mailContent->{'subject'},
'MAILBODY' => $mailContent->{'body'}
}
];
if($mailContent->{'type'} eq 'text')
{
$infos->[0]{'MAILTEXT'} = 1;
}
else
{
$infos->[0]{'MAILHTML'} = 1;
}
return $self->processtmpl('template.tmpl', $infos);
}
1;
I am not very experienced in this field so this is why I am seeking help.
I recently changed hosts and a script stopped working. After moving all files the new hosts said mysql is deprecated, so after doing a bit of search I decided to change mysql with mysqli and that deprecated error was gone. But now I get a new error (error select db). I think I must make adidtional changes to the script to connect mysqli. This is the file I have
<?php
class Model{
var $conn;
public function openDb($dbhost, $dbuser, $dbpass, $dbname, $conn)
{
//echo "Se creo la conexion ";
$conn = mysqli_connect($dbhost, $dbuser, $dbpass) or die('Error connecting to mysqli');
mysqli_select_db($dbname) or die('Error select db');
mysqli_query("SET NAMES utf8");
return $conn;
}
public function closeDb($conn)
{
mysqli_close($conn);
}
public function query($query)
{
if ($result = mysqli_query($query) or die("Error de Query: </br >" . mysqli_error()."<br/>".$query)) {
//if ($result = mysqli_query($query)) {
} else {
$result = false;
}
return $result;
}
function __construct()
{
$this->openDb(dbhost, dbuser, dbpass, dbname, $conn);
}
//insertGenerico con indedices asiciativos
function insertar($tabla, $datos)
{
$columnas = implode(", ", array_keys($datos));
$valores = implode(", ", $datos);
$query = "INSERT INTO $tabla
($columnas)
VALUES
(" . $valores . ")";
return $this -> query($query);
}
function insertarRelacionArray($tabla, $tablaRelacion, $datos)
{
foreach ($datos as $row) {
$query = "INSERT INTO $tabla
($tablaRelacion[0],$tablaRelacion[2])
VALUES
($tablaRelacion[1],$row)";
//echo '<br>'.$query;
$this -> query($query);
}
}
//getGenerico
function get($tabla, $where = false, $order = false)
{
$query = "SELECT *
FROM $tabla
$where
$order";
return $this -> query($query);
}
//deleteGenerico
function delete($tabla, $id, $idTag = false)
{
if($idTag==false)
$idTag = "id";
$query = "DELETE FROM $tabla
WHERE $idTag = $id";
return $this -> query($query);
}
//update generico
function update($tabla, $datos, $id, $idTag = false)
{
$columnas = array_keys($datos);
$SET = 'SET ';
$i = 0;
foreach ($datos as $key => $value) {
if (next($datos)) {
$SET .= "$key = $value ,";
} else {
$SET .= "$key = $value ";
}
}
if($idTag==false)
$idTag = "id";
$query = "UPDATE $tabla $SET WHERE $idTag = $id;";
return $this -> query($query);
}
}
?>
As described on http://php.net/manual/en/mysqli.select-db.php first parameter should be $link.
bool mysqli_select_db ( mysqli $link , string $dbname )
So change
mysqli_select_db($dbname) or die('Error select db');
to
mysqli_select_db($conn, $dbname) or die('Error select db');
and it should work.
I think that you need to return $conn in your _construct function too, or store it in the object ($this->conn = $conn) so you can use it again.
In addition (thanks to Ki Jéy) you should also update
mysql_query($query)
to
mysqli_query($conn, $query)
and
mysql_real_escape_string($string)
to
mysqli_real_escape_string ($conn, $string)
class Gene
package Gene;
use strict;
use Carp;
use vars qw($AUTOLOAD);
my %_ok_gene_attr = (
"id" => "string",
"name" => "string",
"chrom" => "string", # chromosome or seq id
"txtStart" => "int", # 1-based
"txtEnd" => "int" # 1-based
);
sub new {
my ($class, %arg) = #_;
my $self = {};
$self->{_id} = $arg{id} || croak "no id";
$self->{_name} = $arg{name} || croak "no db";
$self->{_chrom} = $arg{chrom} || croak "no seq_id";
$self->{_strand} = $arg{strand} || -1;
$self->{_txStart} = $arg{txStart} || -1;
$self->{_txEnd} = $arg{txEnd} || -1;
$self->{_cdsStart} = $arg{cdsStart} || -1;
$self->{_cdsEnd} = $arg{cdsEnd} || -1;
$self->{_exonCount} = $arg{exonCount} || -1;
$self->{_exonStarts} = $arg{exonStarts} || -1;
$self->{_exonEnds} = $arg{exonEnds} || -1;
$self->{_score} = $arg{score} || -1;
$self->{_name2} = $arg{name2} || -1;
$self->{_cdsStartStat} = $arg{cdsStartStat} || -1;
$self->{_cdsEndStat} = $arg{cdsEndStat} || -1;
$self->{_exonFrames} = $arg{exonFrames} || -1;
bless($self, $class);
}
sub AUTOLOAD {
my ($self, $newvalue) = #_;
my ($operation, $attribute) = ($AUTOLOAD =~ /(get)(_\w+)$/);
unless ($operation && $attribute) {
croak "Method name $AUTOLOAD is not in the recognized form get_attribute\n";
}
unless (defined $_ok_gene_attr{$attribute}) {
croak "No such attribute '$attribute' exists in the class ", ref($self);
}
if ($operation eq 'get') {
*{$AUTOLOAD} = sub { shift->{$attribute} };
} elsif($operation eq 'set') {
*{$AUTOLOAD} = sub { shift->{$attribute} = shift };
$self->{$attribute} = $newvalue;
}
return $self->{$attribute};
}
sub DESTROY {
my($self) = #_;
$self->_decr_count( );
}
I use this class in
open my $in, '<', q/C:\Users\Jesse\Desktop\refGene.txt/ or die "Cannot open file : $!";
while(<$in>) {
chomp $_;
my #temp= split(/\t/, $_);
my $i=0;
my $temp1= Gene->new("id" => $temp[0],"name"=>$temp[1],"chrom"=>$temp[2], "strand"=>$temp[3],"txStart"=>$temp[4], "txEnd"=>$temp[5],"cdsStart"=>$temp[6],"cdsEnd"=>$temp[7],"exonCount"=>$temp[8],"exonStarts"=>$temp[9],"exonEnds"=>$temp[10],"score"=>$temp[11],"name2"=>$temp[12],"cdsStartStat"=>$temp[13],"cdsEndStat"=>$temp[14],"exonFrames"=>$temp[15]);
}
is has a error
(in cleanup) Method name Gene::_decr_count is not in the recognized form get_attribute
It also seems the autoload function doesn't work when i use $temp1->set_id("1234")?
Try this some kind of fixed version of Gene.pm:
package Gene;
use strict;
use Carp;
use vars qw($AUTOLOAD);
use Data::Dumper;
my %_ok_gene_attr = (
"_id" => "string",
"_name" => "string",
"_chrom" => "string", # chromosome or seq id
"_txtStart" => "int", # 1-based
"_txtEnd" => "int" # 1-based
);
sub new {
my ($class, %arg) = #_;
my $self = {};
$self->{_id} = $arg{id} || croak "no id";
$self->{_name} = $arg{name} || croak "no db";
$self->{_chrom} = $arg{chrom} || croak "no seq_id";
$self->{_strand} = $arg{strand} || -1;
$self->{_txStart} = $arg{txStart} || -1;
$self->{_txEnd} = $arg{txEnd} || -1;
$self->{_cdsStart} = $arg{cdsStart} || -1;
$self->{_cdsEnd} = $arg{cdsEnd} || -1;
$self->{_exonCount} = $arg{exonCount} || -1;
$self->{_exonStarts} = $arg{exonStarts} || -1;
$self->{_exonEnds} = $arg{exonEnds} || -1;
$self->{_score} = $arg{score} || -1;
$self->{_name2} = $arg{name2} || -1;
$self->{_cdsStartStat} = $arg{cdsStartStat} || -1;
$self->{_cdsEndStat} = $arg{cdsEndStat} || -1;
$self->{_exonFrames} = $arg{exonFrames} || -1;
bless($self, $class);
}
sub AUTOLOAD {
my ($self, $newvalue) = #_;
my ($operation, $attribute) = ($AUTOLOAD =~ /(get|set)(_\w+)$/);
unless ($operation && $attribute) {
croak "Method name $AUTOLOAD is not in the recognized form set_{attribute} or get_{attribute}\n";
}
unless (defined $_ok_gene_attr{$attribute}) {
croak "No such attribute '$attribute' exists in the class ", ref($self);
}
if ($operation eq 'get') {
no strict 'refs';
*{$AUTOLOAD} = sub { shift->{$attribute} };
} elsif($operation eq 'set') {
no strict 'refs';
*{$AUTOLOAD} = sub { shift->{$attribute} = shift };
$self->{$attribute} = $newvalue;
}
return $self->{$attribute};
}
sub DESTROY {
my($self) = #_;
$self->_decr_count( );
}
Tested with the following script:
#!/usr/bin/perl
use strict;
use warnings;
use Gene;
my $t1 = Gene->new(id=>"id", name=>"name", chrom=>"chrom");
$t1->set_id("1234");
print $t1->get_id(), "\n";
Beside using Perl's builtin OO system, you may also want to take a look of some other OO system, such as Moose. See perlootut for further details.
Can anyone tell me why my expression is not used in the query below?
SELECT accountreset.* FROM accountreset WHERE (reset_id = '34') LIMIT 1
public function findByResetId($resetId, $model = null) {
$result = null;
if (isset($resetId)) {
$select = $this->getDao()->select(
array('expiration' => new Zend_Db_Expr('UNIX_TIMESTAMP(expiration)'))
);
$select->where('reset_id = ?', $resetId);
$row = $this->getDao()->fetchRow($select);
if (null != $row) {
if (!($model instanceof Stage5_Model_PasswordResetter)) {
$model = new Stage5_Model_PasswordResetter();
}
// vul het model object
$model->setResetId($row->reset_id);
$model->setUserId($row->user_id);
$model->setExpiration($row->expiration);
$result = $model;
}
}
return $result;
}
Your Zend_Db_Expr should go into from() method instead of select()
$select = $this->getDao()
->select()
->from(
$this->getDao()->info('name'),
array('expiration' => new Zend_Db_Expr('UNIX_TIMESTAMP(expiration)'))
);